Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Linus Torvalds [Fri, 22 Jul 2011 20:44:18 +0000 (13:44 -0700)]
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (78 commits)
  mmc: MAINTAINERS: add myself as a tmio-mmc maintainer
  mmc: print debug messages for runtime PM actions
  mmc: fix runtime PM with -ENOSYS suspend case
  mmc: at91_mci: move register header from include/ to drivers/
  mmc: mxs-mmc: fix clock rate setting
  mmc: tmio: fix a deadlock
  mmc: tmio: fix a recently introduced bug in DMA code
  mmc: sh_mmcif: maximize power saving
  mmc: tmio: maximize power saving
  mmc: tmio: fix recursive spinlock, don't schedule with interrupts disabled
  mmc: Added quirks for Ricoh 1180:e823 lower base clock frequency
  mmc: omap_hsmmc: fix oops in omap_hsmmc_dma_cb()
  mmc: omap_hsmmc: refactor duplicated code
  mmc: omap_hsmmc: fix a few bugs when setting the clock divisor
  mmc: omap_hsmmc: introduce start_clock and re-use stop_clock
  mmc: omap_hsmmc: split duplicate code to calc_divisor() function
  mmc: omap_hsmmc: move hardcoded frequency constants to defines
  mmc: omap_hsmmc: correct debug report error status mnemonics
  mmc: block: fixed NULL pointer dereference
  mmc: documentation of mmc non-blocking request usage and design.
  ...

168 files changed:
Documentation/filesystems/ubifs.txt
Documentation/x86/boot.txt
MAINTAINERS
Makefile
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/gpio.c
arch/arm/mach-davinci/irq.c
arch/sparc/include/asm/ptrace.h
arch/x86/Kconfig
arch/x86/kernel/reboot.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/intel_display.c
drivers/mtd/ubi/build.c
drivers/mtd/ubi/debug.c
drivers/mtd/ubi/debug.h
drivers/mtd/ubi/io.c
drivers/mtd/ubi/scan.c
drivers/mtd/ubi/ubi.h
drivers/mtd/ubi/vmt.c
drivers/mtd/ubi/vtbl.c
drivers/mtd/ubi/wl.c
fs/cifs/cifsfs.c
fs/cifs/file.c
fs/dcache.c
fs/dlm/ast.c
fs/dlm/ast.h
fs/dlm/config.c
fs/dlm/config.h
fs/dlm/dlm_internal.h
fs/dlm/lock.c
fs/dlm/lockspace.c
fs/dlm/lowcomms.c
fs/dlm/memory.c
fs/dlm/memory.h
fs/dlm/recoverd.c
fs/dlm/user.c
fs/fscache/page.c
fs/gfs2/bmap.c
fs/gfs2/dir.c
fs/gfs2/dir.h
fs/gfs2/file.c
fs/gfs2/glock.c
fs/gfs2/glock.h
fs/gfs2/glops.c
fs/gfs2/incore.h
fs/gfs2/main.c
fs/gfs2/ops_fstype.c
fs/gfs2/rgrp.c
fs/gfs2/rgrp.h
fs/gfs2/super.c
fs/hfsplus/brec.c
fs/hfsplus/catalog.c
fs/hfsplus/dir.c
fs/hfsplus/extents.c
fs/hfsplus/hfsplus_fs.h
fs/hfsplus/inode.c
fs/hfsplus/part_tbl.c
fs/hfsplus/super.c
fs/hfsplus/unicode.c
fs/hfsplus/wrapper.c
fs/libfs.c
fs/namei.c
fs/ubifs/commit.c
fs/ubifs/debug.c
fs/ubifs/debug.h
fs/ubifs/dir.c
fs/ubifs/file.c
fs/ubifs/io.c
fs/ubifs/log.c
fs/ubifs/lprops.c
fs/ubifs/lpt.c
fs/ubifs/lpt_commit.c
fs/ubifs/misc.h
fs/ubifs/orphan.c
fs/ubifs/recovery.c
fs/ubifs/replay.c
fs/ubifs/sb.c
fs/ubifs/scan.c
fs/ubifs/super.c
fs/ubifs/tnc.c
fs/ubifs/tnc_commit.c
fs/ubifs/ubifs.h
fs/xfs/Makefile
fs/xfs/linux-2.6/xfs_acl.c
fs/xfs/linux-2.6/xfs_aops.c
fs/xfs/linux-2.6/xfs_buf.c
fs/xfs/linux-2.6/xfs_buf.h
fs/xfs/linux-2.6/xfs_export.c
fs/xfs/linux-2.6/xfs_file.c
fs/xfs/linux-2.6/xfs_iops.c
fs/xfs/linux-2.6/xfs_linux.h
fs/xfs/linux-2.6/xfs_super.c
fs/xfs/linux-2.6/xfs_sync.c
fs/xfs/linux-2.6/xfs_sync.h
fs/xfs/linux-2.6/xfs_trace.h
fs/xfs/quota/xfs_dquot.c
fs/xfs/quota/xfs_dquot.h
fs/xfs/quota/xfs_qm.c
fs/xfs/quota/xfs_qm.h
fs/xfs/quota/xfs_qm_syscalls.c
fs/xfs/quota/xfs_trans_dquot.c
fs/xfs/xfs.h
fs/xfs/xfs_alloc.c
fs/xfs/xfs_alloc_btree.c
fs/xfs/xfs_arch.h [deleted file]
fs/xfs/xfs_attr.c
fs/xfs/xfs_attr_leaf.c
fs/xfs/xfs_bmap.c
fs/xfs/xfs_bmap_btree.c
fs/xfs/xfs_btree.c
fs/xfs/xfs_btree.h
fs/xfs/xfs_btree_trace.c [deleted file]
fs/xfs/xfs_btree_trace.h [deleted file]
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_da_btree.c
fs/xfs/xfs_da_btree.h
fs/xfs/xfs_dir2.c
fs/xfs/xfs_dir2.h
fs/xfs/xfs_dir2_block.c
fs/xfs/xfs_dir2_block.h [deleted file]
fs/xfs/xfs_dir2_data.c
fs/xfs/xfs_dir2_data.h [deleted file]
fs/xfs/xfs_dir2_format.h [new file with mode: 0644]
fs/xfs/xfs_dir2_leaf.c
fs/xfs/xfs_dir2_leaf.h [deleted file]
fs/xfs/xfs_dir2_node.c
fs/xfs/xfs_dir2_node.h [deleted file]
fs/xfs/xfs_dir2_priv.h [new file with mode: 0644]
fs/xfs/xfs_dir2_sf.c
fs/xfs/xfs_dir2_sf.h [deleted file]
fs/xfs/xfs_fs.h
fs/xfs/xfs_ialloc.c
fs/xfs/xfs_ialloc_btree.c
fs/xfs/xfs_iget.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_inum.h
fs/xfs/xfs_log.c
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans_ail.c
fs/xfs/xfs_trans_buf.c
fs/xfs/xfs_trans_inode.c
fs/xfs/xfs_trans_priv.h
fs/xfs/xfs_vnodeops.c
fs/xfs/xfs_vnodeops.h
include/linux/mtd/ubi.h
include/linux/sched.h
include/linux/slab.h
include/linux/slab_def.h
include/linux/slob_def.h
include/linux/slub_def.h
kernel/rcutree_plugin.h
kernel/sched.c
kernel/sched_fair.c
kernel/sched_features.h
kernel/signal.c
kernel/softirq.c
mm/slab.c
mm/slob.c
mm/slub.c
mm/vmscan.c
net/ceph/ceph_fs.c

index 8e4fab6..a0a61d2 100644 (file)
@@ -111,34 +111,6 @@ The following is an example of the kernel boot arguments to attach mtd0
 to UBI and mount volume "rootfs":
 ubi.mtd=0 root=ubi0:rootfs rootfstype=ubifs
 
-
-Module Parameters for Debugging
-===============================
-
-When UBIFS has been compiled with debugging enabled, there are 2 module
-parameters that are available to control aspects of testing and debugging.
-
-debug_chks     Selects extra checks that UBIFS can do while running:
-
-               Check                                   Flag value
-
-               General checks                          1
-               Check Tree Node Cache (TNC)             2
-               Check indexing tree size                4
-               Check orphan area                       8
-               Check old indexing tree                 16
-               Check LEB properties (lprops)           32
-               Check leaf nodes and inodes             64
-
-debug_tsts     Selects a mode of testing, as follows:
-
-               Test mode                               Flag value
-
-               Failure mode for recovery testing       4
-
-For example, set debug_chks to 3 to enable general and TNC checks.
-
-
 References
 ==========
 
index 9b7221a..7c3a880 100644 (file)
@@ -674,7 +674,7 @@ Protocol:   2.10+
 
 Field name:    init_size
 Type:          read
-Offset/size:   0x25c/4
+Offset/size:   0x260/4
 
   This field indicates the amount of linear contiguous memory starting
   at the kernel runtime start address that the kernel needs before it
index 63ee3a7..789fed6 100644 (file)
@@ -1,4 +1,5 @@
 
+
        List of maintainers and how to submit kernel changes
 
 Please try to follow the guidelines below.  This will make things
@@ -6325,7 +6326,7 @@ F:        drivers/scsi/u14-34f.c
 
 UBI FILE SYSTEM (UBIFS)
 M:     Artem Bityutskiy <dedekind1@gmail.com>
-M:     Adrian Hunter <adrian.hunter@nokia.com>
+M:     Adrian Hunter <adrian.hunter@intel.com>
 L:     linux-mtd@lists.infradead.org
 T:     git git://git.infradead.org/ubifs-2.6.git
 W:     http://www.linux-mtd.infradead.org/doc/ubifs.html
index 60d91f7..6a5bdad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 0
 SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
 NAME = Sneaky Weasel
 
 # *DOCUMENTATION*
index c67f684..09a87e6 100644 (file)
@@ -520,7 +520,7 @@ fail:
         */
        if (have_imager()) {
                label = "HD imager";
-               mux |= 1;
+               mux |= 2;
 
                /* externally mux MMC1/ENET/AIC33 to imager */
                mux |= BIT(6) | BIT(5) | BIT(3);
@@ -540,7 +540,7 @@ fail:
                resets &= ~BIT(1);
 
                if (have_tvp7002()) {
-                       mux |= 2;
+                       mux |= 1;
                        resets &= ~BIT(2);
                        label = "tvp7002 HD";
                } else {
index e722139..cafbe13 100644 (file)
@@ -254,8 +254,10 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc)
 {
        struct davinci_gpio_regs __iomem *g;
        u32 mask = 0xffff;
+       struct davinci_gpio_controller *d;
 
-       g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc);
+       d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc);
+       g = (struct davinci_gpio_regs __iomem *)d->regs;
 
        /* we only care about one bank */
        if (irq & 1)
@@ -274,11 +276,14 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc)
                if (!status)
                        break;
                __raw_writel(status, &g->intstat);
-               if (irq & 1)
-                       status >>= 16;
 
                /* now demux them to the right lowlevel handler */
-               n = (int)irq_get_handler_data(irq);
+               n = d->irq_base;
+               if (irq & 1) {
+                       n += 16;
+                       status >>= 16;
+               }
+
                while (status) {
                        res = ffs(status);
                        n += res;
@@ -424,7 +429,13 @@ static int __init davinci_gpio_irq_setup(void)
 
                /* set up all irqs in this bank */
                irq_set_chained_handler(bank_irq, gpio_irq_handler);
-               irq_set_handler_data(bank_irq, (__force void *)g);
+
+               /*
+                * Each chip handles 32 gpios, and each irq bank consists of 16
+                * gpio irqs. Pass the irq bank's corresponding controller to
+                * the chained irq handler.
+                */
+               irq_set_handler_data(bank_irq, &chips[gpio / 32]);
 
                for (i = 0; i < 16 && gpio < ngpio; i++, irq++, gpio++) {
                        irq_set_chip(irq, &gpio_irqchip);
index d8c1af0..952dc12 100644 (file)
@@ -52,6 +52,12 @@ davinci_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
        struct irq_chip_type *ct;
 
        gc = irq_alloc_generic_chip("AINTC", 1, irq_start, base, handle_edge_irq);
+       if (!gc) {
+               pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n",
+                      __func__, irq_start);
+               return;
+       }
+
        ct = gc->chip_types;
        ct->chip.irq_ack = irq_gc_ack_set_bit;
        ct->chip.irq_mask = irq_gc_mask_clr_bit;
index c7ad3fe..b928b31 100644 (file)
@@ -205,6 +205,7 @@ do {        current_thread_info()->syscall_noerror = 1; \
 } while (0)
 #define user_mode(regs) (!((regs)->tstate & TSTATE_PRIV))
 #define instruction_pointer(regs) ((regs)->tpc)
+#define instruction_pointer_set(regs, val) ((regs)->tpc = (val))
 #define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP])
 #define regs_return_value(regs) ((regs)->u_regs[UREG_I0])
 #ifdef CONFIG_SMP
index da34972..37357a5 100644 (file)
@@ -1170,7 +1170,7 @@ comment "NUMA (Summit) requires SMP, 64GB highmem support, ACPI"
 config AMD_NUMA
        def_bool y
        prompt "Old style AMD Opteron NUMA detection"
-       depends on NUMA && PCI
+       depends on X86_64 && NUMA && PCI
        ---help---
          Enable AMD NUMA node topology detection.  You should say Y here if
          you have a multi processor AMD system. This uses an old method to
index 4f0d46f..9242436 100644 (file)
@@ -419,6 +419,30 @@ static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"),
                },
        },
+       {       /* Handle problems with rebooting on the Latitude E6320. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E6320",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"),
+               },
+       },
+       {       /* Handle problems with rebooting on the Latitude E5420. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E5420",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5420"),
+               },
+       },
+       {       /* Handle problems with rebooting on the Latitude E6420. */
+               .callback = set_pci_reboot,
+               .ident = "Dell Latitude E6420",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6420"),
+               },
+       },
        { }
 };
 
index f245c58..ce7914c 100644 (file)
@@ -262,6 +262,7 @@ enum intel_pch {
 };
 
 #define QUIRK_PIPEA_FORCE (1<<0)
+#define QUIRK_LVDS_SSC_DISABLE (1<<1)
 
 struct intel_fbdev;
 
@@ -1194,7 +1195,9 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
 void i915_gem_release(struct drm_device *dev, struct drm_file *file);
 
 uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj);
+i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
+                                   uint32_t size,
+                                   int tiling_mode);
 
 /* i915_gem_gtt.c */
 void i915_gem_restore_gtt_mappings(struct drm_device *dev);
index 5c0d124..a087e1b 100644 (file)
@@ -1374,25 +1374,24 @@ i915_gem_free_mmap_offset(struct drm_i915_gem_object *obj)
 }
 
 static uint32_t
-i915_gem_get_gtt_size(struct drm_i915_gem_object *obj)
+i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
 {
-       struct drm_device *dev = obj->base.dev;
-       uint32_t size;
+       uint32_t gtt_size;
 
        if (INTEL_INFO(dev)->gen >= 4 ||
-           obj->tiling_mode == I915_TILING_NONE)
-               return obj->base.size;
+           tiling_mode == I915_TILING_NONE)
+               return size;
 
        /* Previous chips need a power-of-two fence region when tiling */
        if (INTEL_INFO(dev)->gen == 3)
-               size = 1024*1024;
+               gtt_size = 1024*1024;
        else
-               size = 512*1024;
+               gtt_size = 512*1024;
 
-       while (size < obj->base.size)
-               size <<= 1;
+       while (gtt_size < size)
+               gtt_size <<= 1;
 
-       return size;
+       return gtt_size;
 }
 
 /**
@@ -1403,59 +1402,52 @@ i915_gem_get_gtt_size(struct drm_i915_gem_object *obj)
  * potential fence register mapping.
  */
 static uint32_t
-i915_gem_get_gtt_alignment(struct drm_i915_gem_object *obj)
+i915_gem_get_gtt_alignment(struct drm_device *dev,
+                          uint32_t size,
+                          int tiling_mode)
 {
-       struct drm_device *dev = obj->base.dev;
-
        /*
         * Minimum alignment is 4k (GTT page size), but might be greater
         * if a fence register is needed for the object.
         */
        if (INTEL_INFO(dev)->gen >= 4 ||
-           obj->tiling_mode == I915_TILING_NONE)
+           tiling_mode == I915_TILING_NONE)
                return 4096;
 
        /*
         * Previous chips need to be aligned to the size of the smallest
         * fence register that can contain the object.
         */
-       return i915_gem_get_gtt_size(obj);
+       return i915_gem_get_gtt_size(dev, size, tiling_mode);
 }
 
 /**
  * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an
  *                                      unfenced object
- * @obj: object to check
+ * @dev: the device
+ * @size: size of the object
+ * @tiling_mode: tiling mode of the object
  *
  * Return the required GTT alignment for an object, only taking into account
  * unfenced tiled surface requirements.
  */
 uint32_t
-i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj)
+i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev,
+                                   uint32_t size,
+                                   int tiling_mode)
 {
-       struct drm_device *dev = obj->base.dev;
-       int tile_height;
-
        /*
         * Minimum alignment is 4k (GTT page size) for sane hw.
         */
        if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) ||
-           obj->tiling_mode == I915_TILING_NONE)
+           tiling_mode == I915_TILING_NONE)
                return 4096;
 
-       /*
-        * Older chips need unfenced tiled buffers to be aligned to the left
-        * edge of an even tile row (where tile rows are counted as if the bo is
-        * placed in a fenced gtt region).
+       /* Previous hardware however needs to be aligned to a power-of-two
+        * tile height. The simplest method for determining this is to reuse
+        * the power-of-tile object size.
         */
-       if (IS_GEN2(dev))
-               tile_height = 16;
-       else if (obj->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
-               tile_height = 32;
-       else
-               tile_height = 8;
-
-       return tile_height * obj->stride * 2;
+       return i915_gem_get_gtt_size(dev, size, tiling_mode);
 }
 
 int
@@ -2744,9 +2736,16 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
                return -EINVAL;
        }
 
-       fence_size = i915_gem_get_gtt_size(obj);
-       fence_alignment = i915_gem_get_gtt_alignment(obj);
-       unfenced_alignment = i915_gem_get_unfenced_gtt_alignment(obj);
+       fence_size = i915_gem_get_gtt_size(dev,
+                                          obj->base.size,
+                                          obj->tiling_mode);
+       fence_alignment = i915_gem_get_gtt_alignment(dev,
+                                                    obj->base.size,
+                                                    obj->tiling_mode);
+       unfenced_alignment =
+               i915_gem_get_unfenced_gtt_alignment(dev,
+                                                   obj->base.size,
+                                                   obj->tiling_mode);
 
        if (alignment == 0)
                alignment = map_and_fenceable ? fence_alignment :
index 82d70fd..99c4faa 100644 (file)
@@ -348,7 +348,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                /* Rebind if we need a change of alignment */
                if (!obj->map_and_fenceable) {
                        u32 unfenced_alignment =
-                               i915_gem_get_unfenced_gtt_alignment(obj);
+                               i915_gem_get_unfenced_gtt_alignment(dev,
+                                                                   obj->base.size,
+                                                                   args->tiling_mode);
                        if (obj->gtt_offset & (unfenced_alignment - 1))
                                ret = i915_gem_object_unbind(obj);
                }
index 21b6f93..0f1c799 100644 (file)
@@ -4305,7 +4305,8 @@ static void intel_update_watermarks(struct drm_device *dev)
 
 static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
 {
-       return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
+       return dev_priv->lvds_use_ssc && i915_panel_use_ssc
+               && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
 }
 
 static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
@@ -7810,6 +7811,15 @@ static void quirk_pipea_force (struct drm_device *dev)
        DRM_DEBUG_DRIVER("applying pipe a force quirk\n");
 }
 
+/*
+ * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason
+ */
+static void quirk_ssc_force_disable(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       dev_priv->quirks |= QUIRK_LVDS_SSC_DISABLE;
+}
+
 struct intel_quirk {
        int device;
        int subsystem_vendor;
@@ -7838,6 +7848,9 @@ struct intel_quirk intel_quirks[] = {
        /* 855 & before need to leave pipe A & dpll A up */
        { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
        { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+
+       /* Lenovo U160 cannot use SSC on LVDS */
+       { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
 };
 
 static void intel_init_quirks(struct drm_device *dev)
index 65626c1..6c3fb5a 100644 (file)
@@ -953,10 +953,14 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        if (!ubi->peb_buf2)
                goto out_free;
 
+       err = ubi_debugging_init_dev(ubi);
+       if (err)
+               goto out_free;
+
        err = attach_by_scanning(ubi);
        if (err) {
                dbg_err("failed to attach by scanning, error %d", err);
-               goto out_free;
+               goto out_debugging;
        }
 
        if (ubi->autoresize_vol_id != -1) {
@@ -969,12 +973,16 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        if (err)
                goto out_detach;
 
+       err = ubi_debugfs_init_dev(ubi);
+       if (err)
+               goto out_uif;
+
        ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
        if (IS_ERR(ubi->bgt_thread)) {
                err = PTR_ERR(ubi->bgt_thread);
                ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
                        err);
-               goto out_uif;
+               goto out_debugfs;
        }
 
        ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
@@ -1008,12 +1016,18 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
        ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
        return ubi_num;
 
+out_debugfs:
+       ubi_debugfs_exit_dev(ubi);
 out_uif:
+       get_device(&ubi->dev);
+       ubi_assert(ref);
        uif_close(ubi);
 out_detach:
        ubi_wl_close(ubi);
        free_internal_volumes(ubi);
        vfree(ubi->vtbl);
+out_debugging:
+       ubi_debugging_exit_dev(ubi);
 out_free:
        vfree(ubi->peb_buf1);
        vfree(ubi->peb_buf2);
@@ -1080,11 +1094,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
         */
        get_device(&ubi->dev);
 
+       ubi_debugfs_exit_dev(ubi);
        uif_close(ubi);
        ubi_wl_close(ubi);
        free_internal_volumes(ubi);
        vfree(ubi->vtbl);
        put_mtd_device(ubi->mtd);
+       ubi_debugging_exit_dev(ubi);
        vfree(ubi->peb_buf1);
        vfree(ubi->peb_buf2);
        ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
@@ -1199,6 +1215,11 @@ static int __init ubi_init(void)
        if (!ubi_wl_entry_slab)
                goto out_dev_unreg;
 
+       err = ubi_debugfs_init();
+       if (err)
+               goto out_slab;
+
+
        /* Attach MTD devices */
        for (i = 0; i < mtd_devs; i++) {
                struct mtd_dev_param *p = &mtd_dev_param[i];
@@ -1247,6 +1268,8 @@ out_detach:
                        ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
                        mutex_unlock(&ubi_devices_mutex);
                }
+       ubi_debugfs_exit();
+out_slab:
        kmem_cache_destroy(ubi_wl_entry_slab);
 out_dev_unreg:
        misc_deregister(&ubi_ctrl_cdev);
@@ -1270,6 +1293,7 @@ static void __exit ubi_exit(void)
                        ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
                        mutex_unlock(&ubi_devices_mutex);
                }
+       ubi_debugfs_exit();
        kmem_cache_destroy(ubi_wl_entry_slab);
        misc_deregister(&ubi_ctrl_cdev);
        class_remove_file(ubi_class, &ubi_version);
index 2224cbe..ab80c0d 100644 (file)
 #ifdef CONFIG_MTD_UBI_DEBUG
 
 #include "ubi.h"
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-
-unsigned int ubi_chk_flags;
-unsigned int ubi_tst_flags;
-
-module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
 
 /**
  * ubi_dbg_dump_ec_hdr - dump an erase counter header.
@@ -239,4 +231,261 @@ out:
        return;
 }
 
+/**
+ * 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)
+{
+       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)
+{
+       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;
+}
+
+/* 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;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+       if (inode->i_private)
+               file->private_data = inode->i_private;
+
+       return 0;
+}
+
+/* File operations for all UBI debugfs files */
+static const struct file_operations dfs_fops = {
+       .read   = dfs_file_read,
+       .write  = dfs_file_write,
+       .open   = default_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;
+
+       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)
+{
+       debugfs_remove_recursive(ubi->dbg->dfs_dir);
+}
+
 #endif /* CONFIG_MTD_UBI_DEBUG */
index 3f1a09c..65b5b76 100644 (file)
 #ifndef __UBI_DEBUG_H__
 #define __UBI_DEBUG_H__
 
-struct ubi_ec_hdr;
-struct ubi_vid_hdr;
-struct ubi_volume;
-struct ubi_vtbl_record;
-struct ubi_scan_volume;
-struct ubi_scan_leb;
-struct ubi_mkvol_req;
-
 #ifdef CONFIG_MTD_UBI_DEBUG
 #include <linux/random.h>
 
@@ -71,86 +63,103 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
 void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
 void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
 void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
-
-extern unsigned int ubi_chk_flags;
-
-/*
- * Debugging check flags.
- *
- * UBI_CHK_GEN: general checks
- * UBI_CHK_IO: check writes and erases
- */
-enum {
-       UBI_CHK_GEN = 0x1,
-       UBI_CHK_IO  = 0x2,
-};
-
 int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
 int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
                        int offset, int len);
-
-extern unsigned int ubi_tst_flags;
+int ubi_debugging_init_dev(struct ubi_device *ubi);
+void ubi_debugging_exit_dev(struct ubi_device *ubi);
+int ubi_debugfs_init(void);
+void ubi_debugfs_exit(void);
+int ubi_debugfs_init_dev(struct ubi_device *ubi);
+void ubi_debugfs_exit_dev(struct ubi_device *ubi);
 
 /*
- * Special testing flags.
+ * The UBI debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 2 for the number plus 1 for the trailing zero byte.
+ */
+#define UBI_DFS_DIR_NAME "ubi%d"
+#define UBI_DFS_DIR_LEN  (3 + 2 + 1)
+
+/**
+ * struct ubi_debug_info - debugging information for an UBI device.
  *
- * UBIFS_TST_DISABLE_BGT: disable the background thread
- * UBI_TST_EMULATE_BITFLIPS: emulate bit-flips
- * UBI_TST_EMULATE_WRITE_FAILURES: emulate write failures
- * UBI_TST_EMULATE_ERASE_FAILURES: emulate erase failures
+ * @chk_gen: if UBI general extra checks are enabled
+ * @chk_io: if UBI I/O extra checks are enabled
+ * @disable_bgt: disable the background task for testing purposes
+ * @emulate_bitflips: emulate bit-flips for testing purposes
+ * @emulate_io_failures: emulate write/erase failures for testing purposes
+ * @dfs_dir_name: name of debugfs directory containing files of this UBI device
+ * @dfs_dir: direntry object of the UBI device debugfs directory
+ * @dfs_chk_gen: debugfs knob to enable UBI general extra checks
+ * @dfs_chk_io: debugfs knob to enable UBI I/O extra checks
+ * @dfs_disable_bgt: debugfs knob to disable the background task
+ * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips
+ * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures
  */
-enum {
-       UBI_TST_DISABLE_BGT            = 0x1,
-       UBI_TST_EMULATE_BITFLIPS       = 0x2,
-       UBI_TST_EMULATE_WRITE_FAILURES = 0x4,
-       UBI_TST_EMULATE_ERASE_FAILURES = 0x8,
+struct ubi_debug_info {
+       unsigned int chk_gen:1;
+       unsigned int chk_io:1;
+       unsigned int disable_bgt:1;
+       unsigned int emulate_bitflips:1;
+       unsigned int emulate_io_failures:1;
+       char dfs_dir_name[UBI_DFS_DIR_LEN + 1];
+       struct dentry *dfs_dir;
+       struct dentry *dfs_chk_gen;
+       struct dentry *dfs_chk_io;
+       struct dentry *dfs_disable_bgt;
+       struct dentry *dfs_emulate_bitflips;
+       struct dentry *dfs_emulate_io_failures;
 };
 
 /**
  * ubi_dbg_is_bgt_disabled - if the background thread is disabled.
+ * @ubi: UBI device description object
  *
  * Returns non-zero if the UBI background thread is disabled for testing
  * purposes.
  */
-static inline int ubi_dbg_is_bgt_disabled(void)
+static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
 {
-       return ubi_tst_flags & UBI_TST_DISABLE_BGT;
+       return ubi->dbg->disable_bgt;
 }
 
 /**
  * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ * @ubi: UBI device description object
  *
  * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
  */
-static inline int ubi_dbg_is_bitflip(void)
+static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
 {
-       if (ubi_tst_flags & UBI_TST_EMULATE_BITFLIPS)
+       if (ubi->dbg->emulate_bitflips)
                return !(random32() % 200);
        return 0;
 }
 
 /**
  * ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ * @ubi: UBI device description object
  *
  * Returns non-zero if a write failure should be emulated, otherwise returns
  * zero.
  */
-static inline int ubi_dbg_is_write_failure(void)
+static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
 {
-       if (ubi_tst_flags & UBI_TST_EMULATE_WRITE_FAILURES)
+       if (ubi->dbg->emulate_io_failures)
                return !(random32() % 500);
        return 0;
 }
 
 /**
  * ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ * @ubi: UBI device description object
  *
  * Returns non-zero if an erase failure should be emulated, otherwise returns
  * zero.
  */
-static inline int ubi_dbg_is_erase_failure(void)
+static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
 {
-       if (ubi_tst_flags & UBI_TST_EMULATE_ERASE_FAILURES)
+       if (ubi->dbg->emulate_io_failures)
                return !(random32() % 400);
        return 0;
 }
@@ -201,11 +210,6 @@ static inline void ubi_dbg_dump_flash(struct ubi_device *ubi,
 static inline void
 ubi_dbg_print_hex_dump(const char *l, const char *ps, int pt, int r,
                       int g, const void *b, size_t len, bool a)     { return; }
-
-static inline int ubi_dbg_is_bgt_disabled(void)                    { return 0; }
-static inline int ubi_dbg_is_bitflip(void)                         { return 0; }
-static inline int ubi_dbg_is_write_failure(void)                   { return 0; }
-static inline int ubi_dbg_is_erase_failure(void)                   { return 0; }
 static inline int ubi_dbg_check_all_ff(struct ubi_device *ubi,
                                       int pnum, int offset,
                                       int len)                    { return 0; }
@@ -213,5 +217,20 @@ static inline int ubi_dbg_check_write(struct ubi_device *ubi,
                                      const void *buf, int pnum,
                                      int offset, int len)         { return 0; }
 
+static inline int ubi_debugging_init_dev(struct ubi_device *ubi)   { return 0; }
+static inline void ubi_debugging_exit_dev(struct ubi_device *ubi)  { return; }
+static inline int ubi_debugfs_init(void)                           { return 0; }
+static inline void ubi_debugfs_exit(void)                          { return; }
+static inline int ubi_debugfs_init_dev(struct ubi_device *ubi)     { return 0; }
+static inline void ubi_debugfs_exit_dev(struct ubi_device *ubi)    { return; }
+
+static inline int
+ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)              { return 0; }
+static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) { return 0; }
+static inline int
+ubi_dbg_is_write_failure(const struct ubi_device *ubi)             { return 0; }
+static inline int
+ubi_dbg_is_erase_failure(const struct ubi_device *ubi)             { return 0; }
+
 #endif /* !CONFIG_MTD_UBI_DEBUG */
 #endif /* !__UBI_DEBUG_H__ */
index 8c1b1c7..6ba55c2 100644 (file)
@@ -212,7 +212,7 @@ retry:
        } else {
                ubi_assert(len == read);
 
-               if (ubi_dbg_is_bitflip()) {
+               if (ubi_dbg_is_bitflip(ubi)) {
                        dbg_gen("bit-flip (emulated)");
                        err = UBI_IO_BITFLIPS;
                }
@@ -281,7 +281,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
                        return err;
        }
 
-       if (ubi_dbg_is_write_failure()) {
+       if (ubi_dbg_is_write_failure(ubi)) {
                dbg_err("cannot write %d bytes to PEB %d:%d "
                        "(emulated)", len, pnum, offset);
                ubi_dbg_dump_stack();
@@ -396,7 +396,7 @@ retry:
        if (err)
                return err;
 
-       if (ubi_dbg_is_erase_failure()) {
+       if (ubi_dbg_is_erase_failure(ubi)) {
                dbg_err("cannot erase PEB %d (emulated)", pnum);
                return -EIO;
        }
@@ -1146,7 +1146,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
 {
        int err;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        err = ubi_io_is_bad(ubi, pnum);
@@ -1173,7 +1173,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
        int err;
        uint32_t magic;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        magic = be32_to_cpu(ec_hdr->magic);
@@ -1211,7 +1211,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
        uint32_t crc, hdr_crc;
        struct ubi_ec_hdr *ec_hdr;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1255,7 +1255,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
        int err;
        uint32_t magic;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        magic = be32_to_cpu(vid_hdr->magic);
@@ -1296,7 +1296,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
        struct ubi_vid_hdr *vid_hdr;
        void *p;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
@@ -1348,7 +1348,7 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
        void *buf1;
        loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
@@ -1412,7 +1412,7 @@ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
        void *buf;
        loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
 
-       if (!(ubi_chk_flags & UBI_CHK_IO))
+       if (!ubi->dbg->chk_io)
                return 0;
 
        buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
index 2135a53..a3a198f 100644 (file)
@@ -1347,7 +1347,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
        struct ubi_scan_leb *seb, *last_seb;
        uint8_t *buf;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        /*
index c6c2229..dc64c76 100644 (file)
@@ -44,7 +44,6 @@
 
 #include "ubi-media.h"
 #include "scan.h"
-#include "debug.h"
 
 /* Maximum number of supported UBI devices */
 #define UBI_MAX_DEVICES 32
@@ -390,6 +389,8 @@ struct ubi_wl_entry;
  * @peb_buf2: another buffer of PEB size used for different purposes
  * @buf_mutex: protects @peb_buf1 and @peb_buf2
  * @ckvol_mutex: serializes static volume checking when opening
+ *
+ * @dbg: debugging information for this UBI device
  */
 struct ubi_device {
        struct cdev cdev;
@@ -472,8 +473,12 @@ struct ubi_device {
        void *peb_buf2;
        struct mutex buf_mutex;
        struct mutex ckvol_mutex;
+
+       struct ubi_debug_info *dbg;
 };
 
+#include "debug.h"
+
 extern struct kmem_cache *ubi_wl_entry_slab;
 extern const struct file_operations ubi_ctrl_cdev_operations;
 extern const struct file_operations ubi_cdev_operations;
@@ -662,6 +667,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi)
        if (!ubi->ro_mode) {
                ubi->ro_mode = 1;
                ubi_warn("switch to read-only mode");
+               ubi_dbg_dump_stack();
        }
 }
 
index 366eb70..97e093d 100644 (file)
@@ -871,7 +871,7 @@ static int paranoid_check_volumes(struct ubi_device *ubi)
 {
        int i, err = 0;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        for (i = 0; i < ubi->vtbl_slots; i++) {
index fd3bf77..4b50a30 100644 (file)
@@ -307,8 +307,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
 {
        int err, tries = 0;
        static struct ubi_vid_hdr *vid_hdr;
-       struct ubi_scan_volume *sv;
-       struct ubi_scan_leb *new_seb, *old_seb = NULL;
+       struct ubi_scan_leb *new_seb;
 
        ubi_msg("create volume table (copy #%d)", copy + 1);
 
@@ -316,15 +315,6 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
        if (!vid_hdr)
                return -ENOMEM;
 
-       /*
-        * Check if there is a logical eraseblock which would have to contain
-        * this volume table copy was found during scanning. It has to be wiped
-        * out.
-        */
-       sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID);
-       if (sv)
-               old_seb = ubi_scan_find_seb(sv, copy);
-
 retry:
        new_seb = ubi_scan_get_free_peb(ubi, si);
        if (IS_ERR(new_seb)) {
@@ -351,8 +341,8 @@ retry:
                goto write_error;
 
        /*
-        * And add it to the scanning information. Don't delete the old
-        * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'.
+        * And add it to the scanning information. Don't delete the old version
+        * of this LEB as it will be deleted and freed in 'ubi_scan_add_used()'.
         */
        err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec,
                                vid_hdr, 0);
@@ -876,7 +866,7 @@ out_free:
  */
 static void paranoid_vtbl_check(const struct ubi_device *ubi)
 {
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return;
 
        if (vtbl_check(ubi, ubi->vtbl)) {
index ff2c495..42c684c 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * @ubi: UBI device description object
  * Copyright (c) International Business Machines Corp., 2006
  *
  * This program is free software; you can redistribute it and/or modify
@@ -163,12 +164,14 @@ struct ubi_work {
 
 #ifdef CONFIG_MTD_UBI_DEBUG
 static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec);
-static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+static int paranoid_check_in_wl_tree(const struct ubi_device *ubi,
+                                    struct ubi_wl_entry *e,
                                     struct rb_root *root);
-static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e);
+static int paranoid_check_in_pq(const struct ubi_device *ubi,
+                               struct ubi_wl_entry *e);
 #else
 #define paranoid_check_ec(ubi, pnum, ec) 0
-#define paranoid_check_in_wl_tree(e, root)
+#define paranoid_check_in_wl_tree(ubi, e, root)
 #define paranoid_check_in_pq(ubi, e) 0
 #endif
 
@@ -449,7 +452,7 @@ retry:
                BUG();
        }
 
-       paranoid_check_in_wl_tree(e, &ubi->free);
+       paranoid_check_in_wl_tree(ubi, e, &ubi->free);
 
        /*
         * Move the physical eraseblock to the protection queue where it will
@@ -613,7 +616,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
        list_add_tail(&wrk->list, &ubi->works);
        ubi_assert(ubi->works_count >= 0);
        ubi->works_count += 1;
-       if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled())
+       if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi))
                wake_up_process(ubi->bgt_thread);
        spin_unlock(&ubi->wl_lock);
 }
@@ -712,7 +715,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                               e1->ec, e2->ec);
                        goto out_cancel;
                }
-               paranoid_check_in_wl_tree(e1, &ubi->used);
+               paranoid_check_in_wl_tree(ubi, e1, &ubi->used);
                rb_erase(&e1->u.rb, &ubi->used);
                dbg_wl("move PEB %d EC %d to PEB %d EC %d",
                       e1->pnum, e1->ec, e2->pnum, e2->ec);
@@ -721,12 +724,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
                scrubbing = 1;
                e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, u.rb);
                e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
-               paranoid_check_in_wl_tree(e1, &ubi->scrub);
+               paranoid_check_in_wl_tree(ubi, e1, &ubi->scrub);
                rb_erase(&e1->u.rb, &ubi->scrub);
                dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);
        }
 
-       paranoid_check_in_wl_tree(e2, &ubi->free);
+       paranoid_check_in_wl_tree(ubi, e2, &ubi->free);
        rb_erase(&e2->u.rb, &ubi->free);
        ubi->move_from = e1;
        ubi->move_to = e2;
@@ -1169,13 +1172,13 @@ retry:
                return 0;
        } else {
                if (in_wl_tree(e, &ubi->used)) {
-                       paranoid_check_in_wl_tree(e, &ubi->used);
+                       paranoid_check_in_wl_tree(ubi, e, &ubi->used);
                        rb_erase(&e->u.rb, &ubi->used);
                } else if (in_wl_tree(e, &ubi->scrub)) {
-                       paranoid_check_in_wl_tree(e, &ubi->scrub);
+                       paranoid_check_in_wl_tree(ubi, e, &ubi->scrub);
                        rb_erase(&e->u.rb, &ubi->scrub);
                } else if (in_wl_tree(e, &ubi->erroneous)) {
-                       paranoid_check_in_wl_tree(e, &ubi->erroneous);
+                       paranoid_check_in_wl_tree(ubi, e, &ubi->erroneous);
                        rb_erase(&e->u.rb, &ubi->erroneous);
                        ubi->erroneous_peb_count -= 1;
                        ubi_assert(ubi->erroneous_peb_count >= 0);
@@ -1242,7 +1245,7 @@ retry:
        }
 
        if (in_wl_tree(e, &ubi->used)) {
-               paranoid_check_in_wl_tree(e, &ubi->used);
+               paranoid_check_in_wl_tree(ubi, e, &ubi->used);
                rb_erase(&e->u.rb, &ubi->used);
        } else {
                int err;
@@ -1364,7 +1367,7 @@ int ubi_thread(void *u)
 
                spin_lock(&ubi->wl_lock);
                if (list_empty(&ubi->works) || ubi->ro_mode ||
-                   !ubi->thread_enabled || ubi_dbg_is_bgt_disabled()) {
+                   !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        spin_unlock(&ubi->wl_lock);
                        schedule();
@@ -1579,7 +1582,7 @@ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
        long long read_ec;
        struct ubi_ec_hdr *ec_hdr;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1609,16 +1612,18 @@ out_free:
 
 /**
  * paranoid_check_in_wl_tree - check that wear-leveling entry is in WL RB-tree.
+ * @ubi: UBI device description object
  * @e: the wear-leveling entry to check
  * @root: the root of the tree
  *
  * This function returns zero if @e is in the @root RB-tree and %-EINVAL if it
  * is not.
  */
-static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+static int paranoid_check_in_wl_tree(const struct ubi_device *ubi,
+                                    struct ubi_wl_entry *e,
                                     struct rb_root *root)
 {
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        if (in_wl_tree(e, root))
@@ -1638,12 +1643,13 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
  *
  * This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not.
  */
-static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
+static int paranoid_check_in_pq(const struct ubi_device *ubi,
+                               struct ubi_wl_entry *e)
 {
        struct ubi_wl_entry *p;
        int i;
 
-       if (!(ubi_chk_flags & UBI_CHK_GEN))
+       if (!ubi->dbg->chk_gen)
                return 0;
 
        for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
index 3e29899..bc4b12c 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/delay.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
+#include <linux/namei.h>
 #include <net/ipv6.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
 static struct dentry *
 cifs_get_root(struct smb_vol *vol, struct super_block *sb)
 {
-       int xid, rc;
-       struct inode *inode;
-       struct qstr name;
-       struct dentry *dparent = NULL, *dchild = NULL, *alias;
+       struct dentry *dentry;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-       unsigned int i, full_len, len;
-       char *full_path = NULL, *pstart;
+       char *full_path = NULL;
+       char *s, *p;
        char sep;
+       int xid;
 
        full_path = cifs_build_path_to_root(vol, cifs_sb,
                                            cifs_sb_master_tcon(cifs_sb));
@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
 
        xid = GetXid();
        sep = CIFS_DIR_SEP(cifs_sb);
-       dparent = dget(sb->s_root);
-       full_len = strlen(full_path);
-       full_path[full_len] = sep;
-       pstart = full_path + 1;
-
-       for (i = 1, len = 0; i <= full_len; i++) {
-               if (full_path[i] != sep || !len) {
-                       len++;
-                       continue;
-               }
-
-               full_path[i] = 0;
-               cFYI(1, "get dentry for %s", pstart);
-
-               name.name = pstart;
-               name.len = len;
-               name.hash = full_name_hash(pstart, len);
-               dchild = d_lookup(dparent, &name);
-               if (dchild == NULL) {
-                       cFYI(1, "not exists");
-                       dchild = d_alloc(dparent, &name);
-                       if (dchild == NULL) {
-                               dput(dparent);
-                               dparent = ERR_PTR(-ENOMEM);
-                               goto out;
-                       }
-               }
-
-               cFYI(1, "get inode");
-               if (dchild->d_inode == NULL) {
-                       cFYI(1, "not exists");
-                       inode = NULL;
-                       if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
-                               rc = cifs_get_inode_info_unix(&inode, full_path,
-                                                             sb, xid);
-                       else
-                               rc = cifs_get_inode_info(&inode, full_path,
-                                                        NULL, sb, xid, NULL);
-                       if (rc) {
-                               dput(dchild);
-                               dput(dparent);
-                               dparent = ERR_PTR(rc);
-                               goto out;
-                       }
-                       alias = d_materialise_unique(dchild, inode);
-                       if (alias != NULL) {
-                               dput(dchild);
-                               if (IS_ERR(alias)) {
-                                       dput(dparent);
-                                       dparent = ERR_PTR(-EINVAL); /* XXX */
-                                       goto out;
-                               }
-                               dchild = alias;
-                       }
-               }
-               cFYI(1, "parent %p, child %p", dparent, dchild);
-
-               dput(dparent);
-               dparent = dchild;
-               len = 0;
-               pstart = full_path + i + 1;
-               full_path[i] = sep;
-       }
-out:
+       dentry = dget(sb->s_root);
+       p = s = full_path;
+
+       do {
+               struct inode *dir = dentry->d_inode;
+               struct dentry *child;
+
+               /* skip separators */
+               while (*s == sep)
+                       s++;
+               if (!*s)
+                       break;
+               p = s++;
+               /* next separator */
+               while (*s && *s != sep)
+                       s++;
+
+               mutex_lock(&dir->i_mutex);
+               child = lookup_one_len(p, dentry, s - p);
+               mutex_unlock(&dir->i_mutex);
+               dput(dentry);
+               dentry = child;
+       } while (!IS_ERR(dentry));
        _FreeXid(xid);
        kfree(full_path);
-       return dparent;
+       return dentry;
 }
 
 static int cifs_set_super(struct super_block *sb, void *data)
index bb71471..a9b4a24 100644 (file)
@@ -1737,7 +1737,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
                        io_parms.pid = pid;
                        io_parms.tcon = pTcon;
                        io_parms.offset = *poffset;
-                       io_parms.length = len;
+                       io_parms.length = cur_len;
                        rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
                                         &read_data, &buf_type);
                        pSMBr = (struct smb_com_read_rsp *)read_data;
index 6e4ea6d..fbdcbca 100644 (file)
@@ -1813,8 +1813,6 @@ seqretry:
                tname = dentry->d_name.name;
                i = dentry->d_inode;
                prefetch(tname);
-               if (i)
-                       prefetch(i);
                /*
                 * This seqcount check is required to ensure name and
                 * len are loaded atomically, so as not to walk off the
index abc49f2..90e5997 100644 (file)
 #include "dlm_internal.h"
 #include "lock.h"
 #include "user.h"
-#include "ast.h"
-
-#define WAKE_ASTS  0
-
-static uint64_t                        ast_seq_count;
-static struct list_head                ast_queue;
-static spinlock_t              ast_queue_lock;
-static struct task_struct *    astd_task;
-static unsigned long           astd_wakeflags;
-static struct mutex            astd_running;
 
+static uint64_t                        dlm_cb_seq;
+static spinlock_t              dlm_cb_seq_spin;
 
 static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb)
 {
@@ -57,21 +49,13 @@ static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb)
        }
 }
 
-void dlm_del_ast(struct dlm_lkb *lkb)
-{
-       spin_lock(&ast_queue_lock);
-       if (!list_empty(&lkb->lkb_astqueue))
-               list_del_init(&lkb->lkb_astqueue);
-       spin_unlock(&ast_queue_lock);
-}
-
 int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                         int status, uint32_t sbflags, uint64_t seq)
 {
        struct dlm_ls *ls = lkb->lkb_resource->res_ls;
        uint64_t prev_seq;
        int prev_mode;
-       int i;
+       int i, rv;
 
        for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
                if (lkb->lkb_callbacks[i].seq)
@@ -100,7 +84,8 @@ int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                                          mode,
                                          (unsigned long long)prev_seq,
                                          prev_mode);
-                               return 0;
+                               rv = 0;
+                               goto out;
                        }
                }
 
@@ -109,6 +94,7 @@ int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                lkb->lkb_callbacks[i].mode = mode;
                lkb->lkb_callbacks[i].sb_status = status;
                lkb->lkb_callbacks[i].sb_flags = (sbflags & 0x000000FF);
+               rv = 0;
                break;
        }
 
@@ -117,21 +103,24 @@ int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                          lkb->lkb_id, (unsigned long long)seq,
                          flags, mode, status, sbflags);
                dlm_dump_lkb_callbacks(lkb);
-               return -1;
+               rv = -1;
+               goto out;
        }
-
-       return 0;
+ out:
+       return rv;
 }
 
 int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb,
                         struct dlm_callback *cb, int *resid)
 {
-       int i;
+       int i, rv;
 
        *resid = 0;
 
-       if (!lkb->lkb_callbacks[0].seq)
-               return -ENOENT;
+       if (!lkb->lkb_callbacks[0].seq) {
+               rv = -ENOENT;
+               goto out;
+       }
 
        /* oldest undelivered cb is callbacks[0] */
 
@@ -163,7 +152,8 @@ int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb,
                                  cb->mode,
                                  (unsigned long long)lkb->lkb_last_cast.seq,
                                  lkb->lkb_last_cast.mode);
-                       return 0;
+                       rv = 0;
+                       goto out;
                }
        }
 
@@ -176,171 +166,150 @@ int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb,
                memcpy(&lkb->lkb_last_bast, cb, sizeof(struct dlm_callback));
                lkb->lkb_last_bast_time = ktime_get();
        }
-
-       return 0;
+       rv = 0;
+ out:
+       return rv;
 }
 
-void dlm_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
-                uint32_t sbflags)
+void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
+               uint32_t sbflags)
 {
-       uint64_t seq;
+       struct dlm_ls *ls = lkb->lkb_resource->res_ls;
+       uint64_t new_seq, prev_seq;
        int rv;
 
-       spin_lock(&ast_queue_lock);
-
-       seq = ++ast_seq_count;
+       spin_lock(&dlm_cb_seq_spin);
+       new_seq = ++dlm_cb_seq;
+       spin_unlock(&dlm_cb_seq_spin);
 
        if (lkb->lkb_flags & DLM_IFL_USER) {
-               spin_unlock(&ast_queue_lock);
-               dlm_user_add_ast(lkb, flags, mode, status, sbflags, seq);
+               dlm_user_add_ast(lkb, flags, mode, status, sbflags, new_seq);
                return;
        }
 
-       rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, seq);
-       if (rv < 0) {
-               spin_unlock(&ast_queue_lock);
-               return;
-       }
+       mutex_lock(&lkb->lkb_cb_mutex);
+       prev_seq = lkb->lkb_callbacks[0].seq;
 
-       if (list_empty(&lkb->lkb_astqueue)) {
+       rv = dlm_add_lkb_callback(lkb, flags, mode, status, sbflags, new_seq);
+       if (rv < 0)
+               goto out;
+
+       if (!prev_seq) {
                kref_get(&lkb->lkb_ref);
-               list_add_tail(&lkb->lkb_astqueue, &ast_queue);
-       }
-       spin_unlock(&ast_queue_lock);
 
-       set_bit(WAKE_ASTS, &astd_wakeflags);
-       wake_up_process(astd_task);
+               if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) {
+                       mutex_lock(&ls->ls_cb_mutex);
+                       list_add(&lkb->lkb_cb_list, &ls->ls_cb_delay);
+                       mutex_unlock(&ls->ls_cb_mutex);
+               } else {
+                       queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work);
+               }
+       }
+ out:
+       mutex_unlock(&lkb->lkb_cb_mutex);
 }
 
-static void process_asts(void)
+void dlm_callback_work(struct work_struct *work)
 {
-       struct dlm_ls *ls = NULL;
-       struct dlm_rsb *r = NULL;
-       struct dlm_lkb *lkb;
+       struct dlm_lkb *lkb = container_of(work, struct dlm_lkb, lkb_cb_work);
+       struct dlm_ls *ls = lkb->lkb_resource->res_ls;
        void (*castfn) (void *astparam);
        void (*bastfn) (void *astparam, int mode);
        struct dlm_callback callbacks[DLM_CALLBACKS_SIZE];
        int i, rv, resid;
 
-repeat:
-       spin_lock(&ast_queue_lock);
-       list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
-               r = lkb->lkb_resource;
-               ls = r->res_ls;
+       memset(&callbacks, 0, sizeof(callbacks));
 
-               if (dlm_locking_stopped(ls))
-                       continue;
-
-               /* we remove from astqueue list and remove everything in
-                  lkb_callbacks before releasing the spinlock so empty
-                  lkb_astqueue is always consistent with empty lkb_callbacks */
-
-               list_del_init(&lkb->lkb_astqueue);
-
-               castfn = lkb->lkb_astfn;
-               bastfn = lkb->lkb_bastfn;
+       mutex_lock(&lkb->lkb_cb_mutex);
+       if (!lkb->lkb_callbacks[0].seq) {
+               /* no callback work exists, shouldn't happen */
+               log_error(ls, "dlm_callback_work %x no work", lkb->lkb_id);
+               dlm_print_lkb(lkb);
+               dlm_dump_lkb_callbacks(lkb);
+       }
 
-               memset(&callbacks, 0, sizeof(callbacks));
+       for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
+               rv = dlm_rem_lkb_callback(ls, lkb, &callbacks[i], &resid);
+               if (rv < 0)
+                       break;
+       }
 
-               for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
-                       rv = dlm_rem_lkb_callback(ls, lkb, &callbacks[i], &resid);
-                       if (rv < 0)
-                               break;
-               }
-               spin_unlock(&ast_queue_lock);
+       if (resid) {
+               /* cbs remain, loop should have removed all, shouldn't happen */
+               log_error(ls, "dlm_callback_work %x resid %d", lkb->lkb_id,
+                         resid);
+               dlm_print_lkb(lkb);
+               dlm_dump_lkb_callbacks(lkb);
+       }
+       mutex_unlock(&lkb->lkb_cb_mutex);
 
-               if (resid) {
-                       /* shouldn't happen, for loop should have removed all */
-                       log_error(ls, "callback resid %d lkb %x",
-                                 resid, lkb->lkb_id);
-               }
+       castfn = lkb->lkb_astfn;
+       bastfn = lkb->lkb_bastfn;
 
-               for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
-                       if (!callbacks[i].seq)
-                               break;
-                       if (callbacks[i].flags & DLM_CB_SKIP) {
-                               continue;
-                       } else if (callbacks[i].flags & DLM_CB_BAST) {
-                               bastfn(lkb->lkb_astparam, callbacks[i].mode);
-                       } else if (callbacks[i].flags & DLM_CB_CAST) {
-                               lkb->lkb_lksb->sb_status = callbacks[i].sb_status;
-                               lkb->lkb_lksb->sb_flags = callbacks[i].sb_flags;
-                               castfn(lkb->lkb_astparam);
-                       }
+       for (i = 0; i < DLM_CALLBACKS_SIZE; i++) {
+               if (!callbacks[i].seq)
+                       break;
+               if (callbacks[i].flags & DLM_CB_SKIP) {
+                       continue;
+               } else if (callbacks[i].flags & DLM_CB_BAST) {
+                       bastfn(lkb->lkb_astparam, callbacks[i].mode);
+               } else if (callbacks[i].flags & DLM_CB_CAST) {
+                       lkb->lkb_lksb->sb_status = callbacks[i].sb_status;
+                       lkb->lkb_lksb->sb_flags = callbacks[i].sb_flags;
+                       castfn(lkb->lkb_astparam);
                }
-
-               /* removes ref for ast_queue, may cause lkb to be freed */
-               dlm_put_lkb(lkb);
-
-               cond_resched();
-               goto repeat;
        }
-       spin_unlock(&ast_queue_lock);
-}
-
-static inline int no_asts(void)
-{
-       int ret;
 
-       spin_lock(&ast_queue_lock);
-       ret = list_empty(&ast_queue);
-       spin_unlock(&ast_queue_lock);
-       return ret;
+       /* undo kref_get from dlm_add_callback, may cause lkb to be freed */
+       dlm_put_lkb(lkb);
 }
 
-static int dlm_astd(void *data)
+int dlm_callback_start(struct dlm_ls *ls)
 {
-       while (!kthread_should_stop()) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (!test_bit(WAKE_ASTS, &astd_wakeflags))
-                       schedule();
-               set_current_state(TASK_RUNNING);
-
-               mutex_lock(&astd_running);
-               if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
-                       process_asts();
-               mutex_unlock(&astd_running);
+       ls->ls_callback_wq = alloc_workqueue("dlm_callback",
+                                            WQ_UNBOUND |
+                                            WQ_MEM_RECLAIM |
+                                            WQ_NON_REENTRANT,
+                                            0);
+       if (!ls->ls_callback_wq) {
+               log_print("can't start dlm_callback workqueue");
+               return -ENOMEM;
        }
        return 0;
 }
 
-void dlm_astd_wake(void)
+void dlm_callback_stop(struct dlm_ls *ls)
 {
-       if (!no_asts()) {
-               set_bit(WAKE_ASTS, &astd_wakeflags);
-               wake_up_process(astd_task);
-       }
+       if (ls->ls_callback_wq)
+               destroy_workqueue(ls->ls_callback_wq);
 }
 
-int dlm_astd_start(void)
+void dlm_callback_suspend(struct dlm_ls *ls)
 {
-       struct task_struct *p;
-       int error = 0;
-
-       INIT_LIST_HEAD(&ast_queue);
-       spin_lock_init(&ast_queue_lock);
-       mutex_init(&astd_running);
-
-       p = kthread_run(dlm_astd, NULL, "dlm_astd");
-       if (IS_ERR(p))
-               error = PTR_ERR(p);
-       else
-               astd_task = p;
-       return error;
-}
+       set_bit(LSFL_CB_DELAY, &ls->ls_flags);
 
-void dlm_astd_stop(void)
-{
-       kthread_stop(astd_task);
+       if (ls->ls_callback_wq)
+               flush_workqueue(ls->ls_callback_wq);
 }
 
-void dlm_astd_suspend(void)
+void dlm_callback_resume(struct dlm_ls *ls)
 {
-       mutex_lock(&astd_running);
-}
+       struct dlm_lkb *lkb, *safe;
+       int count = 0;
 
-void dlm_astd_resume(void)
-{
-       mutex_unlock(&astd_running);
+       clear_bit(LSFL_CB_DELAY, &ls->ls_flags);
+
+       if (!ls->ls_callback_wq)
+               return;
+
+       mutex_lock(&ls->ls_cb_mutex);
+       list_for_each_entry_safe(lkb, safe, &ls->ls_cb_delay, lkb_cb_list) {
+               list_del_init(&lkb->lkb_cb_list);
+               queue_work(ls->ls_callback_wq, &lkb->lkb_cb_work);
+               count++;
+       }
+       mutex_unlock(&ls->ls_cb_mutex);
+
+       log_debug(ls, "dlm_callback_resume %d", count);
 }
 
index 8aa89c9..757b551 100644 (file)
@@ -18,14 +18,15 @@ int dlm_add_lkb_callback(struct dlm_lkb *lkb, uint32_t flags, int mode,
                          int status, uint32_t sbflags, uint64_t seq);
 int dlm_rem_lkb_callback(struct dlm_ls *ls, struct dlm_lkb *lkb,
                          struct dlm_callback *cb, int *resid);
-void dlm_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
-                uint32_t sbflags);
+void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
+                uint32_t sbflags);
 
-void dlm_astd_wake(void);
-int dlm_astd_start(void);
-void dlm_astd_stop(void);
-void dlm_astd_suspend(void);
-void dlm_astd_resume(void);
+void dlm_callback_work(struct work_struct *work);
+int dlm_callback_start(struct dlm_ls *ls);
+void dlm_callback_stop(struct dlm_ls *ls);
+void dlm_callback_suspend(struct dlm_ls *ls);
+void dlm_callback_resume(struct dlm_ls *ls);
 
 #endif
 
+
index 9b026ea..6cf72fc 100644 (file)
@@ -28,7 +28,8 @@
  * /config/dlm/<cluster>/spaces/<space>/nodes/<node>/weight
  * /config/dlm/<cluster>/comms/<comm>/nodeid
  * /config/dlm/<cluster>/comms/<comm>/local
- * /config/dlm/<cluster>/comms/<comm>/addr
+ * /config/dlm/<cluster>/comms/<comm>/addr      (write only)
+ * /config/dlm/<cluster>/comms/<comm>/addr_list (read only)
  * The <cluster> level is useless, but I haven't figured out how to avoid it.
  */
 
@@ -80,6 +81,7 @@ static ssize_t comm_local_write(struct dlm_comm *cm, const char *buf,
                                size_t len);
 static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf,
                                size_t len);
+static ssize_t comm_addr_list_read(struct dlm_comm *cm, char *buf);
 static ssize_t node_nodeid_read(struct dlm_node *nd, char *buf);
 static ssize_t node_nodeid_write(struct dlm_node *nd, const char *buf,
                                size_t len);
@@ -92,7 +94,6 @@ struct dlm_cluster {
        unsigned int cl_tcp_port;
        unsigned int cl_buffer_size;
        unsigned int cl_rsbtbl_size;
-       unsigned int cl_lkbtbl_size;
        unsigned int cl_dirtbl_size;
        unsigned int cl_recover_timer;
        unsigned int cl_toss_secs;
@@ -101,13 +102,13 @@ struct dlm_cluster {
        unsigned int cl_protocol;
        unsigned int cl_timewarn_cs;
        unsigned int cl_waitwarn_us;
+       unsigned int cl_new_rsb_count;
 };
 
 enum {
        CLUSTER_ATTR_TCP_PORT = 0,
        CLUSTER_ATTR_BUFFER_SIZE,
        CLUSTER_ATTR_RSBTBL_SIZE,
-       CLUSTER_ATTR_LKBTBL_SIZE,
        CLUSTER_ATTR_DIRTBL_SIZE,
        CLUSTER_ATTR_RECOVER_TIMER,
        CLUSTER_ATTR_TOSS_SECS,
@@ -116,6 +117,7 @@ enum {
        CLUSTER_ATTR_PROTOCOL,
        CLUSTER_ATTR_TIMEWARN_CS,
        CLUSTER_ATTR_WAITWARN_US,
+       CLUSTER_ATTR_NEW_RSB_COUNT,
 };
 
 struct cluster_attribute {
@@ -160,7 +162,6 @@ __CONFIGFS_ATTR(name, 0644, name##_read, name##_write)
 CLUSTER_ATTR(tcp_port, 1);
 CLUSTER_ATTR(buffer_size, 1);
 CLUSTER_ATTR(rsbtbl_size, 1);
-CLUSTER_ATTR(lkbtbl_size, 1);
 CLUSTER_ATTR(dirtbl_size, 1);
 CLUSTER_ATTR(recover_timer, 1);
 CLUSTER_ATTR(toss_secs, 1);
@@ -169,12 +170,12 @@ CLUSTER_ATTR(log_debug, 0);
 CLUSTER_ATTR(protocol, 0);
 CLUSTER_ATTR(timewarn_cs, 1);
 CLUSTER_ATTR(waitwarn_us, 0);
+CLUSTER_ATTR(new_rsb_count, 0);
 
 static struct configfs_attribute *cluster_attrs[] = {
        [CLUSTER_ATTR_TCP_PORT] = &cluster_attr_tcp_port.attr,
        [CLUSTER_ATTR_BUFFER_SIZE] = &cluster_attr_buffer_size.attr,
        [CLUSTER_ATTR_RSBTBL_SIZE] = &cluster_attr_rsbtbl_size.attr,
-       [CLUSTER_ATTR_LKBTBL_SIZE] = &cluster_attr_lkbtbl_size.attr,
        [CLUSTER_ATTR_DIRTBL_SIZE] = &cluster_attr_dirtbl_size.attr,
        [CLUSTER_ATTR_RECOVER_TIMER] = &cluster_attr_recover_timer.attr,
        [CLUSTER_ATTR_TOSS_SECS] = &cluster_attr_toss_secs.attr,
@@ -183,6 +184,7 @@ static struct configfs_attribute *cluster_attrs[] = {
        [CLUSTER_ATTR_PROTOCOL] = &cluster_attr_protocol.attr,
        [CLUSTER_ATTR_TIMEWARN_CS] = &cluster_attr_timewarn_cs.attr,
        [CLUSTER_ATTR_WAITWARN_US] = &cluster_attr_waitwarn_us.attr,
+       [CLUSTER_ATTR_NEW_RSB_COUNT] = &cluster_attr_new_rsb_count.attr,
        NULL,
 };
 
@@ -190,6 +192,7 @@ enum {
        COMM_ATTR_NODEID = 0,
        COMM_ATTR_LOCAL,
        COMM_ATTR_ADDR,
+       COMM_ATTR_ADDR_LIST,
 };
 
 struct comm_attribute {
@@ -217,14 +220,22 @@ static struct comm_attribute comm_attr_local = {
 static struct comm_attribute comm_attr_addr = {
        .attr   = { .ca_owner = THIS_MODULE,
                     .ca_name = "addr",
-                    .ca_mode = S_IRUGO | S_IWUSR },
+                    .ca_mode = S_IWUSR },
        .store  = comm_addr_write,
 };
 
+static struct comm_attribute comm_attr_addr_list = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                    .ca_name = "addr_list",
+                    .ca_mode = S_IRUGO },
+       .show   = comm_addr_list_read,
+};
+
 static struct configfs_attribute *comm_attrs[] = {
        [COMM_ATTR_NODEID] = &comm_attr_nodeid.attr,
        [COMM_ATTR_LOCAL] = &comm_attr_local.attr,
        [COMM_ATTR_ADDR] = &comm_attr_addr.attr,
+       [COMM_ATTR_ADDR_LIST] = &comm_attr_addr_list.attr,
        NULL,
 };
 
@@ -435,7 +446,6 @@ static struct config_group *make_cluster(struct config_group *g,
        cl->cl_tcp_port = dlm_config.ci_tcp_port;
        cl->cl_buffer_size = dlm_config.ci_buffer_size;
        cl->cl_rsbtbl_size = dlm_config.ci_rsbtbl_size;
-       cl->cl_lkbtbl_size = dlm_config.ci_lkbtbl_size;
        cl->cl_dirtbl_size = dlm_config.ci_dirtbl_size;
        cl->cl_recover_timer = dlm_config.ci_recover_timer;
        cl->cl_toss_secs = dlm_config.ci_toss_secs;
@@ -444,6 +454,7 @@ static struct config_group *make_cluster(struct config_group *g,
        cl->cl_protocol = dlm_config.ci_protocol;
        cl->cl_timewarn_cs = dlm_config.ci_timewarn_cs;
        cl->cl_waitwarn_us = dlm_config.ci_waitwarn_us;
+       cl->cl_new_rsb_count = dlm_config.ci_new_rsb_count;
 
        space_list = &sps->ss_group;
        comm_list = &cms->cs_group;
@@ -720,6 +731,50 @@ static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
        return len;
 }
 
+static ssize_t comm_addr_list_read(struct dlm_comm *cm, char *buf)
+{
+       ssize_t s;
+       ssize_t allowance;
+       int i;
+       struct sockaddr_storage *addr;
+       struct sockaddr_in *addr_in;
+       struct sockaddr_in6 *addr_in6;
+       
+       /* Taken from ip6_addr_string() defined in lib/vsprintf.c */
+       char buf0[sizeof("AF_INET6      xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255\n")];
+       
+
+       /* Derived from SIMPLE_ATTR_SIZE of fs/configfs/file.c */
+       allowance = 4096;
+       buf[0] = '\0';
+
+       for (i = 0; i < cm->addr_count; i++) {
+               addr = cm->addr[i];
+
+               switch(addr->ss_family) {
+               case AF_INET:
+                       addr_in = (struct sockaddr_in *)addr;
+                       s = sprintf(buf0, "AF_INET      %pI4\n", &addr_in->sin_addr.s_addr);
+                       break;
+               case AF_INET6:
+                       addr_in6 = (struct sockaddr_in6 *)addr;
+                       s = sprintf(buf0, "AF_INET6     %pI6\n", &addr_in6->sin6_addr);
+                       break;
+               default:
+                       s = sprintf(buf0, "%s\n", "<UNKNOWN>");
+                       break;
+               }
+               allowance -= s;
+               if (allowance >= 0)
+                       strcat(buf, buf0);
+               else {
+                       allowance += s;
+                       break;
+               }
+       }
+       return 4096 - allowance;
+}
+
 static ssize_t show_node(struct config_item *i, struct configfs_attribute *a,
                         char *buf)
 {
@@ -983,7 +1038,6 @@ int dlm_our_addr(struct sockaddr_storage *addr, int num)
 #define DEFAULT_TCP_PORT       21064
 #define DEFAULT_BUFFER_SIZE     4096
 #define DEFAULT_RSBTBL_SIZE     1024
-#define DEFAULT_LKBTBL_SIZE     1024
 #define DEFAULT_DIRTBL_SIZE     1024
 #define DEFAULT_RECOVER_TIMER      5
 #define DEFAULT_TOSS_SECS         10
@@ -992,12 +1046,12 @@ int dlm_our_addr(struct sockaddr_storage *addr, int num)
 #define DEFAULT_PROTOCOL           0
 #define DEFAULT_TIMEWARN_CS      500 /* 5 sec = 500 centiseconds */
 #define DEFAULT_WAITWARN_US       0
+#define DEFAULT_NEW_RSB_COUNT    128
 
 struct dlm_config_info dlm_config = {
        .ci_tcp_port = DEFAULT_TCP_PORT,
        .ci_buffer_size = DEFAULT_BUFFER_SIZE,
        .ci_rsbtbl_size = DEFAULT_RSBTBL_SIZE,
-       .ci_lkbtbl_size = DEFAULT_LKBTBL_SIZE,
        .ci_dirtbl_size = DEFAULT_DIRTBL_SIZE,
        .ci_recover_timer = DEFAULT_RECOVER_TIMER,
        .ci_toss_secs = DEFAULT_TOSS_SECS,
@@ -1005,6 +1059,7 @@ struct dlm_config_info dlm_config = {
        .ci_log_debug = DEFAULT_LOG_DEBUG,
        .ci_protocol = DEFAULT_PROTOCOL,
        .ci_timewarn_cs = DEFAULT_TIMEWARN_CS,
-       .ci_waitwarn_us = DEFAULT_WAITWARN_US
+       .ci_waitwarn_us = DEFAULT_WAITWARN_US,
+       .ci_new_rsb_count = DEFAULT_NEW_RSB_COUNT
 };
 
index dd0ce24..3099d0d 100644 (file)
@@ -20,7 +20,6 @@ struct dlm_config_info {
        int ci_tcp_port;
        int ci_buffer_size;
        int ci_rsbtbl_size;
-       int ci_lkbtbl_size;
        int ci_dirtbl_size;
        int ci_recover_timer;
        int ci_toss_secs;
@@ -29,6 +28,7 @@ struct dlm_config_info {
        int ci_protocol;
        int ci_timewarn_cs;
        int ci_waitwarn_us;
+       int ci_new_rsb_count;
 };
 
 extern struct dlm_config_info dlm_config;
index 0262451..fe2860c 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/jhash.h>
 #include <linux/miscdevice.h>
 #include <linux/mutex.h>
+#include <linux/idr.h>
 #include <asm/uaccess.h>
 
 #include <linux/dlm.h>
@@ -52,7 +53,6 @@ struct dlm_ls;
 struct dlm_lkb;
 struct dlm_rsb;
 struct dlm_member;
-struct dlm_lkbtable;
 struct dlm_rsbtable;
 struct dlm_dirtable;
 struct dlm_direntry;
@@ -108,11 +108,6 @@ struct dlm_rsbtable {
        spinlock_t              lock;
 };
 
-struct dlm_lkbtable {
-       struct list_head        list;
-       rwlock_t                lock;
-       uint16_t                counter;
-};
 
 /*
  * Lockspace member (per node in a ls)
@@ -248,17 +243,18 @@ struct dlm_lkb {
        int8_t                  lkb_wait_count;
        int                     lkb_wait_nodeid; /* for debugging */
 
-       struct list_head        lkb_idtbl_list; /* lockspace lkbtbl */
        struct list_head        lkb_statequeue; /* rsb g/c/w list */
        struct list_head        lkb_rsb_lookup; /* waiting for rsb lookup */
        struct list_head        lkb_wait_reply; /* waiting for remote reply */
-       struct list_head        lkb_astqueue;   /* need ast to be sent */
        struct list_head        lkb_ownqueue;   /* list of locks for a process */
        struct list_head        lkb_time_list;
        ktime_t                 lkb_timestamp;
        ktime_t                 lkb_wait_time;
        unsigned long           lkb_timeout_cs;
 
+       struct mutex            lkb_cb_mutex;
+       struct work_struct      lkb_cb_work;
+       struct list_head        lkb_cb_list; /* for ls_cb_delay or proc->asts */
        struct dlm_callback     lkb_callbacks[DLM_CALLBACKS_SIZE];
        struct dlm_callback     lkb_last_cast;
        struct dlm_callback     lkb_last_bast;
@@ -299,7 +295,7 @@ struct dlm_rsb {
        int                     res_recover_locks_count;
 
        char                    *res_lvbptr;
-       char                    res_name[1];
+       char                    res_name[DLM_RESNAME_MAXLEN+1];
 };
 
 /* find_rsb() flags */
@@ -465,12 +461,12 @@ struct dlm_ls {
        unsigned long           ls_scan_time;
        struct kobject          ls_kobj;
 
+       struct idr              ls_lkbidr;
+       spinlock_t              ls_lkbidr_spin;
+
        struct dlm_rsbtable     *ls_rsbtbl;
        uint32_t                ls_rsbtbl_size;
 
-       struct dlm_lkbtable     *ls_lkbtbl;
-       uint32_t                ls_lkbtbl_size;
-
        struct dlm_dirtable     *ls_dirtbl;
        uint32_t                ls_dirtbl_size;
 
@@ -483,6 +479,10 @@ struct dlm_ls {
        struct mutex            ls_timeout_mutex;
        struct list_head        ls_timeout;
 
+       spinlock_t              ls_new_rsb_spin;
+       int                     ls_new_rsb_count;
+       struct list_head        ls_new_rsb;     /* new rsb structs */
+
        struct list_head        ls_nodes;       /* current nodes in ls */
        struct list_head        ls_nodes_gone;  /* dead node list, recovery */
        int                     ls_num_nodes;   /* number of nodes in ls */
@@ -506,8 +506,12 @@ struct dlm_ls {
 
        struct miscdevice       ls_device;
 
+       struct workqueue_struct *ls_callback_wq;
+
        /* recovery related */
 
+       struct mutex            ls_cb_mutex;
+       struct list_head        ls_cb_delay; /* save for queue_work later */
        struct timer_list       ls_timer;
        struct task_struct      *ls_recoverd_task;
        struct mutex            ls_recoverd_active;
@@ -544,6 +548,7 @@ struct dlm_ls {
 #define LSFL_RCOM_WAIT         4
 #define LSFL_UEVENT_WAIT       5
 #define LSFL_TIMEWARN          6
+#define LSFL_CB_DELAY          7
 
 /* much of this is just saving user space pointers associated with the
    lock that we pass back to the user lib with an ast */
index f71d0b5..83b5e32 100644 (file)
@@ -305,7 +305,7 @@ static void queue_cast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv)
                rv = -EDEADLK;
        }
 
-       dlm_add_ast(lkb, DLM_CB_CAST, lkb->lkb_grmode, rv, lkb->lkb_sbflags);
+       dlm_add_cb(lkb, DLM_CB_CAST, lkb->lkb_grmode, rv, lkb->lkb_sbflags);
 }
 
 static inline void queue_cast_overlap(struct dlm_rsb *r, struct dlm_lkb *lkb)
@@ -319,7 +319,7 @@ static void queue_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rqmode)
        if (is_master_copy(lkb)) {
                send_bast(r, lkb, rqmode);
        } else {
-               dlm_add_ast(lkb, DLM_CB_BAST, rqmode, 0, 0);
+               dlm_add_cb(lkb, DLM_CB_BAST, rqmode, 0, 0);
        }
 }
 
@@ -327,19 +327,68 @@ static void queue_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rqmode)
  * Basic operations on rsb's and lkb's
  */
 
-static struct dlm_rsb *create_rsb(struct dlm_ls *ls, char *name, int len)
+static int pre_rsb_struct(struct dlm_ls *ls)
+{
+       struct dlm_rsb *r1, *r2;
+       int count = 0;
+
+       spin_lock(&ls->ls_new_rsb_spin);
+       if (ls->ls_new_rsb_count > dlm_config.ci_new_rsb_count / 2) {
+               spin_unlock(&ls->ls_new_rsb_spin);
+               return 0;
+       }
+       spin_unlock(&ls->ls_new_rsb_spin);
+
+       r1 = dlm_allocate_rsb(ls);
+       r2 = dlm_allocate_rsb(ls);
+
+       spin_lock(&ls->ls_new_rsb_spin);
+       if (r1) {
+               list_add(&r1->res_hashchain, &ls->ls_new_rsb);
+               ls->ls_new_rsb_count++;
+       }
+       if (r2) {
+               list_add(&r2->res_hashchain, &ls->ls_new_rsb);
+               ls->ls_new_rsb_count++;
+       }
+       count = ls->ls_new_rsb_count;
+       spin_unlock(&ls->ls_new_rsb_spin);
+
+       if (!count)
+               return -ENOMEM;
+       return 0;
+}
+
+/* If ls->ls_new_rsb is empty, return -EAGAIN, so the caller can
+   unlock any spinlocks, go back and call pre_rsb_struct again.
+   Otherwise, take an rsb off the list and return it. */
+
+static int get_rsb_struct(struct dlm_ls *ls, char *name, int len,
+                         struct dlm_rsb **r_ret)
 {
        struct dlm_rsb *r;
+       int count;
 
-       r = dlm_allocate_rsb(ls, len);
-       if (!r)
-               return NULL;
+       spin_lock(&ls->ls_new_rsb_spin);
+       if (list_empty(&ls->ls_new_rsb)) {
+               count = ls->ls_new_rsb_count;
+               spin_unlock(&ls->ls_new_rsb_spin);
+               log_debug(ls, "find_rsb retry %d %d %s",
+                         count, dlm_config.ci_new_rsb_count, name);
+               return -EAGAIN;
+       }
+
+       r = list_first_entry(&ls->ls_new_rsb, struct dlm_rsb, res_hashchain);
+       list_del(&r->res_hashchain);
+       ls->ls_new_rsb_count--;
+       spin_unlock(&ls->ls_new_rsb_spin);
 
        r->res_ls = ls;
        r->res_length = len;
        memcpy(r->res_name, name, len);
        mutex_init(&r->res_mutex);
 
+       INIT_LIST_HEAD(&r->res_hashchain);
        INIT_LIST_HEAD(&r->res_lookup);
        INIT_LIST_HEAD(&r->res_grantqueue);
        INIT_LIST_HEAD(&r->res_convertqueue);
@@ -347,7 +396,8 @@ static struct dlm_rsb *create_rsb(struct dlm_ls *ls, char *name, int len)
        INIT_LIST_HEAD(&r->res_root_list);
        INIT_LIST_HEAD(&r->res_recover_list);
 
-       return r;
+       *r_ret = r;
+       return 0;
 }
 
 static int search_rsb_list(struct list_head *head, char *name, int len,
@@ -405,16 +455,6 @@ static int _search_rsb(struct dlm_ls *ls, char *name, int len, int b,
        return error;
 }
 
-static int search_rsb(struct dlm_ls *ls, char *name, int len, int b,
-                     unsigned int flags, struct dlm_rsb **r_ret)
-{
-       int error;
-       spin_lock(&ls->ls_rsbtbl[b].lock);
-       error = _search_rsb(ls, name, len, b, flags, r_ret);
-       spin_unlock(&ls->ls_rsbtbl[b].lock);
-       return error;
-}
-
 /*
  * Find rsb in rsbtbl and potentially create/add one
  *
@@ -432,35 +472,48 @@ static int search_rsb(struct dlm_ls *ls, char *name, int len, int b,
 static int find_rsb(struct dlm_ls *ls, char *name, int namelen,
                    unsigned int flags, struct dlm_rsb **r_ret)
 {
-       struct dlm_rsb *r = NULL, *tmp;
+       struct dlm_rsb *r = NULL;
        uint32_t hash, bucket;
-       int error = -EINVAL;
+       int error;
 
-       if (namelen > DLM_RESNAME_MAXLEN)
+       if (namelen > DLM_RESNAME_MAXLEN) {
+               error = -EINVAL;
                goto out;
+       }
 
        if (dlm_no_directory(ls))
                flags |= R_CREATE;
 
-       error = 0;
        hash = jhash(name, namelen, 0);
        bucket = hash & (ls->ls_rsbtbl_size - 1);
 
-       error = search_rsb(ls, name, namelen, bucket, flags, &r);
+ retry:
+       if (flags & R_CREATE) {
+               error = pre_rsb_struct(ls);
+               if (error < 0)
+                       goto out;
+       }
+
+       spin_lock(&ls->ls_rsbtbl[bucket].lock);
+
+       error = _search_rsb(ls, name, namelen, bucket, flags, &r);
        if (!error)
-               goto out;
+               goto out_unlock;
 
        if (error == -EBADR && !(flags & R_CREATE))
-               goto out;
+               goto out_unlock;
 
        /* the rsb was found but wasn't a master copy */
        if (error == -ENOTBLK)
-               goto out;
+               goto out_unlock;
 
-       error = -ENOMEM;
-       r = create_rsb(ls, name, namelen);
-       if (!r)
-               goto out;
+       error = get_rsb_struct(ls, name, namelen, &r);
+       if (error == -EAGAIN) {
+               spin_unlock(&ls->ls_rsbtbl[bucket].lock);
+               goto retry;
+       }
+       if (error)
+               goto out_unlock;
 
        r->res_hash = hash;
        r->res_bucket = bucket;
@@ -474,18 +527,10 @@ static int find_rsb(struct dlm_ls *ls, char *name, int namelen,
                        nodeid = 0;
                r->res_nodeid = nodeid;
        }
-
-       spin_lock(&ls->ls_rsbtbl[bucket].lock);
-       error = _search_rsb(ls, name, namelen, bucket, 0, &tmp);
-       if (!error) {
-               spin_unlock(&ls->ls_rsbtbl[bucket].lock);
-               dlm_free_rsb(r);
-               r = tmp;
-               goto out;
-       }
        list_add(&r->res_hashchain, &ls->ls_rsbtbl[bucket].list);
-       spin_unlock(&ls->ls_rsbtbl[bucket].lock);
        error = 0;
+ out_unlock:
+       spin_unlock(&ls->ls_rsbtbl[bucket].lock);
  out:
        *r_ret = r;
        return error;
@@ -580,9 +625,8 @@ static void detach_lkb(struct dlm_lkb *lkb)
 
 static int create_lkb(struct dlm_ls *ls, struct dlm_lkb **lkb_ret)
 {
-       struct dlm_lkb *lkb, *tmp;
-       uint32_t lkid = 0;
-       uint16_t bucket;
+       struct dlm_lkb *lkb;
+       int rv, id;
 
        lkb = dlm_allocate_lkb(ls);
        if (!lkb)
@@ -594,60 +638,42 @@ static int create_lkb(struct dlm_ls *ls, struct dlm_lkb **lkb_ret)
        INIT_LIST_HEAD(&lkb->lkb_ownqueue);
        INIT_LIST_HEAD(&lkb->lkb_rsb_lookup);
        INIT_LIST_HEAD(&lkb->lkb_time_list);
-       INIT_LIST_HEAD(&lkb->lkb_astqueue);
+       INIT_LIST_HEAD(&lkb->lkb_cb_list);
+       mutex_init(&lkb->lkb_cb_mutex);
+       INIT_WORK(&lkb->lkb_cb_work, dlm_callback_work);
 
-       get_random_bytes(&bucket, sizeof(bucket));
-       bucket &= (ls->ls_lkbtbl_size - 1);
-
-       write_lock(&ls->ls_lkbtbl[bucket].lock);
+ retry:
+       rv = idr_pre_get(&ls->ls_lkbidr, GFP_NOFS);
+       if (!rv)
+               return -ENOMEM;
 
-       /* counter can roll over so we must verify lkid is not in use */
+       spin_lock(&ls->ls_lkbidr_spin);
+       rv = idr_get_new_above(&ls->ls_lkbidr, lkb, 1, &id);
+       if (!rv)
+               lkb->lkb_id = id;
+       spin_unlock(&ls->ls_lkbidr_spin);
 
-       while (lkid == 0) {
-               lkid = (bucket << 16) | ls->ls_lkbtbl[bucket].counter++;
+       if (rv == -EAGAIN)
+               goto retry;
 
-               list_for_each_entry(tmp, &ls->ls_lkbtbl[bucket].list,
-                                   lkb_idtbl_list) {
-                       if (tmp->lkb_id != lkid)
-                               continue;
-                       lkid = 0;
-                       break;
-               }
+       if (rv < 0) {
+               log_error(ls, "create_lkb idr error %d", rv);
+               return rv;
        }
 
-       lkb->lkb_id = lkid;
-       list_add(&lkb->lkb_idtbl_list, &ls->ls_lkbtbl[bucket].list);
-       write_unlock(&ls->ls_lkbtbl[bucket].lock);
-
        *lkb_ret = lkb;
        return 0;
 }
 
-static struct dlm_lkb *__find_lkb(struct dlm_ls *ls, uint32_t lkid)
-{
-       struct dlm_lkb *lkb;
-       uint16_t bucket = (lkid >> 16);
-
-       list_for_each_entry(lkb, &ls->ls_lkbtbl[bucket].list, lkb_idtbl_list) {
-               if (lkb->lkb_id == lkid)
-                       return lkb;
-       }
-       return NULL;
-}
-
 static int find_lkb(struct dlm_ls *ls, uint32_t lkid, struct dlm_lkb **lkb_ret)
 {
        struct dlm_lkb *lkb;
-       uint16_t bucket = (lkid >> 16);
-
-       if (bucket >= ls->ls_lkbtbl_size)
-               return -EBADSLT;
 
-       read_lock(&ls->ls_lkbtbl[bucket].lock);
-       lkb = __find_lkb(ls, lkid);
+       spin_lock(&ls->ls_lkbidr_spin);
+       lkb = idr_find(&ls->ls_lkbidr, lkid);
        if (lkb)
                kref_get(&lkb->lkb_ref);
-       read_unlock(&ls->ls_lkbtbl[bucket].lock);
+       spin_unlock(&ls->ls_lkbidr_spin);
 
        *lkb_ret = lkb;
        return lkb ? 0 : -ENOENT;
@@ -668,12 +694,12 @@ static void kill_lkb(struct kref *kref)
 
 static int __put_lkb(struct dlm_ls *ls, struct dlm_lkb *lkb)
 {
-       uint16_t bucket = (lkb->lkb_id >> 16);
+       uint32_t lkid = lkb->lkb_id;
 
-       write_lock(&ls->ls_lkbtbl[bucket].lock);
+       spin_lock(&ls->ls_lkbidr_spin);
        if (kref_put(&lkb->lkb_ref, kill_lkb)) {
-               list_del(&lkb->lkb_idtbl_list);
-               write_unlock(&ls->ls_lkbtbl[bucket].lock);
+               idr_remove(&ls->ls_lkbidr, lkid);
+               spin_unlock(&ls->ls_lkbidr_spin);
 
                detach_lkb(lkb);
 
@@ -683,7 +709,7 @@ static int __put_lkb(struct dlm_ls *ls, struct dlm_lkb *lkb)
                dlm_free_lkb(lkb);
                return 1;
        } else {
-               write_unlock(&ls->ls_lkbtbl[bucket].lock);
+               spin_unlock(&ls->ls_lkbidr_spin);
                return 0;
        }
 }
@@ -849,9 +875,7 @@ void dlm_scan_waiters(struct dlm_ls *ls)
 
                if (!num_nodes) {
                        num_nodes = ls->ls_num_nodes;
-                       warned = kmalloc(GFP_KERNEL, num_nodes * sizeof(int));
-                       if (warned)
-                               memset(warned, 0, num_nodes * sizeof(int));
+                       warned = kzalloc(num_nodes * sizeof(int), GFP_KERNEL);
                }
                if (!warned)
                        continue;
@@ -863,9 +887,7 @@ void dlm_scan_waiters(struct dlm_ls *ls)
                          dlm_config.ci_waitwarn_us, lkb->lkb_wait_nodeid);
        }
        mutex_unlock(&ls->ls_waiters_mutex);
-
-       if (warned)
-               kfree(warned);
+       kfree(warned);
 
        if (debug_expired)
                log_debug(ls, "scan_waiters %u warn %u over %d us max %lld us",
@@ -2401,9 +2423,6 @@ static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb)
 
        if (deadlk) {
                /* it's left on the granted queue */
-               log_debug(r->res_ls, "deadlock %x node %d sts%d g%d r%d %s",
-                         lkb->lkb_id, lkb->lkb_nodeid, lkb->lkb_status,
-                         lkb->lkb_grmode, lkb->lkb_rqmode, r->res_name);
                revert_lock(r, lkb);
                queue_cast(r, lkb, -EDEADLK);
                error = -EDEADLK;
@@ -3993,8 +4012,6 @@ static void _receive_message(struct dlm_ls *ls, struct dlm_message *ms)
        default:
                log_error(ls, "unknown message type %d", ms->m_type);
        }
-
-       dlm_astd_wake();
 }
 
 /* If the lockspace is in recovery mode (locking stopped), then normal
@@ -4133,7 +4150,7 @@ void dlm_recover_waiters_pre(struct dlm_ls *ls)
        struct dlm_message *ms_stub;
        int wait_type, stub_unlock_result, stub_cancel_result;
 
-       ms_stub = kmalloc(GFP_KERNEL, sizeof(struct dlm_message));
+       ms_stub = kmalloc(sizeof(struct dlm_message), GFP_KERNEL);
        if (!ms_stub) {
                log_error(ls, "dlm_recover_waiters_pre no mem");
                return;
@@ -4809,7 +4826,7 @@ int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp,
                goto out_put;
 
        spin_lock(&ua->proc->locks_spin);
-       /* dlm_user_add_ast() may have already taken lkb off the proc list */
+       /* dlm_user_add_cb() may have already taken lkb off the proc list */
        if (!list_empty(&lkb->lkb_ownqueue))
                list_move(&lkb->lkb_ownqueue, &ua->proc->unlocking);
        spin_unlock(&ua->proc->locks_spin);
@@ -4946,7 +4963,7 @@ static int unlock_proc_lock(struct dlm_ls *ls, struct dlm_lkb *lkb)
 
 /* We have to release clear_proc_locks mutex before calling unlock_proc_lock()
    (which does lock_rsb) due to deadlock with receiving a message that does
-   lock_rsb followed by dlm_user_add_ast() */
+   lock_rsb followed by dlm_user_add_cb() */
 
 static struct dlm_lkb *del_proc_lock(struct dlm_ls *ls,
                                     struct dlm_user_proc *proc)
@@ -4969,7 +4986,7 @@ static struct dlm_lkb *del_proc_lock(struct dlm_ls *ls,
        return lkb;
 }
 
-/* The ls_clear_proc_locks mutex protects against dlm_user_add_asts() which
+/* The ls_clear_proc_locks mutex protects against dlm_user_add_cb() which
    1) references lkb->ua which we free here and 2) adds lkbs to proc->asts,
    which we clear here. */
 
@@ -5011,10 +5028,10 @@ void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc)
                dlm_put_lkb(lkb);
        }
 
-       list_for_each_entry_safe(lkb, safe, &proc->asts, lkb_astqueue) {
+       list_for_each_entry_safe(lkb, safe, &proc->asts, lkb_cb_list) {
                memset(&lkb->lkb_callbacks, 0,
                       sizeof(struct dlm_callback) * DLM_CALLBACKS_SIZE);
-               list_del_init(&lkb->lkb_astqueue);
+               list_del_init(&lkb->lkb_cb_list);
                dlm_put_lkb(lkb);
        }
 
@@ -5053,10 +5070,10 @@ static void purge_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc)
        spin_unlock(&proc->locks_spin);
 
        spin_lock(&proc->asts_spin);
-       list_for_each_entry_safe(lkb, safe, &proc->asts, lkb_astqueue) {
+       list_for_each_entry_safe(lkb, safe, &proc->asts, lkb_cb_list) {
                memset(&lkb->lkb_callbacks, 0,
                       sizeof(struct dlm_callback) * DLM_CALLBACKS_SIZE);
-               list_del_init(&lkb->lkb_astqueue);
+               list_del_init(&lkb->lkb_cb_list);
                dlm_put_lkb(lkb);
        }
        spin_unlock(&proc->asts_spin);
index 14cbf40..a1d8f1a 100644 (file)
@@ -15,7 +15,6 @@
 #include "lockspace.h"
 #include "member.h"
 #include "recoverd.h"
-#include "ast.h"
 #include "dir.h"
 #include "lowcomms.h"
 #include "config.h"
@@ -24,6 +23,7 @@
 #include "recover.h"
 #include "requestqueue.h"
 #include "user.h"
+#include "ast.h"
 
 static int                     ls_count;
 static struct mutex            ls_lock;
@@ -359,17 +359,10 @@ static int threads_start(void)
 {
        int error;
 
-       /* Thread which process lock requests for all lockspace's */
-       error = dlm_astd_start();
-       if (error) {
-               log_print("cannot start dlm_astd thread %d", error);
-               goto fail;
-       }
-
        error = dlm_scand_start();
        if (error) {
                log_print("cannot start dlm_scand thread %d", error);
-               goto astd_fail;
+               goto fail;
        }
 
        /* Thread for sending/receiving messages for all lockspace's */
@@ -383,8 +376,6 @@ static int threads_start(void)
 
  scand_fail:
        dlm_scand_stop();
- astd_fail:
-       dlm_astd_stop();
  fail:
        return error;
 }
@@ -393,7 +384,6 @@ static void threads_stop(void)
 {
        dlm_scand_stop();
        dlm_lowcomms_stop();
-       dlm_astd_stop();
 }
 
 static int new_lockspace(const char *name, int namelen, void **lockspace,
@@ -463,7 +453,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        size = dlm_config.ci_rsbtbl_size;
        ls->ls_rsbtbl_size = size;
 
-       ls->ls_rsbtbl = kmalloc(sizeof(struct dlm_rsbtable) * size, GFP_NOFS);
+       ls->ls_rsbtbl = vmalloc(sizeof(struct dlm_rsbtable) * size);
        if (!ls->ls_rsbtbl)
                goto out_lsfree;
        for (i = 0; i < size; i++) {
@@ -472,22 +462,13 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
                spin_lock_init(&ls->ls_rsbtbl[i].lock);
        }
 
-       size = dlm_config.ci_lkbtbl_size;
-       ls->ls_lkbtbl_size = size;
-
-       ls->ls_lkbtbl = kmalloc(sizeof(struct dlm_lkbtable) * size, GFP_NOFS);
-       if (!ls->ls_lkbtbl)
-               goto out_rsbfree;
-       for (i = 0; i < size; i++) {
-               INIT_LIST_HEAD(&ls->ls_lkbtbl[i].list);
-               rwlock_init(&ls->ls_lkbtbl[i].lock);
-               ls->ls_lkbtbl[i].counter = 1;
-       }
+       idr_init(&ls->ls_lkbidr);
+       spin_lock_init(&ls->ls_lkbidr_spin);
 
        size = dlm_config.ci_dirtbl_size;
        ls->ls_dirtbl_size = size;
 
-       ls->ls_dirtbl = kmalloc(sizeof(struct dlm_dirtable) * size, GFP_NOFS);
+       ls->ls_dirtbl = vmalloc(sizeof(struct dlm_dirtable) * size);
        if (!ls->ls_dirtbl)
                goto out_lkbfree;
        for (i = 0; i < size; i++) {
@@ -502,6 +483,9 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        INIT_LIST_HEAD(&ls->ls_timeout);
        mutex_init(&ls->ls_timeout_mutex);
 
+       INIT_LIST_HEAD(&ls->ls_new_rsb);
+       spin_lock_init(&ls->ls_new_rsb_spin);
+
        INIT_LIST_HEAD(&ls->ls_nodes);
        INIT_LIST_HEAD(&ls->ls_nodes_gone);
        ls->ls_num_nodes = 0;
@@ -520,6 +504,9 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        init_completion(&ls->ls_members_done);
        ls->ls_members_result = -1;
 
+       mutex_init(&ls->ls_cb_mutex);
+       INIT_LIST_HEAD(&ls->ls_cb_delay);
+
        ls->ls_recoverd_task = NULL;
        mutex_init(&ls->ls_recoverd_active);
        spin_lock_init(&ls->ls_recover_lock);
@@ -553,18 +540,26 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        list_add(&ls->ls_list, &lslist);
        spin_unlock(&lslist_lock);
 
+       if (flags & DLM_LSFL_FS) {
+               error = dlm_callback_start(ls);
+               if (error) {
+                       log_error(ls, "can't start dlm_callback %d", error);
+                       goto out_delist;
+               }
+       }
+
        /* needs to find ls in lslist */
        error = dlm_recoverd_start(ls);
        if (error) {
                log_error(ls, "can't start dlm_recoverd %d", error);
-               goto out_delist;
+               goto out_callback;
        }
 
        ls->ls_kobj.kset = dlm_kset;
        error = kobject_init_and_add(&ls->ls_kobj, &dlm_ktype, NULL,
                                     "%s", ls->ls_name);
        if (error)
-               goto out_stop;
+               goto out_recoverd;
        kobject_uevent(&ls->ls_kobj, KOBJ_ADD);
 
        /* let kobject handle freeing of ls if there's an error */
@@ -578,7 +573,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
 
        error = do_uevent(ls, 1);
        if (error)
-               goto out_stop;
+               goto out_recoverd;
 
        wait_for_completion(&ls->ls_members_done);
        error = ls->ls_members_result;
@@ -595,19 +590,20 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        do_uevent(ls, 0);
        dlm_clear_members(ls);
        kfree(ls->ls_node_array);
- out_stop:
+ out_recoverd:
        dlm_recoverd_stop(ls);
+ out_callback:
+       dlm_callback_stop(ls);
  out_delist:
        spin_lock(&lslist_lock);
        list_del(&ls->ls_list);
        spin_unlock(&lslist_lock);
        kfree(ls->ls_recover_buf);
  out_dirfree:
-       kfree(ls->ls_dirtbl);
+       vfree(ls->ls_dirtbl);
  out_lkbfree:
-       kfree(ls->ls_lkbtbl);
- out_rsbfree:
-       kfree(ls->ls_rsbtbl);
+       idr_destroy(&ls->ls_lkbidr);
+       vfree(ls->ls_rsbtbl);
  out_lsfree:
        if (do_unreg)
                kobject_put(&ls->ls_kobj);
@@ -641,50 +637,64 @@ int dlm_new_lockspace(const char *name, int namelen, void **lockspace,
        return error;
 }
 
-/* Return 1 if the lockspace still has active remote locks,
- *        2 if the lockspace still has active local locks.
- */
-static int lockspace_busy(struct dlm_ls *ls)
-{
-       int i, lkb_found = 0;
-       struct dlm_lkb *lkb;
-
-       /* NOTE: We check the lockidtbl here rather than the resource table.
-          This is because there may be LKBs queued as ASTs that have been
-          unlinked from their RSBs and are pending deletion once the AST has
-          been delivered */
-
-       for (i = 0; i < ls->ls_lkbtbl_size; i++) {
-               read_lock(&ls->ls_lkbtbl[i].lock);
-               if (!list_empty(&ls->ls_lkbtbl[i].list)) {
-                       lkb_found = 1;
-                       list_for_each_entry(lkb, &ls->ls_lkbtbl[i].list,
-                                           lkb_idtbl_list) {
-                               if (!lkb->lkb_nodeid) {
-                                       read_unlock(&ls->ls_lkbtbl[i].lock);
-                                       return 2;
-                               }
-                       }
-               }
-               read_unlock(&ls->ls_lkbtbl[i].lock);
+static int lkb_idr_is_local(int id, void *p, void *data)
+{
+       struct dlm_lkb *lkb = p;
+
+       if (!lkb->lkb_nodeid)
+               return 1;
+       return 0;
+}
+
+static int lkb_idr_is_any(int id, void *p, void *data)
+{
+       return 1;
+}
+
+static int lkb_idr_free(int id, void *p, void *data)
+{
+       struct dlm_lkb *lkb = p;
+
+       if (lkb->lkb_lvbptr && lkb->lkb_flags & DLM_IFL_MSTCPY)
+               dlm_free_lvb(lkb->lkb_lvbptr);
+
+       dlm_free_lkb(lkb);
+       return 0;
+}
+
+/* NOTE: We check the lkbidr here rather than the resource table.
+   This is because there may be LKBs queued as ASTs that have been unlinked
+   from their RSBs and are pending deletion once the AST has been delivered */
+
+static int lockspace_busy(struct dlm_ls *ls, int force)
+{
+       int rv;
+
+       spin_lock(&ls->ls_lkbidr_spin);
+       if (force == 0) {
+               rv = idr_for_each(&ls->ls_lkbidr, lkb_idr_is_any, ls);
+       } else if (force == 1) {
+               rv = idr_for_each(&ls->ls_lkbidr, lkb_idr_is_local, ls);
+       } else {
+               rv = 0;
        }
-       return lkb_found;
+       spin_unlock(&ls->ls_lkbidr_spin);
+       return rv;
 }
 
 static int release_lockspace(struct dlm_ls *ls, int force)
 {
-       struct dlm_lkb *lkb;
        struct dlm_rsb *rsb;
        struct list_head *head;
        int i, busy, rv;
 
-       busy = lockspace_busy(ls);
+       busy = lockspace_busy(ls, force);
 
        spin_lock(&lslist_lock);
        if (ls->ls_create_count == 1) {
-               if (busy > force)
+               if (busy) {
                        rv = -EBUSY;
-               else {
+               } else {
                        /* remove_lockspace takes ls off lslist */
                        ls->ls_create_count = 0;
                        rv = 0;
@@ -708,12 +718,12 @@ static int release_lockspace(struct dlm_ls *ls, int force)
 
        dlm_recoverd_stop(ls);
 
+       dlm_callback_stop(ls);
+
        remove_lockspace(ls);
 
        dlm_delete_debug_file(ls);
 
-       dlm_astd_suspend();
-
        kfree(ls->ls_recover_buf);
 
        /*
@@ -721,31 +731,15 @@ static int release_lockspace(struct dlm_ls *ls, int force)
         */
 
        dlm_dir_clear(ls);
-       kfree(ls->ls_dirtbl);
+       vfree(ls->ls_dirtbl);
 
        /*
-        * Free all lkb's on lkbtbl[] lists.
+        * Free all lkb's in idr
         */
 
-       for (i = 0; i < ls->ls_lkbtbl_size; i++) {
-               head = &ls->ls_lkbtbl[i].list;
-               while (!list_empty(head)) {
-                       lkb = list_entry(head->next, struct dlm_lkb,
-                                        lkb_idtbl_list);
-
-                       list_del(&lkb->lkb_idtbl_list);
-
-                       dlm_del_ast(lkb);
-
-                       if (lkb->lkb_lvbptr && lkb->lkb_flags & DLM_IFL_MSTCPY)
-                               dlm_free_lvb(lkb->lkb_lvbptr);
-
-                       dlm_free_lkb(lkb);
-               }
-       }
-       dlm_astd_resume();
-
-       kfree(ls->ls_lkbtbl);
+       idr_for_each(&ls->ls_lkbidr, lkb_idr_free, ls);
+       idr_remove_all(&ls->ls_lkbidr);
+       idr_destroy(&ls->ls_lkbidr);
 
        /*
         * Free all rsb's on rsbtbl[] lists
@@ -770,7 +764,14 @@ static int release_lockspace(struct dlm_ls *ls, int force)
                }
        }
 
-       kfree(ls->ls_rsbtbl);
+       vfree(ls->ls_rsbtbl);
+
+       while (!list_empty(&ls->ls_new_rsb)) {
+               rsb = list_first_entry(&ls->ls_new_rsb, struct dlm_rsb,
+                                      res_hashchain);
+               list_del(&rsb->res_hashchain);
+               dlm_free_rsb(rsb);
+       }
 
        /*
         * Free structures on any other lists
index 5e2c71f..990626e 100644 (file)
@@ -512,12 +512,10 @@ static void process_sctp_notification(struct connection *con,
                        }
                        make_sockaddr(&prim.ssp_addr, 0, &addr_len);
                        if (dlm_addr_to_nodeid(&prim.ssp_addr, &nodeid)) {
-                               int i;
                                unsigned char *b=(unsigned char *)&prim.ssp_addr;
                                log_print("reject connect from unknown addr");
-                               for (i=0; i<sizeof(struct sockaddr_storage);i++)
-                                       printk("%02x ", b[i]);
-                               printk("\n");
+                               print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE, 
+                                                    b, sizeof(struct sockaddr_storage));
                                sctp_send_shutdown(prim.ssp_assoc_id);
                                return;
                        }
@@ -748,7 +746,10 @@ static int tcp_accept_from_sock(struct connection *con)
        /* Get the new node's NODEID */
        make_sockaddr(&peeraddr, 0, &len);
        if (dlm_addr_to_nodeid(&peeraddr, &nodeid)) {
+               unsigned char *b=(unsigned char *)&peeraddr;
                log_print("connect from non cluster node");
+               print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE, 
+                                    b, sizeof(struct sockaddr_storage));
                sock_release(newsock);
                mutex_unlock(&con->sock_mutex);
                return -1;
index 8e0d00d..da64df7 100644 (file)
@@ -16,6 +16,7 @@
 #include "memory.h"
 
 static struct kmem_cache *lkb_cache;
+static struct kmem_cache *rsb_cache;
 
 
 int __init dlm_memory_init(void)
@@ -26,6 +27,14 @@ int __init dlm_memory_init(void)
                                __alignof__(struct dlm_lkb), 0, NULL);
        if (!lkb_cache)
                ret = -ENOMEM;
+
+       rsb_cache = kmem_cache_create("dlm_rsb", sizeof(struct dlm_rsb),
+                               __alignof__(struct dlm_rsb), 0, NULL);
+       if (!rsb_cache) {
+               kmem_cache_destroy(lkb_cache);
+               ret = -ENOMEM;
+       }
+
        return ret;
 }
 
@@ -33,6 +42,8 @@ void dlm_memory_exit(void)
 {
        if (lkb_cache)
                kmem_cache_destroy(lkb_cache);
+       if (rsb_cache)
+               kmem_cache_destroy(rsb_cache);
 }
 
 char *dlm_allocate_lvb(struct dlm_ls *ls)
@@ -48,16 +59,11 @@ void dlm_free_lvb(char *p)
        kfree(p);
 }
 
-/* FIXME: have some minimal space built-in to rsb for the name and
-   kmalloc a separate name if needed, like dentries are done */
-
-struct dlm_rsb *dlm_allocate_rsb(struct dlm_ls *ls, int namelen)
+struct dlm_rsb *dlm_allocate_rsb(struct dlm_ls *ls)
 {
        struct dlm_rsb *r;
 
-       DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN,);
-
-       r = kzalloc(sizeof(*r) + namelen, GFP_NOFS);
+       r = kmem_cache_zalloc(rsb_cache, GFP_NOFS);
        return r;
 }
 
@@ -65,7 +71,7 @@ void dlm_free_rsb(struct dlm_rsb *r)
 {
        if (r->res_lvbptr)
                dlm_free_lvb(r->res_lvbptr);
-       kfree(r);
+       kmem_cache_free(rsb_cache, r);
 }
 
 struct dlm_lkb *dlm_allocate_lkb(struct dlm_ls *ls)
index 485fb29..177c11c 100644 (file)
@@ -16,7 +16,7 @@
 
 int dlm_memory_init(void);
 void dlm_memory_exit(void);
-struct dlm_rsb *dlm_allocate_rsb(struct dlm_ls *ls, int namelen);
+struct dlm_rsb *dlm_allocate_rsb(struct dlm_ls *ls);
 void dlm_free_rsb(struct dlm_rsb *r);
 struct dlm_lkb *dlm_allocate_lkb(struct dlm_ls *ls);
 void dlm_free_lkb(struct dlm_lkb *l);
index fd677c8..774da3c 100644 (file)
@@ -58,13 +58,7 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
 
        mutex_lock(&ls->ls_recoverd_active);
 
-       /*
-        * Suspending and resuming dlm_astd ensures that no lkb's from this ls
-        * will be processed by dlm_astd during recovery.
-        */
-
-       dlm_astd_suspend();
-       dlm_astd_resume();
+       dlm_callback_suspend(ls);
 
        /*
         * Free non-master tossed rsb's.  Master rsb's are kept on toss
@@ -202,6 +196,8 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
 
        dlm_adjust_timeouts(ls);
 
+       dlm_callback_resume(ls);
+
        error = enable_locking(ls, rv->seq);
        if (error) {
                log_debug(ls, "enable_locking failed %d", error);
@@ -222,8 +218,6 @@ static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv)
 
        dlm_grant_after_purge(ls);
 
-       dlm_astd_wake();
-
        log_debug(ls, "recover %llx done: %u ms",
                  (unsigned long long)rv->seq,
                  jiffies_to_msecs(jiffies - start));
index e96bf3e..d8ea607 100644 (file)
@@ -213,9 +213,9 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, uint32_t flags, int mode,
                goto out;
        }
 
-       if (list_empty(&lkb->lkb_astqueue)) {
+       if (list_empty(&lkb->lkb_cb_list)) {
                kref_get(&lkb->lkb_ref);
-               list_add_tail(&lkb->lkb_astqueue, &proc->asts);
+               list_add_tail(&lkb->lkb_cb_list, &proc->asts);
                wake_up_interruptible(&proc->wait);
        }
        spin_unlock(&proc->asts_spin);
@@ -832,24 +832,24 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count,
        }
 
        /* if we empty lkb_callbacks, we don't want to unlock the spinlock
-          without removing lkb_astqueue; so empty lkb_astqueue is always
+          without removing lkb_cb_list; so empty lkb_cb_list is always
           consistent with empty lkb_callbacks */
 
-       lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);
+       lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_cb_list);
 
        rv = dlm_rem_lkb_callback(lkb->lkb_resource->res_ls, lkb, &cb, &resid);
        if (rv < 0) {
                /* this shouldn't happen; lkb should have been removed from
                   list when resid was zero */
                log_print("dlm_rem_lkb_callback empty %x", lkb->lkb_id);
-               list_del_init(&lkb->lkb_astqueue);
+               list_del_init(&lkb->lkb_cb_list);
                spin_unlock(&proc->asts_spin);
                /* removes ref for proc->asts, may cause lkb to be freed */
                dlm_put_lkb(lkb);
                goto try_another;
        }
        if (!resid)
-               list_del_init(&lkb->lkb_astqueue);
+               list_del_init(&lkb->lkb_cb_list);
        spin_unlock(&proc->asts_spin);
 
        if (cb.flags & DLM_CB_SKIP) {
index 2f343b4..3f7a59b 100644 (file)
@@ -976,16 +976,12 @@ void __fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
 
        pagevec_init(&pvec, 0);
        next = 0;
-       while (next <= (loff_t)-1 &&
-              pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)
-              ) {
+       do {
+               if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE))
+                       break;
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
-                       pgoff_t page_index = page->index;
-
-                       ASSERTCMP(page_index, >=, next);
-                       next = page_index + 1;
-
+                       next = page->index;
                        if (PageFsCache(page)) {
                                __fscache_wait_on_page_write(cookie, page);
                                __fscache_uncache_page(cookie, page);
@@ -993,7 +989,7 @@ void __fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
                }
                pagevec_release(&pvec);
                cond_resched();
-       }
+       } while (++next);
 
        _leave("");
 }
index e65493a..42e477f 100644 (file)
@@ -854,11 +854,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
                        blen++;
                else {
                        if (bstart) {
-                               if (metadata)
-                                       __gfs2_free_meta(ip, bstart, blen);
-                               else
-                                       __gfs2_free_data(ip, bstart, blen);
-
+                               __gfs2_free_blocks(ip, bstart, blen, metadata);
                                btotal += blen;
                        }
 
@@ -870,11 +866,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
                gfs2_add_inode_blocks(&ip->i_inode, -1);
        }
        if (bstart) {
-               if (metadata)
-                       __gfs2_free_meta(ip, bstart, blen);
-               else
-                       __gfs2_free_data(ip, bstart, blen);
-
+               __gfs2_free_blocks(ip, bstart, blen, metadata);
                btotal += blen;
        }
 
index 091ee47..1cc2f8e 100644 (file)
@@ -339,6 +339,67 @@ fail:
        return (copied) ? copied : error;
 }
 
+/**
+ * gfs2_dir_get_hash_table - Get pointer to the dir hash table
+ * @ip: The inode in question
+ *
+ * Returns: The hash table or an error
+ */
+
+static __be64 *gfs2_dir_get_hash_table(struct gfs2_inode *ip)
+{
+       struct inode *inode = &ip->i_inode;
+       int ret;
+       u32 hsize;
+       __be64 *hc;
+
+       BUG_ON(!(ip->i_diskflags & GFS2_DIF_EXHASH));
+
+       hc = ip->i_hash_cache;
+       if (hc)
+               return hc;
+
+       hsize = 1 << ip->i_depth;
+       hsize *= sizeof(__be64);
+       if (hsize != i_size_read(&ip->i_inode)) {
+               gfs2_consist_inode(ip);
+               return ERR_PTR(-EIO);
+       }
+
+       hc = kmalloc(hsize, GFP_NOFS);
+       ret = -ENOMEM;
+       if (hc == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       ret = gfs2_dir_read_data(ip, (char *)hc, 0, hsize, 1);
+       if (ret < 0) {
+               kfree(hc);
+               return ERR_PTR(ret);
+       }
+
+       spin_lock(&inode->i_lock);
+       if (ip->i_hash_cache)
+               kfree(hc);
+       else
+               ip->i_hash_cache = hc;
+       spin_unlock(&inode->i_lock);
+
+       return ip->i_hash_cache;
+}
+
+/**
+ * gfs2_dir_hash_inval - Invalidate dir hash
+ * @ip: The directory inode
+ *
+ * Must be called with an exclusive glock, or during glock invalidation.
+ */
+void gfs2_dir_hash_inval(struct gfs2_inode *ip)
+{
+       __be64 *hc = ip->i_hash_cache;
+       ip->i_hash_cache = NULL;
+       kfree(hc);
+}
+
 static inline int gfs2_dirent_sentinel(const struct gfs2_dirent *dent)
 {
        return dent->de_inum.no_addr == 0 || dent->de_inum.no_formal_ino == 0;
@@ -686,17 +747,12 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
 static int get_leaf_nr(struct gfs2_inode *dip, u32 index,
                       u64 *leaf_out)
 {
-       __be64 leaf_no;
-       int error;
-
-       error = gfs2_dir_read_data(dip, (char *)&leaf_no,
-                                   index * sizeof(__be64),
-                                   sizeof(__be64), 0);
-       if (error != sizeof(u64))
-               return (error < 0) ? error : -EIO;
-
-       *leaf_out = be64_to_cpu(leaf_no);
+       __be64 *hash;
 
+       hash = gfs2_dir_get_hash_table(dip);
+       if (IS_ERR(hash))
+               return PTR_ERR(hash);
+       *leaf_out = be64_to_cpu(*(hash + index));
        return 0;
 }
 
@@ -966,6 +1022,8 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
        for (x = 0; x < half_len; x++)
                lp[x] = cpu_to_be64(bn);
 
+       gfs2_dir_hash_inval(dip);
+
        error = gfs2_dir_write_data(dip, (char *)lp, start * sizeof(u64),
                                    half_len * sizeof(u64));
        if (error != half_len * sizeof(u64)) {
@@ -1052,70 +1110,54 @@ fail_brelse:
 
 static int dir_double_exhash(struct gfs2_inode *dip)
 {
-       struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
        struct buffer_head *dibh;
        u32 hsize;
-       u64 *buf;
-       u64 *from, *to;
-       u64 block;
-       u64 disksize = i_size_read(&dip->i_inode);
+       u32 hsize_bytes;
+       __be64 *hc;
+       __be64 *hc2, *h;
        int x;
        int error = 0;
 
        hsize = 1 << dip->i_depth;
-       if (hsize * sizeof(u64) != disksize) {
-               gfs2_consist_inode(dip);
-               return -EIO;
-       }
+       hsize_bytes = hsize * sizeof(__be64);
 
-       /*  Allocate both the "from" and "to" buffers in one big chunk  */
+       hc = gfs2_dir_get_hash_table(dip);
+       if (IS_ERR(hc))
+               return PTR_ERR(hc);
 
-       buf = kcalloc(3, sdp->sd_hash_bsize, GFP_NOFS);
-       if (!buf)
+       h = hc2 = kmalloc(hsize_bytes * 2, GFP_NOFS);
+       if (!hc2)
                return -ENOMEM;
 
-       for (block = disksize >> sdp->sd_hash_bsize_shift; block--;) {
-               error = gfs2_dir_read_data(dip, (char *)buf,
-                                           block * sdp->sd_hash_bsize,
-                                           sdp->sd_hash_bsize, 1);
-               if (error != sdp->sd_hash_bsize) {
-                       if (error >= 0)
-                               error = -EIO;
-                       goto fail;
-               }
-
-               from = buf;
-               to = (u64 *)((char *)buf + sdp->sd_hash_bsize);
-
-               for (x = sdp->sd_hash_ptrs; x--; from++) {
-                       *to++ = *from;  /*  No endianess worries  */
-                       *to++ = *from;
-               }
+       error = gfs2_meta_inode_buffer(dip, &dibh);
+       if (error)
+               goto out_kfree;
 
-               error = gfs2_dir_write_data(dip,
-                                            (char *)buf + sdp->sd_hash_bsize,
-                                            block * sdp->sd_sb.sb_bsize,
-                                            sdp->sd_sb.sb_bsize);
-               if (error != sdp->sd_sb.sb_bsize) {
-                       if (error >= 0)
-                               error = -EIO;
-                       goto fail;
-               }
+       for (x = 0; x < hsize; x++) {
+               *h++ = *hc;
+               *h++ = *hc;
+               hc++;
        }
 
-       kfree(buf);
-
-       error = gfs2_meta_inode_buffer(dip, &dibh);
-       if (!gfs2_assert_withdraw(sdp, !error)) {
-               dip->i_depth++;
-               gfs2_dinode_out(dip, dibh->b_data);
-               brelse(dibh);
-       }
+       error = gfs2_dir_write_data(dip, (char *)hc2, 0, hsize_bytes * 2);
+       if (error != (hsize_bytes * 2))
+               goto fail;
 
-       return error;
+       gfs2_dir_hash_inval(dip);
+       dip->i_hash_cache = hc2;
+       dip->i_depth++;
+       gfs2_dinode_out(dip, dibh->b_data);
+       brelse(dibh);
+       return 0;
 
 fail:
-       kfree(buf);
+       /* Replace original hash table & size */
+       gfs2_dir_write_data(dip, (char *)hc, 0, hsize_bytes);
+       i_size_write(&dip->i_inode, hsize_bytes);
+       gfs2_dinode_out(dip, dibh->b_data);
+       brelse(dibh);
+out_kfree:
+       kfree(hc2);
        return error;
 }
 
@@ -1348,6 +1390,7 @@ out:
        return error;
 }
 
+
 /**
  * dir_e_read - Reads the entries from a directory into a filldir buffer
  * @dip: dinode pointer
@@ -1362,9 +1405,7 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
                      filldir_t filldir)
 {
        struct gfs2_inode *dip = GFS2_I(inode);
-       struct gfs2_sbd *sdp = GFS2_SB(inode);
        u32 hsize, len = 0;
-       u32 ht_offset, lp_offset, ht_offset_cur = -1;
        u32 hash, index;
        __be64 *lp;
        int copied = 0;
@@ -1372,37 +1413,17 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
        unsigned depth = 0;
 
        hsize = 1 << dip->i_depth;
-       if (hsize * sizeof(u64) != i_size_read(inode)) {
-               gfs2_consist_inode(dip);
-               return -EIO;
-       }
-
        hash = gfs2_dir_offset2hash(*offset);
        index = hash >> (32 - dip->i_depth);
 
-       lp = kmalloc(sdp->sd_hash_bsize, GFP_NOFS);
-       if (!lp)
-               return -ENOMEM;
+       lp = gfs2_dir_get_hash_table(dip);
+       if (IS_ERR(lp))
+               return PTR_ERR(lp);
 
        while (index < hsize) {
-               lp_offset = index & (sdp->sd_hash_ptrs - 1);
-               ht_offset = index - lp_offset;
-
-               if (ht_offset_cur != ht_offset) {
-                       error = gfs2_dir_read_data(dip, (char *)lp,
-                                               ht_offset * sizeof(__be64),
-                                               sdp->sd_hash_bsize, 1);
-                       if (error != sdp->sd_hash_bsize) {
-                               if (error >= 0)
-                                       error = -EIO;
-                               goto out;
-                       }
-                       ht_offset_cur = ht_offset;
-               }
-
                error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
                                           &copied, &depth,
-                                          be64_to_cpu(lp[lp_offset]));
+                                          be64_to_cpu(lp[index]));
                if (error)
                        break;
 
@@ -1410,8 +1431,6 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
                index = (index & ~(len - 1)) + len;
        }
 
-out:
-       kfree(lp);
        if (error > 0)
                error = 0;
        return error;
@@ -1914,43 +1933,22 @@ out:
 
 int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip)
 {
-       struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
        struct buffer_head *bh;
        struct gfs2_leaf *leaf;
        u32 hsize, len;
-       u32 ht_offset, lp_offset, ht_offset_cur = -1;
        u32 index = 0, next_index;
        __be64 *lp;
        u64 leaf_no;
        int error = 0, last;
 
        hsize = 1 << dip->i_depth;
-       if (hsize * sizeof(u64) != i_size_read(&dip->i_inode)) {
-               gfs2_consist_inode(dip);
-               return -EIO;
-       }
 
-       lp = kmalloc(sdp->sd_hash_bsize, GFP_NOFS);
-       if (!lp)
-               return -ENOMEM;
+       lp = gfs2_dir_get_hash_table(dip);
+       if (IS_ERR(lp))
+               return PTR_ERR(lp);
 
        while (index < hsize) {
-               lp_offset = index & (sdp->sd_hash_ptrs - 1);
-               ht_offset = index - lp_offset;
-
-               if (ht_offset_cur != ht_offset) {
-                       error = gfs2_dir_read_data(dip, (char *)lp,
-                                               ht_offset * sizeof(__be64),
-                                               sdp->sd_hash_bsize, 1);
-                       if (error != sdp->sd_hash_bsize) {
-                               if (error >= 0)
-                                       error = -EIO;
-                               goto out;
-                       }
-                       ht_offset_cur = ht_offset;
-               }
-
-               leaf_no = be64_to_cpu(lp[lp_offset]);
+               leaf_no = be64_to_cpu(lp[index]);
                if (leaf_no) {
                        error = get_leaf(dip, leaf_no, &bh);
                        if (error)
@@ -1976,7 +1974,6 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip)
        }
 
 out:
-       kfree(lp);
 
        return error;
 }
index e686af1..ff5772f 100644 (file)
@@ -35,6 +35,7 @@ extern int gfs2_diradd_alloc_required(struct inode *dir,
                                      const struct qstr *filename);
 extern int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block,
                                   struct buffer_head **bhp);
+extern void gfs2_dir_hash_inval(struct gfs2_inode *ip);
 
 static inline u32 gfs2_disk_hash(const char *data, int len)
 {
index a9f5cbe..bc2590e 100644 (file)
@@ -174,7 +174,9 @@ void gfs2_set_inode_flags(struct inode *inode)
        struct gfs2_inode *ip = GFS2_I(inode);
        unsigned int flags = inode->i_flags;
 
-       flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
+       flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_NOSEC);
+       if ((ip->i_eattr == 0) && !is_sxid(inode->i_mode))
+               inode->i_flags |= S_NOSEC;
        if (ip->i_diskflags & GFS2_DIF_IMMUTABLE)
                flags |= S_IMMUTABLE;
        if (ip->i_diskflags & GFS2_DIF_APPENDONLY)
index 1c1336e..88e8a23 100644 (file)
@@ -409,6 +409,10 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state)
        if (held1 && held2 && list_empty(&gl->gl_holders))
                clear_bit(GLF_QUEUED, &gl->gl_flags);
 
+       if (new_state != gl->gl_target)
+               /* shorten our minimum hold time */
+               gl->gl_hold_time = max(gl->gl_hold_time - GL_GLOCK_HOLD_DECR,
+                                      GL_GLOCK_MIN_HOLD);
        gl->gl_state = new_state;
        gl->gl_tchange = jiffies;
 }
@@ -668,7 +672,7 @@ static void glock_work_func(struct work_struct *work)
            gl->gl_demote_state != LM_ST_EXCLUSIVE) {
                unsigned long holdtime, now = jiffies;
 
-               holdtime = gl->gl_tchange + gl->gl_ops->go_min_hold_time;
+               holdtime = gl->gl_tchange + gl->gl_hold_time;
                if (time_before(now, holdtime))
                        delay = holdtime - now;
 
@@ -679,9 +683,14 @@ static void glock_work_func(struct work_struct *work)
        }
        run_queue(gl, 0);
        spin_unlock(&gl->gl_spin);
-       if (!delay ||
-           queue_delayed_work(glock_workqueue, &gl->gl_work, delay) == 0)
+       if (!delay)
                gfs2_glock_put(gl);
+       else {
+               if (gl->gl_name.ln_type != LM_TYPE_INODE)
+                       delay = 0;
+               if (queue_delayed_work(glock_workqueue, &gl->gl_work, delay) == 0)
+                       gfs2_glock_put(gl);
+       }
        if (drop_ref)
                gfs2_glock_put(gl);
 }
@@ -743,6 +752,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
        gl->gl_tchange = jiffies;
        gl->gl_object = NULL;
        gl->gl_sbd = sdp;
+       gl->gl_hold_time = GL_GLOCK_DFT_HOLD;
        INIT_DELAYED_WORK(&gl->gl_work, glock_work_func);
        INIT_WORK(&gl->gl_delete, delete_work_func);
 
@@ -855,8 +865,15 @@ static int gfs2_glock_demote_wait(void *word)
 
 static void wait_on_holder(struct gfs2_holder *gh)
 {
+       unsigned long time1 = jiffies;
+
        might_sleep();
        wait_on_bit(&gh->gh_iflags, HIF_WAIT, gfs2_glock_holder_wait, TASK_UNINTERRUPTIBLE);
+       if (time_after(jiffies, time1 + HZ)) /* have we waited > a second? */
+               /* Lengthen the minimum hold time. */
+               gh->gh_gl->gl_hold_time = min(gh->gh_gl->gl_hold_time +
+                                             GL_GLOCK_HOLD_INCR,
+                                             GL_GLOCK_MAX_HOLD);
 }
 
 static void wait_on_demote(struct gfs2_glock *gl)
@@ -1093,8 +1110,9 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
 
        gfs2_glock_hold(gl);
        if (test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) &&
-           !test_bit(GLF_DEMOTE, &gl->gl_flags))
-               delay = gl->gl_ops->go_min_hold_time;
+           !test_bit(GLF_DEMOTE, &gl->gl_flags) &&
+           gl->gl_name.ln_type == LM_TYPE_INODE)
+               delay = gl->gl_hold_time;
        if (queue_delayed_work(glock_workqueue, &gl->gl_work, delay) == 0)
                gfs2_glock_put(gl);
 }
@@ -1273,12 +1291,13 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state)
        unsigned long now = jiffies;
 
        gfs2_glock_hold(gl);
-       holdtime = gl->gl_tchange + gl->gl_ops->go_min_hold_time;
-       if (test_bit(GLF_QUEUED, &gl->gl_flags)) {
+       holdtime = gl->gl_tchange + gl->gl_hold_time;
+       if (test_bit(GLF_QUEUED, &gl->gl_flags) &&
+           gl->gl_name.ln_type == LM_TYPE_INODE) {
                if (time_before(now, holdtime))
                        delay = holdtime - now;
                if (test_bit(GLF_REPLY_PENDING, &gl->gl_flags))
-                       delay = gl->gl_ops->go_min_hold_time;
+                       delay = gl->gl_hold_time;
        }
 
        spin_lock(&gl->gl_spin);
@@ -1667,7 +1686,7 @@ static int __dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
        dtime *= 1000000/HZ; /* demote time in uSec */
        if (!test_bit(GLF_DEMOTE, &gl->gl_flags))
                dtime = 0;
-       gfs2_print_dbg(seq, "G:  s:%s n:%u/%llx f:%s t:%s d:%s/%llu a:%d v:%d r:%d\n",
+       gfs2_print_dbg(seq, "G:  s:%s n:%u/%llx f:%s t:%s d:%s/%llu a:%d v:%d r:%d m:%ld\n",
                  state2str(gl->gl_state),
                  gl->gl_name.ln_type,
                  (unsigned long long)gl->gl_name.ln_number,
@@ -1676,7 +1695,7 @@ static int __dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
                  state2str(gl->gl_demote_state), dtime,
                  atomic_read(&gl->gl_ail_count),
                  atomic_read(&gl->gl_revokes),
-                 atomic_read(&gl->gl_ref));
+                 atomic_read(&gl->gl_ref), gl->gl_hold_time);
 
        list_for_each_entry(gh, &gl->gl_holders, gh_list) {
                error = dump_holder(seq, gh);
index 6b2f757..6670711 100644 (file)
@@ -113,6 +113,12 @@ enum {
 
 #define GLR_TRYFAILED          13
 
+#define GL_GLOCK_MAX_HOLD        (long)(HZ / 5)
+#define GL_GLOCK_DFT_HOLD        (long)(HZ / 5)
+#define GL_GLOCK_MIN_HOLD        (long)(10)
+#define GL_GLOCK_HOLD_INCR       (long)(HZ / 20)
+#define GL_GLOCK_HOLD_DECR       (long)(HZ / 40)
+
 struct lm_lockops {
        const char *lm_proto_name;
        int (*lm_mount) (struct gfs2_sbd *sdp, const char *fsname);
index 2cca293..da21eca 100644 (file)
@@ -26,6 +26,7 @@
 #include "rgrp.h"
 #include "util.h"
 #include "trans.h"
+#include "dir.h"
 
 /**
  * __gfs2_ail_flush - remove all buffers for a given lock from the AIL
@@ -218,6 +219,7 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
                if (ip) {
                        set_bit(GIF_INVALID, &ip->i_flags);
                        forget_all_cached_acls(&ip->i_inode);
+                       gfs2_dir_hash_inval(ip);
                }
        }
 
@@ -316,6 +318,8 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
        ip->i_generation = be64_to_cpu(str->di_generation);
 
        ip->i_diskflags = be32_to_cpu(str->di_flags);
+       ip->i_eattr = be64_to_cpu(str->di_eattr);
+       /* i_diskflags and i_eattr must be set before gfs2_set_inode_flags() */
        gfs2_set_inode_flags(&ip->i_inode);
        height = be16_to_cpu(str->di_height);
        if (unlikely(height > GFS2_MAX_META_HEIGHT))
@@ -328,7 +332,6 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
        ip->i_depth = (u8)depth;
        ip->i_entries = be32_to_cpu(str->di_entries);
 
-       ip->i_eattr = be64_to_cpu(str->di_eattr);
        if (S_ISREG(ip->i_inode.i_mode))
                gfs2_set_aops(&ip->i_inode);
 
@@ -549,7 +552,6 @@ const struct gfs2_glock_operations gfs2_inode_glops = {
        .go_lock = inode_go_lock,
        .go_dump = inode_go_dump,
        .go_type = LM_TYPE_INODE,
-       .go_min_hold_time = HZ / 5,
        .go_flags = GLOF_ASPACE,
 };
 
@@ -560,7 +562,6 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = {
        .go_unlock = rgrp_go_unlock,
        .go_dump = gfs2_rgrp_dump,
        .go_type = LM_TYPE_RGRP,
-       .go_min_hold_time = HZ / 5,
        .go_flags = GLOF_ASPACE,
 };
 
index 81206e7..892ac37 100644 (file)
@@ -163,7 +163,6 @@ struct gfs2_glock_operations {
        int (*go_dump)(struct seq_file *seq, const struct gfs2_glock *gl);
        void (*go_callback) (struct gfs2_glock *gl);
        const int go_type;
-       const unsigned long go_min_hold_time;
        const unsigned long go_flags;
 #define GLOF_ASPACE 1
 };
@@ -221,6 +220,7 @@ struct gfs2_glock {
 
        unsigned int gl_hash;
        unsigned long gl_demote_time; /* time of first demote request */
+       long gl_hold_time;
        struct list_head gl_holders;
 
        const struct gfs2_glock_operations *gl_ops;
@@ -285,6 +285,7 @@ struct gfs2_inode {
        u64 i_goal;     /* goal block for allocations */
        struct rw_semaphore i_rw_mutex;
        struct list_head i_trunc_list;
+       __be64 *i_hash_cache;
        u32 i_entries;
        u32 i_diskflags;
        u8 i_height;
index c2b34cd..29e1ace 100644 (file)
@@ -41,6 +41,7 @@ static void gfs2_init_inode_once(void *foo)
        init_rwsem(&ip->i_rw_mutex);
        INIT_LIST_HEAD(&ip->i_trunc_list);
        ip->i_alloc = NULL;
+       ip->i_hash_cache = NULL;
 }
 
 static void gfs2_init_glock_once(void *foo)
index 2a77071..516516e 100644 (file)
@@ -1094,6 +1094,7 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
        if (sdp->sd_args.ar_nobarrier)
                set_bit(SDF_NOBARRIERS, &sdp->sd_flags);
 
+       sb->s_flags |= MS_NOSEC;
        sb->s_magic = GFS2_MAGIC;
        sb->s_op = &gfs2_super_ops;
        sb->s_d_op = &gfs2_dops;
index 9b780df..7f8af1e 100644 (file)
@@ -1607,14 +1607,15 @@ rgrp_error:
 }
 
 /**
- * gfs2_free_data - free a contiguous run of data block(s)
+ * __gfs2_free_blocks - free a contiguous run of block(s)
  * @ip: the inode these blocks are being freed from
  * @bstart: first block of a run of contiguous blocks
  * @blen: the length of the block run
+ * @meta: 1 if the blocks represent metadata
  *
  */
 
-void __gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen)
+void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct gfs2_rgrpd *rgd;
@@ -1631,54 +1632,11 @@ void __gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen)
        gfs2_trans_add_rg(rgd);
 
        /* Directories keep their data in the metadata address space */
-       if (ip->i_depth)
+       if (meta || ip->i_depth)
                gfs2_meta_wipe(ip, bstart, blen);
 }
 
 /**
- * gfs2_free_data - free a contiguous run of data block(s)
- * @ip: the inode these blocks are being freed from
- * @bstart: first block of a run of contiguous blocks
- * @blen: the length of the block run
- *
- */
-
-void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen)
-{
-       struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
-
-       __gfs2_free_data(ip, bstart, blen);
-       gfs2_statfs_change(sdp, 0, +blen, 0);
-       gfs2_quota_change(ip, -(s64)blen, ip->i_inode.i_uid, ip->i_inode.i_gid);
-}
-
-/**
- * gfs2_free_meta - free a contiguous run of data block(s)
- * @ip: the inode these blocks are being freed from
- * @bstart: first block of a run of contiguous blocks
- * @blen: the length of the block run
- *
- */
-
-void __gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen)
-{
-       struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
-       struct gfs2_rgrpd *rgd;
-
-       rgd = rgblk_free(sdp, bstart, blen, GFS2_BLKST_FREE);
-       if (!rgd)
-               return;
-       trace_gfs2_block_alloc(ip, bstart, blen, GFS2_BLKST_FREE);
-       rgd->rd_free += blen;
-
-       gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
-       gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
-
-       gfs2_trans_add_rg(rgd);
-       gfs2_meta_wipe(ip, bstart, blen);
-}
-
-/**
  * gfs2_free_meta - free a contiguous run of data block(s)
  * @ip: the inode these blocks are being freed from
  * @bstart: first block of a run of contiguous blocks
@@ -1690,7 +1648,7 @@ void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
 
-       __gfs2_free_meta(ip, bstart, blen);
+       __gfs2_free_blocks(ip, bstart, blen, 1);
        gfs2_statfs_change(sdp, 0, +blen, 0);
        gfs2_quota_change(ip, -(s64)blen, ip->i_inode.i_uid, ip->i_inode.i_gid);
 }
index a80e303..d253f9a 100644 (file)
@@ -52,9 +52,7 @@ extern int gfs2_ri_update(struct gfs2_inode *ip);
 extern int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n);
 extern int gfs2_alloc_di(struct gfs2_inode *ip, u64 *bn, u64 *generation);
 
-extern void __gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen);
-extern void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen);
-extern void __gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
+extern void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta);
 extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
 extern void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip);
 extern void gfs2_unlink_di(struct inode *inode);
index fb0edf7..b7beadd 100644 (file)
@@ -1533,7 +1533,7 @@ out:
        /* Case 3 starts here */
        truncate_inode_pages(&inode->i_data, 0);
        end_writeback(inode);
-
+       gfs2_dir_hash_inval(ip);
        ip->i_gl->gl_object = NULL;
        gfs2_glock_add_to_lru(ip->i_gl);
        gfs2_glock_put(ip->i_gl);
index 2312de3..2a734cf 100644 (file)
@@ -43,6 +43,10 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
                        node->tree->node_size - (rec + 1) * 2);
                if (!recoff)
                        return 0;
+               if (recoff > node->tree->node_size - 2) {
+                       printk(KERN_ERR "hfs: recoff %d too large\n", recoff);
+                       return 0;
+               }
 
                retval = hfs_bnode_read_u16(node, recoff) + 2;
                if (retval > node->tree->max_key_len + 2) {
index b4ba1b3..4dfbfec 100644 (file)
@@ -212,7 +212,9 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
 
        dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n",
                str->name, cnid, inode->i_nlink);
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       if (err)
+               return err;
 
        hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
        entry_size = hfsplus_fill_cat_thread(sb, &entry,
@@ -269,7 +271,9 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
 
        dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n",
                str ? str->name : NULL, cnid);
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       if (err)
+               return err;
 
        if (!str) {
                int len;
@@ -347,12 +351,14 @@ int hfsplus_rename_cat(u32 cnid,
        struct hfs_find_data src_fd, dst_fd;
        hfsplus_cat_entry entry;
        int entry_size, type;
-       int err = 0;
+       int err;
 
        dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
                cnid, src_dir->i_ino, src_name->name,
                dst_dir->i_ino, dst_name->name);
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
+       if (err)
+               return err;
        dst_fd = src_fd;
 
        /* find the old dir entry and read the data */
index 4df5059..25b2443 100644 (file)
@@ -38,7 +38,9 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
        sb = dir->i_sb;
 
        dentry->d_fsdata = NULL;
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       if (err)
+               return ERR_PTR(err);
        hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
 again:
        err = hfs_brec_read(&fd, &entry, sizeof(entry));
@@ -132,7 +134,9 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
        if (filp->f_pos >= inode->i_size)
                return 0;
 
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       if (err)
+               return err;
        hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
        err = hfs_brec_find(&fd);
        if (err)
index b1991a2..5849e3e 100644 (file)
@@ -119,22 +119,31 @@ static void __hfsplus_ext_write_extent(struct inode *inode,
        set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
 }
 
-static void hfsplus_ext_write_extent_locked(struct inode *inode)
+static int hfsplus_ext_write_extent_locked(struct inode *inode)
 {
+       int res;
+
        if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
                struct hfs_find_data fd;
 
-               hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
+               res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
+               if (res)
+                       return res;
                __hfsplus_ext_write_extent(inode, &fd);
                hfs_find_exit(&fd);
        }
+       return 0;
 }
 
-void hfsplus_ext_write_extent(struct inode *inode)
+int hfsplus_ext_write_extent(struct inode *inode)
 {
+       int res;
+
        mutex_lock(&HFSPLUS_I(inode)->extents_lock);
-       hfsplus_ext_write_extent_locked(inode);
+       res = hfsplus_ext_write_extent_locked(inode);
        mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
+
+       return res;
 }
 
 static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
@@ -194,9 +203,11 @@ static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
            block < hip->cached_start + hip->cached_blocks)
                return 0;
 
-       hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
-       res = __hfsplus_ext_cache_extent(&fd, inode, block);
-       hfs_find_exit(&fd);
+       res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
+       if (!res) {
+               res = __hfsplus_ext_cache_extent(&fd, inode, block);
+               hfs_find_exit(&fd);
+       }
        return res;
 }
 
@@ -209,6 +220,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
        struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
        int res = -EIO;
        u32 ablock, dblock, mask;
+       sector_t sector;
        int was_dirty = 0;
        int shift;
 
@@ -255,10 +267,12 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock,
 done:
        dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n",
                inode->i_ino, (long long)iblock, dblock);
+
        mask = (1 << sbi->fs_shift) - 1;
-       map_bh(bh_result, sb,
-               (dblock << sbi->fs_shift) + sbi->blockoffset +
-                       (iblock & mask));
+       sector = ((sector_t)dblock << sbi->fs_shift) +
+                 sbi->blockoffset + (iblock & mask);
+       map_bh(bh_result, sb, sector);
+
        if (create) {
                set_buffer_new(bh_result);
                hip->phys_size += sb->s_blocksize;
@@ -371,7 +385,9 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
        if (total_blocks == blocks)
                return 0;
 
-       hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
+       res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
+       if (res)
+               return res;
        do {
                res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid,
                                                total_blocks, type);
@@ -469,7 +485,9 @@ out:
 
 insert_extent:
        dprint(DBG_EXTENT, "insert new extent\n");
-       hfsplus_ext_write_extent_locked(inode);
+       res = hfsplus_ext_write_extent_locked(inode);
+       if (res)
+               goto out;
 
        memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
        hip->cached_extents[0].start_block = cpu_to_be32(start);
@@ -500,7 +518,6 @@ void hfsplus_file_truncate(struct inode *inode)
                struct page *page;
                void *fsdata;
                u32 size = inode->i_size;
-               int res;
 
                res = pagecache_write_begin(NULL, mapping, size, 0,
                                                AOP_FLAG_UNINTERRUPTIBLE,
@@ -523,7 +540,12 @@ void hfsplus_file_truncate(struct inode *inode)
                goto out;
 
        mutex_lock(&hip->extents_lock);
-       hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
+       res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
+       if (res) {
+               mutex_unlock(&hip->extents_lock);
+               /* XXX: We lack error handling of hfsplus_file_truncate() */
+               return;
+       }
        while (1) {
                if (alloc_cnt == hip->first_blocks) {
                        hfsplus_free_extents(sb, hip->first_extents,
index d685752..81dfd1e 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/fs.h>
 #include <linux/mutex.h>
 #include <linux/buffer_head.h>
+#include <linux/blkdev.h>
 #include "hfsplus_raw.h"
 
 #define DBG_BNODE_REFS 0x00000001
@@ -110,7 +111,9 @@ struct hfsplus_vh;
 struct hfs_btree;
 
 struct hfsplus_sb_info {
+       void *s_vhdr_buf;
        struct hfsplus_vh *s_vhdr;
+       void *s_backup_vhdr_buf;
        struct hfsplus_vh *s_backup_vhdr;
        struct hfs_btree *ext_tree;
        struct hfs_btree *cat_tree;
@@ -258,6 +261,15 @@ struct hfsplus_readdir_data {
        struct hfsplus_cat_key key;
 };
 
+/*
+ * Find minimum acceptible I/O size for an hfsplus sb.
+ */
+static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
+{
+       return max_t(unsigned short, bdev_logical_block_size(sb->s_bdev),
+                    HFSPLUS_SECTOR_SIZE);
+}
+
 #define hfs_btree_open hfsplus_btree_open
 #define hfs_btree_close hfsplus_btree_close
 #define hfs_btree_write hfsplus_btree_write
@@ -374,7 +386,7 @@ extern const struct file_operations hfsplus_dir_operations;
 
 /* extents.c */
 int hfsplus_ext_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
-void hfsplus_ext_write_extent(struct inode *);
+int hfsplus_ext_write_extent(struct inode *);
 int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
 int hfsplus_free_fork(struct super_block *, u32,
                struct hfsplus_fork_raw *, int);
@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent,
 /* wrapper.c */
 int hfsplus_read_wrapper(struct super_block *);
 int hfs_part_find(struct super_block *, sector_t *, sector_t *);
-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
-               void *data, int rw);
+int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
+               void *buf, void **data, int rw);
 
 /* time macros */
 #define __hfsp_mt2ut(t)                (be32_to_cpu(t) - 2082844800U)
index b248a6c..010cd36 100644 (file)
@@ -195,11 +195,13 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir,
        hip->flags = 0;
        set_bit(HFSPLUS_I_RSRC, &hip->flags);
 
-       hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
-       err = hfsplus_find_cat(sb, dir->i_ino, &fd);
-       if (!err)
-               err = hfsplus_cat_read_inode(inode, &fd);
-       hfs_find_exit(&fd);
+       err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+       if (!err) {
+               err = hfsplus_find_cat(sb, dir->i_ino, &fd);
+               if (!err)
+                       err = hfsplus_cat_read_inode(inode, &fd);
+               hfs_find_exit(&fd);
+       }
        if (err) {
                iput(inode);
                return ERR_PTR(err);
index 40ad88c..eb355d8 100644 (file)
@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm,
        return -ENOENT;
 }
 
-static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
-               sector_t *part_start, sector_t *part_size)
+static int hfs_parse_new_pmap(struct super_block *sb, void *buf,
+               struct new_pmap *pm, sector_t *part_start, sector_t *part_size)
 {
        struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
        int size = be32_to_cpu(pm->pmMapBlkCnt);
+       int buf_size = hfsplus_min_io_size(sb);
        int res;
        int i = 0;
 
@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
                if (++i >= size)
                        return -ENOENT;
 
-               res = hfsplus_submit_bio(sb->s_bdev,
-                                        *part_start + HFS_PMAP_BLK + i,
-                                        pm, READ);
-               if (res)
-                       return res;
+               pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE);
+               if ((u8 *)pm - (u8 *)buf >= buf_size) {
+                       res = hfsplus_submit_bio(sb,
+                                                *part_start + HFS_PMAP_BLK + i,
+                                                buf, (void **)&pm, READ);
+                       if (res)
+                               return res;
+               }
        } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC));
 
        return -ENOENT;
@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm,
 int hfs_part_find(struct super_block *sb,
                sector_t *part_start, sector_t *part_size)
 {
-       void *data;
+       void *buf, *data;
        int res;
 
-       data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
-       if (!data)
+       buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
+       if (!buf)
                return -ENOMEM;
 
-       res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK,
-                                data, READ);
+       res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK,
+                                buf, &data, READ);
        if (res)
                goto out;
 
@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb,
                res = hfs_parse_old_pmap(sb, data, part_start, part_size);
                break;
        case HFS_NEW_PMAP_MAGIC:
-               res = hfs_parse_new_pmap(sb, data, part_start, part_size);
+               res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size);
                break;
        default:
                res = -ENOENT;
                break;
        }
 out:
-       kfree(data);
+       kfree(buf);
        return res;
 }
index 84a47b7..c106ca2 100644 (file)
@@ -73,11 +73,13 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
 
        if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
            inode->i_ino == HFSPLUS_ROOT_CNID) {
-               hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
-               err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
-               if (!err)
-                       err = hfsplus_cat_read_inode(inode, &fd);
-               hfs_find_exit(&fd);
+               err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
+               if (!err) {
+                       err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
+                       if (!err)
+                               err = hfsplus_cat_read_inode(inode, &fd);
+                       hfs_find_exit(&fd);
+               }
        } else {
                err = hfsplus_system_read_inode(inode);
        }
@@ -133,9 +135,13 @@ static int hfsplus_system_write_inode(struct inode *inode)
 static int hfsplus_write_inode(struct inode *inode,
                struct writeback_control *wbc)
 {
+       int err;
+
        dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
 
-       hfsplus_ext_write_extent(inode);
+       err = hfsplus_ext_write_extent(inode);
+       if (err)
+               return err;
 
        if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
            inode->i_ino == HFSPLUS_ROOT_CNID)
@@ -197,17 +203,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
                write_backup = 1;
        }
 
-       error2 = hfsplus_submit_bio(sb->s_bdev,
+       error2 = hfsplus_submit_bio(sb,
                                   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
-                                  sbi->s_vhdr, WRITE_SYNC);
+                                  sbi->s_vhdr_buf, NULL, WRITE_SYNC);
        if (!error)
                error = error2;
        if (!write_backup)
                goto out;
 
-       error2 = hfsplus_submit_bio(sb->s_bdev,
+       error2 = hfsplus_submit_bio(sb,
                                  sbi->part_start + sbi->sect_count - 2,
-                                 sbi->s_backup_vhdr, WRITE_SYNC);
+                                 sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
        if (!error)
                error2 = error;
 out:
@@ -251,8 +257,8 @@ static void hfsplus_put_super(struct super_block *sb)
        hfs_btree_close(sbi->ext_tree);
        iput(sbi->alloc_file);
        iput(sbi->hidden_dir);
-       kfree(sbi->s_vhdr);
-       kfree(sbi->s_backup_vhdr);
+       kfree(sbi->s_vhdr_buf);
+       kfree(sbi->s_backup_vhdr_buf);
        unload_nls(sbi->nls);
        kfree(sb->s_fs_info);
        sb->s_fs_info = NULL;
@@ -393,6 +399,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
        if (!sbi->rsrc_clump_blocks)
                sbi->rsrc_clump_blocks = 1;
 
+       err = generic_check_addressable(sbi->alloc_blksz_shift,
+                                       sbi->total_blocks);
+       if (err) {
+               printk(KERN_ERR "hfs: filesystem size too large.\n");
+               goto out_free_vhdr;
+       }
+
        /* Set up operations so we can load metadata */
        sb->s_op = &hfsplus_sops;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -417,6 +430,8 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
                sb->s_flags |= MS_RDONLY;
        }
 
+       err = -EINVAL;
+
        /* Load metadata objects (B*Trees) */
        sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
        if (!sbi->ext_tree) {
@@ -447,7 +462,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 
        str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
        str.name = HFSP_HIDDENDIR_NAME;
-       hfs_find_init(sbi->cat_tree, &fd);
+       err = hfs_find_init(sbi->cat_tree, &fd);
+       if (err)
+               goto out_put_root;
        hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
        if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
                hfs_find_exit(&fd);
index a3f0bfc..a32998f 100644 (file)
@@ -142,7 +142,11 @@ int hfsplus_uni2asc(struct super_block *sb,
                /* search for single decomposed char */
                if (likely(compose))
                        ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0);
-               if (ce1 && (cc = ce1[0])) {
+               if (ce1)
+                       cc = ce1[0];
+               else
+                       cc = 0;
+               if (cc) {
                        /* start of a possibly decomposed Hangul char */
                        if (cc != 0xffff)
                                goto done;
@@ -209,7 +213,8 @@ int hfsplus_uni2asc(struct super_block *sb,
                                i++;
                                ce2 = ce1;
                        }
-                       if ((cc = ce2[0])) {
+                       cc = ce2[0];
+                       if (cc) {
                                ip += i;
                                ustrlen -= i;
                                goto done;
@@ -301,7 +306,11 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
        while (outlen < HFSPLUS_MAX_STRLEN && len > 0) {
                size = asc2unichar(sb, astr, len, &c);
 
-               if (decompose && (dstr = decompose_unichar(c, &dsize))) {
+               if (decompose)
+                       dstr = decompose_unichar(c, &dsize);
+               else
+                       dstr = NULL;
+               if (dstr) {
                        if (outlen + dsize > HFSPLUS_MAX_STRLEN)
                                break;
                        do {
@@ -346,15 +355,23 @@ int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode,
                astr += size;
                len -= size;
 
-               if (decompose && (dstr = decompose_unichar(c, &dsize))) {
+               if (decompose)
+                       dstr = decompose_unichar(c, &dsize);
+               else
+                       dstr = NULL;
+               if (dstr) {
                        do {
                                c2 = *dstr++;
-                               if (!casefold || (c2 = case_fold(c2)))
+                               if (casefold)
+                                       c2 = case_fold(c2);
+                               if (!casefold || c2)
                                        hash = partial_name_hash(c2, hash);
                        } while (--dsize > 0);
                } else {
                        c2 = c;
-                       if (!casefold || (c2 = case_fold(c2)))
+                       if (casefold)
+                               c2 = case_fold(c2);
+                       if (!casefold || c2)
                                hash = partial_name_hash(c2, hash);
                }
        }
@@ -422,12 +439,14 @@ int hfsplus_compare_dentry(const struct dentry *parent,
                c1 = *dstr1;
                c2 = *dstr2;
                if (casefold) {
-                       if  (!(c1 = case_fold(c1))) {
+                       c1 = case_fold(c1);
+                       if (!c1) {
                                dstr1++;
                                dsize1--;
                                continue;
                        }
-                       if (!(c2 = case_fold(c2))) {
+                       c2 = case_fold(c2);
+                       if (!c2) {
                                dstr2++;
                                dsize2--;
                                continue;
index 4ac88ff..10e515a 100644 (file)
@@ -31,25 +31,67 @@ static void hfsplus_end_io_sync(struct bio *bio, int err)
        complete(bio->bi_private);
 }
 
-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
-               void *data, int rw)
+/*
+ * hfsplus_submit_bio - Perfrom block I/O
+ * @sb: super block of volume for I/O
+ * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
+ * @buf: buffer for I/O
+ * @data: output pointer for location of requested data
+ * @rw: direction of I/O
+ *
+ * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
+ * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
+ * @data will return a pointer to the start of the requested sector,
+ * which may not be the same location as @buf.
+ *
+ * If @sector is not aligned to the bdev logical block size it will
+ * be rounded down. For writes this means that @buf should contain data
+ * that starts at the rounded-down address. As long as the data was
+ * read using hfsplus_submit_bio() and the same buffer is used things
+ * will work correctly.
+ */
+int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
+               void *buf, void **data, int rw)
 {
        DECLARE_COMPLETION_ONSTACK(wait);
        struct bio *bio;
        int ret = 0;
+       unsigned int io_size;
+       loff_t start;
+       int offset;
+
+       /*
+        * Align sector to hardware sector size and find offset. We
+        * assume that io_size is a power of two, which _should_
+        * be true.
+        */
+       io_size = hfsplus_min_io_size(sb);
+       start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
+       offset = start & (io_size - 1);
+       sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
 
        bio = bio_alloc(GFP_NOIO, 1);
        bio->bi_sector = sector;
-       bio->bi_bdev = bdev;
+       bio->bi_bdev = sb->s_bdev;
        bio->bi_end_io = hfsplus_end_io_sync;
        bio->bi_private = &wait;
 
-       /*
-        * We always submit one sector at a time, so bio_add_page must not fail.
-        */
-       if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
-                        offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
-               BUG();
+       if (!(rw & WRITE) && data)
+               *data = (u8 *)buf + offset;
+
+       while (io_size > 0) {
+               unsigned int page_offset = offset_in_page(buf);
+               unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
+                                        io_size);
+
+               ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
+               if (ret != len) {
+                       ret = -EIO;
+                       goto out;
+               }
+               io_size -= len;
+               buf = (u8 *)buf + len;
+       }
 
        submit_bio(rw, bio);
        wait_for_completion(&wait);
@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
        if (!bio_flagged(bio, BIO_UPTODATE))
                ret = -EIO;
 
+out:
        bio_put(bio);
-       return ret;
+       return ret < 0 ? ret : 0;
 }
 
 static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
@@ -141,23 +184,19 @@ int hfsplus_read_wrapper(struct super_block *sb)
 
        if (hfsplus_get_last_session(sb, &part_start, &part_size))
                goto out;
-       if ((u64)part_start + part_size > 0x100000000ULL) {
-               pr_err("hfs: volumes larger than 2TB are not supported yet\n");
-               goto out;
-       }
 
        error = -ENOMEM;
-       sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
-       if (!sbi->s_vhdr)
+       sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
+       if (!sbi->s_vhdr_buf)
                goto out;
-       sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
-       if (!sbi->s_backup_vhdr)
+       sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
+       if (!sbi->s_backup_vhdr_buf)
                goto out_free_vhdr;
 
 reread:
-       error = hfsplus_submit_bio(sb->s_bdev,
-                                  part_start + HFSPLUS_VOLHEAD_SECTOR,
-                                  sbi->s_vhdr, READ);
+       error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
+                                  sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
+                                  READ);
        if (error)
                goto out_free_backup_vhdr;
 
@@ -172,8 +211,9 @@ reread:
                if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
                        goto out_free_backup_vhdr;
                wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
-               part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
-               part_size = wd.embed_count * wd.ablk_size;
+               part_start += (sector_t)wd.ablk_start +
+                              (sector_t)wd.embed_start * wd.ablk_size;
+               part_size = (sector_t)wd.embed_count * wd.ablk_size;
                goto reread;
        default:
                /*
@@ -186,9 +226,9 @@ reread:
                goto reread;
        }
 
-       error = hfsplus_submit_bio(sb->s_bdev,
-                                  part_start + part_size - 2,
-                                  sbi->s_backup_vhdr, READ);
+       error = hfsplus_submit_bio(sb, part_start + part_size - 2,
+                                  sbi->s_backup_vhdr_buf,
+                                  (void **)&sbi->s_backup_vhdr, READ);
        if (error)
                goto out_free_backup_vhdr;
 
index c88eab5..275ca47 100644 (file)
@@ -822,7 +822,7 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
                goto out;
 
        attr->set_buf[size] = '\0';
-       val = simple_strtol(attr->set_buf, NULL, 0);
+       val = simple_strtoll(attr->set_buf, NULL, 0);
        ret = attr->set(attr->data, val);
        if (ret == 0)
                ret = len; /* on success, claim we got the whole input */
index 5c867dd..14ab8d3 100644 (file)
@@ -942,7 +942,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 * Don't forget we might have a non-mountpoint managed dentry
                 * that wants to block transit.
                 */
-               *inode = path->dentry->d_inode;
                if (unlikely(managed_dentry_might_block(path->dentry)))
                        return false;
 
@@ -955,6 +954,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                path->mnt = mounted;
                path->dentry = mounted->mnt_root;
                nd->seq = read_seqcount_begin(&path->dentry->d_seq);
+               /*
+                * Update the inode too. We don't need to re-check the
+                * dentry sequence number here after this d_inode read,
+                * because a mount-point is always pinned.
+                */
+               *inode = path->dentry->d_inode;
        }
        return true;
 }
index 87cd0ea..fb3b5c8 100644 (file)
@@ -78,7 +78,7 @@ static int nothing_to_commit(struct ubifs_info *c)
         * If the root TNC node is dirty, we definitely have something to
         * commit.
         */
-       if (c->zroot.znode && test_bit(DIRTY_ZNODE, &c->zroot.znode->flags))
+       if (c->zroot.znode && ubifs_zn_dirty(c->zroot.znode))
                return 0;
 
        /*
@@ -418,7 +418,7 @@ int ubifs_run_commit(struct ubifs_info *c)
 
        spin_lock(&c->cs_lock);
        if (c->cmt_state == COMMIT_BROKEN) {
-               err = -EINVAL;
+               err = -EROFS;
                goto out;
        }
 
@@ -444,7 +444,7 @@ int ubifs_run_commit(struct ubifs_info *c)
         * re-check it.
         */
        if (c->cmt_state == COMMIT_BROKEN) {
-               err = -EINVAL;
+               err = -EROFS;
                goto out_cmt_unlock;
        }
 
@@ -576,7 +576,7 @@ int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot)
        struct idx_node *i;
        size_t sz;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX))
+       if (!dbg_is_chk_index(c))
                return 0;
 
        INIT_LIST_HEAD(&list);
index 0bb2bce..eef109a 100644 (file)
  * various local functions of those subsystems.
  */
 
-#define UBIFS_DBG_PRESERVE_UBI
-
-#include "ubifs.h"
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/debugfs.h>
 #include <linux/math64.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#include "ubifs.h"
 
 #ifdef CONFIG_UBIFS_FS_DEBUG
 
@@ -42,15 +41,6 @@ DEFINE_SPINLOCK(dbg_lock);
 static char dbg_key_buf0[128];
 static char dbg_key_buf1[128];
 
-unsigned int ubifs_chk_flags;
-unsigned int ubifs_tst_flags;
-
-module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
-
 static const char *get_key_fmt(int fmt)
 {
        switch (fmt) {
@@ -91,6 +81,28 @@ static const char *get_key_type(int type)
        }
 }
 
+static const char *get_dent_type(int type)
+{
+       switch (type) {
+       case UBIFS_ITYPE_REG:
+               return "file";
+       case UBIFS_ITYPE_DIR:
+               return "dir";
+       case UBIFS_ITYPE_LNK:
+               return "symlink";
+       case UBIFS_ITYPE_BLK:
+               return "blkdev";
+       case UBIFS_ITYPE_CHR:
+               return "char dev";
+       case UBIFS_ITYPE_FIFO:
+               return "fifo";
+       case UBIFS_ITYPE_SOCK:
+               return "socket";
+       default:
+               return "unknown/invalid type";
+       }
+}
+
 static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key,
                        char *buffer)
 {
@@ -234,9 +246,13 @@ static void dump_ch(const struct ubifs_ch *ch)
        printk(KERN_DEBUG "\tlen            %u\n", le32_to_cpu(ch->len));
 }
 
-void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
+void dbg_dump_inode(struct ubifs_info *c, const struct inode *inode)
 {
        const struct ubifs_inode *ui = ubifs_inode(inode);
+       struct qstr nm = { .name = NULL };
+       union ubifs_key key;
+       struct ubifs_dent_node *dent, *pdent = NULL;
+       int count = 2;
 
        printk(KERN_DEBUG "Dump in-memory inode:");
        printk(KERN_DEBUG "\tinode          %lu\n", inode->i_ino);
@@ -270,6 +286,32 @@ void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
        printk(KERN_DEBUG "\tlast_page_read %lu\n", ui->last_page_read);
        printk(KERN_DEBUG "\tread_in_a_row  %lu\n", ui->read_in_a_row);
        printk(KERN_DEBUG "\tdata_len       %d\n", ui->data_len);
+
+       if (!S_ISDIR(inode->i_mode))
+               return;
+
+       printk(KERN_DEBUG "List of directory entries:\n");
+       ubifs_assert(!mutex_is_locked(&c->tnc_mutex));
+
+       lowest_dent_key(c, &key, inode->i_ino);
+       while (1) {
+               dent = ubifs_tnc_next_ent(c, &key, &nm);
+               if (IS_ERR(dent)) {
+                       if (PTR_ERR(dent) != -ENOENT)
+                               printk(KERN_DEBUG "error %ld\n", PTR_ERR(dent));
+                       break;
+               }
+
+               printk(KERN_DEBUG "\t%d: %s (%s)\n",
+                      count++, dent->name, get_dent_type(dent->type));
+
+               nm.name = dent->name;
+               nm.len = le16_to_cpu(dent->nlen);
+               kfree(pdent);
+               pdent = dent;
+               key_read(c, &dent->key, &key);
+       }
+       kfree(pdent);
 }
 
 void dbg_dump_node(const struct ubifs_info *c, const void *node)
@@ -278,7 +320,7 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
        union ubifs_key key;
        const struct ubifs_ch *ch = node;
 
-       if (dbg_failure_mode)
+       if (dbg_is_tst_rcvry(c))
                return;
 
        /* If the magic is incorrect, just hexdump the first bytes */
@@ -834,7 +876,7 @@ void dbg_dump_leb(const struct ubifs_info *c, int lnum)
        struct ubifs_scan_node *snod;
        void *buf;
 
-       if (dbg_failure_mode)
+       if (dbg_is_tst_rcvry(c))
                return;
 
        printk(KERN_DEBUG "(pid %d) start dumping LEB %d\n",
@@ -1080,6 +1122,7 @@ out:
 
 /**
  * dbg_check_synced_i_size - check synchronized inode size.
+ * @c: UBIFS file-system description object
  * @inode: inode to check
  *
  * If inode is clean, synchronized inode size has to be equivalent to current
@@ -1087,12 +1130,12 @@ out:
  * has to be locked). Returns %0 if synchronized inode size if correct, and
  * %-EINVAL if not.
  */
-int dbg_check_synced_i_size(struct inode *inode)
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
 {
        int err = 0;
        struct ubifs_inode *ui = ubifs_inode(inode);
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
        if (!S_ISREG(inode->i_mode))
                return 0;
@@ -1125,7 +1168,7 @@ int dbg_check_synced_i_size(struct inode *inode)
  * Note, it is good idea to make sure the @dir->i_mutex is locked before
  * calling this function.
  */
-int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
 {
        unsigned int nlink = 2;
        union ubifs_key key;
@@ -1133,7 +1176,7 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
        struct qstr nm = { .name = NULL };
        loff_t size = UBIFS_INO_NODE_SZ;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        if (!S_ISDIR(dir->i_mode))
@@ -1167,12 +1210,14 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
                          "but calculated size is %llu", dir->i_ino,
                          (unsigned long long)i_size_read(dir),
                          (unsigned long long)size);
+               dbg_dump_inode(c, dir);
                dump_stack();
                return -EINVAL;
        }
        if (dir->i_nlink != nlink) {
                ubifs_err("directory inode %lu has nlink %u, but calculated "
                          "nlink is %u", dir->i_ino, dir->i_nlink, nlink);
+               dbg_dump_inode(c, dir);
                dump_stack();
                return -EINVAL;
        }
@@ -1489,7 +1534,7 @@ int dbg_check_tnc(struct ubifs_info *c, int extra)
        long clean_cnt = 0, dirty_cnt = 0;
        int err, last;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_TNC))
+       if (!dbg_is_chk_index(c))
                return 0;
 
        ubifs_assert(mutex_is_locked(&c->tnc_mutex));
@@ -1736,7 +1781,7 @@ int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
        int err;
        long long calc = 0;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_IDX_SZ))
+       if (!dbg_is_chk_index(c))
                return 0;
 
        err = dbg_walk_index(c, NULL, add_size, &calc);
@@ -2312,7 +2357,7 @@ int dbg_check_filesystem(struct ubifs_info *c)
        int err;
        struct fsck_data fsckd;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_FS))
+       if (!dbg_is_chk_fs(c))
                return 0;
 
        fsckd.inodes = RB_ROOT;
@@ -2347,7 +2392,7 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
        struct list_head *cur;
        struct ubifs_scan_node *sa, *sb;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        for (cur = head->next; cur->next != head; cur = cur->next) {
@@ -2414,7 +2459,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
        struct list_head *cur;
        struct ubifs_scan_node *sa, *sb;
 
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
 
        for (cur = head->next; cur->next != head; cur = cur->next) {
@@ -2491,214 +2536,141 @@ error_dump:
        return 0;
 }
 
-int dbg_force_in_the_gaps(void)
+static inline int chance(unsigned int n, unsigned int out_of)
 {
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
-               return 0;
+       return !!((random32() % out_of) + 1 <= n);
 
-       return !(random32() & 7);
 }
 
-/* Failure mode for recovery testing */
-
-#define chance(n, d) (simple_rand() <= (n) * 32768LL / (d))
-
-struct failure_mode_info {
-       struct list_head list;
-       struct ubifs_info *c;
-};
-
-static LIST_HEAD(fmi_list);
-static DEFINE_SPINLOCK(fmi_lock);
-
-static unsigned int next;
-
-static int simple_rand(void)
-{
-       if (next == 0)
-               next = current->pid;
-       next = next * 1103515245 + 12345;
-       return (next >> 16) & 32767;
-}
-
-static void failure_mode_init(struct ubifs_info *c)
-{
-       struct failure_mode_info *fmi;
-
-       fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS);
-       if (!fmi) {
-               ubifs_err("Failed to register failure mode - no memory");
-               return;
-       }
-       fmi->c = c;
-       spin_lock(&fmi_lock);
-       list_add_tail(&fmi->list, &fmi_list);
-       spin_unlock(&fmi_lock);
-}
-
-static void failure_mode_exit(struct ubifs_info *c)
+static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
 {
-       struct failure_mode_info *fmi, *tmp;
-
-       spin_lock(&fmi_lock);
-       list_for_each_entry_safe(fmi, tmp, &fmi_list, list)
-               if (fmi->c == c) {
-                       list_del(&fmi->list);
-                       kfree(fmi);
-               }
-       spin_unlock(&fmi_lock);
-}
-
-static struct ubifs_info *dbg_find_info(struct ubi_volume_desc *desc)
-{
-       struct failure_mode_info *fmi;
-
-       spin_lock(&fmi_lock);
-       list_for_each_entry(fmi, &fmi_list, list)
-               if (fmi->c->ubi == desc) {
-                       struct ubifs_info *c = fmi->c;
-
-                       spin_unlock(&fmi_lock);
-                       return c;
-               }
-       spin_unlock(&fmi_lock);
-       return NULL;
-}
-
-static int in_failure_mode(struct ubi_volume_desc *desc)
-{
-       struct ubifs_info *c = dbg_find_info(desc);
-
-       if (c && dbg_failure_mode)
-               return c->dbg->failure_mode;
-       return 0;
-}
+       struct ubifs_debug_info *d = c->dbg;
 
-static int do_fail(struct ubi_volume_desc *desc, int lnum, int write)
-{
-       struct ubifs_info *c = dbg_find_info(desc);
-       struct ubifs_debug_info *d;
+       ubifs_assert(dbg_is_tst_rcvry(c));
 
-       if (!c || !dbg_failure_mode)
-               return 0;
-       d = c->dbg;
-       if (d->failure_mode)
-               return 1;
-       if (!d->fail_cnt) {
-               /* First call - decide delay to failure */
+       if (!d->pc_cnt) {
+               /* First call - decide delay to the power cut */
                if (chance(1, 2)) {
-                       unsigned int delay = 1 << (simple_rand() >> 11);
+                       unsigned long delay;
 
                        if (chance(1, 2)) {
-                               d->fail_delay = 1;
-                               d->fail_timeout = jiffies +
-                                                 msecs_to_jiffies(delay);
-                               dbg_rcvry("failing after %ums", delay);
+                               d->pc_delay = 1;
+                               /* Fail withing 1 minute */
+                               delay = random32() % 60000;
+                               d->pc_timeout = jiffies;
+                               d->pc_timeout += msecs_to_jiffies(delay);
+                               ubifs_warn("failing after %lums", delay);
                        } else {
-                               d->fail_delay = 2;
-                               d->fail_cnt_max = delay;
-                               dbg_rcvry("failing after %u calls", delay);
+                               d->pc_delay = 2;
+                               delay = random32() % 10000;
+                               /* Fail within 10000 operations */
+                               d->pc_cnt_max = delay;
+                               ubifs_warn("failing after %lu calls", delay);
                        }
                }
-               d->fail_cnt += 1;
+
+               d->pc_cnt += 1;
        }
+
        /* Determine if failure delay has expired */
-       if (d->fail_delay == 1) {
-               if (time_before(jiffies, d->fail_timeout))
+       if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
                        return 0;
-       } else if (d->fail_delay == 2)
-               if (d->fail_cnt++ < d->fail_cnt_max)
+       if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
                        return 0;
+
        if (lnum == UBIFS_SB_LNUM) {
-               if (write) {
-                       if (chance(1, 2))
-                               return 0;
-               } else if (chance(19, 20))
+               if (write && chance(1, 2))
+                       return 0;
+               if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in super block LEB %d", lnum);
+               ubifs_warn("failing in super block LEB %d", lnum);
        } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
                if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in master LEB %d", lnum);
+               ubifs_warn("failing in master LEB %d", lnum);
        } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
-               if (write) {
-                       if (chance(99, 100))
-                               return 0;
-               } else if (chance(399, 400))
+               if (write && chance(99, 100))
                        return 0;
-               dbg_rcvry("failing in log LEB %d", lnum);
+               if (chance(399, 400))
+                       return 0;
+               ubifs_warn("failing in log LEB %d", lnum);
        } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
-               if (write) {
-                       if (chance(7, 8))
-                               return 0;
-               } else if (chance(19, 20))
+               if (write && chance(7, 8))
                        return 0;
-               dbg_rcvry("failing in LPT LEB %d", lnum);
+               if (chance(19, 20))
+                       return 0;
+               ubifs_warn("failing in LPT LEB %d", lnum);
        } else if (lnum >= c->orph_first && lnum <= c->orph_last) {
-               if (write) {
-                       if (chance(1, 2))
-                               return 0;
-               } else if (chance(9, 10))
+               if (write && chance(1, 2))
+                       return 0;
+               if (chance(9, 10))
                        return 0;
-               dbg_rcvry("failing in orphan LEB %d", lnum);
+               ubifs_warn("failing in orphan LEB %d", lnum);
        } else if (lnum == c->ihead_lnum) {
                if (chance(99, 100))
                        return 0;
-               dbg_rcvry("failing in index head LEB %d", lnum);
+               ubifs_warn("failing in index head LEB %d", lnum);
        } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
                if (chance(9, 10))
                        return 0;
-               dbg_rcvry("failing in GC head LEB %d", lnum);
+               ubifs_warn("failing in GC head LEB %d", lnum);
        } else if (write && !RB_EMPTY_ROOT(&c->buds) &&
                   !ubifs_search_bud(c, lnum)) {
                if (chance(19, 20))
                        return 0;
-               dbg_rcvry("failing in non-bud LEB %d", lnum);
+               ubifs_warn("failing in non-bud LEB %d", lnum);
        } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
                   c->cmt_state == COMMIT_RUNNING_REQUIRED) {
                if (chance(999, 1000))
                        return 0;
-               dbg_rcvry("failing in bud LEB %d commit running", lnum);
+               ubifs_warn("failing in bud LEB %d commit running", lnum);
        } else {
                if (chance(9999, 10000))
                        return 0;
-               dbg_rcvry("failing in bud LEB %d commit not running", lnum);
+               ubifs_warn("failing in bud LEB %d commit not running", lnum);
        }
-       ubifs_err("*** SETTING FAILURE MODE ON (LEB %d) ***", lnum);
-       d->failure_mode = 1;
+
+       d->pc_happened = 1;
+       ubifs_warn("========== Power cut emulated ==========");
        dump_stack();
        return 1;
 }
 
-static void cut_data(const void *buf, int len)
+static void cut_data(const void *buf, unsigned int len)
 {
-       int flen, i;
+       unsigned int from, to, i, ffs = chance(1, 2);
        unsigned char *p = (void *)buf;
 
-       flen = (len * (long long)simple_rand()) >> 15;
-       for (i = flen; i < len; i++)
-               p[i] = 0xff;
-}
+       from = random32() % (len + 1);
+       if (chance(1, 2))
+               to = random32() % (len - from + 1);
+       else
+               to = len;
 
-int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
-                int len, int check)
-{
-       if (in_failure_mode(desc))
-               return -EROFS;
-       return ubi_leb_read(desc, lnum, buf, offset, len, check);
+       if (from < to)
+               ubifs_warn("filled bytes %u-%u with %s", from, to - 1,
+                          ffs ? "0xFFs" : "random data");
+
+       if (ffs)
+               for (i = from; i < to; i++)
+                       p[i] = 0xFF;
+       else
+               for (i = from; i < to; i++)
+                       p[i] = random32() % 0x100;
 }
 
-int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
-                 int offset, int len, int dtype)
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
+                 int offs, int len, int dtype)
 {
        int err, failing;
 
-       if (in_failure_mode(desc))
+       if (c->dbg->pc_happened)
                return -EROFS;
-       failing = do_fail(desc, lnum, 1);
+
+       failing = power_cut_emulated(c, lnum, 1);
        if (failing)
                cut_data(buf, len);
-       err = ubi_leb_write(desc, lnum, buf, offset, len, dtype);
+       err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
        if (err)
                return err;
        if (failing)
@@ -2706,162 +2678,207 @@ int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
        return 0;
 }
 
-int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
                   int len, int dtype)
 {
        int err;
 
-       if (do_fail(desc, lnum, 1))
+       if (c->dbg->pc_happened)
                return -EROFS;
-       err = ubi_leb_change(desc, lnum, buf, len, dtype);
+       if (power_cut_emulated(c, lnum, 1))
+               return -EROFS;
+       err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 1))
+       if (power_cut_emulated(c, lnum, 1))
                return -EROFS;
        return 0;
 }
 
-int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum)
+int dbg_leb_unmap(struct ubifs_info *c, int lnum)
 {
        int err;
 
-       if (do_fail(desc, lnum, 0))
+       if (c->dbg->pc_happened)
+               return -EROFS;
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
-       err = ubi_leb_erase(desc, lnum);
+       err = ubi_leb_unmap(c->ubi, lnum);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 0))
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
        return 0;
 }
 
-int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum)
+int dbg_leb_map(struct ubifs_info *c, int lnum, int dtype)
 {
        int err;
 
-       if (do_fail(desc, lnum, 0))
+       if (c->dbg->pc_happened)
                return -EROFS;
-       err = ubi_leb_unmap(desc, lnum);
+       if (power_cut_emulated(c, lnum, 0))
+               return -EROFS;
+       err = ubi_leb_map(c->ubi, lnum, dtype);
        if (err)
                return err;
-       if (do_fail(desc, lnum, 0))
+       if (power_cut_emulated(c, lnum, 0))
                return -EROFS;
        return 0;
 }
 
-int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
-{
-       if (in_failure_mode(desc))
-               return -EROFS;
-       return ubi_is_mapped(desc, lnum);
-}
+/*
+ * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular file-system mounts.
+ */
+static struct dentry *dfs_rootdir;
 
-int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
+static int dfs_file_open(struct inode *inode, struct file *file)
 {
-       int err;
-
-       if (do_fail(desc, lnum, 0))
-               return -EROFS;
-       err = ubi_leb_map(desc, lnum, dtype);
-       if (err)
-               return err;
-       if (do_fail(desc, lnum, 0))
-               return -EROFS;
-       return 0;
+       file->private_data = inode->i_private;
+       return nonseekable_open(inode, file);
 }
 
 /**
- * ubifs_debugging_init - initialize UBIFS debugging.
- * @c: UBIFS file-system description object
+ * provide_user_output - provide output to the user reading a debugfs file.
+ * @val: boolean value for the answer
+ * @u: the buffer to store the answer at
+ * @count: size of the buffer
+ * @ppos: position in the @u output buffer
  *
- * This function initializes debugging-related data for the file system.
- * Returns zero in case of success and a negative error code in case of
+ * This is a simple helper function which stores @val boolean value in the user
+ * buffer when the user reads one of UBIFS debugfs files. Returns amount of
+ * bytes written to @u in case of success and a negative error code in case of
  * failure.
  */
-int ubifs_debugging_init(struct ubifs_info *c)
+static int provide_user_output(int val, char __user *u, size_t count,
+                              loff_t *ppos)
 {
-       c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
-       if (!c->dbg)
-               return -ENOMEM;
+       char buf[3];
 
-       failure_mode_init(c);
-       return 0;
+       if (val)
+               buf[0] = '1';
+       else
+               buf[0] = '0';
+       buf[1] = '\n';
+       buf[2] = 0x00;
+
+       return simple_read_from_buffer(u, count, ppos, buf, 2);
 }
 
-/**
- * ubifs_debugging_exit - free debugging data.
- * @c: UBIFS file-system description object
- */
-void ubifs_debugging_exit(struct ubifs_info *c)
+static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
+                            loff_t *ppos)
 {
-       failure_mode_exit(c);
-       kfree(c->dbg);
-}
+       struct dentry *dent = file->f_path.dentry;
+       struct ubifs_info *c = file->private_data;
+       struct ubifs_debug_info *d = c->dbg;
+       int val;
+
+       if (dent == d->dfs_chk_gen)
+               val = d->chk_gen;
+       else if (dent == d->dfs_chk_index)
+               val = d->chk_index;
+       else if (dent == d->dfs_chk_orph)
+               val = d->chk_orph;
+       else if (dent == d->dfs_chk_lprops)
+               val = d->chk_lprops;
+       else if (dent == d->dfs_chk_fs)
+               val = d->chk_fs;
+       else if (dent == d->dfs_tst_rcvry)
+               val = d->tst_rcvry;
+       else
+               return -EINVAL;
 
-/*
- * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
- * contain the stuff specific to particular file-system mounts.
- */
-static struct dentry *dfs_rootdir;
+       return provide_user_output(val, u, count, ppos);
+}
 
 /**
- * dbg_debugfs_init - initialize debugfs file-system.
+ * interpret_user_input - interpret user debugfs file input.
+ * @u: user-provided buffer with the input
+ * @count: buffer size
  *
- * UBIFS uses debugfs file-system to expose various debugging knobs to
- * user-space. This function creates "ubifs" directory in the debugfs
- * file-system. Returns zero in case of success and a negative error code in
- * case of failure.
+ * This is a helper function which interpret user input to a boolean UBIFS
+ * debugfs file. Returns %0 or %1 in case of success and a negative error code
+ * in case of failure.
  */
-int dbg_debugfs_init(void)
+static int interpret_user_input(const char __user *u, size_t count)
 {
-       dfs_rootdir = debugfs_create_dir("ubifs", NULL);
-       if (IS_ERR(dfs_rootdir)) {
-               int err = PTR_ERR(dfs_rootdir);
-               ubifs_err("cannot create \"ubifs\" debugfs directory, "
-                         "error %d\n", err);
-               return err;
-       }
+       size_t buf_size;
+       char buf[8];
 
-       return 0;
-}
+       buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, u, buf_size))
+               return -EFAULT;
 
-/**
- * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
- */
-void dbg_debugfs_exit(void)
-{
-       debugfs_remove(dfs_rootdir);
-}
+       if (buf[0] == '1')
+               return 1;
+       else if (buf[0] == '0')
+               return 0;
 
-static int open_debugfs_file(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return nonseekable_open(inode, file);
+       return -EINVAL;
 }
 
-static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
-                                 size_t count, loff_t *ppos)
+static ssize_t dfs_file_write(struct file *file, const char __user *u,
+                             size_t count, loff_t *ppos)
 {
        struct ubifs_info *c = file->private_data;
        struct ubifs_debug_info *d = c->dbg;
+       struct dentry *dent = file->f_path.dentry;
+       int val;
 
-       if (file->f_path.dentry == d->dfs_dump_lprops)
+       /*
+        * TODO: this is racy - the file-system might have already been
+        * unmounted and we'd oops in this case. The plan is to fix it with
+        * help of 'iterate_supers_type()' which we should have in v3.0: when
+        * a debugfs opened, we rember FS's UUID in file->private_data. Then
+        * whenever we access the FS via a debugfs file, we iterate all UBIFS
+        * superblocks and fine the one with the same UUID, and take the
+        * locking right.
+        *
+        * The other way to go suggested by Al Viro is to create a separate
+        * 'ubifs-debug' file-system instead.
+        */
+       if (file->f_path.dentry == d->dfs_dump_lprops) {
                dbg_dump_lprops(c);
-       else if (file->f_path.dentry == d->dfs_dump_budg)
+               return count;
+       }
+       if (file->f_path.dentry == d->dfs_dump_budg) {
                dbg_dump_budg(c, &c->bi);
-       else if (file->f_path.dentry == d->dfs_dump_tnc) {
+               return count;
+       }
+       if (file->f_path.dentry == d->dfs_dump_tnc) {
                mutex_lock(&c->tnc_mutex);
                dbg_dump_tnc(c);
                mutex_unlock(&c->tnc_mutex);
-       } else
+               return count;
+       }
+
+       val = interpret_user_input(u, count);
+       if (val < 0)
+               return val;
+
+       if (dent == d->dfs_chk_gen)
+               d->chk_gen = val;
+       else if (dent == d->dfs_chk_index)
+               d->chk_index = val;
+       else if (dent == d->dfs_chk_orph)
+               d->chk_orph = val;
+       else if (dent == d->dfs_chk_lprops)
+               d->chk_lprops = val;
+       else if (dent == d->dfs_chk_fs)
+               d->chk_fs = val;
+       else if (dent == d->dfs_tst_rcvry)
+               d->tst_rcvry = val;
+       else
                return -EINVAL;
 
        return count;
 }
 
 static const struct file_operations dfs_fops = {
-       .open = open_debugfs_file,
-       .write = write_debugfs_file,
+       .open = dfs_file_open,
+       .read = dfs_file_read,
+       .write = dfs_file_write,
        .owner = THIS_MODULE,
        .llseek = no_llseek,
 };
@@ -2880,12 +2897,20 @@ static const struct file_operations dfs_fops = {
  */
 int dbg_debugfs_init_fs(struct ubifs_info *c)
 {
-       int err;
+       int err, n;
        const char *fname;
        struct dentry *dent;
        struct ubifs_debug_info *d = c->dbg;
 
-       sprintf(d->dfs_dir_name, "ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
+       n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
+                    c->vi.ubi_num, c->vi.vol_id);
+       if (n == UBIFS_DFS_DIR_LEN) {
+               /* The array size is too small */
+               fname = UBIFS_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))
@@ -2910,13 +2935,55 @@ int dbg_debugfs_init_fs(struct ubifs_info *c)
                goto out_remove;
        d->dfs_dump_tnc = dent;
 
+       fname = "chk_general";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_gen = dent;
+
+       fname = "chk_index";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_index = dent;
+
+       fname = "chk_orphans";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_orph = dent;
+
+       fname = "chk_lprops";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_lprops = dent;
+
+       fname = "chk_fs";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_fs = dent;
+
+       fname = "tst_recovery";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_tst_rcvry = dent;
+
        return 0;
 
 out_remove:
        debugfs_remove_recursive(d->dfs_dir);
 out:
        err = dent ? PTR_ERR(dent) : -ENODEV;
-       ubifs_err("cannot create \"%s\" debugfs directory, error %d\n",
+       ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
                  fname, err);
        return err;
 }
@@ -2930,4 +2997,179 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c)
        debugfs_remove_recursive(c->dbg->dfs_dir);
 }
 
+struct ubifs_global_debug_info ubifs_dbg;
+
+static struct dentry *dfs_chk_gen;
+static struct dentry *dfs_chk_index;
+static struct dentry *dfs_chk_orph;
+static struct dentry *dfs_chk_lprops;
+static struct dentry *dfs_chk_fs;
+static struct dentry *dfs_tst_rcvry;
+
+static ssize_t dfs_global_file_read(struct file *file, char __user *u,
+                                   size_t count, loff_t *ppos)
+{
+       struct dentry *dent = file->f_path.dentry;
+       int val;
+
+       if (dent == dfs_chk_gen)
+               val = ubifs_dbg.chk_gen;
+       else if (dent == dfs_chk_index)
+               val = ubifs_dbg.chk_index;
+       else if (dent == dfs_chk_orph)
+               val = ubifs_dbg.chk_orph;
+       else if (dent == dfs_chk_lprops)
+               val = ubifs_dbg.chk_lprops;
+       else if (dent == dfs_chk_fs)
+               val = ubifs_dbg.chk_fs;
+       else if (dent == dfs_tst_rcvry)
+               val = ubifs_dbg.tst_rcvry;
+       else
+               return -EINVAL;
+
+       return provide_user_output(val, u, count, ppos);
+}
+
+static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
+                                    size_t count, loff_t *ppos)
+{
+       struct dentry *dent = file->f_path.dentry;
+       int val;
+
+       val = interpret_user_input(u, count);
+       if (val < 0)
+               return val;
+
+       if (dent == dfs_chk_gen)
+               ubifs_dbg.chk_gen = val;
+       else if (dent == dfs_chk_index)
+               ubifs_dbg.chk_index = val;
+       else if (dent == dfs_chk_orph)
+               ubifs_dbg.chk_orph = val;
+       else if (dent == dfs_chk_lprops)
+               ubifs_dbg.chk_lprops = val;
+       else if (dent == dfs_chk_fs)
+               ubifs_dbg.chk_fs = val;
+       else if (dent == dfs_tst_rcvry)
+               ubifs_dbg.tst_rcvry = val;
+       else
+               return -EINVAL;
+
+       return count;
+}
+
+static const struct file_operations dfs_global_fops = {
+       .read = dfs_global_file_read,
+       .write = dfs_global_file_write,
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+};
+
+/**
+ * dbg_debugfs_init - initialize debugfs file-system.
+ *
+ * UBIFS uses debugfs file-system to expose various debugging knobs to
+ * user-space. This function creates "ubifs" directory in the debugfs
+ * file-system. Returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int dbg_debugfs_init(void)
+{
+       int err;
+       const char *fname;
+       struct dentry *dent;
+
+       fname = "ubifs";
+       dent = debugfs_create_dir(fname, NULL);
+       if (IS_ERR_OR_NULL(dent))
+               goto out;
+       dfs_rootdir = dent;
+
+       fname = "chk_general";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_gen = dent;
+
+       fname = "chk_index";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_index = dent;
+
+       fname = "chk_orphans";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_orph = dent;
+
+       fname = "chk_lprops";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_lprops = dent;
+
+       fname = "chk_fs";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_chk_fs = dent;
+
+       fname = "tst_recovery";
+       dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
+                                  &dfs_global_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       dfs_tst_rcvry = dent;
+
+       return 0;
+
+out_remove:
+       debugfs_remove_recursive(dfs_rootdir);
+out:
+       err = dent ? PTR_ERR(dent) : -ENODEV;
+       ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
+                 fname, err);
+       return err;
+}
+
+/**
+ * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
+ */
+void dbg_debugfs_exit(void)
+{
+       debugfs_remove_recursive(dfs_rootdir);
+}
+
+/**
+ * ubifs_debugging_init - initialize UBIFS debugging.
+ * @c: UBIFS file-system description object
+ *
+ * This function initializes debugging-related data for the file system.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubifs_debugging_init(struct ubifs_info *c)
+{
+       c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
+       if (!c->dbg)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * ubifs_debugging_exit - free debugging data.
+ * @c: UBIFS file-system description object
+ */
+void ubifs_debugging_exit(struct ubifs_info *c)
+{
+       kfree(c->dbg);
+}
+
 #endif /* CONFIG_UBIFS_FS_DEBUG */
index a811ac4..45174b5 100644 (file)
@@ -31,18 +31,25 @@ typedef int (*dbg_znode_callback)(struct ubifs_info *c,
 
 #ifdef CONFIG_UBIFS_FS_DEBUG
 
-#include <linux/random.h>
+/*
+ * The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi"
+ * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
+ */
+#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
+#define UBIFS_DFS_DIR_LEN  (3 + 1 + 2*2 + 1)
 
 /**
  * ubifs_debug_info - per-FS debugging information.
  * @old_zroot: old index root - used by 'dbg_check_old_index()'
  * @old_zroot_level: old index root level - used by 'dbg_check_old_index()'
  * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()'
- * @failure_mode: failure mode for recovery testing
- * @fail_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
- * @fail_timeout: time in jiffies when delay of failure mode expires
- * @fail_cnt: current number of calls to failure mode I/O functions
- * @fail_cnt_max: number of calls by which to delay failure mode
+ *
+ * @pc_happened: non-zero if an emulated power cut happened
+ * @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls
+ * @pc_timeout: time in jiffies when delay of failure mode expires
+ * @pc_cnt: current number of calls to failure mode I/O functions
+ * @pc_cnt_max: number of calls by which to delay failure mode
+ *
  * @chk_lpt_sz: used by LPT tree size checker
  * @chk_lpt_sz2: used by LPT tree size checker
  * @chk_lpt_wastage: used by LPT tree size checker
@@ -56,21 +63,36 @@ typedef int (*dbg_znode_callback)(struct ubifs_info *c,
  * @saved_free: saved amount of free space
  * @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt
  *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ *
  * @dfs_dir_name: name of debugfs directory containing this file-system's files
  * @dfs_dir: direntry object of the file-system debugfs directory
  * @dfs_dump_lprops: "dump lprops" debugfs knob
  * @dfs_dump_budg: "dump budgeting information" debugfs knob
  * @dfs_dump_tnc: "dump TNC" debugfs knob
+ * @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks
+ * @dfs_chk_index: debugfs knob to enable UBIFS index extra checks
+ * @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks
+ * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks
+ * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks
+ * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing
  */
 struct ubifs_debug_info {
        struct ubifs_zbranch old_zroot;
        int old_zroot_level;
        unsigned long long old_zroot_sqnum;
-       int failure_mode;
-       int fail_delay;
-       unsigned long fail_timeout;
-       unsigned int fail_cnt;
-       unsigned int fail_cnt_max;
+
+       int pc_happened;
+       int pc_delay;
+       unsigned long pc_timeout;
+       unsigned int pc_cnt;
+       unsigned int pc_cnt_max;
+
        long long chk_lpt_sz;
        long long chk_lpt_sz2;
        long long chk_lpt_wastage;
@@ -84,11 +106,43 @@ struct ubifs_debug_info {
        long long saved_free;
        int saved_idx_gc_cnt;
 
-       char dfs_dir_name[100];
+       unsigned int chk_gen:1;
+       unsigned int chk_index:1;
+       unsigned int chk_orph:1;
+       unsigned int chk_lprops:1;
+       unsigned int chk_fs:1;
+       unsigned int tst_rcvry:1;
+
+       char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1];
        struct dentry *dfs_dir;
        struct dentry *dfs_dump_lprops;
        struct dentry *dfs_dump_budg;
        struct dentry *dfs_dump_tnc;
+       struct dentry *dfs_chk_gen;
+       struct dentry *dfs_chk_index;
+       struct dentry *dfs_chk_orph;
+       struct dentry *dfs_chk_lprops;
+       struct dentry *dfs_chk_fs;
+       struct dentry *dfs_tst_rcvry;
+};
+
+/**
+ * ubifs_global_debug_info - global (not per-FS) UBIFS debugging information.
+ *
+ * @chk_gen: if general extra checks are enabled
+ * @chk_index: if index xtra checks are enabled
+ * @chk_orph: if orphans extra checks are enabled
+ * @chk_lprops: if lprops extra checks are enabled
+ * @chk_fs: if UBIFS contents extra checks are enabled
+ * @tst_rcvry: if UBIFS recovery testing mode enabled
+ */
+struct ubifs_global_debug_info {
+       unsigned int chk_gen:1;
+       unsigned int chk_index:1;
+       unsigned int chk_orph:1;
+       unsigned int chk_lprops:1;
+       unsigned int chk_fs:1;
+       unsigned int tst_rcvry:1;
 };
 
 #define ubifs_assert(expr) do {                                                \
@@ -127,6 +181,8 @@ const char *dbg_key_str1(const struct ubifs_info *c,
 #define DBGKEY(key) dbg_key_str0(c, (key))
 #define DBGKEY1(key) dbg_key_str1(c, (key))
 
+extern spinlock_t dbg_lock;
+
 #define ubifs_dbg_msg(type, fmt, ...) do {                        \
        spin_lock(&dbg_lock);                                     \
        pr_debug("UBIFS DBG " type ": " fmt "\n", ##__VA_ARGS__); \
@@ -162,41 +218,36 @@ const char *dbg_key_str1(const struct ubifs_info *c,
 /* Additional recovery messages */
 #define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__)
 
-/*
- * Debugging check flags.
- *
- * UBIFS_CHK_GEN: general checks
- * UBIFS_CHK_TNC: check TNC
- * UBIFS_CHK_IDX_SZ: check index size
- * UBIFS_CHK_ORPH: check orphans
- * UBIFS_CHK_OLD_IDX: check the old index
- * UBIFS_CHK_LPROPS: check lprops
- * UBIFS_CHK_FS: check the file-system
- */
-enum {
-       UBIFS_CHK_GEN     = 0x1,
-       UBIFS_CHK_TNC     = 0x2,
-       UBIFS_CHK_IDX_SZ  = 0x4,
-       UBIFS_CHK_ORPH    = 0x8,
-       UBIFS_CHK_OLD_IDX = 0x10,
-       UBIFS_CHK_LPROPS  = 0x20,
-       UBIFS_CHK_FS      = 0x40,
-};
-
-/*
- * Special testing flags.
- *
- * UBIFS_TST_RCVRY: failure mode for recovery testing
- */
-enum {
-       UBIFS_TST_RCVRY             = 0x4,
-};
-
-extern spinlock_t dbg_lock;
+extern struct ubifs_global_debug_info ubifs_dbg;
 
-extern unsigned int ubifs_msg_flags;
-extern unsigned int ubifs_chk_flags;
-extern unsigned int ubifs_tst_flags;
+static inline int dbg_is_chk_gen(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen);
+}
+static inline int dbg_is_chk_index(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.chk_index || c->dbg->chk_index);
+}
+static inline int dbg_is_chk_orph(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph);
+}
+static inline int dbg_is_chk_lprops(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops);
+}
+static inline int dbg_is_chk_fs(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs);
+}
+static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)
+{
+       return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry);
+}
+static inline int dbg_is_power_cut(const struct ubifs_info *c)
+{
+       return !!c->dbg->pc_happened;
+}
 
 int ubifs_debugging_init(struct ubifs_info *c);
 void ubifs_debugging_exit(struct ubifs_info *c);
@@ -207,7 +258,7 @@ const char *dbg_cstate(int cmt_state);
 const char *dbg_jhead(int jhead);
 const char *dbg_get_key_dump(const struct ubifs_info *c,
                             const union ubifs_key *key);
-void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode);
+void dbg_dump_inode(struct ubifs_info *c, const struct inode *inode);
 void dbg_dump_node(const struct ubifs_info *c, const void *node);
 void dbg_dump_lpt_node(const struct ubifs_info *c, void *node, int lnum,
                       int offs);
@@ -240,8 +291,8 @@ int dbg_check_cats(struct ubifs_info *c);
 int dbg_check_ltab(struct ubifs_info *c);
 int dbg_chk_lpt_free_spc(struct ubifs_info *c);
 int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
-int dbg_check_synced_i_size(struct inode *inode);
-int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir);
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode);
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir);
 int dbg_check_tnc(struct ubifs_info *c, int extra);
 int dbg_check_idx_size(struct ubifs_info *c, long long idx_size);
 int dbg_check_filesystem(struct ubifs_info *c);
@@ -254,54 +305,12 @@ int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
 int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head);
 int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head);
 
-/* Force the use of in-the-gaps method for testing */
-static inline int dbg_force_in_the_gaps_enabled(void)
-{
-       return ubifs_chk_flags & UBIFS_CHK_GEN;
-}
-int dbg_force_in_the_gaps(void);
-
-/* Failure mode for recovery testing */
-#define dbg_failure_mode (ubifs_tst_flags & UBIFS_TST_RCVRY)
-
-#ifndef UBIFS_DBG_PRESERVE_UBI
-#define ubi_leb_read   dbg_leb_read
-#define ubi_leb_write  dbg_leb_write
-#define ubi_leb_change dbg_leb_change
-#define ubi_leb_erase  dbg_leb_erase
-#define ubi_leb_unmap  dbg_leb_unmap
-#define ubi_is_mapped  dbg_is_mapped
-#define ubi_leb_map    dbg_leb_map
-#endif
-
-int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
-                int len, int check);
-int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
-                 int offset, int len, int dtype);
-int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
-                  int len, int dtype);
-int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum);
-int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum);
-int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum);
-int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype);
-
-static inline int dbg_read(struct ubi_volume_desc *desc, int lnum, char *buf,
-                          int offset, int len)
-{
-       return dbg_leb_read(desc, lnum, buf, offset, len, 0);
-}
-
-static inline int dbg_write(struct ubi_volume_desc *desc, int lnum,
-                           const void *buf, int offset, int len)
-{
-       return dbg_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
-}
-
-static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
-                                   const void *buf, int len)
-{
-       return dbg_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
-}
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+                 int len, int dtype);
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len,
+                  int dtype);
+int dbg_leb_unmap(struct ubifs_info *c, int lnum);
+int dbg_leb_map(struct ubifs_info *c, int lnum, int dtype);
 
 /* Debugfs-related stuff */
 int dbg_debugfs_init(void);
@@ -313,7 +322,7 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
 
 /* Use "if (0)" to make compiler check arguments even if debugging is off */
 #define ubifs_assert(expr)  do {                                               \
-       if (0 && (expr))                                                       \
+       if (0)                                                                 \
                printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \
                       __func__, __LINE__, current->pid);                      \
 } while (0)
@@ -323,6 +332,9 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
                ubifs_err(fmt, ##__VA_ARGS__);     \
 } while (0)
 
+#define DBGKEY(key)  ((char *)(key))
+#define DBGKEY1(key) ((char *)(key))
+
 #define ubifs_dbg_msg(fmt, ...) do {               \
        if (0)                                     \
                pr_debug(fmt "\n", ##__VA_ARGS__); \
@@ -346,9 +358,6 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c);
 #define dbg_scan(fmt, ...)  ubifs_dbg_msg(fmt, ##__VA_ARGS__)
 #define dbg_rcvry(fmt, ...) ubifs_dbg_msg(fmt, ##__VA_ARGS__)
 
-#define DBGKEY(key)  ((char *)(key))
-#define DBGKEY1(key) ((char *)(key))
-
 static inline int ubifs_debugging_init(struct ubifs_info *c)      { return 0; }
 static inline void ubifs_debugging_exit(struct ubifs_info *c)     { return; }
 static inline const char *dbg_ntype(int type)                     { return ""; }
@@ -357,7 +366,7 @@ static inline const char *dbg_jhead(int jhead)                    { return ""; }
 static inline const char *
 dbg_get_key_dump(const struct ubifs_info *c,
                 const union ubifs_key *key)                      { return ""; }
-static inline void dbg_dump_inode(const struct ubifs_info *c,
+static inline void dbg_dump_inode(struct ubifs_info *c,
                                  const struct inode *inode)      { return; }
 static inline void dbg_dump_node(const struct ubifs_info *c,
                                 const void *node)                { return; }
@@ -409,9 +418,11 @@ static inline int dbg_check_ltab(struct ubifs_info *c)            { return 0; }
 static inline int dbg_chk_lpt_free_spc(struct ubifs_info *c)      { return 0; }
 static inline int dbg_chk_lpt_sz(struct ubifs_info *c,
                                 int action, int len)             { return 0; }
-static inline int dbg_check_synced_i_size(struct inode *inode)    { return 0; }
-static inline int dbg_check_dir_size(struct ubifs_info *c,
-                                    const struct inode *dir)     { return 0; }
+static inline int
+dbg_check_synced_i_size(const struct ubifs_info *c,
+                       struct inode *inode)                      { return 0; }
+static inline int dbg_check_dir(struct ubifs_info *c,
+                               const struct inode *dir)          { return 0; }
 static inline int dbg_check_tnc(struct ubifs_info *c, int extra)  { return 0; }
 static inline int dbg_check_idx_size(struct ubifs_info *c,
                                     long long idx_size)          { return 0; }
@@ -431,9 +442,23 @@ static inline int
 dbg_check_nondata_nodes_order(struct ubifs_info *c,
                              struct list_head *head)             { return 0; }
 
-static inline int dbg_force_in_the_gaps(void)                     { return 0; }
-#define dbg_force_in_the_gaps_enabled() 0
-#define dbg_failure_mode                0
+static inline int dbg_leb_write(struct ubifs_info *c, int lnum,
+                               const void *buf, int offset,
+                               int len, int dtype)               { return 0; }
+static inline int dbg_leb_change(struct ubifs_info *c, int lnum,
+                                const void *buf, int len,
+                                int dtype)                       { return 0; }
+static inline int dbg_leb_unmap(struct ubifs_info *c, int lnum)   { return 0; }
+static inline int dbg_leb_map(struct ubifs_info *c, int lnum,
+                             int dtype)                          { return 0; }
+
+static inline int dbg_is_chk_gen(const struct ubifs_info *c)      { return 0; }
+static inline int dbg_is_chk_index(const struct ubifs_info *c)    { return 0; }
+static inline int dbg_is_chk_orph(const struct ubifs_info *c)     { return 0; }
+static inline int dbg_is_chk_lprops(const struct ubifs_info *c)   { return 0; }
+static inline int dbg_is_chk_fs(const struct ubifs_info *c)       { return 0; }
+static inline int dbg_is_tst_rcvry(const struct ubifs_info *c)    { return 0; }
+static inline int dbg_is_power_cut(const struct ubifs_info *c)    { return 0; }
 
 static inline int dbg_debugfs_init(void)                          { return 0; }
 static inline void dbg_debugfs_exit(void)                         { return; }
index ef5abd3..6834920 100644 (file)
@@ -102,7 +102,7 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
         * UBIFS has to fully control "clean <-> dirty" transitions of inodes
         * to make budgeting work.
         */
-       inode->i_flags |= (S_NOCMTIME);
+       inode->i_flags |= S_NOCMTIME;
 
        inode_init_owner(inode, dir, mode);
        inode->i_mtime = inode->i_atime = inode->i_ctime =
@@ -172,9 +172,11 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
 
 #ifdef CONFIG_UBIFS_FS_DEBUG
 
-static int dbg_check_name(struct ubifs_dent_node *dent, struct qstr *nm)
+static int dbg_check_name(const struct ubifs_info *c,
+                         const struct ubifs_dent_node *dent,
+                         const struct qstr *nm)
 {
-       if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
+       if (!dbg_is_chk_gen(c))
                return 0;
        if (le16_to_cpu(dent->nlen) != nm->len)
                return -EINVAL;
@@ -185,7 +187,7 @@ static int dbg_check_name(struct ubifs_dent_node *dent, struct qstr *nm)
 
 #else
 
-#define dbg_check_name(dent, nm) 0
+#define dbg_check_name(c, dent, nm) 0
 
 #endif
 
@@ -219,7 +221,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
                goto out;
        }
 
-       if (dbg_check_name(dent, &dentry->d_name)) {
+       if (dbg_check_name(c, dent, &dentry->d_name)) {
                err = -EINVAL;
                goto out;
        }
@@ -522,7 +524,7 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
        ubifs_assert(mutex_is_locked(&dir->i_mutex));
        ubifs_assert(mutex_is_locked(&inode->i_mutex));
 
-       err = dbg_check_synced_i_size(inode);
+       err = dbg_check_synced_i_size(c, inode);
        if (err)
                return err;
 
@@ -577,7 +579,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
                inode->i_nlink, dir->i_ino);
        ubifs_assert(mutex_is_locked(&dir->i_mutex));
        ubifs_assert(mutex_is_locked(&inode->i_mutex));
-       err = dbg_check_synced_i_size(inode);
+       err = dbg_check_synced_i_size(c, inode);
        if (err)
                return err;
 
index 5e7fccf..7cf738a 100644 (file)
@@ -1263,7 +1263,7 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
        if (err)
                return err;
 
-       err = dbg_check_synced_i_size(inode);
+       err = dbg_check_synced_i_size(c, inode);
        if (err)
                return err;
 
index 3be645e..9228950 100644 (file)
@@ -86,8 +86,125 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
                c->no_chk_data_crc = 0;
                c->vfs_sb->s_flags |= MS_RDONLY;
                ubifs_warn("switched to read-only mode, error %d", err);
+               dump_stack();
+       }
+}
+
+/*
+ * Below are simple wrappers over UBI I/O functions which include some
+ * additional checks and UBIFS debugging stuff. See corresponding UBI function
+ * for more information.
+ */
+
+int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
+                  int len, int even_ebadmsg)
+{
+       int err;
+
+       err = ubi_read(c->ubi, lnum, buf, offs, len);
+       /*
+        * In case of %-EBADMSG print the error message only if the
+        * @even_ebadmsg is true.
+        */
+       if (err && (err != -EBADMSG || even_ebadmsg)) {
+               ubifs_err("reading %d bytes from LEB %d:%d failed, error %d",
+                         len, lnum, offs, err);
+               dbg_dump_stack();
+       }
+       return err;
+}
+
+int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs,
+                   int len, int dtype)
+{
+       int err;
+
+       ubifs_assert(!c->ro_media && !c->ro_mount);
+       if (c->ro_error)
+               return -EROFS;
+       if (!dbg_is_tst_rcvry(c))
+               err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
+       else
+               err = dbg_leb_write(c, lnum, buf, offs, len, dtype);
+       if (err) {
+               ubifs_err("writing %d bytes to LEB %d:%d failed, error %d",
+                         len, lnum, offs, err);
+               ubifs_ro_mode(c, err);
+               dbg_dump_stack();
+       }
+       return err;
+}
+
+int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len,
+                    int dtype)
+{
+       int err;
+
+       ubifs_assert(!c->ro_media && !c->ro_mount);
+       if (c->ro_error)
+               return -EROFS;
+       if (!dbg_is_tst_rcvry(c))
+               err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
+       else
+               err = dbg_leb_change(c, lnum, buf, len, dtype);
+       if (err) {
+               ubifs_err("changing %d bytes in LEB %d failed, error %d",
+                         len, lnum, err);
+               ubifs_ro_mode(c, err);
+               dbg_dump_stack();
+       }
+       return err;
+}
+
+int ubifs_leb_unmap(struct ubifs_info *c, int lnum)
+{
+       int err;
+
+       ubifs_assert(!c->ro_media && !c->ro_mount);
+       if (c->ro_error)
+               return -EROFS;
+       if (!dbg_is_tst_rcvry(c))
+               err = ubi_leb_unmap(c->ubi, lnum);
+       else
+               err = dbg_leb_unmap(c, lnum);
+       if (err) {
+               ubifs_err("unmap LEB %d failed, error %d", lnum, err);
+               ubifs_ro_mode(c, err);
+               dbg_dump_stack();
+       }
+       return err;
+}
+
+int ubifs_leb_map(struct ubifs_info *c, int lnum, int dtype)
+{
+       int err;
+
+       ubifs_assert(!c->ro_media && !c->ro_mount);
+       if (c->ro_error)
+               return -EROFS;
+       if (!dbg_is_tst_rcvry(c))
+               err = ubi_leb_map(c->ubi, lnum, dtype);
+       else
+               err = dbg_leb_map(c, lnum, dtype);
+       if (err) {
+               ubifs_err("mapping LEB %d failed, error %d", lnum, err);
+               ubifs_ro_mode(c, err);
+               dbg_dump_stack();
+       }
+       return err;
+}
+
+int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
+{
+       int err;
+
+       err = ubi_is_mapped(c->ubi, lnum);
+       if (err < 0) {
+               ubifs_err("ubi_is_mapped failed for LEB %d, error %d",
+                         lnum, err);
                dbg_dump_stack();
        }
+       return err;
 }
 
 /**
@@ -406,14 +523,10 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf)
        dirt = sync_len - wbuf->used;
        if (dirt)
                ubifs_pad(c, wbuf->buf + wbuf->used, dirt);
-       err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf, wbuf->offs,
-                           sync_len, wbuf->dtype);
-       if (err) {
-               ubifs_err("cannot write %d bytes to LEB %d:%d",
-                         sync_len, wbuf->lnum, wbuf->offs);
-               dbg_dump_stack();
+       err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, sync_len,
+                             wbuf->dtype);
+       if (err)
                return err;
-       }
 
        spin_lock(&wbuf->lock);
        wbuf->offs += sync_len;
@@ -605,9 +718,9 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
                if (aligned_len == wbuf->avail) {
                        dbg_io("flush jhead %s wbuf to LEB %d:%d",
                               dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
-                       err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf,
-                                           wbuf->offs, wbuf->size,
-                                           wbuf->dtype);
+                       err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf,
+                                             wbuf->offs, wbuf->size,
+                                             wbuf->dtype);
                        if (err)
                                goto out;
 
@@ -642,8 +755,8 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
                dbg_io("flush jhead %s wbuf to LEB %d:%d",
                       dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs);
                memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail);
-               err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf, wbuf->offs,
-                                   wbuf->size, wbuf->dtype);
+               err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs,
+                                     wbuf->size, wbuf->dtype);
                if (err)
                        goto out;
 
@@ -661,8 +774,8 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
                 */
                dbg_io("write %d bytes to LEB %d:%d",
                       wbuf->si