- Updated to 3.0-rc1.
authorJeff Mahoney <jeffm@suse.com>
Tue, 31 May 2011 03:08:21 +0000 (23:08 -0400)
committerJeff Mahoney <jeffm@suse.com>
Tue, 31 May 2011 03:08:21 +0000 (23:08 -0400)
  - Eliminated 9 patches.

suse-commit: 5710413eb834b0f215f6f4249d6ff7c3f7673eb7

96 files changed:
1  2 
Documentation/kernel-parameters.txt
Documentation/sysctl/kernel.txt
Makefile
arch/ia64/Kconfig
arch/powerpc/kernel/prom_init.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/xmon/xmon.c
arch/s390/Kconfig
arch/x86/Kconfig
arch/x86/Makefile
arch/x86/include/asm/stacktrace.h
arch/x86/include/asm/system.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/apic/bigsmp_32.c
arch/x86/kernel/apic/probe_32.c
arch/x86/kernel/dumpstack.c
arch/x86/kernel/dumpstack_64.c
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/vmlinux.lds.S
arch/x86/kvm/svm.c
arch/x86/kvm/x86.c
arch/x86/mm/srat.c
block/genhd.c
drivers/char/Kconfig
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/isdn/mISDN/socket.c
drivers/md/dm-mpath.c
drivers/md/dm-raid45.c
drivers/md/dm-table.c
drivers/net/ehea/ehea_main.c
drivers/net/tulip/tulip_core.c
drivers/net/wireless/b43/main.c
drivers/scsi/device_handler/scsi_dh_alua.c
drivers/scsi/device_handler/scsi_dh_rdac.c
drivers/scsi/ibmvscsi/ibmvscsi.c
drivers/scsi/megaraid/megaraid_mbox.c
drivers/scsi/scsi_error.c
drivers/scsi/sd.c
drivers/tty/n_tty.c
drivers/tty/serial/8250.c
drivers/tty/vt/keyboard.c
drivers/tty/vt/vt.c
drivers/video/Kconfig
drivers/video/Makefile
fs/Kconfig
fs/ext3/super.c
fs/ext4/Makefile
fs/ext4/ext4.h
fs/ext4/file.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/super.c
fs/ext4/xattr.c
fs/namei.c
fs/nfs/dir.c
fs/nfs/inode.c
fs/novfs/inode.c
fs/partitions/check.c
fs/partitions/efi.c
fs/reiserfs/super.c
fs/super.c
include/asm-generic/vmlinux.lds.h
include/linux/blkdev.h
include/linux/device.h
include/linux/fs.h
include/linux/genhd.h
include/linux/kernel.h
include/linux/lsm_audit.h
include/linux/mm.h
include/linux/module.h
include/linux/printk.h
init/Kconfig
init/main.c
kernel/Kconfig.preempt
kernel/Makefile
kernel/capability.c
kernel/ksysfs.c
kernel/module.c
kernel/printk.c
kernel/sysctl.c
kernel/sysctl_binary.c
lib/Kconfig.debug
mm/page_alloc.c
mm/truncate.c
net/bridge/br_if.c
scripts/Makefile.build
scripts/kconfig/Makefile
scripts/mod/modpost.c
security/apparmor/match.c
security/apparmor/policy_unpack.c
sound/pci/hda/hda_intel.c
virt/kvm/ioapic.c

Simple merge
Simple merge
diff --cc Makefile
+++ b/Makefile
@@@ -402,7 -391,7 +411,8 @@@ export KBUILD_CFLAGS CFLAGS_KERNEL CFLA
  export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
  export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
  export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
+ export KBUILD_ARFLAGS
 +export KBUILD_KMSG_CHECK KMSG_CHECK
  
  # When compiling out-of-tree modules, put MODVERDIR in the module
  # tree rather than in the kernel tree. The kernel tree might
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -1361,23 -1343,7 +1343,22 @@@ static int __init dmi_ignore_irq0_timer
        }
        return 0;
  }
- #endif
  
 +static int __init force_acpi_rsdt(const struct dmi_system_id *d)
 +{
 +      if (!acpi_force) {
 +              printk(KERN_NOTICE "%s detected: force use of acpi=rsdt\n",
 +                     d->ident);
 +              acpi_rsdt_forced = 1;
 +      } else {
 +              printk(KERN_NOTICE
 +                     "Warning: acpi=force overrules DMI blacklist: "
 +                     "acpi=rsdt\n");
 +      }
 +      return 0;
 +
 +}
 +
  /*
   * If your system is blacklisted here, but you find that acpi=force
   * works for you, please contact linux-acpi@vger.kernel.org
Simple merge
@@@ -265,16 -237,16 +237,16 @@@ void __init generic_apic_probe(void
  int __init
  generic_mps_oem_check(struct mpc_table *mpc, char *oem, char *productid)
  {
-       int i;
+       struct apic **drv;
  
-       for (i = 0; apic_probe[i]; ++i) {
-               if (!apic_probe[i]->mps_oem_check)
+       for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
+               if (!((*drv)->mps_oem_check))
                        continue;
-               if (!apic_probe[i]->mps_oem_check(mpc, oem, productid))
+               if (!(*drv)->mps_oem_check(mpc, oem, productid))
                        continue;
  
 -              if (!cmdline_apic) {
 +              if (!cmdline_apic && apic == &apic_default) {
-                       apic = apic_probe[i];
+                       apic = *drv;
                        printk(KERN_INFO "Switched to APIC driver `%s'.\n",
                               apic->name);
                }
  
  int __init default_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
  {
-       int i;
+       struct apic **drv;
  
-       for (i = 0; apic_probe[i]; ++i) {
-               if (!apic_probe[i]->acpi_madt_oem_check)
+       for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
+               if (!(*drv)->acpi_madt_oem_check)
                        continue;
-               if (!apic_probe[i]->acpi_madt_oem_check(oem_id, oem_table_id))
+               if (!(*drv)->acpi_madt_oem_check(oem_id, oem_table_id))
                        continue;
  
 -              if (!cmdline_apic) {
 +              if (!cmdline_apic && apic == &apic_default) {
-                       apic = apic_probe[i];
+                       apic = *drv;
                        printk(KERN_INFO "Switched to APIC driver `%s'.\n",
                               apic->name);
                }
Simple merge
Simple merge
Simple merge
@@@ -1224,41 -1217,7 +1224,41 @@@ ENTRY(call_softirq
        CFI_ENDPROC
  END(call_softirq)
  
 +#ifdef CONFIG_STACK_UNWIND
 +ENTRY(arch_unwind_init_running)
 +      CFI_STARTPROC
 +      movq    %r15, R15(%rdi)
 +      movq    %r14, R14(%rdi)
 +      xchgq   %rsi, %rdx
 +      movq    %r13, R13(%rdi)
 +      movq    %r12, R12(%rdi)
 +      xorl    %eax, %eax
 +      movq    %rbp, RBP(%rdi)
 +      movq    %rbx, RBX(%rdi)
 +      movq    (%rsp), %r9
 +      xchgq   %rdx, %rcx
 +      movq    %rax, R11(%rdi)
 +      movq    %rax, R10(%rdi)
 +      movq    %rax, R9(%rdi)
 +      movq    %rax, R8(%rdi)
 +      movq    %rax, RAX(%rdi)
 +      movq    %rax, RCX(%rdi)
 +      movq    %rax, RDX(%rdi)
 +      movq    %rax, RSI(%rdi)
 +      movq    %rax, RDI(%rdi)
 +      movq    %rax, ORIG_RAX(%rdi)
 +      movq    %r9, RIP(%rdi)
 +      leaq    8(%rsp), %r9
 +      movq    $__KERNEL_CS, CS(%rdi)
 +      movq    %rax, EFLAGS(%rdi)
 +      movq    %r9, RSP(%rdi)
 +      movq    $__KERNEL_DS, SS(%rdi)
 +      jmpq    *%rcx
 +      CFI_ENDPROC
 +END(arch_unwind_init_running)
 +#endif
 +
- #ifdef CONFIG_PARAVIRT_XEN
+ #ifdef CONFIG_XEN
  zeroentry xen_hypervisor_callback xen_do_hypervisor_callback
  
  /*
Simple merge
Simple merge
Simple merge
index 0000000,81dbfde..7efd0c6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,184 +1,188 @@@
+ /*
+  * ACPI 3.0 based NUMA setup
+  * Copyright 2004 Andi Kleen, SuSE Labs.
+  *
+  * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
+  *
+  * Called from acpi_numa_init while reading the SRAT and SLIT tables.
+  * Assumes all memory regions belonging to a single proximity domain
+  * are in one chunk. Holes between them will be included in the node.
+  */
+ #include <linux/kernel.h>
+ #include <linux/acpi.h>
+ #include <linux/mmzone.h>
+ #include <linux/bitmap.h>
+ #include <linux/module.h>
+ #include <linux/topology.h>
+ #include <linux/bootmem.h>
+ #include <linux/memblock.h>
+ #include <linux/mm.h>
+ #include <asm/proto.h>
+ #include <asm/numa.h>
+ #include <asm/e820.h>
+ #include <asm/apic.h>
+ #include <asm/uv/uv.h>
+ int acpi_numa __initdata;
+ static __init int setup_node(int pxm)
+ {
+       return acpi_map_pxm_to_node(pxm);
+ }
+ static __init void bad_srat(void)
+ {
+       printk(KERN_ERR "SRAT: SRAT not used.\n");
+       acpi_numa = -1;
+ }
+ static __init inline int srat_disabled(void)
+ {
+       return acpi_numa < 0;
+ }
+ /* Callback for SLIT parsing */
+ void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
+ {
+       int i, j;
+       for (i = 0; i < slit->locality_count; i++)
+               for (j = 0; j < slit->locality_count; j++)
+                       numa_set_distance(pxm_to_node(i), pxm_to_node(j),
+                               slit->entry[slit->locality_count * i + j]);
+ }
+ /* Callback for Proximity Domain -> x2APIC mapping */
+ void __init
+ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
+ {
+       int pxm, node;
+       int apic_id;
+       if (srat_disabled())
+               return;
+       if (pa->header.length < sizeof(struct acpi_srat_x2apic_cpu_affinity)) {
+               bad_srat();
+               return;
+       }
+       if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
+               return;
+       pxm = pa->proximity_domain;
+       node = setup_node(pxm);
+       if (node < 0) {
+               printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
+               bad_srat();
+               return;
+       }
+       apic_id = pa->apic_id;
+       if (apic_id >= MAX_LOCAL_APIC) {
+               printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node);
+               return;
+       }
+       set_apicid_to_node(apic_id, node);
+       node_set(node, numa_nodes_parsed);
+       acpi_numa = 1;
+       printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
+              pxm, apic_id, node);
+ }
+ /* Callback for Proximity Domain -> LAPIC mapping */
+ void __init
+ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
+ {
+       int pxm, node;
+       int apic_id;
+       if (srat_disabled())
+               return;
+       if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
+               bad_srat();
+               return;
+       }
+       if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
+               return;
+       pxm = pa->proximity_domain_lo;
++      if (acpi_srat_revision >= 2)
++              pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8;
+       node = setup_node(pxm);
+       if (node < 0) {
+               printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
+               bad_srat();
+               return;
+       }
+       if (get_uv_system_type() >= UV_X2APIC)
+               apic_id = (pa->apic_id << 8) | pa->local_sapic_eid;
+       else
+               apic_id = pa->apic_id;
+       if (apic_id >= MAX_LOCAL_APIC) {
+               printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node);
+               return;
+       }
+       set_apicid_to_node(apic_id, node);
+       node_set(node, numa_nodes_parsed);
+       acpi_numa = 1;
+       printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
+              pxm, apic_id, node);
+ }
+ #ifdef CONFIG_MEMORY_HOTPLUG
+ static inline int save_add_info(void) {return 1;}
+ #else
+ static inline int save_add_info(void) {return 0;}
+ #endif
+ /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
+ void __init
+ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
+ {
+       u64 start, end;
+       int node, pxm;
+       if (srat_disabled())
+               return;
+       if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
+               bad_srat();
+               return;
+       }
+       if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
+               return;
+       if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info())
+               return;
+       start = ma->base_address;
+       end = start + ma->length;
+       pxm = ma->proximity_domain;
++      if (acpi_srat_revision <= 1)
++              pxm &= 0xff;
+       node = setup_node(pxm);
+       if (node < 0) {
+               printk(KERN_ERR "SRAT: Too many proximity domains.\n");
+               bad_srat();
+               return;
+       }
+       if (numa_add_memblk(node, start, end) < 0) {
+               bad_srat();
+               return;
+       }
+       printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
+              start, end);
+ }
+ void __init acpi_numa_arch_fixup(void) {}
+ int __init x86_acpi_numa_init(void)
+ {
+       int ret;
+       ret = acpi_numa_init();
+       if (ret < 0)
+               return ret;
+       return srat_disabled() ? -EINVAL : 0;
+ }
diff --cc block/genhd.c
Simple merge
Simple merge
Simple merge
  #define USB_VENDOR_ID_DREAM_CHEEKY    0x1d34
  
  #define USB_VENDOR_ID_ELO             0x04E7
 +#define USB_DEVICE_ID_ELO_4000U               0x0009
+ #define USB_DEVICE_ID_ELO_TS2515      0x0022
  #define USB_DEVICE_ID_ELO_TS2700      0x0020
 +#define USB_DEVICE_ID_ELO_4500U               0x0030
  
  #define USB_VENDOR_ID_EMS             0x2006
  #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
Simple merge
Simple merge
Simple merge
Simple merge
index 3a1a10d,0000000..5312a16
mode 100644,000000..100644
--- /dev/null
@@@ -1,4696 -1,0 +1,4691 @@@
 +/*
 + * Copyright (C) 2005-2009 Red Hat, Inc. All rights reserved.
 + *
 + * Module Author: Heinz Mauelshagen <heinzm@redhat.com>
 + *
 + * This file is released under the GPL.
 + *
 + *
 + * Linux 2.6 Device Mapper RAID4 and RAID5 target.
 + *
 + * Tested-by: Intel; Marcin.Labun@intel.com, krzysztof.wojcik@intel.com
 + *
 + *
 + * Supports the following ATARAID vendor solutions (and SNIA DDF):
 + *
 + *    Adaptec HostRAID ASR
 + *    SNIA DDF1
 + *    Hiphpoint 37x
 + *    Hiphpoint 45x
 + *    Intel IMSM
 + *    Jmicron ATARAID
 + *    LSI Logic MegaRAID
 + *    NVidia RAID
 + *    Promise FastTrack
 + *    Silicon Image Medley
 + *    VIA Software RAID
 + *
 + * via the dmraid application.
 + *
 + *
 + * Features:
 + *
 + *    o RAID4 with dedicated and selectable parity device
 + *    o RAID5 with rotating parity (left+right, symmetric+asymmetric)
 + *    o recovery of out of sync device for initial
 + *      RAID set creation or after dead drive replacement
 + *    o run time optimization of xor algorithm used to calculate parity
 + *
 + *
 + * Thanks to MD for:
 + *    o the raid address calculation algorithm
 + *    o the base of the biovec <-> page list copier.
 + *
 + *
 + * Uses region hash to keep track of how many writes are in flight to
 + * regions in order to use dirty log to keep state of regions to recover:
 + *
 + *    o clean regions (those which are synchronized
 + *    and don't have write io in flight)
 + *    o dirty regions (those with write io in flight)
 + *
 + *
 + * On startup, any dirty regions are migrated to the
 + * 'nosync' state and are subject to recovery by the daemon.
 + *
 + * See raid_ctr() for table definition.
 + *
 + * ANALYZEME: recovery bandwidth
 + */
 +
 +static const char *version = "v0.2597k";
 +
 +#include "dm.h"
 +#include "dm-memcache.h"
 +#include "dm-raid45.h"
 +
 +#include <linux/kernel.h>
 +#include <linux/vmalloc.h>
 +#include <linux/raid/xor.h>
 +#include <linux/slab.h>
 +
 +#include <linux/bio.h>
 +#include <linux/dm-io.h>
 +#include <linux/dm-dirty-log.h>
 +#include <linux/dm-region-hash.h>
 +
 +
 +/*
 + * Configurable parameters
 + */
 +
 +/* Minimum/maximum and default # of selectable stripes. */
 +#define       STRIPES_MIN             8
 +#define       STRIPES_MAX             16384
 +#define       STRIPES_DEFAULT         80
 +
 +/* Maximum and default chunk size in sectors if not set in constructor. */
 +#define       CHUNK_SIZE_MIN          8
 +#define       CHUNK_SIZE_MAX          16384
 +#define       CHUNK_SIZE_DEFAULT      64
 +
 +/* Default io size in sectors if not set in constructor. */
 +#define       IO_SIZE_MIN             CHUNK_SIZE_MIN
 +#define       IO_SIZE_DEFAULT         IO_SIZE_MIN
 +
 +/* Recover io size default in sectors. */
 +#define       RECOVER_IO_SIZE_MIN             64
 +#define       RECOVER_IO_SIZE_DEFAULT         256
 +
 +/* Default, minimum and maximum percentage of recover io bandwidth. */
 +#define       BANDWIDTH_DEFAULT       10
 +#define       BANDWIDTH_MIN           1
 +#define       BANDWIDTH_MAX           100
 +
 +/* # of parallel recovered regions */
 +#define RECOVERY_STRIPES_MIN  1
 +#define RECOVERY_STRIPES_MAX  64
 +#define RECOVERY_STRIPES_DEFAULT      RECOVERY_STRIPES_MIN
 +/*
 + * END Configurable parameters
 + */
 +
 +#define       TARGET  "dm-raid45"
 +#define       DAEMON  "kraid45d"
 +#define       DM_MSG_PREFIX   TARGET
 +
 +#define       SECTORS_PER_PAGE        (PAGE_SIZE >> SECTOR_SHIFT)
 +
 +/* Amount/size for __xor(). */
 +#define       XOR_SIZE        PAGE_SIZE
 +
 +/* Ticks to run xor_speed() test for. */
 +#define       XOR_SPEED_TICKS 5
 +
 +/* Check value in range. */
 +#define       range_ok(i, min, max)   (i >= min && i <= max)
 +
 +/* Structure access macros. */
 +/* Derive raid_set from stripe_cache pointer. */
 +#define       RS(x)   container_of(x, struct raid_set, sc)
 +
 +/* Page reference. */
 +#define PAGE(stripe, p)  ((stripe)->obj[p].pl->page)
 +
 +/* Stripe chunk reference. */
 +#define CHUNK(stripe, p) ((stripe)->chunk + p)
 +
 +/* Bio list reference. */
 +#define       BL(stripe, p, rw)       (stripe->chunk[p].bl + rw)
 +#define       BL_CHUNK(chunk, rw)     (chunk->bl + rw)
 +
 +/* Page list reference. */
 +#define       PL(stripe, p)           (stripe->obj[p].pl)
 +/* END: structure access macros. */
 +
 +/* Factor out to dm-bio-list.h */
 +static inline void bio_list_push(struct bio_list *bl, struct bio *bio)
 +{
 +      bio->bi_next = bl->head;
 +      bl->head = bio;
 +
 +      if (!bl->tail)
 +              bl->tail = bio;
 +}
 +
 +/* Factor out to dm.h */
 +#define TI_ERR_RET(str, ret) \
 +      do { ti->error = str; return ret; } while (0);
 +#define TI_ERR(str)     TI_ERR_RET(str, -EINVAL)
 +
 +/* Macro to define access IO flags access inline functions. */
 +#define       BITOPS(name, what, var, flag) \
 +static inline int TestClear ## name ## what(struct var *v) \
 +{ return test_and_clear_bit(flag, &v->io.flags); } \
 +static inline int TestSet ## name ## what(struct var *v) \
 +{ return test_and_set_bit(flag, &v->io.flags); } \
 +static inline void Clear ## name ## what(struct var *v) \
 +{ clear_bit(flag, &v->io.flags); } \
 +static inline void Set ## name ## what(struct var *v) \
 +{ set_bit(flag, &v->io.flags); } \
 +static inline int name ## what(struct var *v) \
 +{ return test_bit(flag, &v->io.flags); }
 +
 +/*-----------------------------------------------------------------
 + * Stripe cache
 + *
 + * Cache for all reads and writes to raid sets (operational or degraded)
 + *
 + * We need to run all data to and from a RAID set through this cache,
 + * because parity chunks need to get calculated from data chunks
 + * or, in the degraded/resynchronization case, missing chunks need
 + * to be reconstructed using the other chunks of the stripe.
 + *---------------------------------------------------------------*/
 +/* Unique kmem cache name suffix # counter. */
 +static atomic_t _stripe_sc_nr = ATOMIC_INIT(-1); /* kmem cache # counter. */
 +
 +/* A chunk within a stripe (holds bios hanging off). */
 +/* IO status flags for chunks of a stripe. */
 +enum chunk_flags {
 +      CHUNK_DIRTY,            /* Pages of chunk dirty; need writing. */
 +      CHUNK_ERROR,            /* IO error on any chunk page. */
 +      CHUNK_IO,               /* Allow/prohibit IO on chunk pages. */
 +      CHUNK_LOCKED,           /* Chunk pages locked during IO. */
 +      CHUNK_MUST_IO,          /* Chunk must io. */
 +      CHUNK_UNLOCK,           /* Enforce chunk unlock. */
 +      CHUNK_UPTODATE,         /* Chunk pages are uptodate. */
 +};
 +
 +enum bl_type {
 +      WRITE_QUEUED = WRITE + 1,
 +      WRITE_MERGED,
 +      NR_BL_TYPES,    /* Must be last one! */
 +};
 +struct stripe_chunk {
 +      atomic_t cnt;           /* Reference count. */
 +      struct stripe *stripe;  /* Backpointer to stripe for endio(). */
 +      /* Bio lists for reads, writes, and writes merged. */
 +      struct bio_list bl[NR_BL_TYPES];
 +      struct {
 +              unsigned long flags; /* IO status flags. */
 +      } io;
 +};
 +
 +/* Define chunk bit operations. */
 +BITOPS(Chunk, Dirty,   stripe_chunk, CHUNK_DIRTY)
 +BITOPS(Chunk, Error,   stripe_chunk, CHUNK_ERROR)
 +BITOPS(Chunk, Io,      stripe_chunk, CHUNK_IO)
 +BITOPS(Chunk, Locked,  stripe_chunk, CHUNK_LOCKED)
 +BITOPS(Chunk, MustIo,  stripe_chunk, CHUNK_MUST_IO)
 +BITOPS(Chunk, Unlock,  stripe_chunk, CHUNK_UNLOCK)
 +BITOPS(Chunk, Uptodate,        stripe_chunk, CHUNK_UPTODATE)
 +
 +/*
 + * Stripe linked list indexes. Keep order, because the stripe
 + * and the stripe cache rely on the first 3!
 + */
 +enum list_types {
 +      LIST_FLUSH,     /* Stripes to flush for io. */
 +      LIST_ENDIO,     /* Stripes to endio. */
 +      LIST_LRU,       /* Least recently used stripes. */
 +      SC_NR_LISTS,    /* # of lists in stripe cache. */
 +      LIST_HASH = SC_NR_LISTS,        /* Hashed stripes. */
 +      LIST_RECOVER = LIST_HASH, /* For recovery type stripes only. */
 +      STRIPE_NR_LISTS,/* To size array in struct stripe. */
 +};
 +
 +/* Adressing region recovery. */
 +struct recover_addr {
 +      struct dm_region *reg;  /* Actual region to recover. */
 +      sector_t pos;   /* Position within region to recover. */
 +      sector_t end;   /* End of region to recover. */
 +};
 +
 +/* A stripe: the io object to handle all reads and writes to a RAID set. */
 +struct stripe {
 +      atomic_t cnt;                   /* Reference count. */
 +      struct stripe_cache *sc;        /* Backpointer to stripe cache. */
 +
 +      /*
 +       * 4 linked lists:
 +       *   o io list to flush io
 +       *   o endio list
 +       *   o LRU list to put stripes w/o reference count on
 +       *   o stripe cache hash
 +       */
 +      struct list_head lists[STRIPE_NR_LISTS];
 +
 +      sector_t key;    /* Hash key. */
 +      region_t region; /* Region stripe is mapped to. */
 +
 +      struct {
 +              unsigned long flags;    /* Stripe state flags (see below). */
 +
 +              /*
 +               * Pending ios in flight:
 +               *
 +               * used to control move of stripe to endio list
 +               */
 +              atomic_t pending;
 +
 +              /* Sectors to read and write for multi page stripe sets. */
 +              unsigned size;
 +      } io;
 +
 +      /* Address region recovery. */
 +      struct recover_addr *recover;
 +
 +      /* Lock on stripe (Future: for clustering). */
 +      void *lock;
 +
 +      struct {
 +              unsigned short parity;  /* Parity chunk index. */
 +              short recover;          /* Recovery chunk index. */
 +      } idx;
 +
 +      /*
 +       * This stripe's memory cache object (dm-mem-cache);
 +       * i.e. the io chunk pages.
 +       */
 +      struct dm_mem_cache_object *obj;
 +
 +      /* Array of stripe sets (dynamically allocated). */
 +      struct stripe_chunk chunk[0];
 +};
 +
 +/* States stripes can be in (flags field). */
 +enum stripe_states {
 +      STRIPE_ERROR,           /* io error on stripe. */
 +      STRIPE_MERGED,          /* Writes got merged to be written. */
 +      STRIPE_RBW,             /* Read-before-write stripe. */
 +      STRIPE_RECONSTRUCT,     /* Reconstruct of a missing chunk required. */
 +      STRIPE_RECONSTRUCTED,   /* Reconstructed of a missing chunk. */
 +      STRIPE_RECOVER,         /* Stripe used for RAID set recovery. */
 +};
 +
 +/* Define stripe bit operations. */
 +BITOPS(Stripe, Error,       stripe, STRIPE_ERROR)
 +BITOPS(Stripe, Merged,        stripe, STRIPE_MERGED)
 +BITOPS(Stripe, RBW,         stripe, STRIPE_RBW)
 +BITOPS(Stripe, Reconstruct,   stripe, STRIPE_RECONSTRUCT)
 +BITOPS(Stripe, Reconstructed, stripe, STRIPE_RECONSTRUCTED)
 +BITOPS(Stripe, Recover,             stripe, STRIPE_RECOVER)
 +
 +/* A stripe hash. */
 +struct stripe_hash {
 +      struct list_head *hash;
 +      unsigned buckets;
 +      unsigned mask;
 +      unsigned prime;
 +      unsigned shift;
 +};
 +
 +enum sc_lock_types {
 +      LOCK_ENDIO,     /* Protect endio list. */
 +      NR_LOCKS,       /* To size array in struct stripe_cache. */
 +};
 +
 +/* A stripe cache. */
 +struct stripe_cache {
 +      /* Stripe hash. */
 +      struct stripe_hash hash;
 +
 +      spinlock_t locks[NR_LOCKS];     /* Locks to protect lists. */
 +
 +      /* Stripes with io to flush, stripes to endio and LRU lists. */
 +      struct list_head lists[SC_NR_LISTS];
 +
 +      /* Slab cache to allocate stripes from. */
 +      struct {
 +              struct kmem_cache *cache;       /* Cache itself. */
 +              char name[32];  /* Unique name. */
 +      } kc;
 +
 +      struct dm_io_client *dm_io_client; /* dm-io client resource context. */
 +
 +      /* dm-mem-cache client resource context. */
 +      struct dm_mem_cache_client *mem_cache_client;
 +
 +      int stripes_parm;           /* # stripes parameter from constructor. */
 +      atomic_t stripes;           /* actual # of stripes in cache. */
 +      atomic_t stripes_to_set;    /* # of stripes to resize cache to. */
 +      atomic_t stripes_last;      /* last # of stripes in cache. */
 +      atomic_t active_stripes;    /* actual # of active stripes in cache. */
 +
 +      /* REMOVEME: */
 +      atomic_t active_stripes_max; /* actual # of active stripes in cache. */
 +};
 +
 +/* Flag specs for raid_dev */ ;
 +enum raid_dev_flags {
 +      DEV_FAILED,     /* Device failed. */
 +      DEV_IO_QUEUED,  /* Io got queued to device. */
 +};
 +
 +/* The raid device in a set. */
 +struct raid_dev {
 +      struct dm_dev *dev;
 +      sector_t start;         /* Offset to map to. */
 +      struct {        /* Using struct to be able to BITOPS(). */
 +              unsigned long flags;    /* raid_dev_flags. */
 +      } io;
 +};
 +
 +BITOPS(Dev, Failed,   raid_dev, DEV_FAILED)
 +BITOPS(Dev, IoQueued, raid_dev, DEV_IO_QUEUED)
 +
 +/* Flags spec for raid_set. */
 +enum raid_set_flags {
 +      RS_CHECK_OVERWRITE,     /* Check for chunk overwrites. */
 +      RS_DEAD,                /* RAID set inoperational. */
 +      RS_DEAD_ENDIO_MESSAGE,  /* RAID set dead endio one-off message. */
 +      RS_DEGRADED,            /* Io errors on RAID device. */
 +      RS_DEVEL_STATS,         /* REMOVEME: display status information. */
 +      RS_ENFORCE_PARITY_CREATION,/* Enforce parity creation. */
 +      RS_PROHIBIT_WRITES,     /* Prohibit writes on device failure. */
 +      RS_RECOVER,             /* Do recovery. */
 +      RS_RECOVERY_BANDWIDTH,  /* Allow recovery bandwidth (delayed bios). */
 +      RS_SC_BUSY,             /* Stripe cache busy -> send an event. */
 +      RS_SUSPEND,             /* Suspend RAID set. */
 +};
 +
 +/* REMOVEME: devel stats counters. */
 +enum stats_types {
 +      S_BIOS_READ,
 +      S_BIOS_ADDED_READ,
 +      S_BIOS_ENDIO_READ,
 +      S_BIOS_WRITE,
 +      S_BIOS_ADDED_WRITE,
 +      S_BIOS_ENDIO_WRITE,
 +      S_CAN_MERGE,
 +      S_CANT_MERGE,
 +      S_CONGESTED,
 +      S_DM_IO_READ,
 +      S_DM_IO_WRITE,
 +      S_BANDWIDTH,
 +      S_BARRIER,
 +      S_BIO_COPY_PL_NEXT,
 +      S_DEGRADED,
 +      S_DELAYED_BIOS,
 +      S_FLUSHS,
 +      S_HITS_1ST,
 +      S_IOS_POST,
 +      S_INSCACHE,
 +      S_MAX_LOOKUP,
 +      S_CHUNK_LOCKED,
 +      S_NO_BANDWIDTH,
 +      S_NOT_CONGESTED,
 +      S_NO_RW,
 +      S_NOSYNC,
 +      S_OVERWRITE,
 +      S_PROHIBITCHUNKIO,
 +      S_RECONSTRUCT_EI,
 +      S_RECONSTRUCT_DEV,
 +      S_RECONSTRUCT_SET,
 +      S_RECONSTRUCTED,
 +      S_REQUEUE,
 +      S_STRIPE_ERROR,
 +      S_SUM_DELAYED_BIOS,
 +      S_XORS,
 +      S_NR_STATS,     /* # of stats counters. Must be last! */
 +};
 +
 +/* Status type -> string mappings. */
 +struct stats_map {
 +      const enum stats_types type;
 +      const char *str;
 +};
 +
 +static struct stats_map stats_map[] = {
 +      { S_BIOS_READ, "r=" },
 +      { S_BIOS_ADDED_READ, "/" },
 +      { S_BIOS_ENDIO_READ, "/" },
 +      { S_BIOS_WRITE, " w=" },
 +      { S_BIOS_ADDED_WRITE, "/" },
 +      { S_BIOS_ENDIO_WRITE, "/" },
 +      { S_DM_IO_READ, " rc=" },
 +      { S_DM_IO_WRITE, " wc=" },
 +      { S_BANDWIDTH, "\nbw=" },
 +      { S_NO_BANDWIDTH, " no_bw=" },
 +      { S_BARRIER, "\nbarrier=" },
 +      { S_BIO_COPY_PL_NEXT, "\nbio_cp_next=" },
 +      { S_CAN_MERGE, "\nmerge=" },
 +      { S_CANT_MERGE, "/no_merge=" },
 +      { S_CHUNK_LOCKED, "\nchunk_locked=" },
 +      { S_CONGESTED, "\ncgst=" },
 +      { S_NOT_CONGESTED, "/not_cgst=" },
 +      { S_DEGRADED, "\ndegraded=" },
 +      { S_DELAYED_BIOS, "\ndel_bios=" },
 +      { S_SUM_DELAYED_BIOS, "/sum_del_bios=" },
 +      { S_FLUSHS, "\nflushs=" },
 +      { S_HITS_1ST, "\nhits_1st=" },
 +      { S_IOS_POST, " ios_post=" },
 +      { S_INSCACHE, " inscache=" },
 +      { S_MAX_LOOKUP, " maxlookup=" },
 +      { S_NO_RW, "\nno_rw=" },
 +      { S_NOSYNC, " nosync=" },
 +      { S_OVERWRITE, " ovr=" },
 +      { S_PROHIBITCHUNKIO, " prhbt_io=" },
 +      { S_RECONSTRUCT_EI, "\nrec_ei=" },
 +      { S_RECONSTRUCT_DEV, " rec_dev=" },
 +      { S_RECONSTRUCT_SET, " rec_set=" },
 +      { S_RECONSTRUCTED, " rec=" },
 +      { S_REQUEUE, " requeue=" },
 +      { S_STRIPE_ERROR, " stripe_err=" },
 +      { S_XORS, " xors=" },
 +};
 +
 +/*
 + * A RAID set.
 + */
 +#define       dm_rh_client    dm_region_hash
 +enum count_type { IO_WORK = 0, IO_RECOVER, IO_NR_COUNT };
 +typedef void (*xor_function_t)(unsigned count, unsigned long **data);
 +struct raid_set {
 +      struct dm_target *ti;   /* Target pointer. */
 +
 +      struct {
 +              unsigned long flags;    /* State flags. */
 +              struct mutex in_lock;   /* Protects central input list below. */
 +              struct mutex xor_lock;  /* Protects xor algorithm set. */
 +              struct bio_list in;     /* Pending ios (central input list). */
 +              struct bio_list work;   /* ios work set. */
 +              wait_queue_head_t suspendq;     /* suspend synchronization. */
 +              atomic_t in_process;    /* counter of queued bios (suspendq). */
 +              atomic_t in_process_max;/* counter of queued bios max. */
 +
 +              /* io work. */
 +              struct workqueue_struct *wq;
 +              struct delayed_work dws_do_raid;        /* For main worker. */
 +              struct work_struct ws_do_table_event;   /* For event worker. */
 +      } io;
 +
 +      /* Stripe locking abstraction. */
 +      struct dm_raid45_locking_type *locking;
 +
 +      struct stripe_cache sc; /* Stripe cache for this set. */
 +
 +      /* Xor optimization. */
 +      struct {
 +              struct xor_func *f;
 +              unsigned chunks;
 +              unsigned speed;
 +      } xor;
 +
 +      /* Recovery parameters. */
 +      struct recover {
 +              struct dm_dirty_log *dl;        /* Dirty log. */
 +              struct dm_rh_client *rh;        /* Region hash. */
 +
 +              struct dm_io_client *dm_io_client; /* recovery dm-io client. */
 +              /* dm-mem-cache client resource context for recovery stripes. */
 +              struct dm_mem_cache_client *mem_cache_client;
 +
 +              struct list_head stripes;       /* List of recovery stripes. */
 +
 +              region_t nr_regions;
 +              region_t nr_regions_to_recover;
 +              region_t nr_regions_recovered;
 +              unsigned long start_jiffies;
 +              unsigned long end_jiffies;
 +
 +              unsigned bandwidth;      /* Recovery bandwidth [%]. */
 +              unsigned bandwidth_work; /* Recovery bandwidth [factor]. */
 +              unsigned bandwidth_parm; /*  " constructor parm. */
 +              unsigned io_size;        /* recovery io size <= region size. */
 +              unsigned io_size_parm;   /* recovery io size ctr parameter. */
 +              unsigned recovery;       /* Recovery allowed/prohibited. */
 +              unsigned recovery_stripes; /* # of parallel recovery stripes. */
 +
 +              /* recovery io throttling. */
 +              atomic_t io_count[IO_NR_COUNT]; /* counter recover/regular io.*/
 +              unsigned long last_jiffies;
 +      } recover;
 +
 +      /* RAID set parameters. */
 +      struct {
 +              struct raid_type *raid_type;    /* RAID type (eg, RAID4). */
 +              unsigned raid_parms;    /* # variable raid parameters. */
 +
 +              unsigned chunk_size;    /* Sectors per chunk. */
 +              unsigned chunk_size_parm;
 +              unsigned chunk_shift;   /* rsector chunk size shift. */
 +
 +              unsigned io_size;       /* Sectors per io. */
 +              unsigned io_size_parm;
 +              unsigned io_mask;       /* Mask for bio_copy_page_list(). */
 +              unsigned io_inv_mask;   /* Mask for raid_address(). */
 +
 +              sector_t sectors_per_dev;       /* Sectors per device. */
 +
 +              atomic_t failed_devs;           /* Amount of devices failed. */
 +
 +              /* Index of device to initialize. */
 +              int dev_to_init;
 +              int dev_to_init_parm;
 +
 +              /* Raid devices dynamically allocated. */
 +              unsigned raid_devs;     /* # of RAID devices below. */
 +              unsigned data_devs;     /* # of RAID data devices. */
 +
 +              int ei;         /* index of failed RAID device. */
 +
 +              /* Index of dedicated parity device (i.e. RAID4). */
 +              int pi;
 +              int pi_parm;    /* constructor parm for status output. */
 +      } set;
 +
 +      /* REMOVEME: devel stats counters. */
 +      atomic_t stats[S_NR_STATS];
 +
 +      /* Dynamically allocated temporary pointers for xor(). */
 +      unsigned long **data;
 +
 +      /* Dynamically allocated RAID devices. Alignment? */
 +      struct raid_dev dev[0];
 +};
 +
 +/* Define RAID set bit operations. */
 +BITOPS(RS, Bandwidth, raid_set, RS_RECOVERY_BANDWIDTH)
 +BITOPS(RS, CheckOverwrite, raid_set, RS_CHECK_OVERWRITE)
 +BITOPS(RS, Dead, raid_set, RS_DEAD)
 +BITOPS(RS, DeadEndioMessage, raid_set, RS_DEAD_ENDIO_MESSAGE)
 +BITOPS(RS, Degraded, raid_set, RS_DEGRADED)
 +BITOPS(RS, DevelStats, raid_set, RS_DEVEL_STATS)
 +BITOPS(RS, EnforceParityCreation, raid_set, RS_ENFORCE_PARITY_CREATION)
 +BITOPS(RS, ProhibitWrites, raid_set, RS_PROHIBIT_WRITES)
 +BITOPS(RS, Recover, raid_set, RS_RECOVER)
 +BITOPS(RS, ScBusy, raid_set, RS_SC_BUSY)
 +BITOPS(RS, Suspend, raid_set, RS_SUSPEND)
 +#undef BITOPS
 +
 +/*-----------------------------------------------------------------
 + * Raid-4/5 set structures.
 + *---------------------------------------------------------------*/
 +/* RAID level definitions. */
 +enum raid_level {
 +      raid4,
 +      raid5,
 +};
 +
 +/* Symmetric/Asymmetric, Left/Right parity rotating algorithms. */
 +enum raid_algorithm {
 +      none,
 +      left_asym,
 +      right_asym,
 +      left_sym,
 +      right_sym,
 +};
 +
 +struct raid_type {
 +      const char *name;               /* RAID algorithm. */
 +      const char *descr;              /* Descriptor text for logging. */
 +      const unsigned parity_devs;     /* # of parity devices. */
 +      const unsigned minimal_devs;    /* minimal # of devices in set. */
 +      const enum raid_level level;            /* RAID level. */
 +      const enum raid_algorithm algorithm;    /* RAID algorithm. */
 +};
 +
 +/* Supported raid types and properties. */
 +static struct raid_type raid_types[] = {
 +      {"raid4",    "RAID4 (dedicated parity disk)", 1, 3, raid4, none},
 +      {"raid5_la", "RAID5 (left asymmetric)",       1, 3, raid5, left_asym},
 +      {"raid5_ra", "RAID5 (right asymmetric)",      1, 3, raid5, right_asym},
 +      {"raid5_ls", "RAID5 (left symmetric)",        1, 3, raid5, left_sym},
 +      {"raid5_rs", "RAID5 (right symmetric)",       1, 3, raid5, right_sym},
 +};
 +
 +/* Address as calculated by raid_address(). */
 +struct raid_address {
 +      sector_t key;           /* Hash key (address of stripe % chunk_size). */
 +      unsigned di, pi;        /* Data and parity disks index. */
 +};
 +
 +/* REMOVEME: reset statistics counters. */
 +static void stats_reset(struct raid_set *rs)
 +{
 +      unsigned s = S_NR_STATS;
 +
 +      while (s--)
 +              atomic_set(rs->stats + s, 0);
 +}
 +
 +/*----------------------------------------------------------------
 + * RAID set management routines.
 + *--------------------------------------------------------------*/
 +/*
 + * Begin small helper functions.
 + */
 +/* No need to be called from region hash indirectly at dm_rh_dec(). */
 +static void wake_dummy(void *context) {}
 +
 +/* Return # of io reference. */
 +static int io_ref(struct raid_set *rs)
 +{
 +      return atomic_read(&rs->io.in_process);
 +}
 +
 +/* Get an io reference. */
 +static void io_get(struct raid_set *rs)
 +{
 +      int p = atomic_inc_return(&rs->io.in_process);
 +
 +      if (p > atomic_read(&rs->io.in_process_max))
 +              atomic_set(&rs->io.in_process_max, p); /* REMOVEME: max. */
 +}
 +
 +/* Put the io reference and conditionally wake io waiters. */
 +static void io_put(struct raid_set *rs)
 +{
 +      /* Intel: rebuild data corrupter? */
 +      if (atomic_dec_and_test(&rs->io.in_process))
 +              wake_up(&rs->io.suspendq);
 +      else
 +              BUG_ON(io_ref(rs) < 0);
 +}
 +
 +/* Wait until all io has been processed. */
 +static void wait_ios(struct raid_set *rs)
 +{
 +      wait_event(rs->io.suspendq, !io_ref(rs));
 +}
 +
 +/* Queue (optionally delayed) io work. */
 +static void wake_do_raid_delayed(struct raid_set *rs, unsigned long delay)
 +{
 +      queue_delayed_work(rs->io.wq, &rs->io.dws_do_raid, delay);
 +}
 +
 +/* Queue io work immediately (called from region hash too). */
 +static void wake_do_raid(void *context)
 +{
 +      struct raid_set *rs = context;
 +
 +      queue_work(rs->io.wq, &rs->io.dws_do_raid.work);
 +}
 +
 +/* Calculate device sector offset. */
 +static sector_t _sector(struct raid_set *rs, struct bio *bio)
 +{
 +      sector_t sector = bio->bi_sector;
 +
 +      sector_div(sector, rs->set.data_devs);
 +      return sector;
 +}
 +
 +/* Return # of active stripes in stripe cache. */
 +static int sc_active(struct stripe_cache *sc)
 +{
 +      return atomic_read(&sc->active_stripes);
 +}
 +
 +/* Stripe cache busy indicator. */
 +static int sc_busy(struct raid_set *rs)
 +{
 +      return sc_active(&rs->sc) >
 +             atomic_read(&rs->sc.stripes) - (STRIPES_MIN / 2);
 +}
 +
 +/* Set chunks states. */
 +enum chunk_dirty_type { CLEAN, DIRTY, ERROR };
 +static void chunk_set(struct stripe_chunk *chunk, enum chunk_dirty_type type)
 +{
 +      switch (type) {
 +      case CLEAN:
 +              ClearChunkDirty(chunk);
 +              break;
 +      case DIRTY:
 +              SetChunkDirty(chunk);
 +              break;
 +      case ERROR:
 +              SetChunkError(chunk);
 +              SetStripeError(chunk->stripe);
 +              return;
 +      default:
 +              BUG();
 +      }
 +
 +      SetChunkUptodate(chunk);
 +      SetChunkIo(chunk);
 +      ClearChunkError(chunk);
 +}
 +
 +/* Return region state for a sector. */
 +static int region_state(struct raid_set *rs, sector_t sector,
 +                      enum dm_rh_region_states state)
 +{
 +      struct dm_rh_client *rh = rs->recover.rh;
 +      region_t region = dm_rh_sector_to_region(rh, sector);
 +
 +      return !!(dm_rh_get_state(rh, region, 1) & state);
 +}
 +
 +/*
 + * Return true in case a chunk should be read/written
 + *
 + * Conditions to read/write:
 + *    o chunk not uptodate
 + *    o chunk dirty
 + *
 + * Conditios to avoid io:
 + *    o io already ongoing on chunk
 + *    o io explitely prohibited
 + */
 +static int chunk_io(struct stripe_chunk *chunk)
 +{
 +      /* 2nd run optimization (flag set below on first run). */
 +      if (TestClearChunkMustIo(chunk))
 +              return 1;
 +
 +      /* Avoid io if prohibited or a locked chunk. */
 +      if (!ChunkIo(chunk) || ChunkLocked(chunk))
 +              return 0;
 +
 +      if (!ChunkUptodate(chunk) || ChunkDirty(chunk)) {
 +              SetChunkMustIo(chunk); /* 2nd run optimization. */
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +/* Call a function on each chunk needing io unless device failed. */
 +static unsigned for_each_io_dev(struct stripe *stripe,
 +                              void (*f_io)(struct stripe *stripe, unsigned p))
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned p, r = 0;
 +
 +      for (p = 0; p < rs->set.raid_devs; p++) {
 +              if (chunk_io(CHUNK(stripe, p)) && !DevFailed(rs->dev + p)) {
 +                      f_io(stripe, p);
 +                      r++;
 +              }
 +      }
 +
 +      return r;
 +}
 +
 +/*
 + * Index of device to calculate parity on.
 + *
 + * Either the parity device index *or* the selected
 + * device to init after a spare replacement.
 + */
 +static int dev_for_parity(struct stripe *stripe, int *sync)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      int r = region_state(rs, stripe->key, DM_RH_NOSYNC | DM_RH_RECOVERING);
 +
 +      *sync = !r;
 +
 +      /* Reconstruct a particular device ?. */
 +      if (r && rs->set.dev_to_init > -1)
 +              return rs->set.dev_to_init;
 +      else if (rs->set.raid_type->level == raid4)
 +              return rs->set.pi;
 +      else if (!StripeRecover(stripe))
 +              return stripe->idx.parity;
 +      else
 +              return -1;
 +}
 +
 +/* RAID set congested function. */
 +static int rs_congested(void *congested_data, int bdi_bits)
 +{
 +      int r;
 +      unsigned p;
 +      struct raid_set *rs = congested_data;
 +
 +      if (sc_busy(rs) || RSSuspend(rs) || RSProhibitWrites(rs))
 +              r = 1;
 +      else for (r = 0, p = rs->set.raid_devs; !r && p--; ) {
 +              /* If any of our component devices are overloaded. */
 +              struct request_queue *q = bdev_get_queue(rs->dev[p].dev->bdev);
 +
 +              r |= bdi_congested(&q->backing_dev_info, bdi_bits);
 +      }
 +
 +      /* REMOVEME: statistics. */
 +      atomic_inc(rs->stats + (r ? S_CONGESTED : S_NOT_CONGESTED));
 +      return r;
 +}
 +
 +/* RAID device degrade check. */
 +static void rs_check_degrade_dev(struct raid_set *rs,
 +                               struct stripe *stripe, unsigned p)
 +{
 +      if (TestSetDevFailed(rs->dev + p))
 +              return;
 +
 +      /* Through an event in case of member device errors. */
 +      if ((atomic_inc_return(&rs->set.failed_devs) >
 +           rs->set.raid_type->parity_devs) &&
 +           !TestSetRSDead(rs)) {
 +              /* Display RAID set dead message once. */
 +              unsigned p;
 +              char buf[BDEVNAME_SIZE];
 +
 +              DMERR("FATAL: too many devices failed -> RAID set broken");
 +              for (p = 0; p < rs->set.raid_devs; p++) {
 +                      if (DevFailed(rs->dev + p))
 +                              DMERR("device /dev/%s failed",
 +                                    bdevname(rs->dev[p].dev->bdev, buf));
 +              }
 +      }
 +
 +      /* Only log the first member error. */
 +      if (!TestSetRSDegraded(rs)) {
 +              char buf[BDEVNAME_SIZE];
 +
 +              /* Store index for recovery. */
 +              rs->set.ei = p;
 +              DMERR("CRITICAL: %sio error on device /dev/%s "
 +                    "in region=%llu; DEGRADING RAID set\n",
 +                    stripe ? "" : "FAKED ",
 +                    bdevname(rs->dev[p].dev->bdev, buf),
 +                    (unsigned long long) (stripe ? stripe->key : 0));
 +              DMERR("further device error messages suppressed");
 +      }
 +
 +      /* Prohibit further writes to allow for userpace to update metadata. */
 +      SetRSProhibitWrites(rs);
 +      schedule_work(&rs->io.ws_do_table_event);
 +}
 +
 +/* RAID set degrade check. */
 +static void rs_check_degrade(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned p = rs->set.raid_devs;
 +
 +      while (p--) {
 +              if (ChunkError(CHUNK(stripe, p)))
 +                      rs_check_degrade_dev(rs, stripe, p);
 +      }
 +}
 +
 +/* Lookup a RAID device by name or by major:minor number. */
 +static int raid_dev_lookup(struct raid_set *rs, struct raid_dev *dev_lookup)
 +{
 +      unsigned p;
 +      struct raid_dev *dev;
 +
 +      /*
 +       * Must be an incremental loop, because the device array
 +       * can have empty slots still on calls from raid_ctr()
 +       */
 +      for (dev = rs->dev, p = 0;
 +           dev->dev && p < rs->set.raid_devs;
 +           dev++, p++) {
 +              if (dev_lookup->dev->bdev->bd_dev == dev->dev->bdev->bd_dev)
 +                      return p;
 +      }
 +
 +      return -ENODEV;
 +}
 +/*
 + * End small helper functions.
 + */
 +
 +/*
 + * Stripe hash functions
 + */
 +/* Initialize/destroy stripe hash. */
 +static int hash_init(struct stripe_hash *hash, unsigned stripes)
 +{
 +      unsigned buckets = roundup_pow_of_two(stripes >> 1);
 +      static unsigned hash_primes[] = {
 +              /* Table of primes for hash_fn/table size optimization. */
 +              1, 2, 3, 7, 13, 27, 53, 97, 193, 389, 769,
 +              1543, 3079, 6151, 12289, 24593, 49157, 98317,
 +      };
 +
 +      /* Allocate stripe hash buckets. */
 +      hash->hash = vmalloc(buckets * sizeof(*hash->hash));
 +      if (!hash->hash)
 +              return -ENOMEM;
 +
 +      hash->buckets = buckets;
 +      hash->mask = buckets - 1;
 +      hash->shift = ffs(buckets);
 +      if (hash->shift > ARRAY_SIZE(hash_primes))
 +              hash->shift = ARRAY_SIZE(hash_primes) - 1;
 +
 +      BUG_ON(hash->shift < 2);
 +      hash->prime = hash_primes[hash->shift];
 +
 +      /* Initialize buckets. */
 +      while (buckets--)
 +              INIT_LIST_HEAD(hash->hash + buckets);
 +      return 0;
 +}
 +
 +static void hash_exit(struct stripe_hash *hash)
 +{
 +      if (hash->hash) {
 +              vfree(hash->hash);
 +              hash->hash = NULL;
 +      }
 +}
 +
 +static unsigned hash_fn(struct stripe_hash *hash, sector_t key)
 +{
 +      return (unsigned) (((key * hash->prime) >> hash->shift) & hash->mask);
 +}
 +
 +static struct list_head *hash_bucket(struct stripe_hash *hash, sector_t key)
 +{
 +      return hash->hash + hash_fn(hash, key);
 +}
 +
 +/* Insert an entry into a hash. */
 +static void stripe_insert(struct stripe_hash *hash, struct stripe *stripe)
 +{
 +      list_add(stripe->lists + LIST_HASH, hash_bucket(hash, stripe->key));
 +}
 +
 +/* Lookup an entry in the stripe hash. */
 +static struct stripe *stripe_lookup(struct stripe_cache *sc, sector_t key)
 +{
 +      unsigned look = 0;
 +      struct stripe *stripe;
 +      struct list_head *bucket = hash_bucket(&sc->hash, key);
 +
 +      list_for_each_entry(stripe, bucket, lists[LIST_HASH]) {
 +              look++;
 +
 +              if (stripe->key == key) {
 +                      /* REMOVEME: statisics. */
 +                      if (look > atomic_read(RS(sc)->stats + S_MAX_LOOKUP))
 +                              atomic_set(RS(sc)->stats + S_MAX_LOOKUP, look);
 +                      return stripe;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/* Resize the stripe cache hash on size changes. */
 +static int sc_hash_resize(struct stripe_cache *sc)
 +{
 +      /* Resize indicated ? */
 +      if (atomic_read(&sc->stripes) != atomic_read(&sc->stripes_last)) {
 +              int r;
 +              struct stripe_hash hash;
 +
 +              r = hash_init(&hash, atomic_read(&sc->stripes));
 +              if (r)
 +                      return r;
 +
 +              if (sc->hash.hash) {
 +                      unsigned b = sc->hash.buckets;
 +                      struct list_head *pos, *tmp;
 +
 +                      /* Walk old buckets and insert into new. */
 +                      while (b--) {
 +                              list_for_each_safe(pos, tmp, sc->hash.hash + b)
 +                                  stripe_insert(&hash,
 +                                                list_entry(pos, struct stripe,
 +                                                           lists[LIST_HASH]));
 +                      }
 +
 +              }
 +
 +              hash_exit(&sc->hash);
 +              memcpy(&sc->hash, &hash, sizeof(sc->hash));
 +              atomic_set(&sc->stripes_last, atomic_read(&sc->stripes));
 +      }
 +
 +      return 0;
 +}
 +/* End hash stripe hash function. */
 +
 +/* List add, delete, push and pop functions. */
 +/* Add stripe to flush list. */
 +#define       DEL_LIST(lh) \
 +      if (!list_empty(lh)) \
 +              list_del_init(lh);
 +
 +/* Delete stripe from hash. */
 +static void stripe_hash_del(struct stripe *stripe)
 +{
 +      DEL_LIST(stripe->lists + LIST_HASH);
 +}
 +
 +/* Return stripe reference count. */
 +static inline int stripe_ref(struct stripe *stripe)
 +{
 +      return atomic_read(&stripe->cnt);
 +}
 +
 +static void stripe_flush_add(struct stripe *stripe)
 +{
 +      struct stripe_cache *sc = stripe->sc;
 +      struct list_head *lh = stripe->lists + LIST_FLUSH;
 +
 +      if (!StripeReconstruct(stripe) && list_empty(lh))
 +              list_add_tail(lh, sc->lists + LIST_FLUSH);
 +}
 +
 +/*
 + * Add stripe to LRU (inactive) list.
 + *
 + * Need lock, because of concurrent access from message interface.
 + */
 +static void stripe_lru_add(struct stripe *stripe)
 +{
 +      if (!StripeRecover(stripe)) {
 +              struct list_head *lh = stripe->lists + LIST_LRU;
 +
 +              if (list_empty(lh))
 +                      list_add_tail(lh, stripe->sc->lists + LIST_LRU);
 +      }
 +}
 +
 +#define POP_LIST(list) \
 +      do { \
 +              if (list_empty(sc->lists + (list))) \
 +                      stripe = NULL; \
 +              else { \
 +                      stripe = list_first_entry(sc->lists + (list), \
 +                                                struct stripe, \
 +                                                lists[(list)]); \
 +                      list_del_init(stripe->lists + (list)); \
 +              } \
 +      } while (0);
 +
 +/* Pop an available stripe off the LRU list. */
 +static struct stripe *stripe_lru_pop(struct stripe_cache *sc)
 +{
 +      struct stripe *stripe;
 +
 +      POP_LIST(LIST_LRU);
 +      return stripe;
 +}
 +
 +/* Pop an available stripe off the io list. */
 +static struct stripe *stripe_io_pop(struct stripe_cache *sc)
 +{
 +      struct stripe *stripe;
 +
 +      POP_LIST(LIST_FLUSH);
 +      return stripe;
 +}
 +
 +/* Push a stripe safely onto the endio list to be handled by do_endios(). */
 +static void stripe_endio_push(struct stripe *stripe)
 +{
 +      unsigned long flags;
 +      struct stripe_cache *sc = stripe->sc;
 +      struct list_head *stripe_list = stripe->lists + LIST_ENDIO,
 +                       *sc_list = sc->lists + LIST_ENDIO;
 +      spinlock_t *lock = sc->locks + LOCK_ENDIO;
 +
 +      /* This runs in parallel with do_endios(). */
 +      spin_lock_irqsave(lock, flags);
 +      if (list_empty(stripe_list))
 +              list_add_tail(stripe_list, sc_list);
 +      spin_unlock_irqrestore(lock, flags);
 +
 +      wake_do_raid(RS(sc)); /* Wake myself. */
 +}
 +
 +/* Pop a stripe off safely off the endio list. */
 +static struct stripe *stripe_endio_pop(struct stripe_cache *sc)
 +{
 +      struct stripe *stripe;
 +      spinlock_t *lock = sc->locks + LOCK_ENDIO;
 +
 +      /* This runs in parallel with endio(). */
 +      spin_lock_irq(lock);
 +      POP_LIST(LIST_ENDIO)
 +      spin_unlock_irq(lock);
 +      return stripe;
 +}
 +#undef POP_LIST
 +
 +/*
 + * Stripe cache locking functions
 + */
 +/* Dummy lock function for single host RAID4+5. */
 +static void *no_lock(sector_t key, enum dm_lock_type type)
 +{
 +      return &no_lock;
 +}
 +
 +/* Dummy unlock function for single host RAID4+5. */
 +static void no_unlock(void *lock_handle)
 +{
 +}
 +
 +/* No locking (for single host RAID 4+5). */
 +static struct dm_raid45_locking_type locking_none = {
 +      .lock = no_lock,
 +      .unlock = no_unlock,
 +};
 +
 +/* Lock a stripe (for clustering). */
 +static int
 +stripe_lock(struct stripe *stripe, int rw, sector_t key)
 +{
 +      stripe->lock = RS(stripe->sc)->locking->lock(key, rw == READ ? DM_RAID45_SHARED : DM_RAID45_EX);
 +      return stripe->lock ? 0 : -EPERM;
 +}
 +
 +/* Unlock a stripe (for clustering). */
 +static void stripe_unlock(struct stripe *stripe)
 +{
 +      RS(stripe->sc)->locking->unlock(stripe->lock);
 +      stripe->lock = NULL;
 +}
 +
 +/* Test io pending on stripe. */
 +static int stripe_io_ref(struct stripe *stripe)
 +{
 +      return atomic_read(&stripe->io.pending);
 +}
 +
 +static void stripe_io_get(struct stripe *stripe)
 +{
 +      if (atomic_inc_return(&stripe->io.pending) == 1)
 +              /* REMOVEME: statistics */
 +              atomic_inc(&stripe->sc->active_stripes);
 +      else
 +              BUG_ON(stripe_io_ref(stripe) < 0);
 +}
 +
 +static void stripe_io_put(struct stripe *stripe)
 +{
 +      if (atomic_dec_and_test(&stripe->io.pending)) {
 +              if (unlikely(StripeRecover(stripe)))
 +                      /* Don't put recovery stripe on endio list. */
 +                      wake_do_raid(RS(stripe->sc));
 +              else
 +                      /* Add regular stripe to endio list and wake daemon. */
 +                      stripe_endio_push(stripe);
 +
 +              /* REMOVEME: statistics */
 +              atomic_dec(&stripe->sc->active_stripes);
 +      } else
 +              BUG_ON(stripe_io_ref(stripe) < 0);
 +}
 +
 +/* Take stripe reference out. */
 +static int stripe_get(struct stripe *stripe)
 +{
 +      int r;
 +      struct list_head *lh = stripe->lists + LIST_LRU;
 +
 +      /* Delete stripe from LRU (inactive) list if on. */
 +      DEL_LIST(lh);
 +      BUG_ON(stripe_ref(stripe) < 0);
 +
 +      /* Lock stripe on first reference */
 +      r = (atomic_inc_return(&stripe->cnt) == 1) ?
 +          stripe_lock(stripe, WRITE, stripe->key) : 0;
 +
 +      return r;
 +}
 +#undef DEL_LIST
 +
 +/* Return references on a chunk. */
 +static int chunk_ref(struct stripe_chunk *chunk)
 +{
 +      return atomic_read(&chunk->cnt);
 +}
 +
 +/* Take out reference on a chunk. */
 +static int chunk_get(struct stripe_chunk *chunk)
 +{
 +      return atomic_inc_return(&chunk->cnt);
 +}
 +
 +/* Drop reference on a chunk. */
 +static void chunk_put(struct stripe_chunk *chunk)
 +{
 +      BUG_ON(atomic_dec_return(&chunk->cnt) < 0);
 +}
 +
 +/*
 + * Drop reference on a stripe.
 + *
 + * Move it to list of LRU stripes if zero.
 + */
 +static void stripe_put(struct stripe *stripe)
 +{
 +      if (atomic_dec_and_test(&stripe->cnt)) {
 +              BUG_ON(stripe_io_ref(stripe));
 +              stripe_unlock(stripe);
 +      } else
 +              BUG_ON(stripe_ref(stripe) < 0);
 +}
 +
 +/* Helper needed by for_each_io_dev(). */
 +static void stripe_get_references(struct stripe *stripe, unsigned p)
 +{
 +
 +      /*
 +       * Another one to reference the stripe in
 +       * order to protect vs. LRU list moves.
 +       */
 +      io_get(RS(stripe->sc)); /* Global io references. */
 +      stripe_get(stripe);
 +      stripe_io_get(stripe);  /* One for each chunk io. */
 +}
 +
 +/* Helper for endio() to put all take references. */
 +static void stripe_put_references(struct stripe *stripe)
 +{
 +      stripe_io_put(stripe);  /* One for each chunk io. */
 +      stripe_put(stripe);
 +      io_put(RS(stripe->sc));
 +}
 +
 +/*
 + * Stripe cache functions.
 + */
 +/*
 + * Invalidate all chunks (i.e. their pages)  of a stripe.
 + *
 + * I only keep state for the whole chunk.
 + */
 +static inline void stripe_chunk_invalidate(struct stripe_chunk *chunk)
 +{
 +      chunk->io.flags = 0;
 +}
 +
 +static void
 +stripe_chunks_invalidate(struct stripe *stripe)
 +{
 +      unsigned p = RS(stripe->sc)->set.raid_devs;
 +
 +      while (p--)
 +              stripe_chunk_invalidate(CHUNK(stripe, p));
 +}
 +
 +/* Prepare stripe for (re)use. */
 +static void stripe_invalidate(struct stripe *stripe)
 +{
 +      stripe->io.flags = 0;
 +      stripe->idx.parity = stripe->idx.recover = -1;
 +      stripe_chunks_invalidate(stripe);
 +}
 +
 +/*
 + * Allow io on all chunks of a stripe.
 + * If not set, IO will not occur; i.e. it's prohibited.
 + *
 + * Actual IO submission for allowed chunks depends
 + * on their !uptodate or dirty state.
 + */
 +static void stripe_allow_io(struct stripe *stripe)
 +{
 +      unsigned p = RS(stripe->sc)->set.raid_devs;
 +
 +      while (p--)
 +              SetChunkIo(CHUNK(stripe, p));
 +}
 +
 +/* Initialize a stripe. */
 +static void stripe_init(struct stripe_cache *sc, struct stripe *stripe)
 +{
 +      unsigned i, p = RS(sc)->set.raid_devs;
 +
 +      /* Work all io chunks. */
 +      while (p--) {
 +              struct stripe_chunk *chunk = CHUNK(stripe, p);
 +
 +              atomic_set(&chunk->cnt, 0);
 +              chunk->stripe = stripe;
 +              i = ARRAY_SIZE(chunk->bl);
 +              while (i--)
 +                      bio_list_init(chunk->bl + i);
 +      }
 +
 +      stripe->sc = sc;
 +
 +      i = ARRAY_SIZE(stripe->lists);
 +      while (i--)
 +              INIT_LIST_HEAD(stripe->lists + i);
 +
 +      stripe->io.size = RS(sc)->set.io_size;
 +      atomic_set(&stripe->cnt, 0);
 +      atomic_set(&stripe->io.pending, 0);
 +      stripe_invalidate(stripe);
 +}
 +
 +/* Number of pages per chunk. */
 +static inline unsigned chunk_pages(unsigned sectors)
 +{
 +      return dm_div_up(sectors, SECTORS_PER_PAGE);
 +}
 +
 +/* Number of pages per stripe. */
 +static inline unsigned stripe_pages(struct raid_set *rs, unsigned io_size)
 +{
 +      return chunk_pages(io_size) * rs->set.raid_devs;
 +}
 +
 +/* Initialize part of page_list (recovery). */
 +static void stripe_zero_pl_part(struct stripe *stripe, int p,
 +                              unsigned start, unsigned count)
 +{
 +      unsigned o = start / SECTORS_PER_PAGE, pages = chunk_pages(count);
 +      /* Get offset into the page_list. */
 +      struct page_list *pl = pl_elem(PL(stripe, p), o);
 +
 +      BUG_ON(!pl);
 +      while (pl && pages--) {
 +              BUG_ON(!pl->page);
 +              memset(page_address(pl->page), 0, PAGE_SIZE);
 +              pl = pl->next;
 +      }
 +}
 +
 +/* Initialize parity chunk of stripe. */
 +static void stripe_zero_chunk(struct stripe *stripe, int p)
 +{
 +      if (p > -1)
 +              stripe_zero_pl_part(stripe, p, 0, stripe->io.size);
 +}
 +
 +/* Return dynamic stripe structure size. */
 +static size_t stripe_size(struct raid_set *rs)
 +{
 +      return sizeof(struct stripe) +
 +                    rs->set.raid_devs * sizeof(struct stripe_chunk);
 +}
 +
 +/* Allocate a stripe and its memory object. */
 +/* XXX adjust to cope with stripe cache and recovery stripe caches. */
 +enum grow { SC_GROW, SC_KEEP };
 +static struct stripe *stripe_alloc(struct stripe_cache *sc,
 +                                 struct dm_mem_cache_client *mc,
 +                                 enum grow grow)
 +{
 +      int r;
 +      struct stripe *stripe;
 +
 +      stripe = kmem_cache_zalloc(sc->kc.cache, GFP_KERNEL);
 +      if (stripe) {
 +              /* Grow the dm-mem-cache by one object. */
 +              if (grow == SC_GROW) {
 +                      r = dm_mem_cache_grow(mc, 1);
 +                      if (r)
 +                              goto err_free;
 +              }
 +
 +              stripe->obj = dm_mem_cache_alloc(mc);
 +              if (IS_ERR(stripe->obj))
 +                      goto err_shrink;
 +
 +              stripe_init(sc, stripe);
 +      }
 +
 +      return stripe;
 +
 +err_shrink:
 +      if (grow == SC_GROW)
 +              dm_mem_cache_shrink(mc, 1);
 +err_free:
 +      kmem_cache_free(sc->kc.cache, stripe);
 +      return NULL;
 +}
 +
 +/*
 + * Free a stripes memory object, shrink the
 + * memory cache and free the stripe itself.
 + */
 +static void stripe_free(struct stripe *stripe, struct dm_mem_cache_client *mc)
 +{
 +      dm_mem_cache_free(mc, stripe->obj);
 +      dm_mem_cache_shrink(mc, 1);
 +      kmem_cache_free(stripe->sc->kc.cache, stripe);
 +}
 +
 +/* Free the recovery stripe. */
 +static void stripe_recover_free(struct raid_set *rs)
 +{
 +      struct recover *rec = &rs->recover;
 +      struct dm_mem_cache_client *mc;
 +
 +      mc = rec->mem_cache_client;
 +      rec->mem_cache_client = NULL;
 +      if (mc) {
 +              struct stripe *stripe;
 +
 +              while (!list_empty(&rec->stripes)) {
 +                      stripe = list_first_entry(&rec->stripes, struct stripe,
 +                                                lists[LIST_RECOVER]);
 +                      list_del(stripe->lists + LIST_RECOVER);
 +                      kfree(stripe->recover);
 +                      stripe_free(stripe, mc);
 +              }
 +
 +              dm_mem_cache_client_destroy(mc);
 +              dm_io_client_destroy(rec->dm_io_client);
 +              rec->dm_io_client = NULL;
 +      }
 +}
 +
 +/* Grow stripe cache. */
 +static int sc_grow(struct stripe_cache *sc, unsigned stripes, enum grow grow)
 +{
 +      int r = 0;
 +
 +      /* Try to allocate this many (additional) stripes. */
 +      while (stripes--) {
 +              struct stripe *stripe =
 +                      stripe_alloc(sc, sc->mem_cache_client, grow);
 +
 +              if (likely(stripe)) {
 +                      stripe_lru_add(stripe);
 +                      atomic_inc(&sc->stripes);
 +              } else {
 +                      r = -ENOMEM;
 +                      break;
 +              }
 +      }
 +
 +      return r ? r : sc_hash_resize(sc);
 +}
 +
 +/* Shrink stripe cache. */
 +static int sc_shrink(struct stripe_cache *sc, unsigned stripes)
 +{
 +      int r = 0;
 +
 +      /* Try to get unused stripe from LRU list. */
 +      while (stripes--) {
 +              struct stripe *stripe;
 +
 +              stripe = stripe_lru_pop(sc);
 +              if (stripe) {
 +                      /* An LRU stripe may never have ios pending! */
 +                      BUG_ON(stripe_io_ref(stripe));
 +                      BUG_ON(stripe_ref(stripe));
 +                      atomic_dec(&sc->stripes);
 +                      /* Remove from hash if on before deletion. */
 +                      stripe_hash_del(stripe);
 +                      stripe_free(stripe, sc->mem_cache_client);
 +              } else {
 +                      r = -ENOENT;
 +                      break;
 +              }
 +      }
 +
 +      /* Check if stats are still sane. */
 +      if (atomic_read(&sc->active_stripes_max) >
 +          atomic_read(&sc->stripes))
 +              atomic_set(&sc->active_stripes_max, 0);
 +
 +      if (r)
 +              return r;
 +
 +      return atomic_read(&sc->stripes) ? sc_hash_resize(sc) : 0;
 +}
 +
 +/* Create stripe cache and recovery. */
 +static int sc_init(struct raid_set *rs, unsigned stripes)
 +{
 +      unsigned i, r, rstripes;
 +      struct stripe_cache *sc = &rs->sc;
 +      struct stripe *stripe;
 +      struct recover *rec = &rs->recover;
 +      struct mapped_device *md;
 +      struct gendisk *disk;
 +
 +
 +      /* Initialize lists and locks. */
 +      i = ARRAY_SIZE(sc->lists);
 +      while (i--)
 +              INIT_LIST_HEAD(sc->lists + i);
 +
 +      INIT_LIST_HEAD(&rec->stripes);
 +
 +      /* Initialize endio and LRU list locks. */
 +      i = NR_LOCKS;
 +      while (i--)
 +              spin_lock_init(sc->locks + i);
 +
 +      /* Initialize atomic variables. */
 +      atomic_set(&sc->stripes, 0);
 +      atomic_set(&sc->stripes_to_set, 0);
 +      atomic_set(&sc->active_stripes, 0);
 +      atomic_set(&sc->active_stripes_max, 0); /* REMOVEME: statistics. */
 +
 +      /*
 +       * We need a runtime unique # to suffix the kmem cache name
 +       * because we'll have one for each active RAID set.
 +       */
 +      md = dm_table_get_md(rs->ti->table);
 +      disk = dm_disk(md);
 +      snprintf(sc->kc.name, sizeof(sc->kc.name), "%s-%d.%d", TARGET,
 +               disk->first_minor, atomic_inc_return(&_stripe_sc_nr));
 +      sc->kc.cache = kmem_cache_create(sc->kc.name, stripe_size(rs),
 +                                       0, 0, NULL);
 +      if (!sc->kc.cache)
 +              return -ENOMEM;
 +
 +      /* Create memory cache client context for RAID stripe cache. */
 +      sc->mem_cache_client =
 +              dm_mem_cache_client_create(stripes, rs->set.raid_devs,
 +                                         chunk_pages(rs->set.io_size));
 +      if (IS_ERR(sc->mem_cache_client))
 +              return PTR_ERR(sc->mem_cache_client);
 +
 +      /* Create memory cache client context for RAID recovery stripe(s). */
 +      rstripes = rec->recovery_stripes;
 +      rec->mem_cache_client =
 +              dm_mem_cache_client_create(rstripes, rs->set.raid_devs,
 +                                         chunk_pages(rec->io_size));
 +      if (IS_ERR(rec->mem_cache_client))
 +              return PTR_ERR(rec->mem_cache_client);
 +
 +      /* Create dm-io client context for IO stripes. */
-       sc->dm_io_client =
-               dm_io_client_create((stripes > 32 ? 32 : stripes) *
-                                   rs->set.raid_devs *
-                                   chunk_pages(rs->set.io_size));
++      sc->dm_io_client = dm_io_client_create();
 +      if (IS_ERR(sc->dm_io_client))
 +              return PTR_ERR(sc->dm_io_client);
 +
 +      /* FIXME: intermingeled with stripe cache initialization. */
 +      /* Create dm-io client context for recovery stripes. */
-       rec->dm_io_client =
-               dm_io_client_create(rstripes * rs->set.raid_devs *
-                                   chunk_pages(rec->io_size));
++      rec->dm_io_client = dm_io_client_create();
 +      if (IS_ERR(rec->dm_io_client))
 +              return PTR_ERR(rec->dm_io_client);
 +
 +      /* Allocate stripes for set recovery. */
 +      while (rstripes--) {
 +              stripe = stripe_alloc(sc, rec->mem_cache_client, SC_KEEP);
 +              if (!stripe)
 +                      return -ENOMEM;
 +
 +              stripe->recover = kzalloc(sizeof(*stripe->recover), GFP_KERNEL);
 +              if (!stripe->recover) {
 +                      stripe_free(stripe, rec->mem_cache_client);
 +                      return -ENOMEM;
 +              }
 +
 +              SetStripeRecover(stripe);
 +              stripe->io.size = rec->io_size;
 +              list_add_tail(stripe->lists + LIST_RECOVER, &rec->stripes);
 +              /* Don't add recovery stripes to LRU list! */
 +      }
 +
 +      /*
 +       * Allocate the stripe objetcs from the
 +       * cache and add them to the LRU list.
 +       */
 +      r = sc_grow(sc, stripes, SC_KEEP);
 +      if (!r)
 +              atomic_set(&sc->stripes_last, stripes);
 +
 +      return r;
 +}
 +
 +/* Destroy the stripe cache. */
 +static void sc_exit(struct stripe_cache *sc)
 +{
 +      struct raid_set *rs = RS(sc);
 +
 +      if (sc->kc.cache) {
 +              stripe_recover_free(rs);
 +              BUG_ON(sc_shrink(sc, atomic_read(&sc->stripes)));
 +              kmem_cache_destroy(sc->kc.cache);
 +              sc->kc.cache = NULL;
 +
 +              if (sc->mem_cache_client && !IS_ERR(sc->mem_cache_client))
 +                      dm_mem_cache_client_destroy(sc->mem_cache_client);
 +
 +              if (sc->dm_io_client && !IS_ERR(sc->dm_io_client))
 +                      dm_io_client_destroy(sc->dm_io_client);
 +
 +              hash_exit(&sc->hash);
 +      }
 +}
 +
 +/*
 + * Calculate RAID address
 + *
 + * Delivers tuple with the index of the data disk holding the chunk
 + * in the set, the parity disks index and the start of the stripe
 + * within the address space of the set (used as the stripe cache hash key).
 + */
 +/* thx MD. */
 +static struct raid_address *raid_address(struct raid_set *rs, sector_t sector,
 +                                       struct raid_address *addr)
 +{
 +      sector_t stripe, tmp;
 +
 +      /*
 +       * chunk_number = sector / chunk_size
 +       * stripe_number = chunk_number / data_devs
 +       * di = stripe % data_devs;
 +       */
 +      stripe = sector >> rs->set.chunk_shift;
 +      addr->di = sector_div(stripe, rs->set.data_devs);
 +
 +      switch (rs->set.raid_type->level) {
 +      case raid4:
 +              addr->pi = rs->set.pi;
 +              goto check_shift_di;
 +      case raid5:
 +              tmp = stripe;
 +              addr->pi = sector_div(tmp, rs->set.raid_devs);
 +
 +              switch (rs->set.raid_type->algorithm) {
 +              case left_asym:         /* Left asymmetric. */
 +                      addr->pi = rs->set.data_devs - addr->pi;
 +              case right_asym:        /* Right asymmetric. */
 +check_shift_di:
 +                      if (addr->di >= addr->pi)
 +                              addr->di++;
 +                      break;
 +              case left_sym:          /* Left symmetric. */
 +                      addr->pi = rs->set.data_devs - addr->pi;
 +              case right_sym:         /* Right symmetric. */
 +                      addr->di = (addr->pi + addr->di + 1) %
 +                                 rs->set.raid_devs;
 +                      break;
 +              case none: /* Ain't happen: RAID4 algorithm placeholder. */
 +                      BUG();
 +              }
 +      }
 +
 +      /*
 +       * Start offset of the stripes chunk on any single device of the RAID
 +       * set, adjusted in case io size differs from chunk size.
 +       */
 +      addr->key = (stripe << rs->set.chunk_shift) +
 +                  (sector & rs->set.io_inv_mask);
 +      return addr;
 +}
 +
 +/*
 + * Copy data across between stripe pages and bio vectors.
 + *
 + * Pay attention to data alignment in stripe and bio pages.
 + */
 +static void bio_copy_page_list(int rw, struct stripe *stripe,
 +                             struct page_list *pl, struct bio *bio)
 +{
 +      unsigned i, page_offset;
 +      void *page_addr;
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct bio_vec *bv;
 +
 +      /* Get start page in page list for this sector. */
 +      i = (bio->bi_sector & rs->set.io_mask) / SECTORS_PER_PAGE;
 +      pl = pl_elem(pl, i);
 +      BUG_ON(!pl);
 +      BUG_ON(!pl->page);
 +
 +      page_addr = page_address(pl->page);
 +      page_offset = to_bytes(bio->bi_sector & (SECTORS_PER_PAGE - 1));
 +
 +      /* Walk all segments and copy data across between bio_vecs and pages. */
 +      bio_for_each_segment(bv, bio, i) {
 +              int len = bv->bv_len, size;
 +              unsigned bio_offset = 0;
 +              void *bio_addr = __bio_kmap_atomic(bio, i, KM_USER0);
 +redo:
 +              size = (page_offset + len > PAGE_SIZE) ?
 +                     PAGE_SIZE - page_offset : len;
 +
 +              if (rw == READ)
 +                      memcpy(bio_addr + bio_offset,
 +                             page_addr + page_offset, size);
 +              else
 +                      memcpy(page_addr + page_offset,
 +                             bio_addr + bio_offset, size);
 +
 +              page_offset += size;
 +              if (page_offset == PAGE_SIZE) {
 +                      /*
 +                       * We reached the end of the chunk page ->
 +                       * need to refer to the next one to copy more data.
 +                       */
 +                      len -= size;
 +                      if (len) {
 +                              /* Get next page. */
 +                              pl = pl->next;
 +                              BUG_ON(!pl);
 +                              BUG_ON(!pl->page);
 +                              page_addr = page_address(pl->page);
 +                              page_offset = 0;
 +                              bio_offset += size;
 +                              /* REMOVEME: statistics. */
 +                              atomic_inc(rs->stats + S_BIO_COPY_PL_NEXT);
 +                              goto redo;
 +                      }
 +              }
 +
 +              __bio_kunmap_atomic(bio_addr, KM_USER0);
 +      }
 +}
 +
 +/*
 + * Xor optimization macros.
 + */
 +/* Xor data pointer declaration and initialization macros. */
 +#define DECLARE_2     unsigned long *d0 = data[0], *d1 = data[1]
 +#define DECLARE_3     DECLARE_2, *d2 = data[2]
 +#define DECLARE_4     DECLARE_3, *d3 = data[3]
 +#define DECLARE_5     DECLARE_4, *d4 = data[4]
 +#define DECLARE_6     DECLARE_5, *d5 = data[5]
 +#define DECLARE_7     DECLARE_6, *d6 = data[6]
 +#define DECLARE_8     DECLARE_7, *d7 = data[7]
 +
 +/* Xor unrole macros. */
 +#define D2(n) d0[n] = d0[n] ^ d1[n]
 +#define D3(n) D2(n) ^ d2[n]
 +#define D4(n) D3(n) ^ d3[n]
 +#define D5(n) D4(n) ^ d4[n]
 +#define D6(n) D5(n) ^ d5[n]
 +#define D7(n) D6(n) ^ d6[n]
 +#define D8(n) D7(n) ^ d7[n]
 +
 +#define       X_2(macro, offset)      macro(offset); macro(offset + 1);
 +#define       X_4(macro, offset)      X_2(macro, offset); X_2(macro, offset + 2);
 +#define       X_8(macro, offset)      X_4(macro, offset); X_4(macro, offset + 4);
 +#define       X_16(macro, offset)     X_8(macro, offset); X_8(macro, offset + 8);
 +#define       X_32(macro, offset)     X_16(macro, offset); X_16(macro, offset + 16);
 +#define       X_64(macro, offset)     X_32(macro, offset); X_32(macro, offset + 32);
 +
 +/* Define a _xor_#chunks_#xors_per_run() function. */
 +#define       _XOR(chunks, xors_per_run) \
 +static void _xor ## chunks ## _ ## xors_per_run(unsigned long **data) \
 +{ \
 +      unsigned end = XOR_SIZE / sizeof(data[0]), i; \
 +      DECLARE_ ## chunks; \
 +\
 +      for (i = 0; i < end; i += xors_per_run) { \
 +              X_ ## xors_per_run(D ## chunks, i); \
 +      } \
 +}
 +
 +/* Define xor functions for 2 - 8 chunks and xors per run. */
 +#define       MAKE_XOR_PER_RUN(xors_per_run) \
 +      _XOR(2, xors_per_run); _XOR(3, xors_per_run); \
 +      _XOR(4, xors_per_run); _XOR(5, xors_per_run); \
 +      _XOR(6, xors_per_run); _XOR(7, xors_per_run); \
 +      _XOR(8, xors_per_run);
 +
 +MAKE_XOR_PER_RUN(8)   /* Define _xor_*_8() functions. */
 +MAKE_XOR_PER_RUN(16)  /* Define _xor_*_16() functions. */
 +MAKE_XOR_PER_RUN(32)  /* Define _xor_*_32() functions. */
 +MAKE_XOR_PER_RUN(64)  /* Define _xor_*_64() functions. */
 +
 +#define MAKE_XOR(xors_per_run) \
 +struct { \
 +      void (*f)(unsigned long **); \
 +} static xor_funcs ## xors_per_run[] = { \
 +      { NULL }, /* NULL pointers to optimize indexing in xor(). */ \
 +      { NULL }, \
 +      { _xor2_ ## xors_per_run }, \
 +      { _xor3_ ## xors_per_run }, \
 +      { _xor4_ ## xors_per_run }, \
 +      { _xor5_ ## xors_per_run }, \
 +      { _xor6_ ## xors_per_run }, \
 +      { _xor7_ ## xors_per_run }, \
 +      { _xor8_ ## xors_per_run }, \
 +}; \
 +\
 +static void xor_ ## xors_per_run(unsigned n, unsigned long **data) \
 +{ \
 +      /* Call respective function for amount of chunks. */ \
 +      xor_funcs ## xors_per_run[n].f(data); \
 +}
 +
 +/* Define xor_8() - xor_64 functions. */
 +MAKE_XOR(8)
 +MAKE_XOR(16)
 +MAKE_XOR(32)
 +MAKE_XOR(64)
 +/*
 + * END xor optimization macros.
 + */
 +
 +/* Maximum number of chunks, which can be xor'ed in one go. */
 +#define       XOR_CHUNKS_MAX  (ARRAY_SIZE(xor_funcs8) - 1)
 +
 +/* xor_blocks wrapper to allow for using that crypto library function. */
 +static void xor_blocks_wrapper(unsigned n, unsigned long **data)
 +{
 +      BUG_ON(n < 2 || n > MAX_XOR_BLOCKS + 1);
 +      xor_blocks(n - 1, XOR_SIZE, (void *) data[0], (void **) data + 1);
 +}
 +
 +struct xor_func {
 +      xor_function_t f;
 +      const char *name;
 +} static xor_funcs[] = {
 +      { xor_64,  "xor_64" },
 +      { xor_32,  "xor_32" },
 +      { xor_16,  "xor_16" },
 +      { xor_8,   "xor_8"  },
 +      { xor_blocks_wrapper, "xor_blocks" },
 +};
 +
 +/*
 + * Check, if chunk has to be xored in/out:
 + *
 + * o if writes are queued
 + * o if writes are merged
 + * o if stripe is to be reconstructed
 + * o if recovery stripe
 + */
 +static inline int chunk_must_xor(struct stripe_chunk *chunk)
 +{
 +      if (ChunkUptodate(chunk)) {
 +              BUG_ON(!bio_list_empty(BL_CHUNK(chunk, WRITE_QUEUED)) &&
 +                     !bio_list_empty(BL_CHUNK(chunk, WRITE_MERGED)));
 +
 +              if (!bio_list_empty(BL_CHUNK(chunk, WRITE_QUEUED)) ||
 +                  !bio_list_empty(BL_CHUNK(chunk, WRITE_MERGED)))
 +                      return 1;
 +
 +              if (StripeReconstruct(chunk->stripe) ||
 +                  StripeRecover(chunk->stripe))
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Calculate crc.
 + *
 + * This indexes into the chunks of a stripe and their pages.
 + *
 + * All chunks will be xored into the indexed (@pi)
 + * chunk in maximum groups of xor.chunks.
 + *
 + */
 +static void xor(struct stripe *stripe, unsigned pi, unsigned sector)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned max_chunks = rs->xor.chunks, n = 1,
 +               o = sector / SECTORS_PER_PAGE, /* Offset into the page_list. */
 +               p = rs->set.raid_devs;
 +      unsigned long **d = rs->data;
 +      xor_function_t xor_f = rs->xor.f->f;
 +
 +      BUG_ON(sector > stripe->io.size);
 +
 +      /* Address of parity page to xor into. */
 +      d[0] = page_address(pl_elem(PL(stripe, pi), o)->page);
 +
 +      while (p--) {
 +              /* Preset pointers to data pages. */
 +              if (p != pi && chunk_must_xor(CHUNK(stripe, p)))
 +                      d[n++] = page_address(pl_elem(PL(stripe, p), o)->page);
 +
 +              /* If max chunks -> xor. */
 +              if (n == max_chunks) {
 +                      mutex_lock(&rs->io.xor_lock);
 +                      xor_f(n, d);
 +                      mutex_unlock(&rs->io.xor_lock);
 +                      n = 1;
 +              }
 +      }
 +
 +      /* If chunks -> xor. */
 +      if (n > 1) {
 +              mutex_lock(&rs->io.xor_lock);
 +              xor_f(n, d);
 +              mutex_unlock(&rs->io.xor_lock);
 +      }
 +}
 +
 +/* Common xor loop through all stripe page lists. */
 +static void common_xor(struct stripe *stripe, sector_t count,
 +                     unsigned off, unsigned pi)
 +{
 +      unsigned sector;
 +
 +      BUG_ON(!count);
 +      for (sector = off; sector < count; sector += SECTORS_PER_PAGE)
 +              xor(stripe, pi, sector);
 +
 +      /* Set parity page uptodate and clean. */
 +      chunk_set(CHUNK(stripe, pi), CLEAN);
 +      atomic_inc(RS(stripe->sc)->stats + S_XORS); /* REMOVEME: statistics. */
 +}
 +
 +/*
 + * Calculate parity sectors on intact stripes.
 + *
 + * Need to calculate raid address for recover stripe, because its
 + * chunk sizes differs and is typically larger than io chunk size.
 + */
 +static void parity_xor(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      int size_differs = stripe->io.size != rs->set.io_size;
 +      unsigned chunk_size = rs->set.chunk_size, io_size = stripe->io.size,
 +               xor_size = chunk_size > io_size ? io_size : chunk_size;
 +      sector_t off;
 +
 +      /* This can be the recover stripe with a larger io size. */
 +      for (off = 0; off < io_size; off += xor_size) {
 +              /*
 +               * Recover stripe is likely bigger than regular io
 +               * ones and has no precalculated parity disk index ->
 +               * need to calculate RAID address.
 +               */
 +              if (unlikely(size_differs)) {
 +                      struct raid_address addr;
 +
 +                      raid_address(rs, (stripe->key + off) *
 +                                       rs->set.data_devs, &addr);
 +                      stripe->idx.parity = addr.pi;
 +                      stripe_zero_pl_part(stripe, addr.pi, off, xor_size);
 +              }
 +
 +              common_xor(stripe, xor_size, off, stripe->idx.parity);
 +              chunk_set(CHUNK(stripe, stripe->idx.parity), DIRTY);
 +      }
 +}
 +
 +/* Reconstruct missing chunk. */
 +static void stripe_reconstruct(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      int p = rs->set.raid_devs, pr = stripe->idx.recover;
 +
 +      BUG_ON(pr < 0);
 +
 +      /* Check if all but the chunk to be reconstructed are uptodate. */
 +      while (p--)
 +              BUG_ON(p != pr && !ChunkUptodate(CHUNK(stripe, p)));
 +
 +      /* REMOVEME: statistics. */
 +      atomic_inc(rs->stats + (RSDegraded(rs) ? S_RECONSTRUCT_EI :
 +                                               S_RECONSTRUCT_DEV));
 +      /* Zero chunk to be reconstructed. */
 +      stripe_zero_chunk(stripe, pr);
 +      common_xor(stripe, stripe->io.size, 0, pr);
 +}
 +
 +/*
 + * Recovery io throttling
 + */
 +/* Conditionally reset io counters. */
 +static int recover_io_reset(struct raid_set *rs)
 +{
 +      unsigned long j = jiffies;
 +
 +      /* Pay attention to jiffies overflows. */
 +      if (j > rs->recover.last_jiffies + HZ ||
 +          j < rs->recover.last_jiffies) {
 +              atomic_set(rs->recover.io_count + IO_WORK, 0);
 +              atomic_set(rs->recover.io_count + IO_RECOVER, 0);
 +              rs->recover.last_jiffies = j;
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +/* Count ios. */
 +static void recover_io_count(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +
 +      atomic_inc(rs->recover.io_count +
 +                 (StripeRecover(stripe) ? IO_RECOVER : IO_WORK));
 +}
 +
 +/* Try getting a stripe either from the hash or from the LRU list. */
 +static struct stripe *stripe_find(struct raid_set *rs,
 +                                struct raid_address *addr)
 +{
 +      int r;
 +      struct stripe_cache *sc = &rs->sc;
 +      struct stripe *stripe;
 +
 +      /* Try stripe from hash. */
 +      stripe = stripe_lookup(sc, addr->key);
 +      if (stripe) {
 +              r = stripe_get(stripe);
 +              if (r)
 +                      goto get_lock_failed;
 +
 +              atomic_inc(rs->stats + S_HITS_1ST); /* REMOVEME: statistics. */
 +      } else {
 +              /* Not in hash -> try to get an LRU stripe. */
 +              stripe = stripe_lru_pop(sc);
 +              if (stripe) {
 +                      /*
 +                       * An LRU stripe may not be referenced
 +                       * and may never have ios pending!
 +                       */
 +                      BUG_ON(stripe_ref(stripe));
 +                      BUG_ON(stripe_io_ref(stripe));
 +
 +                      /* Remove from hash if on before reuse. */
 +                      stripe_hash_del(stripe);
 +
 +                      /* Invalidate before reinserting with changed key. */
 +                      stripe_invalidate(stripe);
 +
 +                      stripe->key = addr->key;
 +                      stripe->region = dm_rh_sector_to_region(rs->recover.rh,
 +                                                              addr->key);
 +                      stripe->idx.parity = addr->pi;
 +                      r = stripe_get(stripe);
 +                      if (r)
 +                              goto get_lock_failed;
 +
 +                      /* Insert stripe into the stripe hash. */
 +                      stripe_insert(&sc->hash, stripe);
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_INSCACHE);
 +              }
 +      }
 +
 +      return stripe;
 +
 +get_lock_failed:
 +      stripe_put(stripe);
 +      return NULL;
 +}
 +
 +/*
 + * Process end io
 + *
 + * I need to do it here because I can't in interrupt
 + */
 +/* End io all bios on a bio list. */
 +static void bio_list_endio(struct stripe *stripe, struct bio_list *bl,
 +                         int p, int error)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct bio *bio;
 +      struct page_list *pl = PL(stripe, p);
 +      struct stripe_chunk *chunk = CHUNK(stripe, p);
 +
 +      /* Update region counters. */
 +      while ((bio = bio_list_pop(bl))) {
 +              if (bio_data_dir(bio) == WRITE)
 +                      /* Drop io pending count for any writes. */
 +                      dm_rh_dec(rs->recover.rh, stripe->region);
 +              else if (!error)
 +                      /* Copy data accross. */
 +                      bio_copy_page_list(READ, stripe, pl, bio);
 +
 +              bio_endio(bio, error);
 +
 +              /* REMOVEME: statistics. */
 +              atomic_inc(rs->stats + (bio_data_dir(bio) == READ ?
 +                         S_BIOS_ENDIO_READ : S_BIOS_ENDIO_WRITE));
 +
 +              chunk_put(chunk);
 +              stripe_put(stripe);
 +              io_put(rs);     /* Wake any suspend waiters on last bio. */
 +      }
 +}
 +
 +/*
 + * End io all reads/writes on a stripe copying
 + * read data accross from stripe to bios and
 + * decrementing region counters for writes.
 + *
 + * Processing of ios depeding on state:
 + * o no chunk error -> endio ok
 + * o degraded:
 + *   - chunk error and read -> ignore to be requeued
 + *   - chunk error and write -> endio ok
 + * o dead (more than parity_devs failed) and chunk_error-> endio failed
 + */
 +static void stripe_endio(int rw, struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned p = rs->set.raid_devs;
 +      int write = (rw != READ);
 +
 +      while (p--) {
 +              struct stripe_chunk *chunk = CHUNK(stripe, p);
 +              struct bio_list *bl;
 +
 +              BUG_ON(ChunkLocked(chunk));
 +
 +              bl = BL_CHUNK(chunk, rw);
 +              if (bio_list_empty(bl))
 +                      continue;
 +
 +              if (unlikely(ChunkError(chunk) || !ChunkUptodate(chunk))) {
 +                      /* RAID set dead. */
 +                      if (unlikely(RSDead(rs)))
 +                              bio_list_endio(stripe, bl, p, -EIO);
 +                      /* RAID set degraded. */
 +                      else if (write)
 +                              bio_list_endio(stripe, bl, p, 0);
 +              } else {
 +                      BUG_ON(!RSDegraded(rs) && ChunkDirty(chunk));
 +                      bio_list_endio(stripe, bl, p, 0);
 +              }
 +      }
 +}
 +
 +/* Fail all ios hanging off all bio lists of a stripe. */
 +static void stripe_fail_io(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned p = rs->set.raid_devs;
 +
 +      while (p--) {
 +              struct stripe_chunk *chunk = CHUNK(stripe, p);
 +              int i = ARRAY_SIZE(chunk->bl);
 +
 +              /* Fail all bios on all bio lists of the stripe. */
 +              while (i--) {
 +                      struct bio_list *bl = chunk->bl + i;
 +
 +                      if (!bio_list_empty(bl))
 +                              bio_list_endio(stripe, bl, p, -EIO);
 +              }
 +      }
 +
 +      /* Put stripe on LRU list. */
 +      BUG_ON(stripe_io_ref(stripe));
 +      BUG_ON(stripe_ref(stripe));
 +}
 +
 +/* Unlock all required chunks. */
 +static void stripe_chunks_unlock(struct stripe *stripe)
 +{
 +      unsigned p = RS(stripe->sc)->set.raid_devs;
 +      struct stripe_chunk *chunk;
 +
 +      while (p--) {
 +              chunk = CHUNK(stripe, p);
 +
 +              if (TestClearChunkUnlock(chunk))
 +                      ClearChunkLocked(chunk);
 +      }
 +}
 +
 +/*
 + * Queue reads and writes to a stripe by hanging
 + * their bios off the stripesets read/write lists.
 + */
 +static int stripe_queue_bio(struct raid_set *rs, struct bio *bio,
 +                          struct bio_list *reject)
 +{
 +      struct raid_address addr;
 +      struct stripe *stripe;
 +
 +      stripe = stripe_find(rs, raid_address(rs, bio->bi_sector, &addr));
 +      if (stripe) {
 +              int r = 0, rw = bio_data_dir(bio);
 +
 +              /* Distinguish reads and writes. */
 +              bio_list_add(BL(stripe, addr.di, rw), bio);
 +
 +              if (rw == READ)
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_BIOS_ADDED_READ);
 +              else {
 +                      /* Inrement pending write count on region. */
 +                      dm_rh_inc(rs->recover.rh, stripe->region);
 +                      r = 1;
 +
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_BIOS_ADDED_WRITE);
 +              }
 +
 +              /*
 +               * Put on io (flush) list in case of
 +               * initial bio queued to chunk.
 +               */
 +              if (chunk_get(CHUNK(stripe, addr.di)) == 1)
 +                      stripe_flush_add(stripe);
 +
 +              return r;
 +      }
 +
 +      /* Got no stripe from cache or failed to lock it -> reject bio. */
 +      bio_list_add(reject, bio);
 +      atomic_inc(rs->stats + S_IOS_POST); /* REMOVEME: statistics. */
 +      return 0;
 +}
 +
 +/*
 + * Handle all stripes by handing them to the daemon, because we can't
 + * map their chunk pages to copy the data in interrupt context.
 + *
 + * We don't want to handle them here either, while interrupts are disabled.
 + */
 +
 +/* Read/write endio function for dm-io (interrupt context). */
 +static void endio(unsigned long error, void *context)
 +{
 +      struct stripe_chunk *chunk = context;
 +
 +      if (unlikely(error)) {
 +              chunk_set(chunk, ERROR);
 +              /* REMOVEME: statistics. */
 +              atomic_inc(RS(chunk->stripe->sc)->stats + S_STRIPE_ERROR);
 +      } else
 +              chunk_set(chunk, CLEAN);
 +
 +      /*
 +       * For recovery stripes, I need to reset locked locked
 +       * here, because those aren't processed in do_endios().
 +       */
 +      if (unlikely(StripeRecover(chunk->stripe)))
 +              ClearChunkLocked(chunk);
 +      else
 +              SetChunkUnlock(chunk);
 +
 +      /* Indirectly puts stripe on cache's endio list via stripe_io_put(). */
 +      stripe_put_references(chunk->stripe);
 +}
 +
 +/* Read/Write a chunk asynchronously. */
 +static void stripe_chunk_rw(struct stripe *stripe, unsigned p)
 +{
 +      struct stripe_cache *sc = stripe->sc;
 +      struct raid_set *rs = RS(sc);
 +      struct dm_mem_cache_object *obj = stripe->obj + p;
 +      struct page_list *pl = obj->pl;
 +      struct stripe_chunk *chunk = CHUNK(stripe, p);
 +      struct raid_dev *dev = rs->dev + p;
 +      struct dm_io_region io = {
 +              .bdev = dev->dev->bdev,
 +              .sector = stripe->key,
 +              .count = stripe->io.size,
 +      };
 +      struct dm_io_request control = {
 +              .bi_rw = ChunkDirty(chunk) ? WRITE : READ,
 +              .mem = {
 +                      .type = DM_IO_PAGE_LIST,
 +                      .ptr.pl = pl,
 +                      .offset = 0,
 +              },
 +              .notify = {
 +                      .fn = endio,
 +                      .context = chunk,
 +              },
 +              .client = StripeRecover(stripe) ? rs->recover.dm_io_client :
 +                                                sc->dm_io_client,
 +      };
 +
 +      BUG_ON(ChunkLocked(chunk));
 +      BUG_ON(!ChunkUptodate(chunk) && ChunkDirty(chunk));
 +      BUG_ON(ChunkUptodate(chunk) && !ChunkDirty(chunk));
 +
 +      /*
 +       * Don't rw past end of device, which can happen, because
 +       * typically sectors_per_dev isn't divisible by io_size.
 +       */
 +      if (unlikely(io.sector + io.count > rs->set.sectors_per_dev))
 +              io.count = rs->set.sectors_per_dev - io.sector;
 +
 +      BUG_ON(!io.count);
 +      io.sector += dev->start;        /* Add <offset>. */
 +      if (RSRecover(rs))
 +              recover_io_count(stripe);       /* Recovery io accounting. */
 +
 +      /* REMOVEME: statistics. */
 +      atomic_inc(rs->stats + (ChunkDirty(chunk) ? S_DM_IO_WRITE :
 +                                                  S_DM_IO_READ));
 +      SetChunkLocked(chunk);
 +      SetDevIoQueued(dev);
 +      BUG_ON(dm_io(&control, 1, &io, NULL));
 +}
 +
 +/*
 + * Write dirty or read not uptodate page lists of a stripe.
 + */
 +static int stripe_chunks_rw(struct stripe *stripe)
 +{
 +      int r;
 +      struct raid_set *rs = RS(stripe->sc);
 +
 +      /*
 +       * Increment the pending count on the stripe
 +       * first, so that we don't race in endio().
 +       *
 +       * An inc (IO) is needed for any chunk unless !ChunkIo(chunk):
 +       *
 +       * o not uptodate
 +       * o dirtied by writes merged
 +       * o dirtied by parity calculations
 +       */
 +      r = for_each_io_dev(stripe, stripe_get_references);
 +      if (r) {
 +              /* Io needed: chunks are either not uptodate or dirty. */
 +              int max;        /* REMOVEME: */
 +              struct stripe_cache *sc = &rs->sc;
 +
 +              /* Submit actual io. */
 +              for_each_io_dev(stripe, stripe_chunk_rw);
 +
 +              /* REMOVEME: statistics */
 +              max = sc_active(sc);
 +              if (atomic_read(&sc->active_stripes_max) < max)
 +                      atomic_set(&sc->active_stripes_max, max);
 +
 +              atomic_inc(rs->stats + S_FLUSHS);
 +              /* END REMOVEME: statistics */
 +      }
 +
 +      return r;
 +}
 +
 +/* Merge in all writes hence dirtying respective chunks. */
 +static void stripe_merge_writes(struct stripe *stripe)
 +{
 +      unsigned p = RS(stripe->sc)->set.raid_devs;
 +
 +      while (p--) {
 +              struct stripe_chunk *chunk = CHUNK(stripe, p);
 +              struct bio_list *write = BL_CHUNK(chunk, WRITE_QUEUED);
 +
 +              if (!bio_list_empty(write)) {
 +                      struct bio *bio;
 +                      struct page_list *pl = stripe->obj[p].pl;
 +
 +                      /*
 +                       * We can play with the lists without holding a lock,
 +                       * because it is just us accessing them anyway.
 +                       */
 +                      bio_list_for_each(bio, write)
 +                              bio_copy_page_list(WRITE, stripe, pl, bio);
 +
 +                      bio_list_merge(BL_CHUNK(chunk, WRITE_MERGED), write);
 +                      bio_list_init(write);
 +                      chunk_set(chunk, DIRTY);
 +              }
 +      }
 +}
 +
 +/* Queue all writes to get merged. */
 +static int stripe_queue_writes(struct stripe *stripe)
 +{
 +      int r = 0;
 +      unsigned p = RS(stripe->sc)->set.raid_devs;
 +
 +      while (p--) {
 +              struct stripe_chunk *chunk = CHUNK(stripe, p);
 +              struct bio_list *write = BL_CHUNK(chunk, WRITE);
 +
 +              if (!bio_list_empty(write)) {
 +                      bio_list_merge(BL_CHUNK(chunk, WRITE_QUEUED), write);
 +                      bio_list_init(write);
 +SetChunkIo(chunk);
 +                      r = 1;
 +              }
 +      }
 +
 +      return r;
 +}
 +
 +
 +/* Check, if a chunk gets completely overwritten. */
 +static int stripe_check_chunk_overwrite(struct stripe *stripe, unsigned p)
 +{
 +      unsigned sectors = 0;
 +      struct bio *bio;
 +      struct bio_list *bl = BL(stripe, p, WRITE_QUEUED);
 +
 +      bio_list_for_each(bio, bl)
 +              sectors += bio_sectors(bio);
 +
 +      BUG_ON(sectors > RS(stripe->sc)->set.io_size);
 +      return sectors == RS(stripe->sc)->set.io_size;
 +}
 +
 +/*
 + * Avoid io on broken/reconstructed drive in order to
 + * reconstruct date on endio.
 + *
 + * (*1*) We set StripeReconstruct() in here, so that _do_endios()
 + *     will trigger a reconstruct call before resetting it.
 + */
 +static int stripe_chunk_set_io_flags(struct stripe *stripe, int pr)
 +{
 +      struct stripe_chunk *chunk = CHUNK(stripe, pr);
 +
 +      /*
 +       * Allow io on all chunks but the indexed one,
 +       * because we're either degraded or prohibit it
 +       * on the one for later reconstruction.
 +       */
 +      /* Includes ClearChunkIo(), ClearChunkUptodate(). */
 +      stripe_chunk_invalidate(chunk);
 +      stripe->idx.recover = pr;
 +      SetStripeReconstruct(stripe);
 +
 +      /* REMOVEME: statistics. */
 +      atomic_inc(RS(stripe->sc)->stats + S_PROHIBITCHUNKIO);
 +      return -EPERM;
 +}
 +
 +/* Chunk locked/uptodate and device failed tests. */
 +static struct stripe_chunk *
 +stripe_chunk_check(struct stripe *stripe, unsigned p, unsigned *chunks_uptodate)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct stripe_chunk *chunk = CHUNK(stripe, p);
 +
 +      /* Can't access active chunks. */
 +      if (ChunkLocked(chunk)) {
 +              /* REMOVEME: statistics. */
 +              atomic_inc(rs->stats + S_CHUNK_LOCKED);
 +              return NULL;
 +      }
 +
 +      /* Can't access broken devive. */
 +      if (ChunkError(chunk) || DevFailed(rs->dev + p))
 +              return NULL;
 +
 +      /* Can access uptodate chunks. */
 +      if (ChunkUptodate(chunk)) {
 +              (*chunks_uptodate)++;
 +              return NULL;
 +      }
 +
 +      return chunk;
 +}
 +
 +/*
 + * Degraded/reconstruction mode.
 + *
 + * Check stripe state to figure which chunks don't need IO.
 + *
 + * Returns 0 for fully operational, -EPERM for degraded/resynchronizing.
 + */
 +static int stripe_check_reconstruct(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +
 +      if (RSDead(rs)) {
 +              ClearStripeReconstruct(stripe);
 +              ClearStripeReconstructed(stripe);
 +              stripe_allow_io(stripe);
 +              return 0;
 +      }
 +
 +      /* Avoid further reconstruction setting, when already set. */
 +      if (StripeReconstruct(stripe)) {
 +              /* REMOVEME: statistics. */
 +              atomic_inc(rs->stats + S_RECONSTRUCT_SET);
 +              return -EBUSY;
 +      }
 +
 +      /* Initially allow io on all chunks. */
 +      stripe_allow_io(stripe);
 +
 +      /* Return if stripe is already reconstructed. */
 +      if (StripeReconstructed(stripe)) {
 +              atomic_inc(rs->stats + S_RECONSTRUCTED);
 +              return 0;
 +      }
 +
 +      /*
 +       * Degraded/reconstruction mode (device failed) ->
 +       * avoid io on the failed device.
 +       */
 +      if (unlikely(RSDegraded(rs))) {
 +              /* REMOVEME: statistics. */
 +              atomic_inc(rs->stats + S_DEGRADED);
 +              /* Allow IO on all devices but the dead one. */
 +              BUG_ON(rs->set.ei < 0);
 +              return stripe_chunk_set_io_flags(stripe, rs->set.ei);
 +      } else {
 +              int sync, pi = dev_for_parity(stripe, &sync);
 +
 +              /*
 +               * Reconstruction mode (ie. a particular (replaced) device or
 +               * some (rotating) parity chunk is being resynchronized) ->
 +               *   o make sure all needed chunks are read in
 +               *   o cope with 3/4 disk array special case where it
 +               *     doesn't make a difference to read in parity
 +               *     to xor data in/out
 +               */
 +              if (RSEnforceParityCreation(rs) || !sync) {
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_NOSYNC);
 +                      /* Allow IO on all devs but the one to reconstruct. */
 +                      return stripe_chunk_set_io_flags(stripe, pi);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Check, if stripe is ready to merge writes.
 + * I.e. if all chunks present to allow to merge bios.
 + *
 + * We prohibit io on:
 + *
 + * o chunks without bios
 + * o chunks which get completely written over
 + */
 +static int stripe_merge_possible(struct stripe *stripe, int nosync)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned chunks_overwrite = 0, chunks_prohibited = 0,
 +               chunks_uptodate = 0, p = rs->set.raid_devs;
 +
 +      /* Walk all chunks. */
 +      while (p--) {
 +              struct stripe_chunk *chunk;
 +
 +              /* Prohibit io on broken devices. */
 +              if (DevFailed(rs->dev + p)) {
 +                      chunk = CHUNK(stripe, p);
 +                      goto prohibit_io;
 +              }
 +
 +              /* We can't optimize any further if no chunk. */
 +              chunk = stripe_chunk_check(stripe, p, &chunks_uptodate);
 +              if (!chunk || nosync)
 +                      continue;
 +
 +              /*
 +               * We have a chunk, which is not uptodate.
 +               *
 +               * If this is not parity and we don't have
 +               * reads queued, we can optimize further.
 +               */
 +              if (p != stripe->idx.parity &&
 +                  bio_list_empty(BL_CHUNK(chunk, READ)) &&
 +                  bio_list_empty(BL_CHUNK(chunk, WRITE_MERGED))) {
 +                      if (bio_list_empty(BL_CHUNK(chunk, WRITE_QUEUED)))
 +                              goto prohibit_io;
 +                      else if (RSCheckOverwrite(rs) &&
 +                               stripe_check_chunk_overwrite(stripe, p))
 +                              /* Completely overwritten chunk. */
 +                              chunks_overwrite++;
 +              }
 +
 +              /* Allow io for chunks with bios and overwritten ones. */
 +              SetChunkIo(chunk);
 +              continue;
 +
 +prohibit_io:
 +              /* No io for broken devices or for chunks w/o bios. */
 +              ClearChunkIo(chunk);
 +              chunks_prohibited++;
 +              /* REMOVEME: statistics. */
 +              atomic_inc(RS(stripe->sc)->stats + S_PROHIBITCHUNKIO);
 +      }
 +
 +      /* All data chunks will get written over. */
 +      if (chunks_overwrite == rs->set.data_devs)
 +              atomic_inc(rs->stats + S_OVERWRITE); /* REMOVEME: statistics.*/
 +      else if (chunks_uptodate + chunks_prohibited < rs->set.raid_devs) {
 +              /* We don't have enough chunks to merge. */
 +              atomic_inc(rs->stats + S_CANT_MERGE); /* REMOVEME: statistics.*/
 +              return -EPERM;
 +      }
 +
 +      /*
 +       * If we have all chunks up to date or overwrite them, we
 +       * just zero the parity chunk and let stripe_rw() recreate it.
 +       */
 +      if (chunks_uptodate == rs->set.raid_devs ||
 +          chunks_overwrite == rs->set.data_devs) {
 +              stripe_zero_chunk(stripe, stripe->idx.parity);
 +              BUG_ON(StripeReconstruct(stripe));
 +              SetStripeReconstruct(stripe);   /* Enforce xor in caller. */
 +      } else {
 +              /*
 +               * With less chunks, we xor parity out.
 +               *
 +               * (*4*) We rely on !StripeReconstruct() in chunk_must_xor(),
 +               *       so that only chunks with queued or merged writes
 +               *       are being xored.
 +               */
 +              parity_xor(stripe);
 +      }
 +
 +      /*
 +       * We do have enough chunks to merge.
 +       * All chunks are uptodate or get written over.
 +       */
 +      atomic_inc(rs->stats + S_CAN_MERGE); /* REMOVEME: statistics. */
 +      return 0;
 +}
 +
 +/*
 + * Avoid reading chunks in case we're fully operational.
 + *
 + * We prohibit io on any chunks without bios but the parity chunk.
 + */
 +static void stripe_avoid_reads(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      unsigned dummy = 0, p = rs->set.raid_devs;
 +
 +      /* Walk all chunks. */
 +      while (p--) {
 +              struct stripe_chunk *chunk =
 +                      stripe_chunk_check(stripe, p, &dummy);
 +
 +              if (!chunk)
 +                      continue;
 +
 +              /* If parity or any bios pending -> allow io. */
 +              if (chunk_ref(chunk) || p == stripe->idx.parity)
 +                      SetChunkIo(chunk);
 +              else {
 +                      ClearChunkIo(chunk);
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(RS(stripe->sc)->stats + S_PROHIBITCHUNKIO);
 +              }
 +      }
 +}
 +
 +/*
 + * Read/write a stripe.
 + *
 + * All stripe read/write activity goes through this function
 + * unless recovery, which has to call stripe_chunk_rw() directly.
 + *
 + * Make sure we don't try already merged stripes in order
 + * to avoid data corruption.
 + *
 + * Check the state of the RAID set and if degraded (or
 + * resynchronizing for reads), read in all other chunks but
 + * the one on the dead/resynchronizing device in order to be
 + * able to reconstruct the missing one in _do_endios().
 + *
 + * Can be called on active stripes in order
 + * to dispatch new io on inactive chunks.
 + *
 + * States to cover:
 + *   o stripe to read and/or write
 + *   o stripe with error to reconstruct
 + */
 +static int stripe_rw(struct stripe *stripe)
 +{
 +      int nosync, r;
 +      struct raid_set *rs = RS(stripe->sc);
 +
 +      /*
 +       * Check, if a chunk needs to be reconstructed
 +       * because of a degraded set or a region out of sync.
 +       */
 +      nosync = stripe_check_reconstruct(stripe);
 +      switch (nosync) {
 +      case -EBUSY:
 +              return 0; /* Wait for stripe reconstruction to finish. */
 +      case -EPERM:
 +              goto io;
 +      }
 +
 +      /*
 +       * If we don't have merged writes pending, we can schedule
 +       * queued writes to be merged next without corrupting data.
 +       */
 +      if (!StripeMerged(stripe)) {
 +              r = stripe_queue_writes(stripe);
 +              if (r)
 +                      /* Writes got queued -> flag RBW. */
 +                      SetStripeRBW(stripe);
 +      }
 +
 +      /*
 +       * Merge all writes hanging off uptodate/overwritten
 +       * chunks of the stripe.
 +       */
 +      if (StripeRBW(stripe)) {
 +              r = stripe_merge_possible(stripe, nosync);
 +              if (!r) { /* Merge possible. */
 +                      struct stripe_chunk *chunk;
 +
 +                      /*
 +                       * I rely on valid parity in order
 +                       * to xor a fraction of chunks out
 +                       * of parity and back in.
 +                       */
 +                      stripe_merge_writes(stripe);    /* Merge writes in. */
 +                      parity_xor(stripe);             /* Update parity. */
 +                      ClearStripeReconstruct(stripe); /* Reset xor enforce. */
 +                      SetStripeMerged(stripe);        /* Writes merged. */
 +                      ClearStripeRBW(stripe);         /* Disable RBW. */
 +
 +                      /*
 +                       * REMOVEME: sanity check on parity chunk
 +                       *           states after writes got merged.
 +                       */
 +                      chunk = CHUNK(stripe, stripe->idx.parity);
 +                      BUG_ON(ChunkLocked(chunk));
 +                      BUG_ON(!ChunkUptodate(chunk));
 +                      BUG_ON(!ChunkDirty(chunk));
 +                      BUG_ON(!ChunkIo(chunk));
 +              }
 +      } else if (!nosync && !StripeMerged(stripe))
 +              /* Read avoidance if not degraded/resynchronizing/merged. */
 +              stripe_avoid_reads(stripe);
 +
 +io:
 +      /* Now submit any reads/writes for non-uptodate or dirty chunks. */
 +      r = stripe_chunks_rw(stripe);
 +      if (!r) {
 +              /*
 +               * No io submitted because of chunk io
 +               * prohibited or locked chunks/failed devices
 +               * -> push to end io list for processing.
 +               */
 +              stripe_endio_push(stripe);
 +              atomic_inc(rs->stats + S_NO_RW); /* REMOVEME: statistics. */
 +      }
 +
 +      return r;
 +}
 +
 +/*
 + * Recovery functions
 + */
 +/* Read a stripe off a raid set for recovery. */
 +static int stripe_recover_read(struct stripe *stripe, int pi)
 +{
 +      BUG_ON(stripe_io_ref(stripe));
 +
 +      /* Invalidate all chunks so that they get read in. */
 +      stripe_chunks_invalidate(stripe);
 +      stripe_allow_io(stripe); /* Allow io on all recovery chunks. */
 +
 +      /*
 +       * If we are reconstructing a perticular device, we can avoid
 +       * reading the respective chunk in, because we're going to
 +       * reconstruct it anyway.
 +       *
 +       * We can't do that for resynchronization of rotating parity,
 +       * because the recovery stripe chunk size is typically larger
 +       * than the sets chunk size.
 +       */
 +      if (pi > -1)
 +              ClearChunkIo(CHUNK(stripe, pi));
 +
 +      return stripe_chunks_rw(stripe);
 +}
 +
 +/* Write a stripe to a raid set for recovery. */
 +static int stripe_recover_write(struct stripe *stripe, int pi)
 +{
 +      BUG_ON(stripe_io_ref(stripe));
 +
 +      /*
 +       * If this is a reconstruct of a particular device, then
 +       * reconstruct the respective chunk, else create parity chunk.
 +       */
 +      if (pi > -1) {
 +              stripe_zero_chunk(stripe, pi);
 +              common_xor(stripe, stripe->io.size, 0, pi);
 +              chunk_set(CHUNK(stripe, pi), DIRTY);
 +      } else
 +              parity_xor(stripe);
 +
 +      return stripe_chunks_rw(stripe);
 +}
 +
 +/* Read/write a recovery stripe. */
 +static int stripe_recover_rw(struct stripe *stripe)
 +{
 +      int r = 0, sync = 0;
 +
 +      /* Read/write flip-flop. */
 +      if (TestClearStripeRBW(stripe)) {
 +              SetStripeMerged(stripe);
 +              stripe->key = stripe->recover->pos;
 +              r = stripe_recover_read(stripe, dev_for_parity(stripe, &sync));
 +              BUG_ON(!r);
 +      } else if (TestClearStripeMerged(stripe)) {
 +              r = stripe_recover_write(stripe, dev_for_parity(stripe, &sync));
 +              BUG_ON(!r);
 +      }
 +
 +      BUG_ON(sync);
 +      return r;
 +}
 +
 +/* Recover bandwidth available ?. */
 +static int recover_bandwidth(struct raid_set *rs)
 +{
 +      int r, work;
 +
 +      /* On reset or when bios delayed -> allow recovery. */
 +      r = recover_io_reset(rs);
 +      if (r || RSBandwidth(rs))
 +              goto out;
 +
 +      work = atomic_read(rs->recover.io_count + IO_WORK);
 +      if (work) {
 +              /* Pay attention to larger recover stripe size. */
 +              int recover = atomic_read(rs->recover.io_count + IO_RECOVER) *
 +                                        rs->recover.io_size / rs->set.io_size;
 +
 +              /*
 +               * Don't use more than given bandwidth
 +               * of the work io for recovery.
 +               */
 +              if (recover > work / rs->recover.bandwidth_work) {
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_NO_BANDWIDTH);
 +                      return 0;
 +              }
 +      }
 +
 +out:
 +      atomic_inc(rs->stats + S_BANDWIDTH);    /* REMOVEME: statistics. */
 +      return 1;
 +}
 +
 +/* Try to get a region to recover. */
 +static int stripe_recover_get_region(struct stripe *stripe)
 +{
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct recover *rec = &rs->recover;
 +      struct recover_addr *addr = stripe->recover;
 +      struct dm_dirty_log *dl = rec->dl;
 +      struct dm_rh_client *rh = rec->rh;
 +
 +      BUG_ON(!dl);
 +      BUG_ON(!rh);
 +
 +      /* Return, that we have region first to finish it during suspension. */
 +      if (addr->reg)
 +              return 1;
 +
 +      if (RSSuspend(rs))
 +              return -EPERM;
 +
 +      if (dl->type->get_sync_count(dl) >= rec->nr_regions)
 +              return -ENOENT;
 +
 +      /* If we don't have enough bandwidth, we don't proceed recovering. */
 +      if (!recover_bandwidth(rs))
 +              return -EAGAIN;
 +
 +      /* Start quiescing a region. */
 +      dm_rh_recovery_prepare(rh);
 +      addr->reg = dm_rh_recovery_start(rh);
 +      if (!addr->reg)
 +              return -EAGAIN;
 +
 +      addr->pos = dm_rh_region_to_sector(rh, dm_rh_get_region_key(addr->reg));
 +      addr->end = addr->pos + dm_rh_get_region_size(rh);
 +
 +      /*
 +       * Take one global io reference out for the
 +       * whole region, which is going to be released
 +       * when the region is completely done with.
 +       */
 +      io_get(rs);
 +      return 0;
 +}
 +
 +/* Update region hash state. */
 +enum recover_type { REC_FAILURE = 0, REC_SUCCESS = 1 };
 +static void recover_rh_update(struct stripe *stripe, enum recover_type success)
 +{
 +      struct recover_addr *addr = stripe->recover;
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct recover *rec = &rs->recover;
 +
 +      if (!addr->reg) {
 +              DMERR("%s- Called w/o region", __func__);
 +              return;
 +      }
 +
 +      dm_rh_recovery_end(addr->reg, success);
 +      if (success)
 +              rec->nr_regions_recovered++;
 +
 +      addr->reg = NULL;
 +
 +      /*
 +       * Completely done with this region ->
 +       * release the 1st io reference.
 +       */
 +      io_put(rs);
 +}
 +
 +/* Set start of recovery state. */
 +static void set_start_recovery(struct raid_set *rs)
 +{
 +      /* Initialize recovery. */
 +      rs->recover.start_jiffies = jiffies;
 +      rs->recover.end_jiffies = 0;
 +}
 +
 +/* Set end of recovery state. */
 +static void set_end_recovery(struct raid_set *rs)
 +{
 +      ClearRSRecover(rs);
 +/* Achtung: nicht mehr zurück setzten -> 'i' belibt in status output und userpace könnte sich darauf verlassen, das es verschiwndet!!!! */
 +      rs->set.dev_to_init = -1;
 +
 +      /* Check for jiffies overrun. */
 +      rs->recover.end_jiffies = jiffies;
 +      if (rs->recover.end_jiffies < rs->recover.start_jiffies)
 +              rs->recover.end_jiffies = ~0;
 +}
 +
 +/* Handle recovery on one recovery stripe. */
 +static int _do_recovery(struct stripe *stripe)
 +{
 +      int r;
 +      struct raid_set *rs = RS(stripe->sc);
 +      struct recover_addr *addr = stripe->recover;
 +
 +      /* If recovery is active -> return. */
 +      if (stripe_io_ref(stripe))
 +              return 1;
 +
 +      /* IO error is fatal for recovery -> stop it. */
 +      if (unlikely(StripeError(stripe)))
 +              goto err;
 +
 +      /* Recovery end required. */
 +      if (unlikely(RSDegraded(rs)))
 +              goto err;
 +
 +      /* Get a region to recover. */
 +      r = stripe_recover_get_region(stripe);
 +      switch (r) {
 +      case 0: /* Got a new region: flag initial read before write. */
 +              SetStripeRBW(stripe);
 +      case 1: /* Have a region in the works. */
 +              break;
 +      case -EAGAIN:
 +              /* No bandwidth/quiesced region yet, try later. */
 +              if (!io_ref(rs))
 +                      wake_do_raid_delayed(rs, HZ / 4);
 +      case -EPERM:
 +              /* Suspend. */
 +              return 1;
 +      case -ENOENT:   /* No more regions to recover. */
 +              schedule_work(&rs->io.ws_do_table_event);
 +              return 0;
 +      default:
 +              BUG();
 +      }
 +
 +      /* Read/write a recover stripe. */
 +      r = stripe_recover_rw(stripe);
 +      if (r)
 +              /* IO initiated. */
 +              return 1;
 +
 +      /* Read and write finished-> update recovery position within region. */
 +      addr->pos += stripe->io.size;
 +
 +      /* If we're at end of region, update region hash. */
 +      if (addr->pos >= addr->end ||
 +          addr->pos >= rs->set.sectors_per_dev)
 +              recover_rh_update(stripe, REC_SUCCESS);
 +      else
 +              /* Prepare to read next region segment. */
 +              SetStripeRBW(stripe);
 +
 +      /* Schedule myself for another round... */
 +      wake_do_raid(rs);
 +      return 1;
 +
 +err:
 +      /* FIXME: rather try recovering other regions on error? */
 +      rs_check_degrade(stripe);
 +      recover_rh_update(stripe, REC_FAILURE);
 +
 +      /* Check state of partially recovered array. */
 +      if (RSDegraded(rs) && !RSDead(rs) &&
 +          rs->set.dev_to_init != -1 &&
 +          rs->set.ei != rs->set.dev_to_init) {
 +              /* Broken drive != drive to recover -> FATAL. */
 +              SetRSDead(rs);
 +              DMERR("FATAL: failed device != device to initialize -> "
 +                    "RAID set broken");
 +      }
 +
 +      if (StripeError(stripe) || RSDegraded(rs)) {
 +              char buf[BDEVNAME_SIZE];
 +
 +              DMERR("stopping recovery due to "
 +                    "ERROR on /dev/%s, stripe at offset %llu",
 +                    bdevname(rs->dev[rs->set.ei].dev->bdev, buf),
 +                    (unsigned long long) stripe->key);
 +
 +      }
 +
 +      /* Make sure, that all quiesced regions get released. */
 +      while (addr->reg) {
 +              dm_rh_recovery_end(addr->reg, -EIO);
 +              addr->reg = dm_rh_recovery_start(rs->recover.rh);
 +      }
 +
 +      return 0;
 +}
 +
 +/* Called by main io daemon to recover regions. */
 +static int do_recovery(struct raid_set *rs)
 +{
 +      if (RSRecover(rs)) {
 +              int r = 0;
 +              struct stripe *stripe;
 +
 +              list_for_each_entry(stripe, &rs->recover.stripes,
 +                                  lists[LIST_RECOVER])
 +                      r += _do_recovery(stripe);
 +
 +              if (r)
 +                      return r;
 +
 +              set_end_recovery(rs);
 +              stripe_recover_free(rs);
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * END recovery functions
 + */
 +
 +/* End io process all stripes handed in by endio() callback. */
 +static void _do_endios(struct raid_set *rs, struct stripe *stripe,
 +                     struct list_head *flush_list)
 +{
 +      /* First unlock all required chunks. */
 +      stripe_chunks_unlock(stripe);
 +
 +      /*
 +       * If an io error on a stripe occured, degrade the RAID set
 +       * and try to endio as many bios as possible. If any bios can't
 +       * be endio processed, requeue the stripe (stripe_ref() != 0).
 +       */
 +      if (TestClearStripeError(stripe)) {
 +              /*
 +               * FIXME: if read, rewrite the failed chunk after reconstruction
 +               *        in order to trigger disk bad sector relocation.
 +               */
 +              rs_check_degrade(stripe); /* Resets ChunkError(). */
 +              ClearStripeReconstruct(stripe);
 +              ClearStripeReconstructed(stripe);
 +
 +              /*
 +               * FIXME: if write, don't endio writes in flight and don't
 +               *        allow for new writes until userspace has updated
 +               *        its metadata.
 +               */
 +      }
 +
 +      /* Got to reconstruct a missing chunk. */
 +      if (StripeReconstruct(stripe)) {
 +              /*
 +               * (*2*) We use StripeReconstruct() to allow for
 +               *       all chunks to be xored into the reconstructed
 +               *       one (see chunk_must_xor()).
 +               */
 +              stripe_reconstruct(stripe);
 +
 +              /*
 +               * (*3*) Now we reset StripeReconstruct() and flag
 +               *       StripeReconstructed() to show to stripe_rw(),
 +               *       that we have reconstructed a missing chunk.
 +               */
 +              ClearStripeReconstruct(stripe);
 +              SetStripeReconstructed(stripe);
 +
 +              /* FIXME: reschedule to be written in case of read. */
 +              /* if (!RSDead && RSDegraded(rs) !StripeRBW(stripe)) {
 +                      chunk_set(CHUNK(stripe, stripe->idx.recover), DIRTY);
 +                      stripe_chunks_rw(stripe);
 +              } */
 +
 +              stripe->idx.recover = -1;
 +      }
 +
 +      /*
 +       * Now that we eventually got a complete stripe, we
 +       * can process the rest of the end ios on reads.
 +       */
 +      stripe_endio(READ, stripe);
 +
 +      /* End io all merged writes if not prohibited. */
 +      if (!RSProhibitWrites(rs) && StripeMerged(stripe)) {
 +              ClearStripeMerged(stripe);
 +              stripe_endio(WRITE_MERGED, stripe);
 +      }
 +
 +      /* If RAID set is dead -> fail any ios to dead drives. */
 +      if (RSDead(rs)) {
 +              if (!TestSetRSDeadEndioMessage(rs))
 +                      DMERR("RAID set dead: failing ios to dead devices");
 +
 +              stripe_fail_io(stripe);
 +      }
 +
 +      /*
 +       * We have stripe references still,
 +       * beacuse of read before writes or IO errors ->
 +       * got to put on flush list for processing.
 +       */
 +      if (stripe_ref(stripe)) {
 +              BUG_ON(!list_empty(stripe->lists + LIST_LRU));
 +              list_add_tail(stripe->lists + LIST_FLUSH, flush_list);
 +              atomic_inc(rs->stats + S_REQUEUE); /* REMOVEME: statistics. */
 +      } else
 +              stripe_lru_add(stripe);
 +}
 +
 +/* Pop any endio stripes off of the endio list and belabour them. */
 +static void do_endios(struct raid_set *rs)
 +{
 +      struct stripe_cache *sc = &rs->sc;
 +      struct stripe *stripe;
 +      /* IO flush list for sorted requeued stripes. */
 +      struct list_head flush_list;
 +
 +      INIT_LIST_HEAD(&flush_list);
 +
 +      while ((stripe = stripe_endio_pop(sc))) {
 +              /* Avoid endio on stripes with newly io'ed chunks. */
 +              if (!stripe_io_ref(stripe))
 +                      _do_endios(rs, stripe, &flush_list);
 +      }
 +
 +      /*
 +       * Insert any requeued stripes in the proper
 +       * order at the beginning of the io (flush) list.
 +       */
 +      list_splice(&flush_list, sc->lists + LIST_FLUSH);
 +}
 +
 +/* Flush any stripes on the io list. */
 +static int do_flush(struct raid_set *rs)
 +{
 +      int r = 0;
 +      struct stripe *stripe;
 +
 +      while ((stripe = stripe_io_pop(&rs->sc)))
 +              r += stripe_rw(stripe); /* Read/write stripe. */
 +
 +      return r;
 +}
 +
 +/* Stripe cache resizing. */
 +static void do_sc_resize(struct raid_set *rs)
 +{
 +      unsigned set = atomic_read(&rs->sc.stripes_to_set);
 +
 +      if (set) {
 +              unsigned cur = atomic_read(&rs->sc.stripes);
 +              int r = (set > cur) ? sc_grow(&rs->sc, set - cur, SC_GROW) :
 +                                    sc_shrink(&rs->sc, cur - set);
 +
 +              /* Flag end of resizeing if ok. */
 +              if (!r)
 +                      atomic_set(&rs->sc.stripes_to_set, 0);
 +      }
 +}
 +
 +/*
 + * Process all ios
 + *
 + * We do different things with the io depending
 + * on the state of the region that it is in:
 + *
 + * o reads: hang off stripe cache or postpone if full
 + *
 + * o writes:
 + *
 + *  CLEAN/DIRTY/NOSYNC:       increment pending and hang io off stripe's stripe set.
 + *                    In case stripe cache is full or busy, postpone the io.
 + *
 + *  RECOVERING:               delay the io until recovery of the region completes.
 + *
 + */
 +static void do_ios(struct raid_set *rs, struct bio_list *ios)
 +{
 +      int r;
 +      unsigned flush = 0, delay = 0;
 +      sector_t sector;
 +      struct dm_rh_client *rh = rs->recover.rh;
 +      struct bio *bio;
 +      struct bio_list reject;
 +
 +      bio_list_init(&reject);
 +
 +      /*
 +       * Classify each io:
 +       *    o delay writes to recovering regions (let reads go through)
 +       *    o queue io to all other regions
 +       */
 +      while ((bio = bio_list_pop(ios))) {
 +              /*
 +               * In case we get a barrier bio, push it back onto
 +               * the input queue unless all work queues are empty
 +               * and the stripe cache is inactive.
 +               */
 +              if (bio->bi_rw & REQ_FLUSH) {
 +                      /* REMOVEME: statistics. */
 +                      atomic_inc(rs->stats + S_BARRIER);
 +                      if (delay ||
 +                          !list_empty(rs->sc.lists + LIST_FLUSH) ||
 +                          !bio_list_empty(&reject) ||
 +                          sc_active(&rs->sc)) {
 +                              bio_list_push(ios, bio);
 +                              break;
 +                      }
 +              }
 +
 +              /* If writes prohibited because of failures -> postpone. */
 +              if (RSProhibitWrites(rs) && bio_data_dir(bio) == WRITE) {
 +                      bio_list_add(&reject, bio);
 +                      continue;
 +              }
 +
 +              /* Check for recovering regions. */
 +              sector = _sector(rs, bio);
 +              r = region_state(rs, sector, DM_RH_RECOVERING);
 +              if (unlikely(r)) {
 +                      delay++;
 +                      /* Wait writing to recovering regions. */
 +                      dm_rh_delay_by_region(rh, bio,
 +                                            dm_rh_sector_to_region(rh,
 +                                                                   sector));
 +                      /* REMOVEME: statistics.*/
 +                      atomic_inc(rs->stats + S_DELAYED_BIOS);
 +                      atomic_inc(rs->stats + S_SUM_DELAYED_BIOS);
 +
 +                      /* Force bandwidth tests in recovery. */
 +                      SetRSBandwidth(rs);
 +              } else {
 +                      /*
 +                       * Process ios to non-recovering regions by queueing
 +                       * them to stripes (does dm_rh_inc()) for writes).
 +                       */
 +                      flush += stripe_queue_bio(rs, bio, &reject);
 +              }
 +      }
 +
 +      if (flush) {
 +              /* FIXME: better error handling. */
 +              r = dm_rh_flush(rh); /* Writes got queued -> flush dirty log. */
 +              if (r)
 +                      DMERR_LIMIT("dirty log flush");
 +      }
 +
 +      /* Merge any rejected bios back to the head of the input list. */
 +      bio_list_merge_head(ios, &reject);
 +}
 +
 +/* Send an event in case we're getting too busy. */
 +static void do_busy_event(struct raid_set *rs)
 +{
 +      if (sc_busy(rs)) {
 +              if (!TestSetRSScBusy(rs))
 +                      schedule_work(&rs->io.ws_do_table_event);
 +      } else
 +              ClearRSScBusy(rs);
 +}
 +
 +/* Throw an event. */
 +static void do_table_event(struct work_struct *ws)
 +{
 +      struct raid_set *rs = container_of(ws, struct raid_set,
 +                                         io.ws_do_table_event);
 +      dm_table_event(rs->ti->table);
 +}
 +
 +
 +/*-----------------------------------------------------------------
 + * RAID daemon
 + *---------------------------------------------------------------*/
 +/*
 + * o belabour all end ios
 + * o update the region hash states
 + * o optionally shrink the stripe cache
 + * o optionally do recovery
 + * o unplug any component raid devices with queued bios
 + * o grab the input queue
 + * o work an all requeued or new ios and perform stripe cache flushs
 + * o unplug any component raid devices with queued bios
 + * o check, if the stripe cache gets too busy and throw an event if so
 + */
 +static void do_raid(struct work_struct *ws)
 +{
 +      int r;
 +      struct raid_set *rs = container_of(ws, struct raid_set,
 +                                         io.dws_do_raid.work);
 +      struct bio_list *ios = &rs->io.work, *ios_in = &rs->io.in;
 +
 +      /*
 +       * We always need to end io, so that ios can get errored in
 +       * case the set failed and the region counters get decremented
 +       * before we update region hash states and go any further.
 +       */
 +      do_endios(rs);
 +      dm_rh_update_states(rs->recover.rh, 1);
 +
 +      /*
 +       * Now that we've end io'd, which may have put stripes on the LRU list
 +       * to allow for shrinking, we resize the stripe cache if requested.
 +       */
 +      do_sc_resize(rs);
 +
 +      /* Try to recover regions. */
 +      r = do_recovery(rs);
 +
 +      /* Quickly grab all new ios queued and add them to the work list. */
 +      mutex_lock(&rs->io.in_lock);
 +      bio_list_merge(ios, ios_in);
 +      bio_list_init(ios_in);
 +      mutex_unlock(&rs->io.in_lock);
 +
 +      if (!bio_list_empty(ios))
 +              do_ios(rs, ios); /* Got ios to work into the cache. */
 +
 +      r = do_flush(rs);               /* Flush any stripes on io list. */
 +
 +      do_busy_event(rs);      /* Check if we got too busy. */
 +}
 +
 +/*
 + * Callback for region hash to dispatch
 + * delayed bios queued to recovered regions
 + * (gets called via dm_rh_update_states()).
 + */
 +static void dispatch_delayed_bios(void *context, struct bio_list *bl)
 +{
 +      struct raid_set *rs = context;
 +      struct bio *bio;
 +
 +      /* REMOVEME: statistics; decrement pending delayed bios counter. */
 +      bio_list_for_each(bio, bl)
 +              atomic_dec(rs->stats + S_DELAYED_BIOS);
 +
 +      /* Merge region hash private list to work list. */
 +      bio_list_merge_head(&rs->io.work, bl);
 +      bio_list_init(bl);
 +      ClearRSBandwidth(rs);
 +}
 +
 +/*************************************************************
 + * Constructor helpers
 + *************************************************************/
 +/* Calculate MB/sec. */
 +static unsigned mbpers(struct raid_set *rs, unsigned io_size)
 +{
 +      return to_bytes((rs->xor.speed * rs->set.data_devs *
 +                       io_size * HZ / XOR_SPEED_TICKS) >> 10) >> 10;
 +}
 +
 +/*
 + * Discover fastest xor algorithm and # of chunks combination.
 + */
 +/* Calculate speed of particular algorithm and # of chunks. */
 +static unsigned xor_speed(struct stripe *stripe)
 +{
 +      int ticks = XOR_SPEED_TICKS;
 +      unsigned p = RS(stripe->sc)->set.raid_devs, r = 0;
 +      unsigned long j;
 +
 +      /* Set uptodate so that common_xor()->xor() will belabour chunks. */
 +      while (p--)
 +              SetChunkUptodate(CHUNK(stripe, p));
 +
 +      /* Wait for next tick. */
 +      for (j = jiffies; j == jiffies; );
 +
 +      /* Do xors for a few ticks. */
 +      while (ticks--) {
 +              unsigned xors = 0;
 +
 +              for (j = jiffies; j == jiffies; ) {
 +                      mb();
 +                      common_xor(stripe, stripe->io.size, 0, 0);
 +                      mb();
 +                      xors++;
 +                      mb();
 +              }
 +
 +              if (xors > r)
 +                      r = xors;
 +      }
 +
 +      return r;
 +}
 +
 +/* Define for xor multi recovery stripe optimization runs. */
 +#define DMRAID45_XOR_TEST
 +
 +/* Optimize xor algorithm for this RAID set. */
 +static unsigned xor_optimize(struct raid_set *rs)
 +{
 +      unsigned chunks_max = 2, speed_max = 0;
 +      struct xor_func *f = ARRAY_END(xor_funcs), *f_max = NULL;
 +      struct stripe *stripe;
 +      unsigned io_size = 0, speed_hm = 0, speed_min = ~0, speed_xor_blocks = 0;
 +
 +      BUG_ON(list_empty(&rs->recover.stripes));
 +#ifndef DMRAID45_XOR_TEST
 +      stripe = list_first_entry(&rs->recover.stripes, struct stripe,
 +                                lists[LIST_RECOVER]);
 +#endif
 +
 +      /* Try all xor functions. */
 +      while (f-- > xor_funcs) {
 +              unsigned speed;
 +
 +#ifdef DMRAID45_XOR_TEST
 +              list_for_each_entry(stripe, &rs->recover.stripes,
 +                                  lists[LIST_RECOVER]) {
 +                      io_size = stripe->io.size;
 +#endif
 +
 +                      /* Set actual xor function for common_xor(). */
 +                      rs->xor.f = f;
 +                      rs->xor.chunks = (f->f == xor_blocks_wrapper ?
 +                                        (MAX_XOR_BLOCKS + 1) :
 +                                        XOR_CHUNKS_MAX);
 +                      if (rs->xor.chunks > rs->set.raid_devs)
 +                              rs->xor.chunks = rs->set.raid_devs;
 +
 +                      for ( ; rs->xor.chunks > 1; rs->xor.chunks--) {
 +                              speed = xor_speed(stripe);
 +
 +#ifdef DMRAID45_XOR_TEST
 +                              if (f->f == xor_blocks_wrapper) {
 +                                      if (speed > speed_xor_blocks)
 +                                              speed_xor_blocks = speed;
 +                              } else if (speed > speed_hm)
 +                                      speed_hm = speed;
 +
 +                              if (speed < speed_min)
 +                                      speed_min = speed;
 +#endif
 +
 +                              if (speed > speed_max) {
 +                                      speed_max = speed;
 +                                      chunks_max = rs->xor.chunks;
 +                                      f_max = f;
 +                              }
 +                      }
 +#ifdef DMRAID45_XOR_TEST
 +              }
 +#endif
 +      }
 +
 +      /* Memorize optimal parameters. */
 +      rs->xor.f = f_max;
 +      rs->xor.chunks = chunks_max;
 +#ifdef DMRAID45_XOR_TEST
 +      DMINFO("%s stripes=%u/size=%u min=%u xor_blocks=%u hm=%u max=%u",
 +             speed_max == speed_hm ? "HM" : "NB",
 +             rs->recover.recovery_stripes, io_size, speed_min,
 +             speed_xor_blocks, speed_hm, speed_max);
 +#endif
 +      return speed_max;
 +}
 +
 +/*
 + * Allocate a RAID context (a RAID set)
 + */
 +/* Structure for variable RAID parameters. */
 +struct variable_parms {
 +      int bandwidth;
 +      int bandwidth_parm;
 +      int chunk_size;
 +      int chunk_size_parm;
 +      int io_size;
 +      int io_size_parm;
 +      int stripes;
 +      int stripes_parm;
 +      int recover_io_size;
 +      int recover_io_size_parm;
 +      int raid_parms;
 +      int recovery;
 +      int recovery_stripes;
 +      int recovery_stripes_parm;
 +};
 +
 +static struct raid_set *
 +context_alloc(struct raid_type *raid_type, struct variable_parms *p,
 +            unsigned raid_devs, sector_t sectors_per_dev,
 +            struct dm_target *ti, unsigned dl_parms, char **argv)
 +{
 +      int r;
 +      size_t len;
 +      sector_t region_size, ti_len;
 +      struct raid_set *rs = NULL;
 +      struct dm_dirty_log *dl;
 +      struct recover *rec;
 +
 +      /*
 +       * Create the dirty log
 +       *
 +       * We need to change length for the dirty log constructor,
 +       * because we want an amount of regions for all stripes derived
 +       * from the single device size, so that we can keep region
 +       * size = 2^^n independant of the number of devices
 +       */
 +      ti_len = ti->len;
 +      ti->len = sectors_per_dev;
 +      dl = dm_dirty_log_create(argv[0], ti, NULL, dl_parms, argv + 2);
 +      ti->len = ti_len;
 +      if (!dl)
 +              goto bad_dirty_log;
 +
 +      /* Chunk size *must* be smaller than region size. */
 +      region_size = dl->type->get_region_size(dl);
 +      if (p->chunk_size > region_size)
 +              goto bad_chunk_size;
 +
 +      /* Recover io size *must* be smaller than region size as well. */
 +      if (p->recover_io_size > region_size)
 +              goto bad_recover_io_size;
 +
 +      /* Size and allocate the RAID set structure. */
 +      len = sizeof(*rs->data) + sizeof(*rs->dev);
 +      if (dm_array_too_big(sizeof(*rs), len, raid_devs))
 +              goto bad_array;
 +
 +      len = sizeof(*rs) + raid_devs * len;
 +      rs = kzalloc(len, GFP_KERNEL);
 +      if (!rs)
 +              goto bad_alloc;
 +
 +      rec = &rs->recover;
 +      atomic_set(&rs->io.in_process, 0);
 +      atomic_set(&rs->io.in_process_max, 0);
 +      rec->io_size = p->recover_io_size;
 +
 +      /* Pointer to data array. */
 +      rs->data = (unsigned long **)
 +                 ((void *) rs->dev + raid_devs * sizeof(*rs->dev));
 +      rec->dl = dl;
 +      rs->set.raid_devs = raid_devs;
 +      rs->set.data_devs = raid_devs - raid_type->parity_devs;
 +      rs->set.raid_type = raid_type;
 +
 +      rs->set.raid_parms = p->raid_parms;
 +      rs->set.chunk_size_parm = p->chunk_size_parm;
 +      rs->set.io_size_parm = p->io_size_parm;
 +      rs->sc.stripes_parm = p->stripes_parm;
 +      rec->io_size_parm = p->recover_io_size_parm;
 +      rec->bandwidth_parm = p->bandwidth_parm;
 +      rec->recovery = p->recovery;
 +      rec->recovery_stripes = p->recovery_stripes;
 +
 +      /*
 +       * Set chunk and io size and respective shifts
 +       * (used to avoid divisions)
 +       */
 +      rs->set.chunk_size = p->chunk_size;
 +      rs->set.chunk_shift = ffs(p->chunk_size) - 1;
 +
 +      rs->set.io_size = p->io_size;
 +      rs->set.io_mask = p->io_size - 1;
 +      /* Mask to adjust address key in case io_size != chunk_size. */
 +      rs->set.io_inv_mask = (p->chunk_size - 1) & ~rs->set.io_mask;
 +
 +      rs->set.sectors_per_dev = sectors_per_dev;
 +
 +      rs->set.ei = -1;        /* Indicate no failed device. */
 +      atomic_set(&rs->set.failed_devs, 0);
 +
 +      rs->ti = ti;
 +
 +      atomic_set(rec->io_count + IO_WORK, 0);
 +      atomic_set(rec->io_count + IO_RECOVER, 0);
 +
 +      /* Initialize io lock and queues. */
 +      mutex_init(&rs->io.in_lock);
 +      mutex_init(&rs->io.xor_lock);
 +      bio_list_init(&rs->io.in);
 +      bio_list_init(&rs->io.work);
 +
 +      init_waitqueue_head(&rs->io.suspendq);  /* Suspend waiters (dm-io). */
 +
 +      rec->nr_regions = dm_sector_div_up(sectors_per_dev, region_size);
 +      rec->rh = dm_region_hash_create(rs, dispatch_delayed_bios,
 +                      wake_dummy, wake_do_raid, 0, p->recovery_stripes,
 +                      dl, region_size, rec->nr_regions);
 +      if (IS_ERR(rec->rh))
 +              goto bad_rh;
 +
 +      /* Initialize stripe cache. */
 +      r = sc_init(rs, p->stripes);
 +      if (r)
 +              goto bad_sc;
 +
 +      /* REMOVEME: statistics. */
 +      stats_reset(rs);
 +      ClearRSDevelStats(rs);  /* Disnable development status. */
 +      return rs;
 +
 +bad_dirty_log:
 +      TI_ERR_RET("Error creating dirty log", ERR_PTR(-ENOMEM));
 +
 +bad_chunk_size:
 +      dm_dirty_log_destroy(dl);
 +      TI_ERR_RET("Chunk size larger than region size", ERR_PTR(-EINVAL));
 +
 +bad_recover_io_size:
 +      dm_dirty_log_destroy(dl);
 +      TI_ERR_RET("Recover stripe io size larger than region size",
 +                      ERR_PTR(-EINVAL));
 +
 +bad_array:
 +      dm_dirty_log_destroy(dl);
 +      TI_ERR_RET("Arry too big", ERR_PTR(-EINVAL));
 +
 +bad_alloc:
 +      dm_dirty_log_destroy(dl);
 +      TI_ERR_RET("Cannot allocate raid context", ERR_PTR(-ENOMEM));
 +
 +bad_rh:
 +      dm_dirty_log_destroy(dl);
 +      ti->error = DM_MSG_PREFIX "Error creating dirty region hash";
 +      goto free_rs;
 +
 +bad_sc:
 +      dm_region_hash_destroy(rec->rh); /* Destroys dirty log too. */
 +      sc_exit(&rs->sc);
 +      ti->error = DM_MSG_PREFIX "Error creating stripe cache";
 +free_rs:
 +      kfree(rs);
 +      return ERR_PTR(-ENOMEM);
 +}
 +
 +/* Free a RAID context (a RAID set). */
 +static void context_free(struct raid_set *rs, unsigned p)
 +{
 +      while (p--)
 +              dm_put_device(rs->ti, rs->dev[p].dev);
 +
 +      sc_exit(&rs->sc);
 +      dm_region_hash_destroy(rs->recover.rh); /* Destroys dirty log too. */
 +      kfree(rs);
 +}
 +
 +/* Create work queue and initialize delayed work. */
 +static int rs_workqueue_init(struct raid_set *rs)
 +{
 +      struct dm_target *ti = rs->ti;
 +
 +      rs->io.wq = create_singlethread_workqueue(DAEMON);
 +      if (!rs->io.wq)
 +              TI_ERR_RET("failed to create " DAEMON, -ENOMEM);
 +
 +      INIT_DELAYED_WORK(&rs->io.dws_do_raid, do_raid);
 +      INIT_WORK(&rs->io.ws_do_table_event, do_table_event);
 +      return 0;
 +}
 +
 +/* Return pointer to raid_type structure for raid name. */
 +static struct raid_type *get_raid_type(char *name)
 +{
 +      struct raid_type *r = ARRAY_END(raid_types);
 +
 +      while (r-- > raid_types) {
 +              if (!strcmp(r->name, name))
 +                      return r;
 +      }
 +
 +      return NULL;
 +}
 +
 +/* FIXME: factor out to dm core. */
 +static int multiple(sector_t a, sector_t b, sector_t *n)
 +{
 +      sector_t r = a;
 +
 +      sector_div(r, b);
 +      *n = r;
 +      return a == r * b;
 +}
 +
 +/* Log RAID set information to kernel log. */
 +static void rs_log(struct raid_set *rs, unsigned io_size)
 +{
 +      unsigned p;
 +      char buf[BDEVNAME_SIZE];
 +
 +      for (p = 0; p < rs->set.raid_devs; p++)
 +              DMINFO("/dev/%s is raid disk %u%s",
 +                              bdevname(rs->dev[p].dev->bdev, buf), p,
 +                              (p == rs->set.pi) ? " (parity)" : "");
 +
 +      DMINFO("%d/%d/%d sectors chunk/io/recovery size, %u stripes\n"
 +             "algorithm \"%s\", %u chunks with %uMB/s\n"
 +             "%s set with net %u/%u devices",
 +             rs->set.chunk_size, rs->set.io_size, rs->recover.io_size,
 +             atomic_read(&rs->sc.stripes),
 +             rs->xor.f->name, rs->xor.chunks, mbpers(rs, io_size),
 +             rs->set.raid_type->descr, rs->set.data_devs, rs->set.raid_devs);
 +}
 +
 +/* Get all devices and offsets. */
 +static int dev_parms(struct raid_set *rs, char **argv, int *p)
 +{
 +      struct dm_target *ti = rs->ti;
 +
 +DMINFO("rs->set.sectors_per_dev=%llu", (unsigned long long) rs->set.sectors_per_dev);
 +      for (*p = 0; *p < rs->set.raid_devs; (*p)++, argv += 2) {
 +              int r;
 +              unsigned long long tmp;
 +              struct raid_dev *dev = rs->dev + *p;
 +
 +              /* Get offset and device. */
 +              if (sscanf(argv[1], "%llu", &tmp) != 1 ||
 +                  tmp > rs->set.sectors_per_dev)
 +                      TI_ERR("Invalid RAID device offset parameter");
 +
 +              dev->start = tmp;
 +              r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
 +                                &dev->dev);
 +              if (r)
 +                      TI_ERR_RET("RAID device lookup failure", r);
 +
 +              r = raid_dev_lookup(rs, dev);
 +              if (r != -ENODEV && r < *p) {
 +                      (*p)++; /* Ensure dm_put_device() on actual device. */
 +                      TI_ERR_RET("Duplicate RAID device", -ENXIO);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +/* Set recovery bandwidth. */
 +static void
 +recover_set_bandwidth(struct raid_set *rs, unsigned bandwidth)
 +{
 +      rs->recover.bandwidth = bandwidth;
 +      rs->recover.bandwidth_work = 100 / bandwidth;
 +}
 +
 +/* Handle variable number of RAID parameters. */
 +static int get_raid_variable_parms(struct dm_target *ti, char **argv,
 +                                 struct variable_parms *vp)
 +{
 +      int p, value;
 +      struct {
 +              int action; /* -1: skip, 0: no power2 check, 1: power2 check */
 +              char *errmsg;
 +              int min, max;
 +              int *var, *var2, *var3;
 +      } argctr[] = {
 +              { 1,
 +                "Invalid chunk size; must be -1 or 2^^n and <= 16384",
 +                IO_SIZE_MIN, CHUNK_SIZE_MAX,
 +                &vp->chunk_size_parm, &vp->chunk_size, &vp->io_size },
 +              { 0,
 +                "Invalid number of stripes: must be -1 or >= 8 and <= 16384",
 +                STRIPES_MIN, STRIPES_MAX,
 +                &vp->stripes_parm, &vp->stripes, NULL },
 +              { 1,
 +                "Invalid io size; must -1 or >= 8, 2^^n and less equal "
 +                "min(BIO_MAX_SECTORS/2, chunk size)",
 +                IO_SIZE_MIN, 0, /* Needs to be updated in loop below. */
 +                &vp->io_size_parm, &vp->io_size, NULL },
 +              { 1,
 +                "Invalid recovery io size; must be -1 or "
 +                "2^^n and less equal BIO_MAX_SECTORS/2",
 +                RECOVER_IO_SIZE_MIN, BIO_MAX_SECTORS / 2,
 +                &vp->recover_io_size_parm, &vp->recover_io_size, NULL },
 +              { 0,
 +                "Invalid recovery bandwidth percentage; "
 +                "must be -1 or > 0 and <= 100",
 +                BANDWIDTH_MIN, BANDWIDTH_MAX,
 +                &vp->bandwidth_parm, &vp->bandwidth, NULL },
 +              /* Handle sync argument seperately in loop. */
 +              { -1,
 +                "Invalid recovery switch; must be \"sync\" or \"nosync\"" },
 +              { 0,
 +                "Invalid number of recovery stripes;"
 +                "must be -1, > 0 and <= 64",
 +                RECOVERY_STRIPES_MIN, RECOVERY_STRIPES_MAX,
 +                &vp->recovery_stripes_parm, &vp->recovery_stripes, NULL },
 +      }, *varp;
 +
 +      /* Fetch # of variable raid parameters. */
 +      if (sscanf(*(argv++), "%d", &vp->raid_parms) != 1 ||
 +          !range_ok(vp->raid_parms, 0, 7))
 +              TI_ERR("Bad variable raid parameters number");
 +
 +      /* Preset variable RAID parameters. */
 +      vp->chunk_size = CHUNK_SIZE_DEFAULT;
 +      vp->io_size = IO_SIZE_DEFAULT;
 +      vp->stripes = STRIPES_DEFAULT;
 +      vp->recover_io_size = RECOVER_IO_SIZE_DEFAULT;
 +      vp->bandwidth = BANDWIDTH_DEFAULT;
 +      vp->recovery = 1;
 +      vp->recovery_stripes = RECOVERY_STRIPES_DEFAULT;
 +
 +      /* Walk the array of argument constraints for all given ones. */
 +      for (p = 0, varp = argctr; p < vp->raid_parms; p++, varp++) {
 +              BUG_ON(varp >= ARRAY_END(argctr));
 +
 +              /* Special case for "[no]sync" string argument. */
 +              if (varp->action < 0) {
 +                      if (!strcmp(*argv, "sync"))
 +                              ;
 +                      else if (!strcmp(*argv, "nosync"))
 +                              vp->recovery = 0;
 +                      else
 +                              TI_ERR(varp->errmsg);
 +
 +                      argv++;
 +                      continue;
 +              }
 +
 +              /*
 +               * Special case for io_size depending
 +               * on previously set chunk size.
 +               */
 +              if (p == 2)
 +                      varp->max = min(BIO_MAX_SECTORS / 2, vp->chunk_size);
 +
 +              if (sscanf(*(argv++), "%d", &value) != 1 ||
 +                  (value != -1 &&
 +                   ((varp->action && !is_power_of_2(value)) ||
 +                    !range_ok(value, varp->min, varp->max))))
 +                      TI_ERR(varp->errmsg);
 +
 +              *varp->var = value;
 +              if (value != -1) {
 +                      if (varp->var2)
 +                              *varp->var2 = value;
 +                      if (varp->var3)
 +                              *varp->var3 = value;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +/* Parse optional locking parameters. */
 +static int get_raid_locking_parms(struct dm_target *ti, char **argv,
 +                                int *locking_parms,
 +                                struct dm_raid45_locking_type **locking_type)
 +{
 +      if (!strnicmp(argv[0], "locking", strlen(argv[0]))) {
 +              char *lckstr = argv[1];
 +              size_t lcksz = strlen(lckstr);
 +
 +              if (!strnicmp(lckstr, "none", lcksz)) {
 +                      *locking_type = &locking_none;
 +                      *locking_parms = 2;
 +              } else if (!strnicmp(lckstr, "cluster", lcksz)) {
 +                      DMERR("locking type \"%s\" not yet implemented",
 +                            lckstr);
 +                      return -EINVAL;
 +              } else {
 +                      DMERR("unknown locking type \"%s\"", lckstr);
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      *locking_parms = 0;
 +      *locking_type = &locking_none;
 +      return 0;
 +}
 +
 +/* Set backing device read ahead properties of RAID set. */
 +static void rs_set_read_ahead(struct raid_set *rs,
 +                            unsigned sectors, unsigned stripes)
 +{
 +      unsigned ra_pages = dm_div_up(sectors, SECTORS_PER_PAGE);
 +      struct mapped_device *md = dm_table_get_md(rs->ti->table);
 +      struct backing_dev_info *bdi = &dm_disk(md)->queue->backing_dev_info;
 +
 +      /* Set read-ahead for the RAID set and the component devices. */
 +      if (ra_pages) {
 +              unsigned p = rs->set.raid_devs;
 +
 +              bdi->ra_pages = stripes * ra_pages * rs->set.data_devs;
 +
 +              while (p--) {
 +                      struct request_queue *q =
 +                              bdev_get_queue(rs->dev[p].dev->bdev);
 +
 +                      q->backing_dev_info.ra_pages = ra_pages;
 +              }
 +      }
 +}
 +
 +/* Set congested function. */
 +static void rs_set_congested_fn(struct raid_set *rs)
 +{
 +      struct mapped_device *md = dm_table_get_md(rs->ti->table);
 +      struct backing_dev_info *bdi = &dm_disk(md)->queue->backing_dev_info;
 +
 +      /* Set congested function and data. */
 +      bdi->congested_fn = rs_congested;
 +      bdi->congested_data = rs;
 +}
 +
 +/*
 + * Construct a RAID4/5 mapping:
 + *
 + * log_type #log_params <log_params> \
 + * raid_type [#parity_dev] #raid_variable_params <raid_params> \
 + * [locking "none"/"cluster"]
 + * #raid_devs #dev_to_initialize [<dev_path> <offset>]{3,}
 + *
 + * log_type = "core"/"disk",
 + * #log_params = 1-3 (1-2 for core dirty log type, 3 for disk dirty log only)
 + * log_params = [dirty_log_path] region_size [[no]sync])
 + *
 + * raid_type = "raid4", "raid5_la", "raid5_ra", "raid5_ls", "raid5_rs"
 + *
 + * #parity_dev = N if raid_type = "raid4"
 + * o N = -1: pick default = last device
 + * o N >= 0 and < #raid_devs: parity device index
 + *
 + * #raid_variable_params = 0-7; raid_params (-1 = default):
 + *   [chunk_size [#stripes [io_size [recover_io_size \
 + *    [%recovery_bandwidth [recovery_switch [#recovery_stripes]]]]]]]
 + *   o chunk_size (unit to calculate drive addresses; must be 2^^n, > 8
 + *     and <= CHUNK_SIZE_MAX)
 + *   o #stripes is number of stripes allocated to stripe cache
 + *     (must be > 1 and < STRIPES_MAX)
 + *   o io_size (io unit size per device in sectors; must be 2^^n and > 8)
 + *   o recover_io_size (io unit size per device for recovery in sectors;
 + must be 2^^n, > SECTORS_PER_PAGE and <= region_size)
 + *   o %recovery_bandwith is the maximum amount spend for recovery during
 + *     application io (1-100%)
 + *   o recovery switch = [sync|nosync]
 + *   o #recovery_stripes is the number of recovery stripes used for
 + *     parallel recovery of the RAID set
 + * If raid_variable_params = 0, defaults will be used.
 + * Any raid_variable_param can be set to -1 to apply a default
 + *
 + * #raid_devs = N (N >= 3)
 + *
 + * #dev_to_initialize = N
 + * -1: initialize parity on all devices
 + * >= 0 and < #raid_devs: initialize raid_path; used to force reconstruction
 + * of a failed devices content after replacement
 + *
 + * <dev_path> = device_path (eg, /dev/sdd1)
 + * <offset>   = begin at offset on <dev_path>
 + *
 + */
 +#define       MIN_PARMS       13
 +static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
 +{
 +      int dev_to_init, dl_parms, i, locking_parms,
 +          parity_parm, pi = -1, r, raid_devs;
 +      sector_t tmp, sectors_per_dev;
 +      struct dm_raid45_locking_type *locking;
 +      struct raid_set *rs;
 +      struct raid_type *raid_type;
 +      struct variable_parms parms;
 +
 +      /* Ensure minimum number of parameters. */
 +      if (argc < MIN_PARMS)
 +              TI_ERR("Not enough parameters");
 +
 +      /* Fetch # of dirty log parameters. */
 +      if (sscanf(argv[1], "%d", &dl_parms) != 1 ||
 +          !range_ok(dl_parms, 1, 4711)) /* ;-) */
 +              TI_ERR("Bad dirty log parameters number");
 +
 +      /* Check raid_type. */
 +      raid_type = get_raid_type(argv[dl_parms + 2]);
 +      if (!raid_type)
 +              TI_ERR("Bad raid type");
 +
 +      /* In case of RAID4, parity drive is selectable. */
 +      parity_parm = !!(raid_type->level == raid4);
 +
 +      /* Handle variable number of RAID parameters. */
 +      r = get_raid_variable_parms(ti, argv + dl_parms + parity_parm + 3,
 +                                  &parms);
 +      if (r)
 +              return r;
 +
 +      /* Handle any locking parameters. */
 +      r = get_raid_locking_parms(ti,
 +                                 argv + dl_parms + parity_parm +
 +                                 parms.raid_parms + 4,
 +                                 &locking_parms, &locking);
 +      if (r)
 +              return r;
 +
 +      /* # of raid devices. */
 +      i = dl_parms + parity_parm + parms.raid_parms + locking_parms + 4;
 +      if (sscanf(argv[i], "%d", &raid_devs) != 1 ||
 +          raid_devs < raid_type->minimal_devs)
 +              TI_ERR("Invalid number of raid devices");
 +
 +      /* In case of RAID4, check parity drive index is in limits. */
 +      if (raid_type->level == raid4) {
 +              /* Fetch index of parity device. */
 +              if (sscanf(argv[dl_parms + 3], "%d", &pi) != 1 ||
 +                  (pi != -1 && !range_ok(pi, 0, raid_devs - 1)))
 +                      TI_ERR("Invalid RAID4 parity device index");
 +      }
 +
 +      /*
 +       * Index of device to initialize starts at 0
 +       *
 +       * o -1 -> don't initialize a selected device;
 +       *         initialize parity conforming to algorithm
 +       * o 0..raid_devs-1 -> initialize respective device
 +       *   (used for reconstruction of a replaced device)
 +       */
 +      if (sscanf(argv[dl_parms + parity_parm + parms.raid_parms +
 +                 locking_parms + 5], "%d", &dev_to_init) != 1 ||
 +          !range_ok(dev_to_init, -1, raid_devs - 1))
 +              TI_ERR("Invalid number for raid device to initialize");
 +
 +      /* Check # of raid device arguments. */
 +      if (argc - dl_parms - parity_parm - parms.raid_parms - 6 !=
 +          2 * raid_devs)
 +              TI_ERR("Wrong number of raid device/offset arguments");
 +
 +      /*
 +       * Check that the table length is devisable
 +       * w/o rest by (raid_devs - parity_devs)
 +       */
 +      if (!multiple(ti->len, raid_devs - raid_type->parity_devs,
 +                    &sectors_per_dev))
 +              TI_ERR("Target length not divisible by number of data devices");
 +
 +      /*
 +       * Check that the device size is
 +       * devisable w/o rest by chunk size
 +       */
 +      if (!multiple(sectors_per_dev, parms.chunk_size, &tmp))
 +              TI_ERR("Device length not divisible by chunk_size");
 +
 +      /****************************************************************
 +       * Now that we checked the constructor arguments ->
 +       * let's allocate the RAID set
 +       ****************************************************************/
 +      rs = context_alloc(raid_type, &parms, raid_devs, sectors_per_dev,
 +                         ti, dl_parms, argv);
 +      if (IS_ERR(rs))
 +              return PTR_ERR(rs);
 +
 +
 +      rs->set.dev_to_init = rs->set.dev_to_init_parm = dev_to_init;
 +      rs->set.pi = rs->set.pi_parm = pi;
 +
 +      /* Set RAID4 parity drive index. */
 +      if (raid_type->level == raid4)
 +              rs->set.pi = (pi == -1) ? rs->set.data_devs : pi;
 +
 +      recover_set_bandwidth(rs, parms.bandwidth);
 +
 +      /* Use locking type to lock stripe access. */
 +      rs->locking = locking;
 +
 +      /* Get the device/offset tupels. */
 +      argv += dl_parms + 6 + parity_parm + parms.raid_parms;
 +      r = dev_parms(rs, argv, &i);
 +      if (r)
 +              goto err;
 +
 +      /* Set backing device information (eg. read ahead). */
 +      rs_set_read_ahead(rs, 2 * rs->set.chunk_size /* sectors per device */,
 +                            2 /* # of stripes */);
 +      rs_set_congested_fn(rs); /* Set congested function. */
 +      SetRSCheckOverwrite(rs); /* Allow chunk overwrite checks. */
 +      rs->xor.speed = xor_optimize(rs); /* Select best xor algorithm. */
 +
 +      /* Set for recovery of any nosync regions. */
 +      if (parms.recovery)
 +              SetRSRecover(rs);
 +      else {
 +              /*
 +               * Need to free recovery stripe(s) here in case
 +               * of nosync, because xor_optimize uses one.
 +               */
 +              set_start_recovery(rs);
 +              set_end_recovery(rs);
 +              stripe_recover_free(rs);
 +      }
 +
 +      /*
 +       * Enable parity chunk creation enformcement for
 +       * little numbers of array members where it doesn'ti
 +       * gain us performance to xor parity out and back in as
 +       * with larger array member numbers.
 +       */
 +      if (rs->set.raid_devs <= rs->set.raid_type->minimal_devs + 1)
 +              SetRSEnforceParityCreation(rs);
 +
 +      /*
 +       * Make sure that dm core only hands maximum io size
 +       * length down and pays attention to io boundaries.
 +       */
 +      ti->split_io = rs->set.io_size;
 +      ti->private = rs;
 +
 +      /* Initialize work queue to handle this RAID set's io. */
 +      r = rs_workqueue_init(rs);
 +      if (r)
 +              goto err;
 +
 +      rs_log(rs, rs->recover.io_size); /* Log information about RAID set. */
 +      return 0;
 +
 +err:
 +      context_free(rs, i);
 +      return r;
 +}
 +
 +/*
 + * Destruct a raid mapping
 + */
 +static void raid_dtr(struct dm_target *ti)
 +{
 +      struct raid_set *rs = ti->private;
 +
 +      destroy_workqueue(rs->io.wq);
 +      context_free(rs, rs->set.raid_devs);
 +}
 +
 +/* Raid mapping function. */
 +static int raid_map(struct dm_target *ti, struct bio *bio,
 +                  union map_info *map_context)
 +{
 +      /* I don't want to waste stripe cache capacity. */
 +      if (bio_rw(bio) == READA)
 +              return -EIO;
 +      else {
 +              struct raid_set *rs = ti->private;
 +
 +              /*
 +               * Get io reference to be waiting for to drop
 +               * to zero on device suspension/destruction.
 +               */
 +              io_get(rs);
 +              bio->bi_sector -= ti->begin;    /* Remap sector. */
 +
 +              /* Queue io to RAID set. */
 +              mutex_lock(&rs->io.in_lock);
 +              bio_list_add(&rs->io.in, bio);
 +              mutex_unlock(&rs->io.in_lock);
 +
 +              /* Wake daemon to process input list. */
 +              wake_do_raid(rs);
 +
 +              /* REMOVEME: statistics. */
 +              atomic_inc(rs->stats + (bio_data_dir(bio) == READ ?
 +                                      S_BIOS_READ : S_BIOS_WRITE));
 +              return DM_MAPIO_SUBMITTED;      /* Handle later. */
 +      }
 +}
 +
 +/* Device suspend. */
 +static void raid_presuspend(struct dm_target *ti)
 +{
 +      struct raid_set *rs = ti->private;
 +      struct dm_dirty_log *dl = rs->recover.dl;
 +
 +      SetRSSuspend(rs);
 +
 +      if (RSRecover(rs))
 +              dm_rh_stop_recovery(rs->recover.rh);
 +
 +      cancel_delayed_work(&rs->io.dws_do_raid);
 +      flush_workqueue(rs->io.wq);
 +      wait_ios(rs);   /* Wait for completion of all ios being processed. */
 +
 +      if (dl->type->presuspend && dl->type->presuspend(dl))
 +              /* FIXME: need better error handling. */
 +              DMWARN("log presuspend failed");
 +}
 +
 +static void raid_postsuspend(struct dm_target *ti)
 +{
 +      struct raid_set *rs = ti->private;
 +      struct dm_dirty_log *dl = rs->recover.dl;
 +
 +      if (dl->type->postsuspend && dl->type->postsuspend(dl))
 +              /* FIXME: need better error handling. */
 +              DMWARN("log postsuspend failed");
 +
 +}
 +
 +/* Device resume. */
 +static void raid_resume(struct dm_target *ti)
 +{
 +      struct raid_set *rs = ti->private;
 +      struct recover *rec = &rs->recover;
 +      struct dm_dirty_log *dl = rec->dl;
 +
 +DMINFO("%s...", __func__);
 +      if (dl->type->resume && dl->type->resume(dl))
 +              /* Resume dirty log. */
 +              /* FIXME: need better error handling. */
 +              DMWARN("log resume failed");
 +
 +      rec->nr_regions_to_recover =
 +              rec->nr_regions - dl->type->get_sync_count(dl);
 +
 +      /* Restart any unfinished recovery. */
 +      if (RSRecover(rs)) {
 +              set_start_recovery(rs);
 +              dm_rh_start_recovery(rec->rh);
 +      }
 +
 +      ClearRSSuspend(rs);
 +}
 +
 +/* Return stripe cache size. */
 +static unsigned sc_size(struct raid_set *rs)
 +{
 +      return to_sector(atomic_read(&rs->sc.stripes) *
 +                       (sizeof(struct stripe) +
 +                        (sizeof(struct stripe_chunk) +
 +                         (sizeof(struct page_list) +
 +                          to_bytes(rs->set.io_size) *
 +                          rs->set.raid_devs)) +
 +                        (rs->recover.end_jiffies ?
 +                         0 : rs->recover.recovery_stripes *
 +                         to_bytes(rs->set.raid_devs * rs->recover.io_size))));
 +}
 +
 +/* REMOVEME: status output for development. */
 +static void raid_devel_stats(struct dm_target *ti, char *result,
 +                           unsigned *size, unsigned maxlen)
 +{
 +      unsigned sz = *size;
 +      unsigned long j;
 +      char buf[BDEVNAME_SIZE], *p;
 +      struct stats_map *sm;
 +      struct raid_set *rs = ti->private;
 +      struct recover *rec = &rs->recover;
 +      struct timespec ts;
 +
 +      DMEMIT("%s %s=%u bw=%u\n",
 +             version, rs->xor.f->name, rs->xor.chunks, rs->recover.bandwidth);
 +      DMEMIT("act_ios=%d ", io_ref(rs));
 +      DMEMIT("act_ios_max=%d\n", atomic_read(&rs->io.in_process_max));
 +      DMEMIT("act_stripes=%d ", sc_active(&rs->sc));
 +      DMEMIT("act_stripes_max=%d\n",
 +             atomic_read(&rs->sc.active_stripes_max));
 +
 +      for (sm = stats_map; sm < ARRAY_END(stats_map); sm++)
 +              DMEMIT("%s%d", sm->str, atomic_read(rs->stats + sm->type));
 +
 +      DMEMIT(" checkovr=%s\n", RSCheckOverwrite(rs) ? "on" : "off");
 +      DMEMIT("sc=%u/%u/%u/%u/%u/%u/%u\n", rs->set.chunk_size,
 +             atomic_read(&rs->sc.stripes), rs->set.io_size,
 +             rec->recovery_stripes, rec->io_size, rs->sc.hash.buckets,
 +             sc_size(rs));
 +
 +      j = (rec->end_jiffies ? rec->end_jiffies : jiffies) -
 +          rec->start_jiffies;
 +      jiffies_to_timespec(j, &ts);
 +      sprintf(buf, "%ld.%ld", ts.tv_sec, ts.tv_nsec);
 +      p = strchr(buf, '.');
 +      p[3] = 0;
 +
 +      DMEMIT("rg=%llu/%llu/%llu/%u %s\n",
 +             (unsigned long long) rec->nr_regions_recovered,
 +             (unsigned long long) rec->nr_regions_to_recover,
 +             (unsigned long long) rec->nr_regions, rec->bandwidth, buf);
 +
 +      *size = sz;
 +}
 +
 +static int raid_status(struct dm_target *ti, status_type_t type,
 +                     char *result, unsigned maxlen)
 +{
 +      unsigned p, sz = 0;
 +      char buf[BDEVNAME_SIZE];
 +      struct raid_set *rs = ti->private;
 +      struct dm_dirty_log *dl = rs->recover.dl;
 +      int raid_parms[] = {
 +              rs->set.chunk_size_parm,
 +              rs->sc.stripes_parm,
 +              rs->set.io_size_parm,
 +              rs->recover.io_size_parm,
 +              rs->recover.bandwidth_parm,
 +              -2,
 +              rs->recover.recovery_stripes,
 +      };
 +
 +      switch (type) {
 +      case STATUSTYPE_INFO:
 +              /* REMOVEME: statistics. */
 +              if (RSDevelStats(rs))
 +                      raid_devel_stats(ti, result, &sz, maxlen);
 +
 +              DMEMIT("%u ", rs->set.raid_devs);
 +
 +              for (p = 0; p < rs->set.raid_devs; p++)
 +                      DMEMIT("%s ",
 +                             format_dev_t(buf, rs->dev[p].dev->bdev->bd_dev));
 +
 +              DMEMIT("2 ");
 +              for (p = 0; p < rs->set.raid_devs; p++) {
 +                      DMEMIT("%c", !DevFailed(rs->dev + p) ? 'A' : 'D');
 +
 +                      if (p == rs->set.pi)
 +                              DMEMIT("p");
 +
 +                      if (p == rs->set.dev_to_init)
 +                              DMEMIT("i");
 +              }
 +
 +              DMEMIT(" %llu/%llu ",
 +                    (unsigned long long) dl->type->get_sync_count(dl),
 +                    (unsigned long long) rs->recover.nr_regions);
 +
 +              sz += dl->type->status(dl, type, result+sz, maxlen-sz);
 +              break;
 +      case STATUSTYPE_TABLE:
 +              sz = rs->recover.dl->type->status(rs->recover.dl, type,
 +                                                result, maxlen);
 +              DMEMIT("%s %u ", rs->set.raid_type->name, rs->set.raid_parms);
 +
 +              for (p = 0; p < rs->set.raid_parms; p++) {
 +                      if (raid_parms[p] > -2)
 +                              DMEMIT("%d ", raid_parms[p]);
 +                      else
 +                              DMEMIT("%s ", rs->recover.recovery ?
 +                                            "sync" : "nosync");
 +              }
 +
 +              DMEMIT("%u %d ", rs->set.raid_devs, rs->set.dev_to_init);
 +
 +              for (p = 0; p < rs->set.raid_devs; p++)
 +                      DMEMIT("%s %llu ",
 +                             format_dev_t(buf, rs->dev[p].dev->bdev->bd_dev),
 +                             (unsigned long long) rs->dev[p].start);
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Message interface
 + */
 +/* Turn a delta into an absolute value. */
 +static int _absolute(char *action, int act, int r)
 +{
 +      size_t len = strlen(action);
 +
 +      if (len < 2)
 +              len = 2;
 +
 +      /* Make delta absolute. */
 +      if (!strncmp("set", action, len))
 +              ;
 +      else if (!strncmp("grow", action, len))
 +              r += act;
 +      else if (!strncmp("shrink", action, len))
 +              r = act - r;
 +      else
 +              r = -EINVAL;
 +
 +      return r;
 +}
 +
 + /* Change recovery io bandwidth. */
 +static int bandwidth_change(struct raid_set *rs, int argc, char **argv,
 +                          enum raid_set_flags flag)
 +{
 +      int act = rs->recover.bandwidth, bandwidth;
 +
 +      if (argc != 2)
 +              return -EINVAL;
 +
 +      if (sscanf(argv[1], "%d", &bandwidth) == 1 &&
 +          range_ok(bandwidth, BANDWIDTH_MIN, BANDWIDTH_MAX)) {
 +              /* Make delta bandwidth absolute. */
 +              bandwidth = _absolute(argv[0], act, bandwidth);
 +
 +              /* Check range. */
 +              if (range_ok(bandwidth, BANDWIDTH_MIN, BANDWIDTH_MAX)) {
 +                      recover_set_bandwidth(rs, bandwidth);
 +                      return 0;
 +              }
 +      }
 +
 +      return -EINVAL;
 +}
 +
 +/* Set/reset development feature flags. */
 +static int devel_flags(struct raid_set *rs, int argc, char **argv,
 +                     enum raid_set_flags flag)
 +{
 +      size_t len;
 +
 +      if (argc != 1)
 +              return -EINVAL;
 +
 +      len = strlen(argv[0]);
 +      if (len < 2)
 +              len = 2;
 +
 +      if (!strncmp(argv[0], "on", len))
 +              return test_and_set_bit(flag, &rs->io.flags) ? -EPERM : 0;
 +      else if (!strncmp(argv[0], "off", len))
 +              return test_and_clear_bit(flag, &rs->io.flags) ? 0 : -EPERM;
 +      else if (!strncmp(argv[0], "reset", len)) {
 +              if (flag == RS_DEVEL_STATS) {
 +                      if  (test_bit(flag, &rs->io.flags)) {
 +                              stats_reset(rs);
 +                              return 0;
 +                      } else
 +                              return -EPERM;
 +              } else  {
 +                      set_bit(flag, &rs->io.flags);
 +                      return 0;
 +              }
 +      }
 +
 +      return -EINVAL;
 +}
 +
 +/* Resize the stripe cache. */
 +static int sc_resize(struct raid_set *rs, int argc, char **argv,
 +                   enum raid_set_flags flag)
 +{
 +      int act, stripes;
 +
 +      if (argc != 2)
 +              return -EINVAL;
 +
 +      /* Deny permission in case the daemon is still resizing!. */
 +      if (atomic_read(&rs->sc.stripes_to_set))
 +              return -EPERM;
 +
 +      if (sscanf(argv[1], "%d", &stripes) == 1 &&
 +          stripes > 0) {
 +              act = atomic_read(&rs->sc.stripes);
 +
 +              /* Make delta stripes absolute. */
 +              stripes = _absolute(argv[0], act, stripes);
 +
 +              /*
 +               * Check range and that the # of stripes changes.
 +               * We leave the resizing to the wroker.
 +               */
 +              if (range_ok(stripes, STRIPES_MIN, STRIPES_MAX) &&
 +                  stripes != atomic_read(&rs->sc.stripes)) {
 +                      atomic_set(&rs->sc.stripes_to_set, stripes);
 +                      wake_do_raid(rs);
 +                      return 0;
 +              }
 +      }
 +
 +      return -EINVAL;
 +}
 +
 +/* Change xor algorithm and number of chunks. */
 +static int xor_set(struct raid_set *rs, int argc, char **argv,
 +                 enum raid_set_flags flag)
 +{
 +      if (argc == 2) {
 +              int chunks;
 +              char *algorithm = argv[0];
 +              struct xor_func *f = ARRAY_END(xor_funcs);
 +
 +              if (sscanf(argv[1], "%d", &chunks) == 1 &&
 +                  range_ok(chunks, 2, XOR_CHUNKS_MAX) &&
 +                  chunks <= rs->set.raid_devs) {
 +                      while (f-- > xor_funcs) {
 +                              if (!strcmp(algorithm, f->name)) {
 +                                      unsigned io_size = 0;
 +                                      struct stripe *stripe = stripe_alloc(&rs->sc, rs->sc.mem_cache_client, SC_GROW);
 +
 +                                      DMINFO("xor: %s", f->name);
 +                                      if (f->f == xor_blocks_wrapper &&
 +                                          chunks > MAX_XOR_BLOCKS + 1) {
 +                                              DMERR("chunks > MAX_XOR_BLOCKS"
 +                                                    " + 1");
 +                                              break;
 +                                      }
 +
 +                                      mutex_lock(&rs->io.xor_lock);
 +                                      rs->xor.f = f;
 +                                      rs->xor.chunks = chunks;
 +                                      rs->xor.speed = 0;
 +                                      mutex_unlock(&rs->io.xor_lock);
 +
 +                                      if (stripe) {
 +                                              rs->xor.speed = xor_speed(stripe);
 +                                              io_size = stripe->io.size;
 +                                              stripe_free(stripe, rs->sc.mem_cache_client);
 +                                      }
 +
 +                                      rs_log(rs, io_size);
 +                                      return 0;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return -EINVAL;
 +}
 +
 +/*
 + * Allow writes after they got prohibited because of a device failure.
 + *
 + * This needs to be called after userspace updated metadata state
 + * based on an event being thrown during device failure processing.
 + */
 +static int allow_writes(struct raid_set *rs, int argc, char **argv,
 +                      enum raid_set_flags flag)
 +{
 +      if (TestClearRSProhibitWrites(rs)) {
 +DMINFO("%s waking", __func__);
 +              wake_do_raid(rs);
 +              return 0;
 +      }
 +
 +      return -EPERM;
 +}
 +
 +/* Parse the RAID message. */
 +/*
 + * 'all[ow_writes]'
 + * 'ba[ndwidth] {se[t],g[row],sh[rink]} #'    # e.g 'ba se 50'
 + * "o[verwrite]  {on,of[f],r[eset]}'          # e.g. 'o of'
 + * 'sta[tistics] {on,of[f],r[eset]}'          # e.g. 'stat of'
 + * 'str[ipecache] {se[t],g[row],sh[rink]} #'  # e.g. 'stripe set 1024'
 + * 'xor algorithm #chunks'                    # e.g. 'xor xor_8 5'
 + *
 + */
 +static int raid_message(struct dm_target *ti, unsigned argc, char **argv)
 +{
 +      if (argc) {
 +              size_t len = strlen(argv[0]);
 +              struct raid_set *rs = ti->private;
 +              struct {
 +                      const char *name;
 +                      int (*f) (struct raid_set *rs, int argc, char **argv,
 +                                enum raid_set_flags flag);
 +                      enum raid_set_flags flag;
 +              } msg_descr[] = {
 +                      { "allow_writes", allow_writes, 0 },
 +                      { "bandwidth", bandwidth_change, 0 },
 +                      { "overwrite", devel_flags, RS_CHECK_OVERWRITE },
 +                      { "statistics", devel_flags, RS_DEVEL_STATS },
 +                      { "stripe_cache", sc_resize, 0 },
 +                      { "xor", xor_set, 0 },
 +              }, *m = ARRAY_END(msg_descr);
 +
 +              if (len < 3)
 +                      len = 3;
 +
 +              while (m-- > msg_descr) {
 +                      if (!strncmp(argv[0], m->name, len))
 +                              return m->f(rs, argc - 1, argv + 1, m->flag);
 +              }
 +
 +      }
 +
 +      return -EINVAL;
 +}
 +/*
 + * END message interface
 + */
 +
 +/* Provide io hints. */
 +static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
 +{
 +      struct raid_set *rs = ti->private;
 +
 +      blk_limits_io_min(limits, rs->set.chunk_size);
 +      blk_limits_io_opt(limits, rs->set.chunk_size * rs->set.data_devs);
 +}
 +
 +static struct target_type raid_target = {
 +      .name = "raid45",
 +      .version = {1, 0, 0},
 +      .module = THIS_MODULE,
 +      .ctr = raid_ctr,
 +      .dtr = raid_dtr,
 +      .map = raid_map,
 +      .presuspend = raid_presuspend,
 +      .postsuspend = raid_postsuspend,
 +      .resume = raid_resume,
 +      .status = raid_status,
 +      .message = raid_message,
 +      .io_hints = raid_io_hints,
 +};
 +
 +static void init_exit(const char *bad_msg, const char *good_msg, int r)
 +{
 +      if (r)
 +              DMERR("Failed to %sregister target [%d]", bad_msg, r);
 +      else
 +              DMINFO("%s %s", good_msg, version);
 +}
 +
 +static int __init dm_raid_init(void)
 +{
 +      int r = dm_register_target(&raid_target);
 +
 +      init_exit("", "initialized", r);
 +      return r;
 +}
 +
 +static void __exit dm_raid_exit(void)
 +{
 +      dm_unregister_target(&raid_target);
 +      init_exit("un", "exit", 0);
 +}
 +
 +/* Module hooks. */
 +module_init(dm_raid_init);
 +module_exit(dm_raid_exit);
 +
 +MODULE_DESCRIPTION(DM_NAME " raid4/5 target");
 +MODULE_AUTHOR("Heinz Mauelshagen <heinzm@redhat.com>");
 +MODULE_LICENSE("GPL");
 +MODULE_ALIAS("dm-raid4");
 +MODULE_ALIAS("dm-raid5");
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc fs/Kconfig
Simple merge
diff --cc fs/ext3/super.c
Simple merge
Simple merge
diff --cc fs/ext4/ext4.h
Simple merge
diff --cc fs/ext4/file.c
Simple merge
diff --cc fs/ext4/inode.c
Simple merge
diff --cc fs/ext4/namei.c
Simple merge
diff --cc fs/ext4/super.c
Simple merge
diff --cc fs/ext4/xattr.c
Simple merge
diff --cc fs/namei.c
Simple merge
diff --cc fs/nfs/dir.c
Simple merge
diff --cc fs/nfs/inode.c
Simple merge
index 0f2b043,0000000..d753792
mode 100644,000000..100644
--- /dev/null
@@@ -1,4104 -1,0 +1,4072 @@@
 +/*
 + * Novell NCP Redirector for Linux
 + * Author: James Turner
 + *
 + * This file contains functions used to control access to the Linux file
 + * system.
 + *
 + * Copyright (C) 2005 Novell, Inc.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + */
 +
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/fs.h>
 +#include <linux/dcache.h>
 +#include <linux/mount.h>
 +#include <linux/pagemap.h>
 +#include <linux/string.h>
 +#include <linux/slab.h>
 +#include <linux/unistd.h>
 +#include <asm/statfs.h>
 +#include <asm/uaccess.h>
 +#include <linux/ctype.h>
 +#include <linux/statfs.h>
 +#include <linux/pagevec.h>
 +#include <linux/writeback.h>
 +#include <linux/backing-dev.h>
 +#include <linux/mm.h>
 +#include <linux/file.h>
 +
 +/*===[ Include files specific to this module ]============================*/
 +#include "vfs.h"
 +
 +struct inode_data {
 +      void *Scope;
 +      unsigned long Flags;
 +      struct list_head IList;
 +      struct inode *Inode;
 +      unsigned long cntDC;
 +      struct list_head DirCache;
 +      struct mutex DirCacheLock;
 +      void *FileHandle;
 +      int CacheFlag;
 +      char Name[1];           /* Needs to be last entry */
 +};
 +
 +#define FILE_UPDATE_TIMEOUT   2
 +
 +/*===[ Function prototypes ]=============================================*/
 +
 +static unsigned long novfs_internal_hash(struct qstr *name);
 +static int novfs_d_add(struct dentry *p, struct dentry *d, struct inode *i, int add);
 +
 +static void novfs_kill_sb(struct super_block *SB);
 +
 +/*
 + * Declared dentry_operations
 + */
 +int novfs_d_revalidate(struct dentry *, struct nameidata *);
 +int novfs_d_hash(const struct dentry *, const struct inode *, struct qstr *);
 +int novfs_d_compare(const struct dentry *, const struct inode *,
 +                  const struct dentry *, const struct inode *,
 +                  unsigned int, const char *, const struct qstr *);
 +int novfs_d_delete(struct dentry *dentry);
 +void novfs_d_release(struct dentry *dentry);
 +void novfs_d_iput(struct dentry *dentry, struct inode *inode);
 +
 +/*
 + * Declared directory operations
 + */
 +int novfs_dir_open(struct inode *inode, struct file *file);
 +int novfs_dir_release(struct inode *inode, struct file *file);
 +loff_t novfs_dir_lseek(struct file *file, loff_t offset, int origin);
 +ssize_t novfs_dir_read(struct file *file, char *buf, size_t len, loff_t * off);
 +void addtodentry(struct dentry *Parent, unsigned char *List, int Level);
 +int novfs_filldir(void *data, const char *name, int namelen, loff_t off, ino_t ino, unsigned ftype);
 +int novfs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir);
 +int novfs_dir_fsync(struct file *file, int datasync);
 +
 +/*
 + * Declared address space operations
 + */
 +int novfs_a_writepage(struct page *page, struct writeback_control *wbc);
 +int novfs_a_writepages(struct address_space *mapping, struct writeback_control *wbc);
 +int novfs_a_write_begin(struct file *file, struct address_space *mapping,
 +                      loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata);
 +int novfs_a_write_end(struct file *file, struct address_space *mapping,
 +                    loff_t pos, unsigned len, unsigned copied, struct page *pagep, void *fsdata);
 +int novfs_a_readpage(struct file *file, struct page *page);
 +int novfs_a_readpages(struct file *file, struct address_space *mapping, struct list_head *page_lst, unsigned nr_pages);
 +ssize_t novfs_a_direct_IO(int rw, struct kiocb *kiocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs);
 +
 +/*
 + * Declared file_operations
 + */
 +ssize_t novfs_f_read(struct file *, char *, size_t, loff_t *);
 +ssize_t novfs_f_write(struct file *, const char *, size_t, loff_t *);
 +int novfs_f_readdir(struct file *, void *, filldir_t);
 +int novfs_f_mmap(struct file *file, struct vm_area_struct *vma);
 +int novfs_f_open(struct inode *, struct file *);
 +int novfs_f_flush(struct file *, fl_owner_t);
 +int novfs_f_release(struct inode *, struct file *);
 +int novfs_f_fsync(struct file *, int datasync);
 +int novfs_f_lock(struct file *, int, struct file_lock *);
 +
 +/*
 + * Declared inode_operations
 + */
 +int novfs_i_create(struct inode *, struct dentry *, int, struct nameidata *);
 +struct dentry *novfs_i_lookup(struct inode *, struct dentry *, struct nameidata *);
 +int novfs_i_mkdir(struct inode *, struct dentry *, int);
 +int novfs_i_unlink(struct inode *dir, struct dentry *dentry);
 +int novfs_i_rmdir(struct inode *, struct dentry *);
 +int novfs_i_mknod(struct inode *, struct dentry *, int, dev_t);
 +int novfs_i_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
 +int novfs_i_setattr(struct dentry *, struct iattr *);
 +int novfs_i_getattr(struct vfsmount *mnt, struct dentry *, struct kstat *);
 +int novfs_i_revalidate(struct dentry *dentry);
 +
 +/*
 + * Extended attributes operations
 + */
 +
 +ssize_t novfs_i_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);
 +int novfs_i_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_size, int flags);
 +ssize_t novfs_i_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 +
 +void update_inode(struct inode *Inode, struct novfs_entry_info *Info);
 +
 +/*
 + * Declared super_operations
 + */
 +void novfs_read_inode(struct inode *inode);
 +void novfs_write_inode(struct inode *inode);
 +int novfs_notify_change(struct dentry *dentry, struct iattr *attr);
 +void novfs_evict_inode(struct inode *inode);
 +int novfs_show_options(struct seq_file *s, struct vfsmount *m);
 +
 +int novfs_statfs(struct dentry *de, struct kstatfs *buf);
 +
 +/*
 + * Declared control interface functions
 + */
 +ssize_t novfs_control_Read(struct file *file, char *buf, size_t nbytes, loff_t * ppos);
 +
 +ssize_t novfs_control_write(struct file *file, const char *buf, size_t nbytes, loff_t * ppos);
 +
 +int __init init_novfs(void);
 +void __exit exit_novfs(void);
 +
 +int novfs_lock_inode_cache(struct inode *i);
 +void novfs_unlock_inode_cache(struct inode *i);
 +int novfs_enumerate_inode_cache(struct inode *i, struct list_head **iteration, ino_t * ino, struct novfs_entry_info *info);
 +int novfs_get_entry(struct inode *i, struct qstr *name, ino_t * ino, struct novfs_entry_info *info);
 +int novfs_get_entry_by_pos(struct inode *i, loff_t pos, ino_t * ino, struct novfs_entry_info *info);
 +int novfs_get_entry_time(struct inode *i, struct qstr *name, ino_t * ino, struct novfs_entry_info *info, u64 * EntryTime);
 +int novfs_get_remove_entry(struct inode *i, ino_t * ino, struct novfs_entry_info *info);
 +void novfs_invalidate_inode_cache(struct inode *i);
 +struct novfs_dir_cache *novfs_lookup_inode_cache(struct inode *i, struct qstr *name, ino_t ino);
 +int novfs_lookup_validate(struct inode *i, struct qstr *name, ino_t ino);
 +int novfs_add_inode_entry(struct inode *i, struct qstr *name, ino_t ino, struct novfs_entry_info *info);
 +int novfs_update_entry(struct inode *i, struct qstr *name, ino_t ino, struct novfs_entry_info *info);
 +void novfs_remove_inode_entry(struct inode *i, struct qstr *name, ino_t ino);
 +void novfs_free_invalid_entries(struct inode *i);
 +void novfs_free_inode_cache(struct inode *i);
 +
 +/*===[ Global variables ]=================================================*/
 +struct dentry_operations novfs_dentry_operations = {
 +      .d_revalidate = novfs_d_revalidate,
 +      .d_hash = novfs_d_hash,
 +      .d_compare = novfs_d_compare,
 +      //.d_delete      = novfs_d_delete,
 +      .d_release = novfs_d_release,
 +      .d_iput = novfs_d_iput,
 +};
 +
 +struct file_operations novfs_dir_operations = {
 +      .owner = THIS_MODULE,
 +      .open = novfs_dir_open,
 +      .release = novfs_dir_release,
 +      .llseek = novfs_dir_lseek,
 +      .read = novfs_dir_read,
 +      .readdir = novfs_dir_readdir,
 +      .fsync = novfs_dir_fsync,
 +};
 +
 +static struct file_operations novfs_file_operations = {
 +      .owner = THIS_MODULE,
 +      .read = novfs_f_read,
 +      .write = novfs_f_write,
 +      .readdir = novfs_f_readdir,
 +      .mmap = novfs_f_mmap,
 +      .open = novfs_f_open,
 +      .flush = novfs_f_flush,
 +      .release = novfs_f_release,
 +      .fsync = novfs_f_fsync,
 +      .llseek = generic_file_llseek,
 +      .lock = novfs_f_lock,
 +};
 +
 +static struct address_space_operations novfs_nocache_aops = {
 +      .readpage = novfs_a_readpage,
 +};
 +
 +struct backing_dev_info novfs_backing_dev_info = {
 +      .ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE,
 +      .state = 0,
 +      .capabilities = BDI_CAP_NO_WRITEBACK | BDI_CAP_MAP_COPY,
 +};
 +
 +static struct address_space_operations novfs_aops = {
 +      .readpage = novfs_a_readpage,
 +      .readpages = novfs_a_readpages,
 +      .writepage = novfs_a_writepage,
 +      .writepages = novfs_a_writepages,
 +      .write_begin = novfs_a_write_begin,
 +      .write_end = novfs_a_write_end,
 +      .set_page_dirty = __set_page_dirty_nobuffers,
 +      .direct_IO = novfs_a_direct_IO,
 +};
 +
 +static struct inode_operations novfs_inode_operations = {
 +      .create = novfs_i_create,
 +      .lookup = novfs_i_lookup,
 +      .unlink = novfs_i_unlink,
 +      .mkdir = novfs_i_mkdir,
 +      .rmdir = novfs_i_rmdir,
 +      .mknod = novfs_i_mknod,
 +      .rename = novfs_i_rename,
 +      .setattr = novfs_i_setattr,
 +      .getattr = novfs_i_getattr,
 +      .getxattr = novfs_i_getxattr,
 +      .setxattr = novfs_i_setxattr,
 +      .listxattr = novfs_i_listxattr,
 +};
 +
 +static struct inode_operations novfs_file_inode_operations = {
 +      .setattr = novfs_i_setattr,
 +      .getattr = novfs_i_getattr,
 +      .getxattr = novfs_i_getxattr,
 +      .setxattr = novfs_i_setxattr,
 +      .listxattr = novfs_i_listxattr,
 +};
 +
 +static struct super_operations novfs_ops = {
 +      .statfs = novfs_statfs,
 +      .evict_inode = novfs_evict_inode,
 +      .drop_inode = generic_delete_inode,
 +      .show_options = novfs_show_options,
 +
 +};
 +
- /* Not currently used
- static struct file_operations novfs_Control_operations = {
-    .read    = novfs_Control_read,
-    .write   = novfs_Control_write,
- };
- */
 +static atomic_t novfs_Inode_Number = ATOMIC_INIT(0);
 +
 +struct dentry *novfs_root = NULL;
 +char *novfs_current_mnt = NULL;
 +
 +DEFINE_MUTEX(InodeList_lock);
 +
 +LIST_HEAD(InodeList);
 +
 +DEFINE_MUTEX(TimeDir_Lock);
 +uint64_t lastTime;
 +char lastDir[PATH_MAX];
 +
 +uint64_t inHAXTime;
 +int inHAX;
 +
 +unsigned long InodeCount = 0, DCCount = 0;
 +unsigned long novfs_update_timeout = FILE_UPDATE_TIMEOUT;
 +int novfs_page_cache = 0;
 +
 +struct file_private {
 +      int listedall;
 +      void *enumHandle;
 +};
 +
 +static void PRINT_DENTRY(const char *s, struct dentry *d)
 +{
 +      __DbgPrint("%s: 0x%p\n", s, d);
 +      __DbgPrint("   d_count:      0x%x\n", d->d_count);
 +      __DbgPrint("   d_lock:       0x%x\n", d->d_lock);
 +      __DbgPrint("   d_inode:      0x%x\n", d->d_inode);
 +      __DbgPrint("   d_lru:        0x%p\n"
 +                 "      next:      0x%p\n" "      prev:      0x%p\n", &d->d_lru, d->d_lru.next, d->d_lru.prev);
 +      __DbgPrint("   d_child:      0x%p\n" "      next:      0x%p\n"
 +                 "      prev:      0x%p\n", &d->d_u.d_child, d->d_u.d_child.next, d->d_u.d_child.prev);
 +      __DbgPrint("   d_subdirs:    0x%p\n" "      next:      0x%p\n"
 +                 "      prev:      0x%p\n", &d->d_subdirs, d->d_subdirs.next, d->d_subdirs.prev);
 +      __DbgPrint("   d_alias:      0x%p\n" "      next:      0x%p\n"
 +                 "      prev:      0x%p\n", &d->d_alias, d->d_alias.next, d->d_alias.prev);
 +      __DbgPrint("   d_time:       0x%x\n", d->d_time);
 +      __DbgPrint("   d_op:         0x%p\n", d->d_op);
 +      __DbgPrint("   d_sb:         0x%p\n", d->d_sb);
 +      __DbgPrint("   d_flags:      0x%x\n", d->d_flags);
 +      __DbgPrint("   d_fsdata:     0x%p\n", d->d_fsdata);
 +/*   DbgPrint("   d_cookie:     0x%x\n", d->d_cookie); */
 +      __DbgPrint("   d_parent:     0x%p\n", d->d_parent);
 +      __DbgPrint("   d_name:       0x%p %.*s\n", &d->d_name, d->d_name.len, d->d_name.name);
 +      __DbgPrint("      name:      0x%p\n" "      len:       %d\n"
 +                 "      hash:      0x%x\n", d->d_name.name, d->d_name.len, d->d_name.hash);
 +      __DbgPrint("   d_hash:       0x%x\n" "      next:      0x%x\n"
 +                 "      pprev:     0x%x\n", d->d_hash, d->d_hash.next, d->d_hash.pprev);
 +}
 +
 +/*++======================================================================*/
 +int novfs_remove_from_root(char *RemoveName)
 +{
 +      struct qstr name;
 +      struct dentry *dentry;
 +      struct inode *dir;
 +
 +      DbgPrint("%s", RemoveName);
 +      name.len = strlen(RemoveName);
 +      name.name = RemoveName;
 +      novfs_d_hash(novfs_root, novfs_root->d_inode, &name);
 +
 +      dentry = d_lookup(novfs_root, &name);
 +      if (dentry) {
 +              if (dentry->d_inode && dentry->d_inode->i_private) {
 +                      struct inode_data *n_inode = dentry->d_inode->i_private;
 +                      n_inode->Scope = NULL;
 +              }
 +              dput(dentry);
 +      }
 +
 +      dir = novfs_root->d_inode;
 +
 +      novfs_lock_inode_cache(dir);
 +      novfs_remove_inode_entry(dir, &name, 0);
 +      novfs_unlock_inode_cache(dir);
 +
 +      return (0);
 +}
 +
 +/*++======================================================================*/
 +int novfs_add_to_root(char *AddName)
 +{
 +      struct qstr name;
 +      struct inode *dir;
 +      struct novfs_entry_info info;
 +      ino_t ino;
 +
 +      DbgPrint("%s", AddName);
 +      name.len = strlen(AddName);
 +      name.name = AddName;
 +      novfs_d_hash(novfs_root, novfs_root->d_inode, &name);
 +
 +      dir = novfs_root->d_inode;
 +
 +      novfs_lock_inode_cache(dir);
 +
 +      ino = 0;
 +
 +      if (!novfs_lookup_inode_cache(dir, &name, 0)) {
 +              info.mode = S_IFDIR | 0700;
 +              info.size = 0;
 +              info.atime = info.ctime = info.mtime = CURRENT_TIME;
 +
 +              ino = (ino_t) atomic_inc_return(&novfs_Inode_Number);
 +              novfs_add_inode_entry(dir, &name, ino, &info);
 +      }
 +
 +      novfs_unlock_inode_cache(dir);
 +
 +      return (0);
 +}
 +
 +/*++======================================================================*/
 +int novfs_Add_to_Root2(char *AddName)
 +{
 +      struct dentry *entry;
 +      struct qstr name;
 +      struct inode *inode;
 +      void *scope;
 +
 +      DbgPrint("%s", AddName);
 +      name.len = strlen(AddName);
 +      name.name = AddName;
 +
 +      novfs_d_hash(novfs_root, novfs_root->d_inode, &name);
 +
 +      entry = d_lookup(novfs_root, &name);
 +      DbgPrint("novfs_d_lookup 0x%p", entry);
 +      if (NULL == entry) {
 +              scope = novfs_scope_lookup();
 +
 +              entry = d_alloc(novfs_root, &name);
 +              DbgPrint("d_alloc 0x%p", entry);
 +              if (entry) {
 +                      entry->d_op = &novfs_dentry_operations;
 +                      entry->d_time = jiffies + (novfs_update_timeout * HZ);
 +                      /*
 +                       * done in novfs_d_add now... entry->d_fsdata = (void *)novfs_internal_hash( &name );
 +                       */
 +                      inode = novfs_get_inode(novfs_root->d_sb, S_IFDIR | 0700, 0, novfs_scope_get_uid(scope), 0, &name);
 +                      DbgPrint("Inode=0x%p", inode);
 +                      if (inode) {
 +                              inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 +                              if (!novfs_d_add(novfs_root, entry, inode, 1)) {
 +                                      if (inode->i_private) {
 +                                              struct inode_data *n_inode = inode->i_private;
 +                                              n_inode->Flags = USER_INODE;
 +                                      }
 +                                      PRINT_DENTRY("After novfs_d_add", entry);
 +                              } else {
 +                                      dput(entry);
 +                                      iput(inode);
 +                              }
 +                      }
 +              }
 +      } else {
 +              dput(entry);
 +              PRINT_DENTRY("novfs_Add_to_Root: After dput Dentry", entry);
 +      }
 +      return (0);
 +}
 +
 +char *novfs_dget_path(struct dentry *Dentry, char *Buf, unsigned int Buflen)
 +{
 +      char *retval = &Buf[Buflen];
 +      struct dentry *p = Dentry;
 +
 +      *(--retval) = '\0';
 +      Buflen--;
 +
 +      if (!IS_ROOT(p) && !IS_ROOT(p->d_parent)) {
 +              while (Buflen && !IS_ROOT(p) && !IS_ROOT(p->d_parent)) {
 +                      if (Buflen > p->d_name.len) {
 +                              retval -= p->d_name.len;
 +                              Buflen -= p->d_name.len;
 +                              memcpy(retval, p->d_name.name, p->d_name.len);
 +                              *(--retval) = '\\';
 +                              Buflen--;
 +                              p = p->d_parent;
 +                      } else {
 +                              retval = NULL;
 +                              break;
 +                      }
 +              }
 +      } else {
 +              *(--retval) = '\\';
 +      }
 +
 +      if (retval)
 +              DbgPrint("%s", retval);
 +      return (retval);
 +}
 +
 +int verify_dentry(struct dentry *dentry, int Flags)
 +{
 +      int retVal = -ENOENT;
 +      struct inode *dir;
 +      struct novfs_entry_info *info = NULL;
 +      struct inode_data *id;
 +      struct novfs_schandle session;
 +      char *path, *list = NULL, *cp;
 +      ino_t ino = 0;
 +      struct qstr name;
 +      int iLock = 0;
 +      struct dentry *parent = NULL;
 +      u64 ctime;
 +      struct inode *inode;
 +
 +      if (IS_ROOT(dentry)) {
 +              DbgPrint("Root entry");
 +              return (0);
 +      }
 +
 +      if (dentry && dentry->d_parent && (dir = dentry->d_parent->d_inode) && (id = dir->i_private)) {
 +              parent = dget_parent(dentry);
 +
 +              info = kmalloc(sizeof(struct novfs_entry_info) + PATH_LENGTH_BUFFER, GFP_KERNEL);
 +
 +              if (info) {
 +                      if (novfs_lock_inode_cache(dir)) {
 +                              name.len = dentry->d_name.len;
 +                              name.name = dentry->d_name.name;
 +                              name.hash = novfs_internal_hash(&name);
 +                              if (!novfs_get_entry_time(dir, &name, &ino, info, &ctime)) {
 +                                      inode = dentry->d_inode;
 +                                      if (inode && inode->i_private &&
 +                                          ((inode->i_size != info->size) || (inode->i_mtime.tv_sec != info->mtime.tv_sec)
 +                                           || (inode->i_mtime.tv_nsec != info->mtime.tv_nsec))) {
 +                                              /*
 +                                               * Values don't match so update.
 +                                               */
 +                                              struct inode_data *n_inode = inode->i_private;
 +                                              n_inode->Flags |= UPDATE_INODE;
 +                                      }
 +
 +                                      ctime = get_jiffies_64() - ctime;
 +                                      if (Flags || ctime < (u64) (novfs_update_timeout * HZ)) {
 +                                              retVal = 0;
 +                                              novfs_unlock_inode_cache(dir);
 +                                              dput(parent);
 +                                              kfree(info);
 +                                              return (0);
 +                                      }
 +                              }
 +                              novfs_unlock_inode_cache(dir);
 +                      }
 +
 +                      if (IS_ROOT(dentry->d_parent)) {
 +                              session = novfs_scope_get_sessionId(novfs_get_scope_from_name(&dentry->d_name));
 +                      } else
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +
 +                      if (!SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      ino = 0;
 +                      retVal = 0;
 +
 +                      if (IS_ROOT(dentry->d_parent)) {
 +                              DbgPrint("parent is Root directory");
 +                              list = novfs_get_scopeusers();
 +
 +                              iLock = novfs_lock_inode_cache(dir);
 +                              novfs_invalidate_inode_cache(dir);
 +
 +                              if (list) {
 +                                      cp = list;
 +                                      while (*cp) {
 +                                              name.name = cp;
 +                                              name.len = strlen(cp);
 +                                              name.hash = novfs_internal_hash(&name);
 +                                              cp += (name.len + 1);
 +                                              ino = 0;
 +                                              if (novfs_get_entry(dir, &name, &ino, info)) {
 +                                                      info->mode = S_IFDIR | 0700;
 +                                                      info->size = 0;
 +                                                      info->atime = info->ctime = info->mtime = CURRENT_TIME;
 +                                                      ino = (ino_t) atomic_inc_return(&novfs_Inode_Number);
 +                                                      novfs_add_inode_entry(dir, &name, ino, info);
 +                                              }
 +                                      }
 +                              }
 +                              novfs_free_invalid_entries(dir);
 +                      } else {
 +
 +                              path = novfs_dget_path(dentry, info->name, PATH_LENGTH_BUFFER);
 +                              if (path) {
 +                                      if (dentry->d_name.len <= NW_MAX_PATH_LENGTH) {
 +                                              name.hash = novfs_internal_hash(&dentry->d_name);
 +                                              name.len = dentry->d_name.len;
 +                                              name.name = dentry->d_name.name;
 +
 +                                              retVal = novfs_get_file_info(path, info, session);
 +                                              if (0 == retVal) {
 +                                                      dentry->d_time = jiffies + (novfs_update_timeout * HZ);
 +                                                      iLock = novfs_lock_inode_cache(dir);
 +                                                      if (novfs_update_entry(dir, &name, 0, info)) {
 +                                                              if (dentry->d_inode) {
 +                                                                      ino = dentry->d_inode->i_ino;
 +                                                              } else {
 +                                                                      ino = (ino_t) atomic_inc_return(&novfs_Inode_Number);
 +                                                              }
 +                                                              novfs_add_inode_entry(dir, &name, ino, info);
 +                                                      }
 +                                                      if (dentry->d_inode) {
 +                                                              update_inode(dentry->d_inode, info);
 +                                                              id->Flags &= ~UPDATE_INODE;
 +
 +                                                              dentry->d_inode->i_flags &= ~S_DEAD;
 +                                                              if (dentry->d_inode->i_private) {
 +                                                                      ((struct inode_data *)dentry->d_inode->i_private)->Scope =
 +                                                                          id->Scope;
 +                                                              }
 +                                                      }
 +                                              } else if (-EINTR != retVal) {
 +                                                      retVal = 0;
 +                                                      iLock = novfs_lock_inode_cache(dir);
 +                                                      novfs_remove_inode_entry(dir, &name, 0);
 +                                                      if (dentry->d_inode && !(dentry->d_inode->i_flags & S_DEAD)) {
 +                                                              dentry->d_inode->i_flags |= S_DEAD;
 +                                                              dentry->d_inode->i_size = 0;
 +                                                              dentry->d_inode->i_atime.tv_sec =
 +                                                                  dentry->d_inode->i_atime.tv_nsec =
 +                                                                  dentry->d_inode->i_ctime.tv_sec =
 +                                                                  dentry->d_inode->i_ctime.tv_nsec =
 +                                                                  dentry->d_inode->i_mtime.tv_sec =
 +                                                                  dentry->d_inode->i_mtime.tv_nsec = 0;
 +                                                              dentry->d_inode->i_blocks = 0;
 +                                                              d_delete(dentry);       /* Remove from cache */
 +                                                      }
 +                                              }
 +                                      } else {
 +                                              retVal = -ENAMETOOLONG;
 +                                      }
 +                              }
 +                      }
 +              } else {
 +                      retVal = -ENOMEM;
 +              }
 +              if (iLock) {
 +                      novfs_unlock_inode_cache(dir);
 +              }
 +              dput(parent);
 +      }
 +
 +      if (list)
 +              kfree(list);
 +      if (info)
 +              kfree(info);
 +
 +      DbgPrint("return=0x%x", retVal);
 +
 +      return (retVal);
 +}
 +
 +static int novfs_d_add(struct dentry *Parent, struct dentry *d, struct inode *i, int a)
 +{
 +      void *scope;
 +      struct inode_data *id = NULL;
 +
 +      char *path, *buf;
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              path = novfs_dget_path(d, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      DbgPrint("inode=0x%p ino=%d path %s", i, i->i_ino, path);
 +              }
 +              kfree(buf);
 +      }
 +
 +      if (Parent && Parent->d_inode && Parent->d_inode->i_private) {
 +              id = (struct inode_data *)Parent->d_inode->i_private;
 +      }
 +
 +      if (id && id->Scope) {
 +              scope = id->Scope;
 +      } else {
 +              scope = novfs_get_scope(d);
 +      }
 +
 +      ((struct inode_data *)i->i_private)->Scope = scope;
 +
 +      d->d_time = jiffies + (novfs_update_timeout * HZ);
 +      if (a) {
 +              d_add(d, i);
 +      } else {
 +              d_instantiate(d, i);
 +      }
 +
 +      return (0);
 +}
 +
 +int novfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
 +{
 +      int retCode = 0;
 +      struct inode *dir;
 +      struct inode_data *id;
 +      struct qstr name;
 +
 +      __DbgPrint("%s: 0x%p %.*s\n"
 +                 "   d_count: %d\n"
 +                 "   d_inode: 0x%p\n", __func__,
 +                 dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count, dentry->d_inode);
 +
 +      if (IS_ROOT(dentry)) {
 +              retCode = 1;
 +      } else {
 +              if (dentry->d_inode && dentry->d_parent && (dir = dentry->d_parent->d_inode) && (id = dir->i_private)) {
 +                      /*
 +                       * Check timer to see if in valid time limit
 +                       */
 +                      if (jiffies > dentry->d_time) {
 +                              /*
 +                               * Revalidate entry
 +                               */
 +                              name.len = dentry->d_name.len;
 +                              name.name = dentry->d_name.name;
 +                              name.hash = novfs_internal_hash(&dentry->d_name);
 +                              dentry->d_time = 0;
 +
 +                              if (0 == verify_dentry(dentry, 0)) {
 +                                      if (novfs_lock_inode_cache(dir)) {
 +                                              if (novfs_lookup_inode_cache(dir, &name, 0)) {
 +                                                      dentry->d_time = jiffies + (novfs_update_timeout * HZ);
 +                                                      retCode = 1;
 +                                              }
 +                                              novfs_unlock_inode_cache(dir);
 +                                      }
 +                              }
 +                      } else {
 +                              retCode = 1;
 +                      }
 +              }
 +      }
 +
 +      if ((0 == retCode) && dentry->d_inode) {
 +              /*
 +               * Entry has become invalid
 +               */
 +/*      dput(dentry);
 +*/
 +      }
 +
 +      DbgPrint("return 0x%x %.*s", retCode, dentry->d_name.len, dentry->d_name.name);
 +
 +      return (retCode);
 +}
 +
 +static unsigned long novfs_internal_hash(struct qstr *name)
 +{
 +      unsigned long hash = 0;
 +      unsigned int len = name->len;
 +      unsigned char *c = (unsigned char *)name->name;
 +
 +      while (len--) {
 +              /*
 +               * Lower case values for the hash.
 +               */
 +              hash = partial_name_hash(tolower(*c++), hash);
 +      }
 +
 +      return (hash);
 +}
 +
 +int novfs_d_hash(const struct dentry *dentry, const struct inode *inode,
 +               struct qstr *name)
 +{
 +      DbgPrint("%.*s", name->len, name->name);
 +
 +      name->hash = novfs_internal_hash(name);
 +
 +      return (0);
 +}
 +
 +static int novfs_d_strcmp(const char *str1, unsigned int len,
 +                        const struct qstr *s2)
 +{
 +      int retCode = 1;
 +      const unsigned char *str2 = s2->name;
 +
 +      DbgPrint("s1=%.*s s2=%.*s", len, str1, s2->len, s2->name);
 +
 +      if (len && (len == s2->len)) {
 +              for (retCode = 0; len--; str1++, str2++) {
 +                      if (*str1 != *str2) {
 +                              if (tolower(*str1) != tolower(*str2)) {
 +                                      retCode = 1;
 +                                      break;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      DbgPrint("retCode=0x%x", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_d_compare(const struct dentry *parent,
 +                  const struct inode *parent_inode,
 +                  const struct dentry *dentry, const struct inode *inode,
 +                  unsigned int len, const char *s1, const struct qstr *s2)
 +{
 +      int retCode;
 +
 +      retCode = novfs_d_strcmp(s1, len, s2);
 +
 +      DbgPrint("retCode=0x%x", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_d_delete(struct dentry *dentry)
 +{
 +      int retVal = 0;
 +
 +      DbgPrint("0x%p %.*s; d_count: %d; d_inode: 0x%p",
 +               dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count, dentry->d_inode);
 +
 +      if (dentry->d_inode && (dentry->d_inode->i_flags & S_DEAD)) {
 +              retVal = 1;
 +      }
 +
 +      dentry->d_time = 0;
 +
 +      return (retVal);
 +}
 +
 +void novfs_d_release(struct dentry *dentry)
 +{
 +      DbgPrint("0x%p %.*s", dentry, dentry->d_name.len, dentry->d_name.name);
 +}
 +
 +void novfs_d_iput(struct dentry *dentry, struct inode *inode)
 +{
 +      DbgPrint("Inode=0x%p Ino=%d Dentry=0x%p i_state=%d Name=%.*s",
 +               inode, inode->i_ino, dentry, inode->i_state, dentry->d_name.len, dentry->d_name.name);
 +
 +      iput(inode);
 +
 +}
 +
 +int novfs_dir_open(struct inode *dir, struct file *file)
 +{
 +      char *path, *buf;
 +      struct file_private *file_private = NULL;
 +
 +      DbgPrint("Inode 0x%p %d Name %.*s", dir, dir->i_ino, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      DbgPrint("path %s", path);
 +              }
 +              kfree(buf);
 +      }
 +
 +      file_private = kmalloc(sizeof(struct file_private), GFP_KERNEL);
 +      file_private->listedall = 0;
 +      file_private->enumHandle = NULL;
 +
 +      file->private_data = file_private;
 +
 +      return (0);
 +}
 +
 +int novfs_dir_release(struct inode *dir, struct file *file)
 +{
 +      struct file_private *file_private = file->private_data;
 +      struct inode *inode = file->f_dentry->d_inode;
 +      struct novfs_schandle sessionId;
 +
 +      DbgPrint("Inode 0x%p %d Name %.*s", dir, dir->i_ino, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +      if (file_private) {
 +              if (file_private->enumHandle && (file_private->enumHandle != ((void *)-1))) {
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      if (SC_PRESENT(sessionId) == 0) {
 +                              ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      }
 +                      novfs_end_directory_enumerate(file_private->enumHandle, sessionId);
 +              }
 +              kfree(file_private);
 +              file->private_data = NULL;
 +      }
 +
 +      return (0);
 +}
 +
 +loff_t novfs_dir_lseek(struct file * file, loff_t offset, int origin)
 +{
 +      struct file_private *file_private = NULL;
 +
 +      DbgPrint("offset %lld %d Name %.*s", offset, origin, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +      //printk("<1> seekdir file = %.*s offset = %i\n", file->f_dentry->d_name.len, file->f_dentry->d_name.name, (int)offset);
 +
 +      if (0 != offset) {
 +              return -ESPIPE;
 +      }
 +
 +      file->f_pos = 0;
 +
 +      file_private = (struct file_private *)file->private_data;
 +      file_private->listedall = 0;
 +      if (file_private->enumHandle && (file_private->enumHandle != ((void *)-1))) {
 +              struct novfs_schandle sessionId;
 +              struct inode *inode = file->f_dentry->d_inode;
 +              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              if (SC_PRESENT(sessionId) == 0) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              }
 +              novfs_end_directory_enumerate(file_private->enumHandle, sessionId);
 +      }
 +      file_private->enumHandle = NULL;
 +
 +      return 0;
 +      //return(default_llseek(file, offset, origin));
 +}
 +
 +ssize_t novfs_dir_read(struct file * file, char *buf, size_t len, loff_t * off)
 +{
 +/*
 +   int rlen = 0;
 +
 +   DbgPrint("dentry path %.*s buf=0x%p len=%d off=%lld", file->f_dentry->d_name.len, file->f_dentry->d_name.name, buf, len, *off);
 +
 +   if (0 == *off)
 +   {
 +      rlen = 8;
 +      rlen -= copy_to_user(buf, "Testing\n", 8);
 +      *off += rlen;
 +   }
 +   return(rlen);
 +*/
 +      DbgPrint("%lld %d Name %.*s", *off, len, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +      return (generic_read_dir(file, buf, len, off));
 +}
 +
 +static void novfs_Dump_Info(struct novfs_entry_info *info)
 +{
 +      char atime_buf[32], mtime_buf[32], ctime_buf[32];
 +      char namebuf[512];
 +      int len = 0;
 +
 +      if (info == NULL) {
 +              DbgPrint("Dump_Info info == NULL");
 +              return;
 +      }
 +
 +      if (info->namelength >= 512) {
 +              len = 511;
 +      } else {
 +              len = info->namelength;
 +      }
 +
 +      memcpy(namebuf, info->name, len);
 +      namebuf[len] = '\0';
 +
 +      ctime_r(&info->atime.tv_sec, atime_buf);
 +      ctime_r(&info->mtime.tv_sec, mtime_buf);
 +      ctime_r(&info->ctime.tv_sec, ctime_buf);
 +      DbgPrint("type = %i", info->type);
 +      DbgPrint("mode = %x", info->mode);
 +      DbgPrint("uid = %d", info->uid);
 +      DbgPrint("gid = %d", info->gid);
 +      DbgPrint("size = %i", info->size);
 +      DbgPrint("atime = %s", atime_buf);
 +      DbgPrint("mtime = %s", mtime_buf);
 +      DbgPrint("ctime = %s", ctime_buf);
 +      DbgPrint("namelength = %i", info->namelength);
 +      DbgPrint("name = %s", namebuf);
 +}
 +
 +void processList(struct file *file, void *dirent, filldir_t filldir, char *list, int type, struct novfs_schandle SessionId)
 +{
 +      unsigned char *path, *buf = NULL, *cp;
 +      struct qstr name;
 +      struct novfs_entry_info *pinfo = NULL;
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      path = buf;
 +      if (buf) {
 +              path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      strcpy(buf, path);
 +              }
 +              path = buf + strlen(buf);
 +              *path++ = '\\';
 +      }
 +
 +      if (list) {
 +              cp = list;
 +              while (*cp) {
 +                      name.name = cp;
 +                      DbgPrint("name.name = %s", name.name);
 +                      name.len = strlen(cp);
 +                      name.hash = novfs_internal_hash(&name);
 +                      cp += (name.len + 1);
 +
 +                      pinfo = kmalloc(sizeof(struct novfs_entry_info) + PATH_LENGTH_BUFFER, GFP_KERNEL);
 +                      pinfo->mode = S_IFDIR | 0700;
 +                      pinfo->size = 0;
 +                      pinfo->atime = pinfo->ctime = pinfo->mtime = CURRENT_TIME;
 +                      strcpy(pinfo->name, name.name);
 +                      pinfo->namelength = name.len;
 +
 +                      novfs_Dump_Info(pinfo);
 +
 +                      filldir(dirent, pinfo->name, pinfo->namelength, file->f_pos, file->f_pos, pinfo->mode >> 12);
 +                      file->f_pos += 1;
 +
 +                      kfree(pinfo);
 +              }
 +      }
 +
 +      if (buf) {
 +              kfree(buf);
 +      }
 +}
 +
 +int processEntries(struct file *file, void *dirent, filldir_t filldir, void **enumHandle, struct novfs_schandle sessionId)
 +{
 +      unsigned char *path = NULL, *buf = NULL;
 +      int count = 0, status = 0;
 +      struct novfs_entry_info *pinfo = NULL;
 +      struct novfs_entry_info *pInfoMem = NULL;
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (!buf) {
 +              return -ENOMEM;
 +      }
 +
 +      path = novfs_dget_path(file->f_dentry, buf, PATH_LENGTH_BUFFER);
 +      if (!path) {
 +              kfree(buf);
 +              return -ENOMEM;
 +      }
 +      //NWSearchfiles
 +      count = 0;
 +      status = novfs_get_dir_listex(path, enumHandle, &count, &pinfo, sessionId);
 +      pInfoMem = pinfo;
 +
 +      if ((count == -1) || (count == 0) || (status != 0)) {
 +              kfree(pInfoMem);
 +              kfree(buf);
 +              return -1;
 +      }
 +      // parse resultset
 +      while (pinfo && count--) {
 +              filldir(dirent, pinfo->name, pinfo->namelength, file->f_pos, file->f_pos, pinfo->mode >> 12);
 +              file->f_pos += 1;
 +
 +              pinfo = (struct novfs_entry_info *)(pinfo->name + pinfo->namelength);
 +      }
 +
 +      kfree(pInfoMem);
 +      kfree(buf);
 +      return 0;
 +}
 +
 +int novfs_dir_readdir(struct file *file, void *dirent, filldir_t filldir)
 +{
 +      unsigned char *list = NULL;
 +      int status = 0;         //-ENOMEM;
 +      struct inode *inode = file->f_dentry->d_inode;
 +      struct novfs_schandle sessionId;
 +      uid_t uid;
 +      int type = 0;
 +      struct file_private *file_private = NULL;
 +      int lComm;
 +
 +      file_private = (struct file_private *)file->private_data;
 +      DbgPrint("Name %.*s", file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +      //printk("<1> file = %.*s\n", file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +// Use this hack by default
 +#ifndef SKIP_CROSSOVER_HACK
 +      // Hack for crossover - begin
 +      mutex_lock(&TimeDir_Lock);
 +      if ((file->f_dentry->d_name.len == 7) &&
 +          ((0 == strncmp(file->f_dentry->d_name.name, " !xover", 7)) ||
 +           (0 == strncmp(file->f_dentry->d_name.name, "z!xover", 7)))) {
 +              //printk("<1> xoverhack: we are in xoverHack\n");
 +
 +              inHAX = 1;
 +              inHAXTime = get_nanosecond_time();
 +              //up( &TimeDir_Lock );
 +              //return 0;
 +              file_private->listedall = 1;
 +      } else {
 +              if (inHAX) {
 +                      if (get_nanosecond_time() - inHAXTime > 100 * 1000 * 1000) {
 +                              //printk("<1> xoverhack: it was long, long, long ago...\n");
 +                              inHAX = 0;
 +                      } else {
 +                              //printk("<1> xoverhack: word gotcha in xoverHack...\n");
 +                              inHAXTime = get_nanosecond_time();
 +                              //up( &TimeDir_Lock );
 +                              //return 0;
 +                              file_private->listedall = 1;
 +                      }
 +              }
 +      }
 +
 +      mutex_unlock(&TimeDir_Lock);
 +      // Hack for crossover - end
 +#endif
 +
 +      if (file->f_pos == 0) {
 +              if (filldir(dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR) < 0)
 +                      return 1;
 +              file->f_pos++;
 +              return 1;
 +      }
 +
 +      if (file->f_pos == 1) {
 +              if (filldir(dirent, "..", 2, file->f_pos, file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
 +                      return 1;
 +              file->f_pos++;
 +              return 1;
 +      }
 +
 +      if (file_private->listedall != 0) {
 +              return 0;
 +      }
 +
 +      inode = file->f_dentry->d_inode;
 +      if (inode && inode->i_private) {
 +              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              if (0 == SC_PRESENT(sessionId)) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              }
 +              uid = novfs_scope_get_uid(((struct inode_data *)inode->i_private)->Scope);
 +      } else {
 +              SC_INITIALIZE(sessionId);
 +              uid = current_euid();
 +      }
 +
 +      if (IS_ROOT(file->f_dentry) ||  // Root
 +          IS_ROOT(file->f_dentry->d_parent) ||        // User
 +          IS_ROOT(file->f_dentry->d_parent->d_parent))        // Server
 +      {
 +              if (IS_ROOT(file->f_dentry)) {
 +                      DbgPrint("Root directory");
 +                      list = novfs_get_scopeusers();
 +                      type = USER_LIST;
 +              } else if (IS_ROOT(file->f_dentry->d_parent)) {
 +                      DbgPrint("Parent is Root directory");
 +                      novfs_get_servers(&list, sessionId);
 +                      type = SERVER_LIST;
 +              } else {
 +                      DbgPrint("Parent-Parent is Root directory");
 +                      novfs_get_vols(&file->f_dentry->d_name, &list, sessionId);
 +                      type = VOLUME_LIST;
 +              }
 +
 +              processList(file, dirent, filldir, list, type, sessionId);
 +              file_private->listedall = 1;
 +      } else {
 +              status = processEntries(file, dirent, filldir, &file_private->enumHandle, sessionId);
 +
 +              if (status != 0) {
 +                      file_private->listedall = 1;
 +#ifndef SKIP_CROSSOVER_HACK
 +                      // Hack for crossover part 2 - begin
 +                      lComm = strlen(current->comm);
 +                      if ((lComm > 4)
 +                          && (0 == strcmp(current->comm + lComm - 4, ".EXE"))) {
 +                              if (filldir(dirent, " !xover", 7, file->f_pos, inode->i_ino, DT_DIR) < 0)
 +                                      return 1;
 +                              if (filldir(dirent, "z!xover", 7, file->f_pos, inode->i_ino, DT_DIR) < 0)
 +                                      return 1;
 +                              file->f_pos += 2;
 +                      }
 +                      // Hack for crossover part2 - end
 +#endif
 +              }
 +      }
 +
 +      file->private_data = file_private;
 +      return 1;
 +}
 +
 +int novfs_dir_fsync(struct file *file, int datasync)
 +{
 +      DbgPrint("Name %.*s", file->f_dentry->d_name.len,
 +               file->f_dentry->d_name.name);
 +      return generic_file_fsync(file, datasync);
 +}
 +
 +ssize_t novfs_f_read(struct file * file, char *buf, size_t len, loff_t * off)
 +{
 +      size_t thisread, totalread = 0;
 +      loff_t offset = *off;
 +      struct inode *inode;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +
 +      if (file->f_dentry && (inode = file->f_dentry->d_inode) && (id = (struct inode_data *)inode->i_private)) {
 +
 +              DbgPrint("(0x%p 0x%p %d %lld %.*s)",
 +                       file->private_data, buf, len, offset, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +              if (novfs_page_cache && !(file->f_flags & O_DIRECT) && id->CacheFlag) {
 +                      totalread = do_sync_read(file, buf, len, off);
 +              } else {
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      while (len > 0 && (offset < i_size_read(inode))) {
 +                              int retval;
 +                              thisread = len;
 +                              retval = novfs_read_file(file->private_data, buf, &thisread, &offset, session);
 +                              if (retval || !thisread) {
 +                                      if (retval) {
 +                                              totalread = retval;
 +                                      }
 +                                      break;
 +                              }
 +                              DbgPrint("thisread = 0x%x", thisread);
 +                              len -= thisread;
 +                              buf += thisread;
 +                              offset += thisread;
 +                              totalread += thisread;
 +                      }
 +                      *off = offset;
 +              }
 +      }
 +      DbgPrint("return = %d", totalread);
 +
 +      return (totalread);
 +}
 +
 +ssize_t novfs_f_write(struct file * file, const char *buf, size_t len, loff_t * off)
 +{
 +      ssize_t thiswrite, totalwrite = 0;
 +      loff_t offset = *off;
 +      struct novfs_schandle session;
 +      struct inode *inode;
 +      int status;
 +      struct inode_data *id;
 +
 +      if (file->f_dentry && (inode = file->f_dentry->d_inode) && (id = file->f_dentry->d_inode->i_private)) {
 +              DbgPrint("(0x%p 0x%p 0x%p %d %lld %.*s)",
 +                       file->private_data, inode, id->FileHandle, len, offset,
 +                       file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +              if (novfs_page_cache && !(file->f_flags & O_DIRECT) && id->CacheFlag && !(file->f_flags & O_WRONLY)) {
 +                      totalwrite = do_sync_write(file, buf, len, off);
 +              } else {
 +                      if (file->f_flags & O_APPEND) {
 +                              offset = i_size_read(inode);
 +                              DbgPrint("appending to end %lld %.*s",
 +                                       offset, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +                      }
 +
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      while (len > 0) {
 +                              thiswrite = len;
 +                              if ((status =
 +                                   novfs_write_file(file->private_data,
 +                                                    (unsigned char *)buf, &thiswrite, &offset, session)) || !thiswrite) {
 +                                      totalwrite = status;
 +                                      break;
 +                              }
 +                              DbgPrint("thiswrite = 0x%x", thiswrite);
 +                              len -= thiswrite;
 +                              buf += thiswrite;
 +                              offset += thiswrite;
 +                              totalwrite += thiswrite;
 +                              if (offset > i_size_read(inode)) {
 +                                      i_size_write(inode, offset);
 +                                      inode->i_blocks = (offset + inode->i_sb->s_blocksize - 1) >> inode->i_blkbits;
 +                              }
 +                              inode->i_mtime = inode->i_atime = CURRENT_TIME;
 +                              id->Flags |= UPDATE_INODE;
 +
 +                      }
 +                      *off = offset;
 +              }
 +      }
 +      DbgPrint("return = 0x%x", totalwrite);
 +
 +      return (totalwrite);
 +}
 +
 +int novfs_f_readdir(struct file *file, void *data, filldir_t fill)
 +{
 +      return -EISDIR;
 +}
 +
 +int novfs_f_mmap(struct file *file, struct vm_area_struct *vma)
 +{
 +      int retCode = -EINVAL;
 +
 +      DbgPrint("file=0x%p %.*s", file, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +      retCode = generic_file_mmap(file, vma);
 +
 +      DbgPrint("retCode=0x%x", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_f_open(struct inode *inode, struct file *file)
 +{
 +      struct novfs_entry_info *info = NULL;
 +      int retCode = -ENOENT;
 +      struct novfs_schandle session;
 +      char *path;
 +      struct dentry *parent;
 +      ino_t ino;
 +      struct inode_data *id;
 +      int errInfo;
 +
 +      DbgPrint("inode=0x%p file=0x%p dentry=0x%p dentry->d_inode=0x%p %.*s",
 +               inode, file, file->f_dentry, file->f_dentry->d_inode, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +      if (file->f_dentry) {
 +              DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o",
 +                       file->f_dentry->d_name.len, file->f_dentry->d_name.name, file->f_flags, file->f_mode, inode->i_mode);
 +      }
 +
 +      if (inode && inode->i_private) {
 +              id = (struct inode_data *)file->f_dentry->d_inode->i_private;
 +              session = novfs_scope_get_sessionId(id->Scope);
 +              if (0 == SC_PRESENT(session)) {
 +                      id->Scope = novfs_get_scope(file->f_dentry);
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +              }
 +
 +              info = kmalloc(sizeof(struct novfs_entry_info) + PATH_LENGTH_BUFFER, GFP_KERNEL);
 +              if (info) {
 +                      path = novfs_dget_path(file->f_dentry, info->name, PATH_LENGTH_BUFFER);
 +                      if (path) {
 +                              if (file->f_flags & O_TRUNC) {
 +                                      errInfo = novfs_get_file_info(path, info, session);
 +
 +                                      if (errInfo || info->size == 0) {
 +                                              // clear O_TRUNC flag, bug #275366
 +                                              file->f_flags = file->f_flags & (~O_TRUNC);
 +                                      }
 +                              }
 +
 +                              DbgPrint("%s", path);
 +                              retCode = novfs_open_file(path, file->f_flags & ~O_EXCL, info, &file->private_data, session);
 +
 +                              DbgPrint("0x%x 0x%p", retCode, file->private_data);
 +                              if (!retCode) {
 +                                      /*
 +                                       *update_inode(inode, &info);
 +                                       */
 +                                      //id->FileHandle = file->private_data;
 +                                      id->CacheFlag = novfs_get_file_cache_flag(path, session);
 +
 +                                      if (!novfs_get_file_info(path, info, session)) {
 +                                              update_inode(inode, info);
 +                                      }
 +
 +                                      parent = dget_parent(file->f_dentry);
 +
 +                                      if (parent && parent->d_inode) {
 +                                              struct inode *dir = parent->d_inode;
 +                                              novfs_lock_inode_cache(dir);
 +                                              ino = 0;
 +                                              if (novfs_get_entry(dir, &file->f_dentry->d_name, &ino, info)) {
 +                                                      ((struct inode_data *)inode->i_private)->Flags |= UPDATE_INODE;
 +                                              }
 +
 +                                              novfs_unlock_inode_cache(dir);
 +                                      }
 +                                      dput(parent);
 +                              }
 +                      }
 +                      kfree(info);
 +              }
 +      }
 +      DbgPrint("retCode=0x%x", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_flush_mapping(void *Handle, struct address_space *mapping, struct novfs_schandle Session)
 +{
 +      struct pagevec pagevec;
 +      unsigned nrpages;
 +      pgoff_t index = 0;
 +      int done, rc = 0;
 +
 +      pagevec_init(&pagevec, 0);
 +
 +      do {
 +              done = 1;
 +              nrpages = pagevec_lookup_tag(&pagevec, mapping, &index, PAGECACHE_TAG_DIRTY, PAGEVEC_SIZE);
 +
 +              if (nrpages) {
 +                      struct page *page;
 +                      int i;
 +
 +                      DbgPrint("%u", nrpages);
 +
 +                      done = 0;
 +                      for (i = 0; !rc && (i < nrpages); i++) {
 +                              page = pagevec.pages[i];
 +
 +                              DbgPrint("page 0x%p %lu", page, page->index);
 +
 +                              lock_page(page);
 +                              page_cache_get(page);
 +                              if (page->mapping == mapping) {
 +                                      if (clear_page_dirty_for_io(page)) {
 +                                              rc = novfs_write_page(Handle, page, Session);
 +                                              if (!rc) {
 +                                                      //ClearPageDirty(page);
 +                                                      radix_tree_tag_clear
 +                                                          (&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY);
 +                                              }
 +                                      }
 +                              }
 +
 +                              page_cache_release(page);
 +                              unlock_page(page);
 +                      }
 +                      pagevec_release(&pagevec);
 +              }
 +      } while (!rc && !done);
 +
 +      DbgPrint("return %d", rc);
 +
 +      return (rc);
 +}
 +
 +int novfs_f_flush(struct file *file, fl_owner_t ownid)
 +{
 +
 +      int rc = 0;
 +#ifdef FLUSH
 +      struct inode *inode;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +
 +      DbgPrint("Called from 0x%p", __builtin_return_address(0));
 +      if (file->f_dentry && (inode = file->f_dentry->d_inode)
 +          && (id = file->f_dentry->d_inode->i_private)) {
 +
 +              if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
 +                      inode = file->f_dentry->d_inode;
 +                      DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o",
 +                               file->f_dentry->d_name.len,
 +                               file->f_dentry->d_name.name, file->f_flags, file->f_mode, inode->i_mode);
 +
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      if (inode && inode->i_mapping && inode->i_mapping->nrpages) {
 +
 +                              DbgPrint("%.*s pages=%lu",
 +                                       file->f_dentry->d_name.len, file->f_dentry->d_name.name, inode->i_mapping->nrpages);
 +
 +                              if (file->f_dentry &&
 +                                  file->f_dentry->d_inode &&
 +                                  file->f_dentry->d_inode->i_mapping &&
 +                                  file->f_dentry->d_inode->i_mapping->a_ops &&
 +                                  file->f_dentry->d_inode->i_mapping->a_ops->writepage) {
 +                                      rc = filemap_fdatawrite(file->f_dentry->d_inode->i_mapping);
 +                              } else {
 +                                      rc = novfs_flush_mapping(file->private_data, file->f_dentry->d_inode->i_mapping, session);
 +                              }
 +                      }
 +              }
 +      }
 +#endif
 +      return (rc);
 +}
 +
 +int novfs_f_release(struct inode *inode, struct file *file)
 +{
 +      int retCode = -EACCES;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +
 +      DbgPrint("path=%.*s handle=%p", file->f_dentry->d_name.len, file->f_dentry->d_name.name, file->private_data);
 +
 +      if (inode && (id = inode->i_private)) {
 +              session = novfs_scope_get_sessionId(id->Scope);
 +              if (0 == SC_PRESENT(session)) {
 +                      id->Scope = novfs_get_scope(file->f_dentry);
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +              }
 +
 +              if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
 +                      DbgPrint("%.*s f_flags=0%o f_mode=0%o i_mode=0%o",
 +                               file->f_dentry->d_name.len,
 +                               file->f_dentry->d_name.name, file->f_flags, file->f_mode, inode->i_mode);
 +
 +                      if (inode->i_mapping && inode->i_mapping->nrpages) {
 +
 +                              DbgPrint("%.*s pages=%lu",
 +                                       file->f_dentry->d_name.len, file->f_dentry->d_name.name, inode->i_mapping->nrpages);
 +
 +                              if (inode->i_mapping->a_ops && inode->i_mapping->a_ops->writepage) {
 +                                      filemap_fdatawrite(file->f_dentry->d_inode->i_mapping);
 +                              } else {
 +                                      novfs_flush_mapping(file->private_data, file->f_dentry->d_inode->i_mapping, session);
 +                              }
 +                      }
 +              }
 +
 +              if (file->f_dentry && file->f_dentry->d_inode) {
 +                      invalidate_remote_inode(file->f_dentry->d_inode);
 +              }
 +
 +              retCode = novfs_close_file(file->private_data, session);
 +              //id->FileHandle = 0;
 +      }
 +      return (retCode);
 +}
 +
 +int novfs_f_fsync(struct file *file, int datasync)
 +{
 +      return 0;
 +}
 +
 +int novfs_f_llseek(struct file *file, loff_t offset, int origin)
 +{
 +      DbgPrint("File=0x%p Name=%.*s offset=%lld origin=%d",
 +               file, file->f_dentry->d_name.len, file->f_dentry->d_name.name, offset, origin);
 +      return (generic_file_llseek(file, offset, origin));
 +}
 +
 +/*++======================================================================*/
 +int novfs_f_lock(struct file *file, int cmd, struct file_lock *lock)
 +/*
 + *  Arguments:
 + *      "file" - pointer to file structure - contains file handle in "file->private_data"
 + *
 + *      "cmd" could be F_SETLK, F_SETLKW, F_GETLK
 + *      F_SETLK/F_SETLKW are for setting/unsetting file lock
 + *      F_GETLK is for getting infomation about region - is it locked, or not
 + *
 + *      "lock" structure - contains "start" and "end" of locking region
 + *
 + *  Returns:
 + *      0 on success
 + *      -ENOSYS on F_GETLK cmd. It's not implemented.
 + *      -EINVAL if (lock->fl_start > lock->fl_end)
 + *      -EAGAIN on all other errors
 + *  Abstract:
 + *
 + *  Notes:
 + *      "lock->fl_start" and "lock->fl_end" are of type "long long",
 + *      but xtier functions in novfsd "NCFsdLockFile" and "NCFsdUnlockFile"
 + *      receive arguments in u64 type.
 + *
 + *
 + *========================================================================*/
 +{
 +      int err_code;
 +
 +      struct inode *inode;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +      loff_t len;
 +
 +      DbgPrint("(0x%p): begin in novfs_f_lock 0x%p", __builtin_return_address(0), file->private_data);
 +      DbgPrint("cmd = %d, F_GETLK = %d, F_SETLK = %d, F_SETLKW = %d", cmd, F_GETLK, F_SETLK, F_SETLKW);
 +      DbgPrint("lock->fl_start = 0x%llX, lock->fl_end = 0x%llX", lock->fl_start, lock->fl_end);
 +
 +      err_code = -1;
 +      if (lock->fl_start <= lock->fl_end) {
 +              /* Get len from "start" and "end" */
 +              len = lock->fl_end - lock->fl_start + 1;
 +              if ((0 == lock->fl_start) && (OFFSET_MAX == lock->fl_end)) {
 +                      len = 0;
 +              }
 +
 +              if (file->f_dentry && (inode = file->f_dentry->d_inode) && (id = (struct inode_data *)inode->i_private)) {
 +                      DbgPrint("(0x%p 0x%p %.*s)",
 +                               file->private_data, inode, file->f_dentry->d_name.len, file->f_dentry->d_name.name);
 +
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      /* fl_type = F_RDLCK, F_WRLCK, F_UNLCK */
 +                      switch (cmd) {
 +                      case F_SETLK:
 +#ifdef F_GETLK64
 +                      case F_SETLK64:
 +#endif
 +
 +                              err_code = novfs_set_file_lock(session, file->private_data, lock->fl_type, lock->fl_start, len);
 +                              break;
 +
 +                      case F_SETLKW:
 +#ifdef F_GETLK64
 +                      case F_SETLKW64:
 +#endif
 +                              err_code = novfs_set_file_lock(session, file->private_data, lock->fl_type, lock->fl_start, len);
 +                              break;
 +
 +                      case F_GETLK:
 +#ifdef F_GETLK64
 +                      case F_GETLK64:
 +#endif
 +                              err_code = -ENOSYS;
 +                              /*
 +                               * Not implemented. We doesn't have appropriate xtier function.
 +                               * */
 +                              break;
 +
 +                      default:
 +                              printk("<1> novfs in novfs_f_lock, not implemented cmd = %d\n", cmd);
 +                              DbgPrint("novfs in novfs_f_lock, not implemented cmd = %d", cmd);
 +                              break;
 +                      }
 +              }
 +
 +              DbgPrint("lock->fl_type = %u, err_code 0x%X", lock->fl_type, err_code);
 +
 +              if ((err_code != 0) && (err_code != -1)
 +                  && (err_code != -ENOSYS)) {
 +                      err_code = -EAGAIN;
 +              }
 +      } else {
 +              err_code = -EINVAL;
 +      }
 +
 +      return (err_code);
 +}
 +
 +/*++======================================================================*/
 +static void novfs_copy_cache_pages(struct address_space *mapping,
 +                                 struct list_head *pages, int bytes_read, char *data, struct pagevec *plru_pvec)
 +{
 +      struct page *page;
 +      char *target;
 +
 +      while (bytes_read > 0) {
 +              if (list_empty(pages))
 +                      break;
 +
 +              page = list_entry(pages->prev, struct page, lru);
 +              list_del(&page->lru);
 +
 +              if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) {
 +                      page_cache_release(page);
 +                      data += PAGE_CACHE_SIZE;
 +                      bytes_read -= PAGE_CACHE_SIZE;
 +                      continue;
 +              }
 +
 +              target = kmap_atomic(page, KM_USER0);
 +
 +              if (PAGE_CACHE_SIZE > bytes_read) {
 +                      memcpy(target, data, bytes_read);
 +                      /* zero the tail end of this partial page */
 +                      memset(target + bytes_read, 0, PAGE_CACHE_SIZE - bytes_read);
 +                      bytes_read = 0;
 +              } else {
 +                      memcpy(target, data, PAGE_CACHE_SIZE);
 +                      bytes_read -= PAGE_CACHE_SIZE;
 +              }
 +              kunmap_atomic(target, KM_USER0);
 +
 +              flush_dcache_page(page);
 +              SetPageUptodate(page);
 +              unlock_page(page);
 +              if (!pagevec_add(plru_pvec, page))
 +                      __pagevec_lru_add_file(plru_pvec);
 +              data += PAGE_CACHE_SIZE;
 +      }
 +      return;
 +}
 +
 +int novfs_a_writepage(struct page *page, struct writeback_control *wbc)
 +{
 +      int retCode = -EFAULT;
 +      struct inode *inode = page->mapping->host;
 +      struct inode_data *id = inode->i_private;
 +      loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT);
 +      struct novfs_schandle session;
 +      struct novfs_data_list dlst[2];
 +      size_t len = PAGE_CACHE_SIZE;
 +
 +      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +
 +      page_cache_get(page);
 +
 +      pos = ((loff_t) page->index << PAGE_CACHE_SHIFT);
 +
 +      /*
 +       * Leave first dlst entry for reply header.
 +       */
 +      dlst[1].page = page;
 +      dlst[1].offset = NULL;
 +      dlst[1].len = len;
 +      dlst[1].rwflag = DLREAD;
 +
 +      /*
 +       * Check size so we don't write pass end of file.
 +       */
 +      if ((pos + (loff_t) len) > i_size_read(inode)) {
 +              len = (size_t) (i_size_read(inode) - pos);
 +      }
 +
 +      retCode = novfs_write_pages(id->FileHandle, dlst, 2, len, pos, session);
 +      if (!retCode) {
 +              SetPageUptodate(page);
 +      }
 +
 +      unlock_page(page);
 +      page_cache_release(page);
 +
 +      return (retCode);
 +}
 +
 +int novfs_a_writepages(struct address_space *mapping, struct writeback_control *wbc)
 +{
 +      int retCode = 0;
 +      struct inode *inode = mapping->host;
 +      struct novfs_schandle session;
 +      void *fh = NULL;
 +      struct inode_data *id = NULL;
 +
 +      int max_page_lookup = novfs_max_iosize / PAGE_CACHE_SIZE;
 +
 +      struct novfs_data_list *dlist, *dlptr;
 +      struct page **pages;
 +
 +      int dlist_idx, i = 0;
 +      pgoff_t index, next_index = 0;
 +      loff_t pos = 0;
 +      size_t tsize;
 +
 +      SC_INITIALIZE(session);
 +      DbgPrint("inode=0x%p mapping=0x%p wbc=0x%p nr_to_write=%d", inode, mapping, wbc, wbc->nr_to_write);
 +
 +      if (inode) {
 +              DbgPrint("Inode=0x%p Ino=%d Id=0x%p", inode, inode->i_ino, inode->i_private);
 +
 +              if (NULL != (id = inode->i_private)) {
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      fh = ((struct inode_data *)inode->i_private)->FileHandle;
 +              }
 +      }
 +
 +      dlist = kmalloc(sizeof(struct novfs_data_list) * max_page_lookup, GFP_KERNEL);
 +      pages = kmalloc(sizeof(struct page *) * max_page_lookup, GFP_KERNEL);
 +
 +      if (id)
 +              DbgPrint("inode=0x%p fh=0x%p dlist=0x%p pages=0x%p %s", inode, fh, dlist, pages, id->Name);
 +      else
 +              DbgPrint("inode=0x%p fh=0x%p dlist=0x%p pages=0x%p", inode, fh, dlist, pages);
 +
 +      if (dlist && pages) {
 +              struct backing_dev_info *bdi = mapping->backing_dev_info;
 +              int done = 0;
 +              int nr_pages = 0;
 +              int scanned = 0;
 +
 +              if (wbc->nonblocking && bdi_write_congested(bdi)) {
 +                      wbc->encountered_congestion = 1;
 +                      return 0;
 +              }
 +
 +              if (wbc->sync_mode == WB_SYNC_NONE) {
 +                      index = mapping->writeback_index;       /* Start from prev offset */
 +              } else {
 +                      index = 0;      /* whole-file sweep */
 +                      scanned = 1;
 +              }
 +
 +              next_index = index;
 +
 +              while (!done && (wbc->nr_to_write > 0)) {
 +                      dlist_idx = 0;
 +                      dlptr = &dlist[1];
 +
 +                      DbgPrint("nr_pages=%d", nr_pages);
 +                      if (!nr_pages) {
 +                              memset(pages, 0, sizeof(struct page *) * max_page_lookup);
 +
 +                              spin_lock_irq(&mapping->tree_lock);
 +
 +                              /*
 +                               * Need to ask for one less then max_page_lookup or we
 +                               * will overflow the request buffer.  This also frees
 +                               * the first entry for the reply buffer.
 +                               */
 +                              nr_pages =
 +                                  radix_tree_gang_lookup_tag(&mapping->page_tree,
 +                                                             (void **)pages, index, max_page_lookup - 1, PAGECACHE_TAG_DIRTY);
 +
 +                              DbgPrint("2; nr_pages=%d\n", nr_pages);
 +                              /*
 +                               * Check to see if there are dirty pages and there is a valid
 +                               * file handle.
 +                               */
 +                              if (nr_pages && !fh) {
 +                                      set_bit(AS_EIO, &mapping->flags);
 +                                      done = 1;
 +                                      DbgPrint("set_bit AS_EIO");
 +                                      break;
 +                              }
 +
 +                              for (i = 0; i < nr_pages; i++) {
 +                                      page_cache_get(pages[i]);
 +                              }
 +
 +                              spin_unlock_irq(&mapping->tree_lock);
 +
 +                              if (nr_pages) {
 +                                      index = pages[nr_pages - 1]->index + 1;
 +                                      pos = (loff_t) pages[0]->index << PAGE_CACHE_SHIFT;
 +                              }
 +
 +                              if (!nr_pages) {
 +                                      if (scanned) {
 +                                              index = 0;
 +                                              scanned = 0;
 +                                              continue;
 +                                      }
 +                                      done = 1;
 +                              } else {
 +                                      next_index = pages[0]->index;
 +                                      i = 0;
 +                              }
 +                      } else {
 +                              if (pages[i]) {
 +                                      pos = (loff_t) pages[i]->index << PAGE_CACHE_SHIFT;
 +                              }
 +                      }
 +
 +                      for (; i < nr_pages; i++) {
 +                              struct page *page = pages[i];
 +
 +                              /*
 +                               * At this point we hold neither mapping->tree_lock nor
 +                               * lock on the page itself: the page may be truncated or
 +                               * invalidated (changing page->mapping to NULL), or even
 +                               * swizzled back from swapper_space to tmpfs file
 +                               * mapping
 +                               */
 +
 +                              DbgPrint
 +                                  ("novfs_a_writepages: pos=0x%llx index=%d page->index=%d next_index=%d\n",
 +                                   pos, index, page->index, next_index);
 +
 +                              if (page->index != next_index) {
 +                                      next_index = page->index;
 +                                      break;
 +                              }
 +                              next_index = page->index + 1;
 +
 +                              lock_page(page);
 +
 +                              if (wbc->sync_mode != WB_SYNC_NONE)
 +                                      wait_on_page_writeback(page);
 +
 +                              if (page->mapping != mapping || PageWriteback(page)
 +                                  || !clear_page_dirty_for_io(page)) {
 +                                      unlock_page(page);
 +                                      continue;
 +                              }
 +
 +                              dlptr[dlist_idx].page = page;
 +                              dlptr[dlist_idx].offset = NULL;
 +                              dlptr[dlist_idx].len = PAGE_CACHE_SIZE;
 +                              dlptr[dlist_idx].rwflag = DLREAD;
 +                              dlist_idx++;
 +                              DbgPrint("Add page=0x%p index=0x%lx", page, page->index);
 +                      }
 +
 +                      DbgPrint("dlist_idx=%d", dlist_idx);
 +                      if (dlist_idx) {
 +                              tsize = dlist_idx * PAGE_CACHE_SIZE;
 +                              /*
 +                               * Check size so we don't write pass end of file.
 +                               */
 +                              if ((pos + tsize) > i_size_read(inode)) {
 +                                      tsize = (size_t) (i_size_read(inode) - pos);
 +                              }
 +
 +                              retCode = novfs_write_pages(fh, dlist, dlist_idx + 1, tsize, pos, session);
 +                              switch (retCode) {
 +                              case 0:
 +                                      wbc->nr_to_write -= dlist_idx;
 +                                      break;
 +
 +                              case -ENOSPC:
 +                                      set_bit(AS_ENOSPC, &mapping->flags);
 +                                      done = 1;
 +                                      break;
 +
 +                              default:
 +                                      set_bit(AS_EIO, &mapping->flags);
 +                                      done = 1;
 +                                      break;
 +                              }
 +
 +                              do {
 +                                      unlock_page((struct page *)
 +                                                  dlptr[dlist_idx - 1].page);
 +                                      page_cache_release((struct page *)
 +                                                         dlptr[dlist_idx - 1].page);
 +                                      DbgPrint("release page=0x%p index=0x%lx", dlptr[dlist_idx - 1].page, ((struct page *)
 +                                                                                                            dlptr[dlist_idx -
 +                                                                                                                  1].page)->
 +                                               index);
 +                                      if (!retCode) {
 +                                              wbc->nr_to_write--;
 +                                      }
 +                              } while (--dlist_idx);
 +                      }
 +
 +                      if (i >= nr_pages) {
 +                              nr_pages = 0;
 +                      }
 +              }
 +
 +              mapping->writeback_index = index;
 +
 +      } else {
 +              DbgPrint("set_bit AS_EIO");
 +              set_bit(AS_EIO, &mapping->flags);
 +      }
 +      if (dlist)
 +              kfree(dlist);
 +      if (pages)
 +              kfree(pages);
 +
 +      DbgPrint("retCode=%d", retCode);
 +      return (0);
 +
 +}
 +
 +int novfs_a_readpage(struct file *file, struct page *page)
 +{
 +      int retCode = 0;
 +      void *pbuf;
 +      struct inode *inode = NULL;
 +      struct dentry *dentry = NULL;
 +      loff_t offset;
 +      size_t len;
 +      struct novfs_schandle session;
 +
 +      SC_INITIALIZE(session);
 +      DbgPrint("File=0x%p Name=%.*s Page=0x%p", file, file->f_dentry->d_name.len, file->f_dentry->d_name.name, page);
 +
 +      dentry = file->f_dentry;
 +
 +      if (dentry) {
 +              DbgPrint("Dentry=0x%p Name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name);
 +              if (dentry->d_inode) {
 +                      inode = dentry->d_inode;
 +              }
 +      }
 +
 +      if (inode) {
 +              DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino);
 +
 +              if (inode->i_private) {
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      }
 +              }
 +      }
 +
 +      if (!PageUptodate(page)) {
 +              struct novfs_data_list dlst[2];
 +
 +              offset = page->index << PAGE_CACHE_SHIFT;
 +              len = PAGE_CACHE_SIZE;
 +
 +              /*
 +               * Save the first entry for the reply header.
 +               */
 +              dlst[1].page = page;
 +              dlst[1].offset = NULL;
 +              dlst[1].len = PAGE_CACHE_SIZE;
 +              dlst[1].rwflag = DLWRITE;
 +
 +              DbgPrint("calling= novfs_Read_Pages %lld", offset);
 +              retCode = novfs_read_pages(file->private_data, dlst, 2, &len, &offset, session);
 +              if (len && (len < PAGE_CACHE_SIZE)) {
 +                      pbuf = kmap_atomic(page, KM_USER0);
 +                      memset(&((char *)pbuf)[len], 0, PAGE_CACHE_SIZE - len);
 +                      kunmap_atomic(pbuf, KM_USER0);
 +              }
 +
 +              flush_dcache_page(page);
 +              SetPageUptodate(page);
 +      }
 +      unlock_page(page);
 +
 +      DbgPrint("retCode=%d", retCode);
 +      return (retCode);
 +
 +}
 +
 +int novfs_a_readpages(struct file *file, struct address_space *mapping, struct list_head *page_lst, unsigned nr_pages)
 +{
 +      int retCode = 0;
 +      struct inode *inode = NULL;
 +      struct dentry *dentry = NULL;
 +      struct novfs_schandle session;
 +      loff_t offset;
 +      size_t len;
 +
 +      unsigned page_idx;
 +      struct pagevec lru_pvec;
 +      pgoff_t next_index;
 +
 +      char *rbuf, done = 0;
 +      SC_INITIALIZE(session);
 +
 +      DbgPrint("File=0x%p Name=%.*s Pages=%d", file, file->f_dentry->d_name.len, file->f_dentry->d_name.name, nr_pages);
 +
 +      dentry = file->f_dentry;
 +
 +      if (dentry) {
 +              DbgPrint("Dentry=0x%p Name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name);
 +              if (dentry->d_inode) {
 +                      inode = dentry->d_inode;
 +              }
 +      }
 +
 +      if (inode) {
 +              DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino);
 +
 +              if (inode->i_private) {
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                              session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      }
 +              }
 +      }
 +
 +      rbuf = kmalloc(novfs_max_iosize, GFP_KERNEL);
 +      if (rbuf) {
 +              pagevec_init(&lru_pvec, 0);
 +              for (page_idx = 0; page_idx < nr_pages && !done;) {
 +                      struct page *page, *tpage;
 +
 +                      if (list_empty(page_lst))
 +                              break;
 +
 +                      page = list_entry(page_lst->prev, struct page, lru);
 +
 +                      next_index = page->index;
 +                      offset = (loff_t) page->index << PAGE_CACHE_SHIFT;
 +                      len = 0;
 +
 +                      /*
 +                       * Count number of contiguous pages.
 +                       */
 +                      list_for_each_entry_reverse(tpage, page_lst, lru) {
 +                              if ((next_index != tpage->index) || (len >= novfs_max_iosize - PAGE_SIZE)) {
 +                                      break;
 +                              }
 +                              len += PAGE_SIZE;
 +                              next_index++;
 +                      }
 +
 +                      if (len && !done) {
 +                              struct novfs_data_list dllst[2];
 +
 +                              dllst[1].page = NULL;
 +                              dllst[1].offset = rbuf;
 +                              dllst[1].len = len;
 +                              dllst[1].rwflag = DLWRITE;
 +
 +                              DbgPrint("calling novfs_Read_Pages %lld", offset);
 +                              if (!novfs_read_pages(file->private_data, dllst, 2, &len, &offset, session)) {
 +                                      novfs_copy_cache_pages(mapping, page_lst, len, rbuf, &lru_pvec);
 +                                      page_idx += len >> PAGE_CACHE_SHIFT;
 +                                      if ((int)(len & PAGE_CACHE_MASK) != len) {
 +                                              page_idx++;
 +                                      }
 +                                      if (len == 0) {
 +                                              done = 1;
 +                                      }
 +                              } else {
 +                                      done = 1;
 +                              }
 +                      }
 +              }
 +
 +              /*
 +               * Free any remaining pages.
 +               */
 +              while (!list_empty(page_lst)) {
 +                      struct page *page = list_entry(page_lst->prev, struct page, lru);
 +
 +                      list_del(&page->lru);
 +                      page_cache_release(page);
 +              }
 +
 +              pagevec_lru_add_file(&lru_pvec);
 +              kfree(rbuf);
 +      } else {
 +              retCode = -ENOMEM;
 +      }
 +
 +      DbgPrint("retCode=%d", retCode);
 +      return (retCode);
 +
 +}
 +
 +int novfs_a_write_begin(struct file *file, struct address_space *mapping,
 +                      loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata)
 +{
 +      int retVal = 0;
 +      loff_t offset = pos;
 +      struct novfs_schandle session;
 +      struct novfs_data_list dllst[2];
 +      struct inode *inode = file->f_dentry->d_inode;
 +      struct page *page;
 +      pgoff_t index;
 +      unsigned from, to;
 +      SC_INITIALIZE(session);
 +
 +      index = pos >> PAGE_CACHE_SHIFT;
 +      from = pos & (PAGE_CACHE_SIZE - 1);
 +      to = from + len;
 +
 +      page = grab_cache_page_write_begin(mapping, index, flags);
 +      if (!page)
 +              return -ENOMEM;
 +
 +      *pagep = page;
 +
 +      DbgPrint("File=0x%p Page=0x%p offset=0x%llx From=%u To=%u "
 +               "filesize=%lld\n", file, page, offset, from, to, i_size_read(file->f_dentry->d_inode));
 +      if (!PageUptodate(page)) {
 +              /*
 +               * Check to see if whole page
 +               */
 +              if ((to == PAGE_CACHE_SIZE) && (from == 0)) {
 +                      SetPageUptodate(page);
 +              }
 +
 +              /*
 +               * Check to see if we can read page.
 +               */
 +              else if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
 +                      /*
 +                       * Get session.
 +                       */
 +                      if (file->f_dentry && file->f_dentry->d_inode) {
 +                              if (file->f_dentry->d_inode->i_private) {
 +                                      session = novfs_scope_get_sessionId(((struct inode_data *)
 +                                                                           inode->i_private)->Scope);
 +                                      if (0 == SC_PRESENT(session)) {
 +                                              ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(file->f_dentry);
 +                                              session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                                      }
 +                              }
 +                      }
 +
 +                      page_cache_get(page);
 +
 +                      len = i_size_read(inode) - offset;
 +                      if (len > PAGE_CACHE_SIZE) {
 +                              len = PAGE_CACHE_SIZE;
 +                      }
 +
 +                      if (len) {
 +                              /*
 +                               * Read page from server.
 +                               */
 +
 +                              dllst[1].page = page;
 +                              dllst[1].offset = 0;
 +                              dllst[1].len = len;
 +                              dllst[1].rwflag = DLWRITE;
 +
 +                              DbgPrint("calling novfs_Read_Pages %lld", offset);
 +                              novfs_read_pages(file->private_data, dllst, 2, &len, &offset, session);
 +
 +                              /*
 +                               * Zero unnsed page.
 +                               */
 +                      }
 +
 +                      if (len < PAGE_CACHE_SIZE) {
 +                              char *adr = kmap_atomic(page, KM_USER0);
 +                              memset(adr + len, 0, PAGE_CACHE_SIZE - len);
 +                              kunmap_atomic(adr, KM_USER0);
 +                      }
 +              } else {
 +                      /*
 +                       * Zero section of memory that not going to be used.
 +                       */
 +                      char *adr = kmap_atomic(page, KM_USER0);
 +                      memset(adr, 0, from);
 +                      memset(adr + to, 0, PAGE_CACHE_SIZE - to);
 +                      kunmap_atomic(adr, KM_USER0);
 +
 +                      DbgPrint("memset 0x%p", adr);
 +              }
 +              flush_dcache_page(page);
 +              SetPageUptodate(page);
 +      }
 +//   DbgPrint("return %d", retVal);
 +      return (retVal);
 +}
 +
 +int novfs_a_write_end(struct file *file, struct address_space *mapping,
 +                    loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata)
 +{
 +      int retCode = 0;
 +      struct inode *inode = page->mapping->host;
 +      loff_t offset = pos;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +      struct novfs_data_list dlst[1];
 +      pgoff_t index;
 +      unsigned from, to;
 +      SC_INITIALIZE(session);
 +
 +      index = pos >> PAGE_CACHE_SHIFT;
 +      from = pos & (PAGE_CACHE_SIZE - 1);
 +      to = from + len;
 +
 +      DbgPrint("File=0x%p Page=0x%p offset=0x%x To=%u filesize=%lld",
 +               file, page, offset, to, i_size_read(file->f_dentry->d_inode));
 +      if (file->f_dentry->d_inode && (id = file->f_dentry->d_inode->i_private)) {
 +              session = novfs_scope_get_sessionId(id->Scope);
 +              if (0 == SC_PRESENT(session)) {
 +                      id->Scope = novfs_get_scope(file->f_dentry);
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +              }
 +
 +              /*
 +               * Setup file handle
 +               */
 +              id->FileHandle = file->private_data;
 +
 +              if (pos > inode->i_size) {
 +                      i_size_write(inode, pos);
 +              }
 +
 +              if (!PageUptodate(page)) {
 +                      pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset;
 +
 +                      if (to < offset) {
 +                              return (retCode);
 +                      }
 +                      dlst[0].page = page;
 +                      dlst[0].offset = (void *)(unsigned long)offset;
 +                      dlst[0].len = len;
 +                      dlst[0].rwflag = DLREAD;
 +
 +                      retCode = novfs_write_pages(id->FileHandle, dlst, 1, len, pos, session);
 +
 +              } else {
 +                      set_page_dirty(page);
 +              }
 +      }
 +
 +      return (retCode);
 +}
 +
 +/*++======================================================================*/
 +ssize_t novfs_a_direct_IO(int rw, struct kiocb * kiocb, const struct iovec * iov, loff_t offset, unsigned long nr_segs)
 +/*
 + *
 + *  Notes:        This is a dummy function so that we can allow a file
 + *                to get the direct IO flag set.  novfs_f_read and
 + *                novfs_f_write will do the work.  Maybe not the best
 + *                way to do but it was the easiest to implement.
 + *
 + *========================================================================*/
 +{
 +      return (-EIO);
 +}
 +
 +/*++======================================================================*/
 +int novfs_i_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
 +{
 +      char *path, *buf;
 +      struct novfs_entry_info info;
 +      void *handle;
 +      struct novfs_schandle session;
 +      int retCode = -EACCES;
 +
 +      DbgPrint("mode=0%o flags=0%o %.*s", mode, nd->NDOPENFLAGS, dentry->d_name.len, dentry->d_name.name);
 +
 +      if (IS_ROOT(dentry) ||  /* Root */
 +          IS_ROOT(dentry->d_parent) ||        /* User */
 +          IS_ROOT(dentry->d_parent->d_parent) ||      /* Server */
 +          IS_ROOT(dentry->d_parent->d_parent->d_parent)) {    /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      if (mode | S_IFREG) {
 +              if (dir->i_private) {
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)dir->i_private)->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              ((struct inode_data *)dir->i_private)->Scope = novfs_get_scope(dentry);
 +                              session = novfs_scope_get_sessionId(((struct inode_data *)dir->i_private)->Scope);
 +                      }
 +
 +                      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +                      if (buf) {
 +                              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +                              if (path) {
 +                                      retCode = novfs_open_file(path, nd->NDOPENFLAGS | O_RDWR, &info, &handle, session);
 +                                      if (!retCode && handle) {
 +                                              novfs_close_file(handle, session);
 +                                              if (!novfs_i_mknod(dir, dentry, mode | S_IFREG, 0)) {
 +                                                      if (dentry->d_inode) {
 +                                                              ((struct inode_data *)
 +                                                               dentry->d_inode->i_private)->Flags |= UPDATE_INODE;
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                              kfree(buf);
 +                      }
 +              }
 +      }
 +      return (retCode);
 +}
 +
 +void update_inode(struct inode *Inode, struct novfs_entry_info *Info)
 +{
 +      static char dbuf[128];
 +
 +      DbgPrint("Inode=0x%p I_ino=%d", Inode, Inode->i_ino);
 +
 +      DbgPrint("atime=%s", ctime_r(&Info->atime.tv_sec, dbuf));
 +      DbgPrint("ctime=%s", ctime_r(&Info->ctime.tv_sec, dbuf));
 +      DbgPrint("mtime=%s %d", ctime_r(&Info->mtime.tv_sec, dbuf), Info->mtime.tv_nsec);
 +      DbgPrint("size=%lld", Info->size);
 +      DbgPrint("mode=0%o", Info->mode);
 +
 +      if (Inode &&
 +          ((Inode->i_size != Info->size) ||
 +           (Inode->i_mtime.tv_sec != Info->mtime.tv_sec) || (Inode->i_mtime.tv_nsec != Info->mtime.tv_nsec))) {
 +              DbgPrint("calling invalidate_remote_inode sz  %d %d", Inode->i_size, Info->size);
 +              DbgPrint("calling invalidate_remote_inode sec %d %d", Inode->i_mtime.tv_sec, Info->mtime.tv_sec);
 +              DbgPrint("calling invalidate_remote_inode ns  %d %d", Inode->i_mtime.tv_nsec, Info->mtime.tv_nsec);
 +
 +              if (Inode && Inode->i_mapping) {
 +                      invalidate_remote_inode(Inode);
 +              }
 +      }
 +
 +      Inode->i_mode = Info->mode;
 +      Inode->i_size = Info->size;
 +      Inode->i_atime = Info->atime;
 +      Inode->i_ctime = Info->ctime;
 +      Inode->i_mtime = Info->mtime;
 +
 +      if (Inode->i_size && Inode->i_sb->s_blocksize) {
 +
 +              /*
 +               * Filling number of blocks as in NSS filesystem.
 +               * The s_blocksize is initialized to PAGE_CACHE_SIZE in
 +               * the super block initialization.
 +               *
 +               * Update i_blocks to have the number of 512 blocks
 +               */
 +              Inode->i_blocks = (((loff_t) Info->size) + Inode->i_sb->s_blocksize - 1)
 +                  >> (loff_t) Inode->i_blkbits;
 +              Inode->i_blocks = Inode->i_blocks << (PAGE_CACHE_SHIFT - 9);
 +              Inode->i_bytes = Info->size & (Inode->i_sb->s_blocksize - 1);
 +
 +              DbgPrint("i_sb->s_blocksize=%d", Inode->i_sb->s_blocksize);
 +              DbgPrint("i_blkbits=%d", Inode->i_blkbits);
 +              DbgPrint("i_blocks=%d", Inode->i_blocks);
 +              DbgPrint("i_bytes=%d", Inode->i_bytes);
 +      }
 +}
 +
 +struct dentry *novfs_i_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 +{
 +      struct dentry *retVal = ERR_PTR(-ENOENT);
 +      struct dentry *parent;
 +      struct novfs_entry_info *info = NULL;
 +      struct inode_data *id;
 +      struct inode *inode = NULL;
 +      uid_t uid = current_euid();
 +      ino_t ino = 0;
 +      struct qstr name;
 +      char *buf;
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              char *path;
 +              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      DbgPrint("dir 0x%p %d hash %d inode 0x%0p %s", dir, dir->i_ino, dentry->d_name.hash, dentry->d_inode, path);
 +              }
 +              kfree(buf);
 +      } else {
 +              DbgPrint("dir 0x%p %d name %.*s hash %d inode 0x%0p",
 +                       dir, dir->i_ino, dentry->d_name.len, dentry->d_name.name, dentry->d_name.hash, dentry->d_inode);
 +      }
 +
 +      if ((dentry->d_name.len == 7)
 +          && (0 == strncmp(dentry->d_name.name, " !xover", 7))) {
 +              dentry->d_op = &novfs_dentry_operations;
 +              igrab(dir);
 +              d_add(dentry, dir);
 +              return NULL;
 +      }
 +      if ((dentry->d_name.len == 7)
 +          && (0 == strncmp(dentry->d_name.name, "z!xover", 7))) {
 +              dentry->d_op = &novfs_dentry_operations;
 +              igrab(dir);
 +              d_add(dentry, dir);
 +              return NULL;
 +      }
 +
 +      if (dir && (id = dir->i_private)) {
 +              retVal = 0;
 +              if (IS_ROOT(dentry)) {
 +                      DbgPrint("Root entry=0x%p", novfs_root);
 +                      inode = novfs_root->d_inode;
 +                      return (0);
 +              } else {
 +                      info = kmalloc(sizeof(struct novfs_entry_info) + PATH_LENGTH_BUFFER, GFP_KERNEL);
 +                      if (info) {
 +                              if (NULL == (retVal = ERR_PTR(verify_dentry(dentry, 1)))) {
 +                                      name.name = dentry->d_name.name;
 +                                      name.len = dentry->d_name.len;
 +                                      name.hash = novfs_internal_hash(&name);
 +
 +                                      if (novfs_lock_inode_cache(dir)) {
 +                                              if (!novfs_get_entry(dir, &name, &ino, info)) {
 +                                                      inode = ilookup(dentry->d_sb, ino);
 +                                                      if (inode) {
 +                                                              update_inode(inode, info);
 +                                                      }
 +                                              }
 +                                              novfs_unlock_inode_cache(dir);
 +                                      }
 +
 +                                      if (!inode && ino) {
 +                                              if (id && id->Scope) {
 +                                                      uid = novfs_scope_get_uid(id->Scope);
 +                                              } else {
 +                                                      uid = novfs_scope_get_uid(novfs_get_scope(dentry));
 +                                              }
 +                                              if (novfs_lock_inode_cache(dir)) {
 +                                                      inode = novfs_get_inode(dentry->d_sb, info->mode, 0, uid, ino, &name);
 +                                                      if (inode) {
 +                                                              if (!novfs_get_entry(dir, &dentry->d_name, &ino, info)) {
 +                                                                      update_inode(inode, info);
 +                                                              }
 +                                                      }
 +                                                      novfs_unlock_inode_cache(dir);
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!retVal) {
 +              dentry->d_op = &novfs_dentry_operations;
 +              if (inode) {
 +                      parent = dget_parent(dentry);
 +                      novfs_d_add(dentry->d_parent, dentry, inode, 1);
 +                      dput(parent);
 +              } else {
 +                      d_add(dentry, inode);
 +              }
 +      }
 +
 +      if (info)
 +              kfree(info);
 +
 +      DbgPrint("inode=0x%p dentry->d_inode=0x%p return=0x%p", dir, dentry->d_inode, retVal);
 +
 +      return (retVal);
 +}
 +
 +int novfs_i_unlink(struct inode *dir, struct dentry *dentry)
 +{
 +      int retCode = -ENOENT;
 +      struct inode *inode;
 +      struct novfs_schandle session;
 +      char *path, *buf;
 +      uint64_t t64;
 +
 +      DbgPrint("dir=0x%p dir->i_ino=%d %.*s", dir, dir->i_ino, dentry->d_name.len, dentry->d_name.name);
 +      DbgPrint("IS_ROOT(dentry)=%d", IS_ROOT(dentry));
 +      DbgPrint("IS_ROOT(dentry->d_parent)=%d", IS_ROOT(dentry->d_parent));
 +      DbgPrint("IS_ROOT(dentry->d_parent->d_parent)=%d", IS_ROOT(dentry->d_parent->d_parent));
 +      DbgPrint("IS_ROOT(dentry->d_parent->d_parent->d_parent)=%d", IS_ROOT(dentry->d_parent->d_parent->d_parent));
 +
 +      if (IS_ROOT(dentry) ||  /* Root */
 +          IS_ROOT(dentry->d_parent) ||        /* User */
 +          (!IS_ROOT(dentry->d_parent->d_parent) &&    /* Server */
 +           IS_ROOT(dentry->d_parent->d_parent->d_parent))) {  /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      inode = dentry->d_inode;
 +      if (inode) {
 +              DbgPrint("dir=0x%p dir->i_ino=%d inode=0x%p ino=%d", dir, dir->i_ino, inode, inode->i_ino);
 +              if (inode->i_private) {
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      if (0 == SC_PRESENT(session)) {
 +                              ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(dentry);
 +                              session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      }
 +
 +                      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +                      if (buf) {
 +                              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +                              if (path) {
 +                                      DbgPrint("path %s mode 0%o", path, inode->i_mode);
 +                                      if (IS_ROOT(dentry->d_parent->d_parent)) {
 +                                              retCode = novfs_daemon_logout(&dentry->d_name, &session);
 +                                      } else {
 +                                              retCode = novfs_delete(path, S_ISDIR(inode->i_mode), session);
 +                                              if (retCode) {
 +                                                      struct iattr ia;
 +                                                      memset(&ia, 0, sizeof(ia));
 +                                                      ia.ia_valid = ATTR_MODE;
 +                                                      ia.ia_mode = S_IRWXU;
 +                                                      novfs_set_attr(path, &ia, session);
 +                                                      retCode = novfs_delete(path, S_ISDIR(inode->i_mode), session);
 +                                              }
 +                                      }
 +                                      if (!retCode || IS_DEADDIR(inode)) {
 +                                              novfs_remove_inode_entry(dir, &dentry->d_name, 0);
 +                                              dentry->d_time = 0;
 +                                              t64 = 0;
 +                                              novfs_scope_set_userspace(&t64, &t64, &t64, &t64);
 +                                              retCode = 0;
 +                                      }
 +                              }
 +                              kfree(buf);
 +                      }
 +              }
 +      }
 +
 +      DbgPrint("retCode 0x%x", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_i_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 +{
 +      char *path, *buf;
 +      struct novfs_schandle session;
 +      int retCode = 0;
 +      struct inode *inode;
 +      struct novfs_entry_info info;
 +      uid_t uid;
 +
 +      DbgPrint("dir=0x%p ino=%d dentry=0x%p %.*s mode=0%lo",
 +               dir, dir->i_ino, dentry, dentry->d_name.len, dentry->d_name.name, mode);
 +
 +      if (IS_ROOT(dentry) ||  /* Root */
 +          IS_ROOT(dentry->d_parent) ||        /* User */
 +          IS_ROOT(dentry->d_parent->d_parent) ||      /* Server */
 +          IS_ROOT(dentry->d_parent->d_parent->d_parent)) {    /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      mode |= S_IFDIR;
 +      mode &= (S_IFMT | S_IRWXU);
 +      if (dir->i_private) {
 +              session = novfs_scope_get_sessionId(((struct inode_data *)dir->i_private)->Scope);
 +              if (0 == SC_PRESENT(session)) {
 +                      ((struct inode_data *)dir->i_private)->Scope = novfs_get_scope(dentry);
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)dir->i_private)->Scope);
 +              }
 +
 +              uid = novfs_scope_get_uid(((struct inode_data *)dir->i_private)->Scope);
 +              buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +              if (buf) {
 +                      path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +                      if (path) {
 +                              DbgPrint("path %s", path);
 +                              retCode = novfs_create(path, S_ISDIR(mode), session);
 +                              if (!retCode) {
 +                                      retCode = novfs_get_file_info(path, &info, session);
 +                                      if (!retCode) {
 +                                              retCode = novfs_i_mknod(dir, dentry, mode, 0);
 +                                              inode = dentry->d_inode;
 +                                              if (inode) {
 +                                                      update_inode(inode, &info);
 +                                                      ((struct inode_data *)inode->i_private)->Flags &= ~UPDATE_INODE;
 +
 +                                                      dentry->d_time = jiffies + (novfs_update_timeout * HZ);
 +
 +                                                      novfs_lock_inode_cache(dir);
 +                                                      if (novfs_update_entry(dir, &dentry->d_name, 0, &info)) {
 +                                                              novfs_add_inode_entry(dir, &dentry->d_name, inode->i_ino, &info);
 +                                                      }
 +                                                      novfs_unlock_inode_cache(dir);
 +                                              }
 +
 +                                      }
 +                              }
 +                      }
 +                      kfree(buf);
 +              }
 +      }
 +
 +      return (retCode);
 +}
 +
 +int novfs_i_rmdir(struct inode *inode, struct dentry *dentry)
 +{
 +      return (novfs_i_unlink(inode, dentry));
 +}
 +
 +int novfs_i_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 +{
 +      struct inode *inode = NULL;
 +      int retCode = -EACCES;
 +      uid_t uid;
 +      struct dentry *parent;
 +
 +      if (IS_ROOT(dentry) ||  /* Root */
 +          IS_ROOT(dentry->d_parent) ||        /* User */
 +          IS_ROOT(dentry->d_parent->d_parent) ||      /* Server */
 +          IS_ROOT(dentry->d_parent->d_parent->d_parent)) {    /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      if (((struct inode_data *)dir->i_private)) {
 +              uid = novfs_scope_get_uid(((struct inode_data *)dir->i_private)->Scope);
 +              if (mode & (S_IFREG | S_IFDIR)) {
 +                      inode = novfs_get_inode(dir->i_sb, mode, dev, uid, 0, &dentry->d_name);
 +              }
 +      }
 +      if (inode) {
 +              struct novfs_entry_info info;
 +
 +              dentry->d_op = &novfs_dentry_operations;
 +              parent = dget_parent(dentry);
 +              novfs_d_add(parent, dentry, inode, 0);
 +              memset(&info, 0, sizeof(info));
 +              info.mode = inode->i_mode;
 +              novfs_lock_inode_cache(dir);
 +              novfs_add_inode_entry(dir, &dentry->d_name, inode->i_ino, &info);
 +              novfs_unlock_inode_cache(dir);
 +
 +              dput(parent);
 +
 +              retCode = 0;
 +      }
 +      DbgPrint("return 0x%x", retCode);
 +      return retCode;
 +}
 +
 +int novfs_i_rename(struct inode *odir, struct dentry *od, struct inode *ndir, struct dentry *nd)
 +{
 +      int retCode = -ENOTEMPTY;
 +      char *newpath, *newbuf, *newcon;
 +      char *oldpath, *oldbuf, *oldcon;
 +      struct qstr oldname;
 +      struct novfs_entry_info *info = NULL;
 +      int oldlen, newlen;
 +      struct novfs_schandle session;
 +      ino_t ino;
 +
 +      if (IS_ROOT(od) ||      /* Root */
 +          IS_ROOT(od->d_parent) ||    /* User */
 +          IS_ROOT(od->d_parent->d_parent) ||  /* Server */
 +          IS_ROOT(od->d_parent->d_parent->d_parent)) {        /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      DbgPrint("odir=0x%p ino=%d ndir=0x%p ino=%d", odir, odir->i_ino, ndir, ndir->i_ino);
 +
 +      oldbuf = kmalloc(PATH_LENGTH_BUFFER * 2, GFP_KERNEL);
 +      newbuf = oldbuf + PATH_LENGTH_BUFFER;
 +      if (oldbuf && newbuf) {
 +              oldpath = novfs_dget_path(od, oldbuf, PATH_LENGTH_BUFFER);
 +              newpath = novfs_dget_path(nd, newbuf, PATH_LENGTH_BUFFER);
 +              if (oldpath && newpath) {
 +                      oldlen = PATH_LENGTH_BUFFER - (int)(oldpath - oldbuf);
 +                      newlen = PATH_LENGTH_BUFFER - (int)(newpath - newbuf);
 +
 +                      DbgPrint("od=0x%p od->inode=0x%p od->inode->i_ino=%d %s", od, od->d_inode, od->d_inode->i_ino, oldpath);
 +                      if (nd->d_inode) {
 +                              DbgPrint("nd=0x%p nd->inode=0x%p nd->inode->i_ino=%d %s",
 +                                       nd, nd->d_inode, nd->d_inode->i_ino, newpath);
 +                      } else {
 +                              DbgPrint("nd=0x%p nd->inode=0x%p %s", nd, nd->d_inode, newpath);
 +                      }
 +
 +                      /*
 +                       * Check to see if two different servers or different volumes
 +                       */
 +                      newcon = strchr(newpath + 1, '\\');
 +                      oldcon = strchr(oldpath + 1, '\\');
 +                      DbgPrint("newcon=0x%p newpath=0x%p", newcon, newpath);
 +                      DbgPrint("oldcon=0x%p oldpath=0x%p", oldcon, oldpath);
 +                      retCode = -EXDEV;
 +                      if (newcon && oldcon && ((int)(newcon - newpath) == (int)(oldcon - oldpath))) {
 +                              newcon = strchr(newcon + 1, '\\');
 +                              oldcon = strchr(oldcon + 1, '\\');
 +                              DbgPrint("2; newcon=0x%p newpath=0x%p", newcon, newpath);
 +                              DbgPrint("2; oldcon=0x%p oldpath=0x%p", oldcon, oldpath);
 +                              if (newcon && oldcon && ((int)(newcon - newpath) == (int)(oldcon - oldpath))) {
 +                                      oldname.name = oldpath;
 +                                      oldname.len = (int)(oldcon - oldpath);
 +                                      oldname.hash = 0;
 +                                      if (!novfs_d_strcmp(newpath,
 +                                                  newcon - newpath,
 +                                                  &oldname)) {
 +
 +                                              if (od->d_inode && od->d_inode->i_private) {
 +
 +                                                      if (nd->d_inode && nd->d_inode->i_private) {
 +                                                              session =
 +                                                                  novfs_scope_get_sessionId
 +                                                                  (((struct inode_data *)ndir->i_private)->Scope);
 +                                                              if (0 == SC_PRESENT(session)) {
 +                                                                      ((struct inode_data *)ndir->i_private)->Scope =
 +                                                                          novfs_get_scope(nd);
 +                                                                      session =
 +                                                                          novfs_scope_get_sessionId(((struct inode_data *)ndir->
 +                                                                                                     i_private)->Scope);
 +                                                              }
 +
 +                                                              retCode =
 +                                                                  novfs_delete(newpath, S_ISDIR(nd->d_inode->i_mode), session);
 +                                                              if (retCode) {
 +                                                                      struct iattr ia;
 +                                                                      memset(&ia, 0, sizeof(ia));
 +                                                                      ia.ia_valid = ATTR_MODE;
 +                                                                      ia.ia_mode = S_IRWXU;
 +                                                                      novfs_set_attr(newpath, &ia, session);
 +                                                                      retCode =
 +                                                                          novfs_delete(newpath, S_ISDIR(nd->d_inode->i_mode),
 +                                                                                       session);
 +                                                              }
 +
 +                                                      }
 +
 +                                                      session =
 +                                                          novfs_scope_get_sessionId(((struct inode_data *)ndir->i_private)->
 +                                                                                    Scope);
 +                                                      if (0 == SC_PRESENT(session)) {
 +                                                              ((struct inode_data *)ndir->i_private)->Scope = novfs_get_scope(nd);
 +                                                              session =
 +                                                                  novfs_scope_get_sessionId(((struct inode_data *)ndir->
 +                                                                                             i_private)->Scope);
 +                                                      }
 +                                                      retCode =
 +                                                          novfs_rename_file(S_ISDIR(od->d_inode->i_mode), oldpath, oldlen - 1,
 +                                                                            newpath, newlen - 1, session);
 +
 +                                                      if (!retCode) {
 +                                                              info = (struct novfs_entry_info *)oldbuf;
 +                                                              od->d_time = 0;
 +                                                              novfs_remove_inode_entry(odir, &od->d_name, 0);
 +                                                              novfs_remove_inode_entry(ndir, &nd->d_name, 0);
 +                                                              novfs_get_file_info(newpath, info, session);
 +                                                              nd->d_time = jiffies + (novfs_update_timeout * HZ);
 +
 +                                                              if (od->d_inode && od->d_inode->i_ino) {
 +                                                                      ino = od->d_inode->i_ino;
 +                                                              } else {
 +                                                                      ino = (ino_t) atomic_inc_return(&novfs_Inode_Number);
 +                                                              }
 +                                                              novfs_add_inode_entry(ndir, &nd->d_name, ino, info);
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (oldbuf)
 +              kfree(oldbuf);
 +
 +      DbgPrint("return %d", retCode);
 +      return (retCode);
 +}
 +
 +int novfs_i_setattr(struct dentry *dentry, struct iattr *attr)
 +{
 +      char *path, *buf;
 +      struct inode *inode = dentry->d_inode;
 +      char atime_buf[32];
 +      char mtime_buf[32];
 +      char ctime_buf[32];
 +      unsigned int ia_valid = attr->ia_valid;
 +      struct novfs_schandle session;
 +      int retVal = 0;
 +
 +      if (IS_ROOT(dentry) ||  /* Root */
 +          IS_ROOT(dentry->d_parent) ||        /* User */
 +          IS_ROOT(dentry->d_parent->d_parent) ||      /* Server */
 +          IS_ROOT(dentry->d_parent->d_parent->d_parent)) {    /* Volume */
 +              return (-EACCES);
 +      }
 +
 +      if (inode && inode->i_private) {
 +              session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              if (0 == SC_PRESENT(session)) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(dentry);
 +                      session = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              }
 +
 +              buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +              if (buf) {
 +                      path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +                      if (path) {
 +                              strcpy(atime_buf, "Unspecified");
 +                              strcpy(mtime_buf, "Unspecified");
 +                              strcpy(ctime_buf, "Unspecified");
 +                              if (attr->ia_valid & ATTR_ATIME) {
 +                                      ctime_r(&attr->ia_atime.tv_sec, atime_buf);
 +                              }
 +                              if (attr->ia_valid & ATTR_MTIME) {
 +                                      ctime_r(&attr->ia_mtime.tv_sec, mtime_buf);
 +                              }
 +                              if (attr->ia_valid & ATTR_CTIME) {
 +                                      ctime_r(&attr->ia_ctime.tv_sec, ctime_buf);
 +                              }
 +                              /* Removed for Bug 132374. jlt */
 +                              __DbgPrint("%s: %s\n"
 +                                         "   ia_valid:      0x%x\n"
 +                                         "   ia_mode:       0%o\n"
 +                                         "   ia_uid:        %d\n"
 +                                         "   ia_gid:        %d\n"
 +                                         "   ia_size:       %lld\n"
 +                                         "   ia_atime:      %s\n"
 +                                         "   ia_mtime:      %s\n"
 +                                         "   ia_ctime:      %s\n", __func__,
 +                                         path,
 +                                         attr->ia_valid,
 +                                         attr->ia_mode,
 +                                         attr->ia_uid, attr->ia_gid, attr->ia_size, atime_buf, mtime_buf, ctime_buf);
 +
 +                              if (ia_valid && !(retVal = novfs_set_attr(path, attr, session))) {
 +                                      ((struct inode_data *)inode->i_private)->Flags |= UPDATE_INODE;
 +
 +                                      if (ia_valid & ATTR_ATIME)
 +                                              inode->i_atime = attr->ia_atime;
 +                                      if (ia_valid & ATTR_MTIME)
 +                                              inode->i_mtime = attr->ia_mtime;
 +                                      if (ia_valid & ATTR_CTIME)
 +                                              inode->i_ctime = attr->ia_ctime;
 +                                      if (ia_valid & ATTR_MODE) {
 +                                              inode->i_mode = attr->ia_mode & (S_IFMT | S_IRWXU);
 +                                      }
 +                              }
 +                      }
 +              }
 +              kfree(buf);
 +      }
 +      DbgPrint("return 0x%x", retVal);
 +
 +      return (retVal);
 +}
 +
 +int novfs_i_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *kstat)
 +{
 +      int retCode = 0;
 +      char atime_buf[32];
 +      char mtime_buf[32];
 +      char ctime_buf[32];
 +      struct inode *inode = dentry->d_inode;
 +
 +      struct novfs_entry_info info;
 +      char *path, *buf;
 +      struct novfs_schandle session;
 +      struct inode_data *id;
 +
 +      if (!IS_ROOT(dentry) && !IS_ROOT(dentry->d_parent)) {
 +              SC_INITIALIZE(session);
 +              id = dentry->d_inode->i_private;
 +
 +              if (id && (id->Flags & UPDATE_INODE)) {
 +                      session = novfs_scope_get_sessionId(id->Scope);
 +
 +                      if (0 == SC_PRESENT(session)) {
 +                              id->Scope = novfs_get_scope(dentry);
 +                              session = novfs_scope_get_sessionId(id->Scope);
 +                      }
 +
 +                      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +                      if (buf) {
 +                              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +                              if (path) {
 +                                      retCode = novfs_get_file_info(path, &info, session);
 +                                      if (!retCode) {
 +                                              update_inode(inode, &info);
 +                                              id->Flags &= ~UPDATE_INODE;
 +                                      }
 +                              }
 +                              kfree(buf);
 +                      }
 +              }
 +      }
 +
 +      kstat->ino = inode->i_ino;
 +      kstat->dev = inode->i_sb->s_dev;
 +      kstat->mode = inode->i_mode;
 +      kstat->nlink = inode->i_nlink;
 +      kstat->uid = inode->i_uid;
 +      kstat->gid = inode->i_gid;
 +      kstat->rdev = inode->i_rdev;
 +      kstat->size = i_size_read(inode);
 +      kstat->atime = inode->i_atime;
 +      kstat->mtime = inode->i_mtime;
 +      kstat->ctime = inode->i_ctime;
 +      kstat->blksize = inode->i_sb->s_blocksize;
 +      kstat->blocks = inode->i_blocks;
 +      if (inode->i_bytes) {
 +              kstat->blocks++;
 +      }
 +      ctime_r(&kstat->atime.tv_sec, atime_buf);
 +      ctime_r(&kstat->mtime.tv_sec, mtime_buf);
 +      ctime_r(&kstat->ctime.tv_sec, ctime_buf);
 +
 +      __DbgPrint("%s: 0x%x 0x%p <%.*s>\n"
 +                 "   ino: %d\n"
 +                 "   dev: 0x%x\n"
 +                 "   mode: 0%o\n"
 +                 "   nlink: 0x%x\n"
 +                 "   uid: 0x%x\n"
 +                 "   gid: 0x%x\n"
 +                 "   rdev: 0x%x\n"
 +                 "   size: 0x%llx\n"
 +                 "   atime: %s\n"
 +                 "   mtime: %s\n"
 +                 "   ctime: %s\n"
 +                 "   blksize: 0x%x\n"
 +                 "   blocks: 0x%x\n", __func__,
 +                 retCode, dentry, dentry->d_name.len, dentry->d_name.name,
 +                 kstat->ino,
 +                 kstat->dev,
 +                 kstat->mode,
 +                 kstat->nlink,
 +                 kstat->uid,
 +                 kstat->gid, kstat->rdev, kstat->size, atime_buf, mtime_buf, ctime_buf, kstat->blksize, kstat->blocks);
 +      return (retCode);
 +}
 +
 +ssize_t novfs_i_getxattr(struct dentry * dentry, const char *name, void *buffer, size_t buffer_size)
 +{
 +      struct inode *inode = dentry->d_inode;
 +      struct novfs_schandle sessionId;
 +      char *path, *buf, *bufRead;
 +      ssize_t dataLen;
 +
 +      int retxcode = 0;
 +
 +      SC_INITIALIZE(sessionId);
 +
 +      DbgPrint("Ian");        /*%.*s\n", dentry->d_name.len, dentry->d_name.name); */
 +      DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", dentry->d_name.len, dentry->d_name.name);
 +      DbgPrint("name %s", name);
 +      DbgPrint("size %u", buffer_size);
 +
 +      if (inode && inode->i_private) {
 +              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              DbgPrint("SessionId = %u", sessionId);
 +              //if (0 == sessionId)
 +              if (0 == SC_PRESENT(sessionId)) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(dentry);
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      DbgPrint("SessionId = %u", sessionId);
 +              }
 +      }
 +
 +      dataLen = 0;
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      bufRead = kmalloc(XA_BUFFER, GFP_KERNEL);
 +                      if (bufRead) {
 +                              retxcode = novfs_getx_file_info(path, name, bufRead, XA_BUFFER, &dataLen, sessionId);
 +                              DbgPrint("after novfs_GetX_File_Info retxcode = %d", retxcode);
 +                              if (!retxcode) {
 +                                      novfs_dump(64, bufRead);
 +                                      if (buffer_size != 0) {
 +                                              if (buffer_size >= dataLen) {
 +                                                      memcpy(buffer, bufRead, dataLen);
 +                                              } else {
 +                                                      DbgPrint("(!!!) not enough buffer_size. buffer_size = %d, dataLen = %d",
 +                                                               buffer_size, dataLen);
 +                                                      retxcode = -ERANGE;
 +                                              }
 +                                      }
 +                              }
 +                              kfree(bufRead);
 +                      }
 +              }
 +              kfree(buf);
 +      }
 +
 +      if (retxcode) {
 +              dataLen = retxcode;
 +      } else {
 +              if ((buffer_size > 0) && (buffer_size < dataLen)) {
 +                      dataLen = -ERANGE;
 +              }
 +      }
 +
 +      return (dataLen);
 +}
 +
 +int novfs_i_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_size, int flags)
 +{
 +
 +      struct inode *inode = dentry->d_inode;
 +      struct novfs_schandle sessionId;
 +      char *path, *buf;
 +      unsigned long bytesWritten = 0;
 +      int retError = 0;
 +      int retxcode = 0;
 +
 +      SC_INITIALIZE(sessionId);
 +
 +      DbgPrint("Ian");        /*%.*s\n", dentry->d_name.len, dentry->d_name.name); */
 +      DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", dentry->d_name.len, dentry->d_name.name);
 +      DbgPrint("name %s", name);
 +      DbgPrint("value_size %u", value_size);
 +      DbgPrint("flags %d", flags);
 +
 +      if (inode && inode->i_private) {
 +              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              DbgPrint("SessionId = %u", sessionId);
 +              //if (0 == sessionId)
 +              if (0 == SC_PRESENT(sessionId)) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(dentry);
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      DbgPrint("SessionId = %u", sessionId);
 +              }
 +      }
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      retxcode = novfs_setx_file_info(path, name, value, value_size, &bytesWritten, flags, sessionId);
 +                      if (!retxcode) {
 +                              DbgPrint("bytesWritten = %u", bytesWritten);
 +                      }
 +              }
 +              kfree(buf);
 +      }
 +
 +      if (retxcode) {
 +              retError = retxcode;
 +      }
 +
 +      if (bytesWritten < value_size) {
 +              retError = retxcode;
 +      }
 +      return (retError);
 +}
 +
 +ssize_t novfs_i_listxattr(struct dentry * dentry, char *buffer, size_t buffer_size)
 +{
 +      struct inode *inode = dentry->d_inode;
 +      struct novfs_schandle sessionId;
 +      char *path, *buf, *bufList;
 +      ssize_t dataLen;
 +      int retxcode = 0;
 +
 +      SC_INITIALIZE(sessionId);
 +
 +      DbgPrint("Ian");        //%.*s\n", dentry->d_name.len, dentry->d_name.name);
 +      DbgPrint("dentry->d_name.len %u, dentry->d_name.name %s", dentry->d_name.len, dentry->d_name.name);
 +      DbgPrint("size %u", buffer_size);
 +
 +      if (inode && inode->i_private) {
 +              sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +              DbgPrint("SessionId = %u", sessionId);
 +              //if (0 == sessionId)
 +              if (0 == SC_PRESENT(sessionId)) {
 +                      ((struct inode_data *)inode->i_private)->Scope = novfs_get_scope(dentry);
 +                      sessionId = novfs_scope_get_sessionId(((struct inode_data *)inode->i_private)->Scope);
 +                      DbgPrint("SessionId = %u", sessionId);
 +              }
 +      }
 +
 +      dataLen = 0;
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              path = novfs_dget_path(dentry, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      bufList = kmalloc(XA_BUFFER, GFP_KERNEL);
 +                      if (bufList) {
 +                              retxcode = novfs_listx_file_info(path, bufList, XA_BUFFER, &dataLen, sessionId);
 +
 +                              novfs_dump(64, bufList);
 +                              if (buffer_size != 0) {
 +                                      if (buffer_size >= dataLen) {
 +                                              memcpy(buffer, bufList, dataLen);
 +                                      } else {
 +                                              DbgPrint("(!!!) not enough buffer_size. buffer_size = %d, dataLen = %d",
 +                                                       buffer_size, dataLen);
 +                                              retxcode = -1;
 +                                      }
 +                              }
 +
 +                              if (bufList) {
 +                                      kfree(bufList);
 +                              }
 +                      }
 +
 +              }
 +              kfree(buf);
 +      }
 +
 +      if (retxcode) {
 +              dataLen = -1;
 +      } else {
 +
 +              if ((buffer_size > 0) && (buffer_size < dataLen)) {
 +                      dataLen = -ERANGE;
 +              }
 +      }
 +      return (dataLen);
 +}
 +
 +int novfs_i_revalidate(struct dentry *dentry)
 +{
 +
 +      DbgPrint("name %.*s", dentry->d_name.len, dentry->d_name.name);
 +
 +      return (0);
 +}
 +
 +void novfs_read_inode(struct inode *inode)
 +{
 +      DbgPrint("0x%p %d", inode, inode->i_ino);
 +}
 +
 +void novfs_write_inode(struct inode *inode)
 +{
 +      DbgPrint("Inode=0x%p Ino=%d", inode, inode->i_ino);
 +}
 +
 +int novfs_notify_change(struct dentry *dentry, struct iattr *attr)
 +{
 +      struct inode *inode = dentry->d_inode;
 +
 +      DbgPrint("Dentry=0x%p Name=%.*s Inode=0x%p Ino=%d ia_valid=0x%x",
 +               dentry, dentry->d_name.len, dentry->d_name.name, inode, inode->i_ino, attr->ia_valid);
 +      return (0);
 +}
 +
 +void novfs_evict_inode(struct inode *inode)
 +{
 +      truncate_inode_pages(&inode->i_data, 0);
 +      end_writeback(inode);
 +
 +      InodeCount--;
 +
 +      if (inode->i_private) {
 +              struct inode_data *id = inode->i_private;
 +
 +              DbgPrint("inode=0x%p ino=%d Scope=0x%p Name=%s", inode, inode->i_ino, id->Scope, id->Name);
 +
 +              novfs_free_inode_cache(inode);
 +
 +              mutex_lock(&InodeList_lock);
 +              list_del(&id->IList);
 +              mutex_unlock(&InodeList_lock);
 +
 +              kfree(inode->i_private);
 +              inode->i_private = NULL;
 +
 +              remove_inode_hash(inode);
 +
 +      } else {
 +              DbgPrint("inode=0x%p ino=%d", inode, inode->i_ino);
 +      }
 +}
 +
 +/* Called when /proc/mounts is read */
 +int novfs_show_options(struct seq_file *s, struct vfsmount *m)
 +{
 +      char *buf, *path, *tmp;
 +
 +      buf = kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
 +      if (buf) {
 +              struct path my_path;
 +              my_path.mnt = m;
 +              my_path.dentry = m->mnt_root;
 +              path = d_path(&my_path, buf, PATH_LENGTH_BUFFER);
 +              if (path) {
 +                      if (!novfs_current_mnt || (novfs_current_mnt && strcmp(novfs_current_mnt, path))) {
 +                              DbgPrint("%.*s %.*s %s",
 +                                       m->mnt_root->d_name.len,
 +                                       m->mnt_root->d_name.name,
 +                                       m->mnt_mountpoint->d_name.len, m->mnt_mountpoint->d_name.name, path);
 +                              tmp = kmalloc(PATH_LENGTH_BUFFER - (int)(path - buf), GFP_KERNEL);
 +                              if (tmp) {
 +                                      strcpy(tmp, path);
 +                                      path = novfs_current_mnt;
 +                                      novfs_current_mnt = tmp;
 +                                      novfs_daemon_set_mnt_point(novfs_current_mnt);
 +
 +                                      if (path) {
 +                                              kfree(path);
 +                                      }
 +                              }
 +                      }
 +              }
 +              kfree(buf);
 +      }
 +      return (0);
 +}
 +
 +/*   Called when statfs(2) system called. */
 +int novfs_statfs(struct dentry *de, struct kstatfs *buf)
 +{
 +      uint64_t td, fd, te, fe;
 +      struct super_block *sb = de->d_sb;
 +
 +      DbgPrint("");
 +
 +      td = fd = te = fe = 0;
 +
 +      novfs_scope_get_userspace(&td, &fd, &te, &fe);
 +
 +      DbgPrint("td=%llu", td);
 +      DbgPrint("fd=%llu", fd);
 +      DbgPrint("te=%llu", te);
 +      DbgPrint("fe=%llu", fd);
 +      /* fix for Nautilus */
 +      if (sb->s_blocksize == 0)
 +              sb->s_blocksize = 4096;
 +
 +      buf->f_type = sb->s_magic;
 +      buf->f_bsize = sb->s_blocksize;
 +      buf->f_namelen = NW_MAX_PATH_LENGTH;
 +      buf->f_blocks = (sector_t) (td + (uint64_t) (sb->s_blocksize - 1)) >> (uint64_t) sb->s_blocksize_bits;
 +      buf->f_bfree = (sector_t) fd >> (uint64_t) sb->s_blocksize_bits;
 +      buf->f_bavail = (sector_t) buf->f_bfree;
 +      buf->f_files = (sector_t) te;
 +      buf->f_ffree = (sector_t) fe;
 +      buf->f_frsize = sb->s_blocksize;
 +      if (te > 0xffffffff)
 +              buf->f_files = 0xffffffff;
 +
 +      if (fe > 0xffffffff)
 +              buf->f_ffree = 0xffffffff;
 +
 +      DbgPrint("f_type:    0x%x", buf->f_type);
 +      DbgPrint("f_bsize:   %u", buf->f_bsize);
 +      DbgPrint("f_namelen: %d", buf->f_namelen);
 +      DbgPrint("f_blocks:  %llu", buf->f_blocks);
 +      DbgPrint("f_bfree:   %llu", buf->f_bfree);
 +      DbgPrint("f_bavail:  %llu", buf->f_bavail);
 +      DbgPrint("f_files:   %llu", buf->f_files);
 +      DbgPrint("f_ffree:   %llu", buf->f_ffree);
 +      DbgPrint("f_frsize:  %u", buf->f_frsize);
 +
 +      return 0;
 +}
 +
 +struct inode *novfs_get_inode(struct super_block *sb, int mode, int dev, uid_t Uid, ino_t ino, struct qstr *name)
 +{
 +      struct inode *inode = new_inode(sb);
 +
 +      if (inode) {
 +              InodeCount++;
 +              inode->i_mode = mode;
 +              inode->i_uid = Uid;
 +              inode->i_gid = 0;
 +              inode->i_blkbits = sb->s_blocksize_bits;
 +              inode->i_blocks = 0;
 +              inode->i_rdev = 0;
 +              inode->i_ino = (ino) ? ino : (ino_t) atomic_inc_return(&novfs_Inode_Number);
 +              if (novfs_page_cache) {
 +                      inode->i_mapping->a_ops = &novfs_aops;
 +              } else {
 +                      inode->i_mapping->a_ops = &novfs_nocache_aops;
 +              }
 +              inode->i_mapping->backing_dev_info = &novfs_backing_dev_info;
 +              inode->i_atime.tv_sec = 0;
 +              inode->i_atime.tv_nsec = 0;
 +              inode->i_mtime = inode->i_ctime = inode->i_atime;
 +
 +              DbgPrint("Inode=0x%p I_ino=%d len=%d", inode, inode->i_ino, name->len);
 +
 +              if (NULL != (inode->i_private = kmalloc(sizeof(struct inode_data) + name->len, GFP_KERNEL))) {
 +                      struct inode_data *id;
 +                      id = inode->i_private;
 +
 +                      DbgPrint("i_private 0x%p", id);
 +
 +                      id->Scope = NULL;
 +                      id->Flags = 0;
 +                      id->Inode = inode;
 +
 +                      id->cntDC = 1;
 +
 +                      INIT_LIST_HEAD(&id->DirCache);
 +                      mutex_init(&id->DirCacheLock);
 +
 +                      id->FileHandle = 0;
 +                      id->CacheFlag = 0;
 +
 +                      mutex_lock(&InodeList_lock);
 +
 +                      list_add_tail(&id->IList, &InodeList);
 +                      mutex_unlock(&InodeList_lock);
 +
 +                      id->Name[0] = '\0';
 +
 +                      memcpy(id->Name, name->name, name->len);
 +                      id->Name[name->len] = '\0';
 +
 +                      DbgPrint("name %s", id->Name);
 +              }
 +
 +              insert_inode_hash(inode);
 +
 +              switch (mode & S_IFMT) {
 +
 +              case S_IFREG:
 +                      inode->i_op = &novfs_file_inode_operations;
 +                      inode->i_fop = &novfs_file_operations;
 +                      break;
 +
 +              case S_IFDIR:
 +                      inode->i_op = &novfs_inode_operations;
 +                      inode->i_fop = &novfs_dir_operations;
 +                      inode->i_blkbits = 0;
 +                      break;
 +
 +              default:
 +                      init_special_inode(inode, mode, dev);
 +                      break;
 +              }
 +
 +              DbgPrint("size=%lld", inode->i_size);
 +              DbgPrint("mode=0%o", inode->i_mode);
 +              DbgPrint("i_sb->s_blocksize=%d", inode->i_sb->s_blocksize);
 +              DbgPrint("i_blkbits=%d", inode->i_blkbits);
 +              DbgPrint("i_blocks=%d", inode->i_blocks);
 +              DbgPrint("i_bytes=%d", inode->i_bytes);
 +      }
 +
 +      DbgPrint("0x%p %d", inode, inode->i_ino);
 +      return (inode);
 +}
 +
 +int novfs_fill_super(struct super_block *SB, void *Data, int Silent)
 +{
 +      struct inode *inode;
 +      struct dentry *server, *tree;
 +      struct qstr name;
 +      struct novfs_entry_info info;
 +
 +      SB->s_blocksize = PAGE_CACHE_SIZE;
 +      SB->s_blocksize_bits = PAGE_CACHE_SHIFT;
 +      SB->s_maxbytes = MAX_LFS_FILESIZE;      /* Max file size */
 +      SB->s_op = &novfs_ops;
 +      SB->s_flags |= (MS_NODIRATIME | MS_NODEV | MS_POSIXACL);
 +      SB->s_magic = NOVFS_MAGIC;
 +
 +      name.len = 1;
 +      name.name = "/";
 +
 +      inode = novfs_get_inode(SB, S_IFDIR | 01777, 0, 0, 0, &name);
 +      if (!inode) {
 +              return (-ENOMEM);
 +      }
 +
 +      novfs_root = d_alloc_root(inode);
 +
 +      if (!novfs_root) {
 +              iput(inode);
 +              return (-ENOMEM);
 +      }
 +      novfs_root->d_time = jiffies + (novfs_update_timeout * HZ);
 +
 +      inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 +
 +      SB->s_root = novfs_root;
 +
 +      DbgPrint("root 0x%p", novfs_root);
 +
 +      if (novfs_root) {
 +              novfs_root->d_op = &novfs_dentry_operations;
 +
 +              name.name = SERVER_DIRECTORY_NAME;
 +              name.len = strlen(SERVER_DIRECTORY_NAME);
 +              name.hash = novfs_internal_hash(&name);
 +
 +              inode = novfs_get_inode(SB, S_IFDIR | 01777, 0, 0, 0, &name);
 +              if (inode) {
 +                      info.mode = inode->i_mode;
 +                      info.namelength = 0;
 +                      inode->i_size = info.size = 0;
 +                      inode->i_uid = info.uid = 0;
 +                      inode->i_gid = info.gid = 0;
 +                      inode->i_atime = info.atime = inode->i_ctime = info.ctime = inode->i_mtime = info.mtime = CURRENT_TIME;
 +
 +                      server = d_alloc(novfs_root, &name);
 +                      if (server) {
 +                              server->d_op = &novfs_dentry_operations;
 +                              server->d_time = 0xffffffff;
 +                              d_add(server, inode);
 +                              DbgPrint("d_add %s 0x%p", SERVER_DIRECTORY_NAME, server);
 +                              novfs_add_inode_entry(novfs_root->d_inode, &name, inode->i_ino, &info);
 +                      }
 +              }
 +
 +              name.name = TREE_DIRECTORY_NAME;
 +              name.len = strlen(TREE_DIRECTORY_NAME);
 +              name.hash = novfs_internal_hash(&name);
 +
 +              inode = novfs_get_inode(SB, S_IFDIR | 01777, 0, 0, 0, &name);
 +              if (inode) {
 +                      info.mode = inode->i_mode;
 +                      info.namelength = 0;
 +                      inode->i_size = info.size = 0;
 +                      inode->i_uid = info.uid = 0;
 +                      inode->i_gid = info.gid = 0;
 +                      inode->i_atime = info.atime = inode->i_ctime = info.ctime = inode->i_mtime = info.mtime = CURRENT_TIME;
 +                      tree = d_alloc(novfs_root, &name);
 +                      if (tree) {
 +                              tree->d_op = &novfs_dentry_operations;
 +                              tree->d_time = 0xffffffff;
 +
 +                              d_add(tree, inode);
 +                              DbgPrint("d_add %s 0x%p", TREE_DIRECTORY_NAME, tree);
 +                              novfs_add_inode_entry(novfs_root->d_inode, &name, inode->i_ino, &info);
 +                      }
 +              }
 +      }
 +
 +      return (0);
 +}
 +
 +static struct dentry *novfs_mount(struct file_system_type *fs_type, int flags,
 +                     const char *dev_name, void *data)
 +{
 +      DbgPrint("Fstype=0x%x Dev_name=%s", fs_type, dev_name);
 +      return mount_nodev(fs_type, flags, data, novfs_fill_super);
 +}
 +
 +static void novfs_kill_sb(struct super_block *super)
 +{
 +      shrink_dcache_sb(super);
 +      kill_litter_super(super);
 +}
 +
- /* This should be removed */
- #ifndef kernel_locked
- #define kernel_locked() (current->lock_depth >= 0)
- #endif
- ssize_t novfs_Control_read(struct file *file, char *buf, size_t nbytes, loff_t * ppos)
- {
-       ssize_t retval = 0;
-       DbgPrint("kernel_locked 0x%x", kernel_locked());
-       return retval;
- }
- ssize_t novfs_Control_write(struct file * file, const char *buf, size_t nbytes, loff_t * ppos)
- {
-       ssize_t retval = 0;
-       DbgPrint("kernel_locked 0x%x", kernel_locked());
-       if (buf && nbytes) {
-       }
-       return (retval);
- }
 +static struct file_system_type novfs_fs_type = {
 +      .name = "novfs",
 +      .mount = novfs_mount,
 +      .kill_sb = novfs_kill_sb,
 +      .owner = THIS_MODULE,
 +};
 +
 +int __init init_novfs(void)
 +{
 +      int retCode;
 +
 +      lastDir[0] = 0;
 +      lastTime = get_nanosecond_time();
 +
 +      inHAX = 0;
 +      inHAXTime = get_nanosecond_time();
 +
 +      retCode = bdi_init(&novfs_backing_dev_info);
 +
 +      if (!retCode)
 +              retCode = bdi_register(&novfs_backing_dev_info, NULL, "novfs-map");
 +      if (retCode) {
 +              bdi_destroy(&novfs_backing_dev_info);
 +              goto bdi_fail;
 +      }
 +
 +      retCode = novfs_proc_init();
 +
 +      novfs_profile_init();
 +
 +      if (!retCode) {
 +              DbgPrint("%s %s %s", __DATE__, __TIME__, NOVFS_VERSION_STRING);
 +              novfs_daemon_queue_init();
 +              novfs_scope_init();
 +              retCode = register_filesystem(&novfs_fs_type);
 +              if (retCode) {
 +                      novfs_proc_exit();
 +                      novfs_daemon_queue_exit();
 +                      novfs_scope_exit();
 +              }
 +      }
 +
 +bdi_fail:
 +      return (retCode);
 +}
 +
 +void __exit exit_novfs(void)
 +{
 +      novfs_scope_exit();
 +      novfs_daemon_queue_exit();
 +      novfs_profile_exit();
 +      novfs_proc_exit();
 +      unregister_filesystem(&novfs_fs_type);
 +
 +      if (novfs_current_mnt) {
 +              kfree(novfs_current_mnt);
 +              novfs_current_mnt = NULL;
 +      }
 +
 +      bdi_destroy(&novfs_backing_dev_info);
 +}
 +
 +int novfs_lock_inode_cache(struct inode *i)
 +{
 +      struct inode_data *id;
 +      int retVal = 0;
 +
 +      DbgPrint("0x%p", i);
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              mutex_lock(&id->DirCacheLock);
 +              retVal = 1;
 +      }
 +      DbgPrint("return %d", retVal);
 +      return (retVal);
 +}
 +
 +void novfs_unlock_inode_cache(struct inode *i)
 +{
 +      struct inode_data *id;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              mutex_unlock(&id->DirCacheLock);
 +      }
 +}
 +
 +int novfs_enumerate_inode_cache(struct inode *i, struct list_head **iteration, ino_t * ino, struct novfs_entry_info *info)
 +/*
 + *  Arguments:   struct inode *i - pointer to directory inode
 + *
 + *  Returns:     0 - item found
 + *              -1 - done
 + *
 + *  Abstract:    Unlocks inode cache.
 + *
 + *  Notes:       DirCacheLock should be held before calling this routine.
 + *========================================================================*/
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *l = NULL;
 +      int retVal = -1;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              if ((NULL == iteration) || (NULL == *iteration)) {
 +                      l = id->DirCache.next;
 +              } else {
 +                      l = *iteration;
 +              }
 +
 +              if (l == &id->DirCache) {
 +                      l = NULL;
 +              } else {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +
 +                      *ino = dc->ino;
 +                      info->type = 0;
 +                      info->mode = dc->mode;
 +                      info->size = dc->size;
 +                      info->atime = dc->atime;
 +                      info->mtime = dc->mtime;
 +                      info->ctime = dc->ctime;
 +                      info->namelength = dc->nameLen;
 +                      memcpy(info->name, dc->name, dc->nameLen);
 +                      info->name[dc->nameLen] = '\0';
 +                      retVal = 0;
 +
 +                      l = l->next;
 +              }
 +      }
 +      *iteration = l;
 +      return (retVal);
 +}
 +
 +/* DirCacheLock should be held before calling this routine. */
 +int novfs_get_entry(struct inode *i, struct qstr *name, ino_t * ino, struct novfs_entry_info *info)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      int retVal = -1;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              if (name && name->len) {
 +                      n = (char *)name->name;
 +                      nl = name->len;
 +              }
 +
 +              dc = novfs_lookup_inode_cache(i, name, *ino);
 +              if (dc) {
 +                      dc->flags |= ENTRY_VALID;
 +                      retVal = 0;
 +                      *ino = dc->ino;
 +                      info->type = 0;
 +                      info->mode = dc->mode;
 +                      info->size = dc->size;
 +                      info->atime = dc->atime;
 +                      info->mtime = dc->mtime;
 +                      info->ctime = dc->ctime;
 +                      info->namelength = dc->nameLen;
 +                      memcpy(info->name, dc->name, dc->nameLen);
 +                      info->name[dc->nameLen] = '\0';
 +                      retVal = 0;
 +              }
 +
 +              DbgPrint("inode: 0x%p; name: %.*s; ino: %d\n", i, nl, n, *ino);
 +      }
 +      DbgPrint("return %d", retVal);
 +      return (retVal);
 +}
 +
 + /*DirCacheLock should be held before calling this routine. */
 +int novfs_get_entry_by_pos(struct inode *i, loff_t pos, ino_t * ino, struct novfs_entry_info *info)
 +{
 +      int retVal = -1;
 +      loff_t count = 0;
 +      loff_t i_pos = pos - 2;
 +      struct list_head *inter = NULL;
 +      while (!novfs_enumerate_inode_cache(i, &inter, ino, info)) {
 +              DbgPrint("info->name = %s", info->name);
 +              if (count == i_pos) {
 +                      retVal = 0;
 +                      break;
 +              } else
 +                      count++;
 +      }
 +
 +      return retVal;
 +}
 +
 +/* DirCacheLock should be held before calling this routine. */
 +int novfs_get_entry_time(struct inode *i, struct qstr *name, ino_t * ino, struct novfs_entry_info *info, u64 * EntryTime)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      int retVal = -1;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              if (name && name->len) {
 +                      n = (char *)name->name;
 +                      nl = name->len;
 +              }
 +              DbgPrint("inode: 0x%p; name:  %.*s; ino: %d", i, nl, n, *ino);
 +
 +              dc = novfs_lookup_inode_cache(i, name, *ino);
 +              if (dc) {
 +                      retVal = 0;
 +                      *ino = dc->ino;
 +                      info->type = 0;
 +                      info->mode = dc->mode;
 +                      info->size = dc->size;
 +                      info->atime = dc->atime;
 +                      info->mtime = dc->mtime;
 +                      info->ctime = dc->ctime;
 +                      info->namelength = dc->nameLen;
 +                      memcpy(info->name, dc->name, dc->nameLen);
 +                      info->name[dc->nameLen] = '\0';
 +                      if (EntryTime) {
 +                              *EntryTime = dc->jiffies;
 +                      }
 +                      retVal = 0;
 +              }
 +      }
 +      DbgPrint("return %d", retVal);
 +      return (retVal);
 +}
 +
 +/*
 + *  Abstract:    This routine will return the first entry on the list
 + *               and then remove it.
 + *
 + *  Notes:       DirCacheLock should be held before calling this routine.
 + *
 + */
 +int novfs_get_remove_entry(struct inode *i, ino_t * ino, struct novfs_entry_info *info)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *l = NULL;
 +      int retVal = -1;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              l = id->DirCache.next;
 +
 +              if (l != &id->DirCache) {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +
 +                      *ino = dc->ino;
 +                      info->type = 0;
 +                      info->mode = dc->mode;
 +                      info->size = dc->size;
 +                      info->atime = dc->atime;
 +                      info->mtime = dc->mtime;
 +                      info->ctime = dc->ctime;
 +                      info->namelength = dc->nameLen;
 +                      memcpy(info->name, dc->name, dc->nameLen);
 +                      info->name[dc->nameLen] = '\0';
 +                      retVal = 0;
 +
 +                      list_del(&dc->list);
 +                      kfree(dc);
 +                      DCCount--;
 +
 +                      id->cntDC--;
 +              }
 +      }
 +      return (retVal);
 +}
 +
 +/*
 + *  Abstract:    Marks all entries in the directory cache as invalid.
 + *
 + *  Notes:       DirCacheLock should be held before calling this routine.
 + *
 + *========================================================================*/
 +void novfs_invalidate_inode_cache(struct inode *i)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *l;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              list_for_each(l, &id->DirCache) {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +                      dc->flags &= ~ENTRY_VALID;
 +              }
 +      }
 +}
 +
 +/*++======================================================================*/
 +struct novfs_dir_cache *novfs_lookup_inode_cache(struct inode *i, struct qstr *name, ino_t ino)
 +/*
 + *  Returns:     struct novfs_dir_cache entry if match
 + *               NULL - if there is no match.
 + *
 + *  Abstract:    Checks a inode directory to see if there are any enties
 + *               matching name or ino.  If name is specified then ino is
 + *               not used.  ino is use if name is not specified.
 + *
 + *  Notes:       DirCacheLock should be held before calling this routine.
 + *
 + *========================================================================*/
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc, *retVal = NULL;
 +      struct list_head *l;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +      int hash = 0;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              if (name && name->name) {
 +                      nl = name->len;
 +                      n = (char *)name->name;
 +                      hash = name->hash;
 +              }
 +              DbgPrint("inode: 0x%p; name:  %.*s; hash:  0x%x;\n" "   len:   %d; ino:   %d", i, nl, n, hash, nl, ino);
 +
 +              list_for_each(l, &id->DirCache) {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +                      if (name) {
 +
 +/*         DbgPrint("novfs_lookup_inode_cache: 0x%p\n" \
 +                  "   ino:   %d\n" \
 +                  "   hash:  0x%x\n" \
 +                  "   len:   %d\n" \
 +                  "   name:  %.*s\n",
 +            dc, dc->ino, dc->hash, dc->nameLen, dc->nameLen, dc->name);
 +*/
 +                              if ((name->hash == dc->hash) &&
 +                                  (name->len == dc->nameLen) && (0 == memcmp(name->name, dc->name, name->len))) {
 +                                      retVal = dc;
 +                                      break;
 +                              }
 +                      } else {
 +                              if (ino == dc->ino) {
 +                                      retVal = dc;
 +                                      break;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      DbgPrint("return 0x%p", retVal);
 +      return (retVal);
 +}
 +
 +/*
 + * Checks a inode directory to see if there are any enties matching name
 + * or ino.  If entry is found the valid bit is set.
 + *
 + * DirCacheLock should be held before calling this routine.
 + */
 +int novfs_lookup_validate(struct inode *i, struct qstr *name, ino_t ino)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      int retVal = -1;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              if (name && name->len) {
 +                      n = (char *)name->name;
 +                      nl = name->len;
 +              }
 +              DbgPrint("inode: 0x%p; name:  %.*s; ino:   %d", i, nl, n, ino);
 +
 +              dc = novfs_lookup_inode_cache(i, name, ino);
 +              if (dc) {
 +                      dc->flags |= ENTRY_VALID;
 +                      retVal = 0;
 +              }
 +      }
 +      return (retVal);
 +}
 +
 +/*
 + * Added entry to directory cache.
 + *
 + * DirCacheLock should be held before calling this routine.
 + */
 +int novfs_add_inode_entry(struct inode *i, struct qstr *name, ino_t ino, struct novfs_entry_info *info)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *new;
 +      int retVal = -ENOMEM;
 +
 +      //SClark
 +      DbgPrint("i: %p", i);
 +      if ((id = i->i_private)) {
 +              DbgPrint("i->i_private: %p", id);
 +              if (id->DirCache.next)
 +                      DbgPrint("id->DirCache.next: %p", id->DirCache.next);
 +      }
 +      //SClark
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              new = kmalloc(sizeof(struct novfs_dir_cache) + name->len, GFP_KERNEL);
 +              if (new) {
 +                      id->cntDC++;
 +
 +                      DCCount++;
 +                      DbgPrint("inode: 0x%p; id: 0x%p; DC: 0x%p; new: 0x%p; "
 +                               "name:  %.*s; ino: %d; size: %lld; mode: 0x%x",
 +                               i, id, &id->DirCache, new, name->len, name->name, ino, info->size, info->mode);
 +
 +                      retVal = 0;
 +                      new->flags = ENTRY_VALID;
 +                      new->jiffies = get_jiffies_64();
 +                      new->size = info->size;
 +                      new->mode = info->mode;
 +                      new->atime = info->atime;
 +                      new->mtime = info->mtime;
 +                      new->ctime = info->ctime;
 +                      new->ino = ino;
 +                      new->hash = name->hash;
 +                      new->nameLen = name->len;
 +                      memcpy(new->name, name->name, name->len);
 +                      new->name[new->nameLen] = '\0';
 +                      list_add(&new->list, &id->DirCache);
 +              }
 +      }
 +      return (retVal);
 +}
 +
 +/*
 + *  DirCacheLock should be held before calling this routine.
 + */
 +int novfs_update_entry(struct inode *i, struct qstr *name, ino_t ino, struct novfs_entry_info *info)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      int retVal = -1;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +      char atime_buf[32];
 +      char mtime_buf[32];
 +      char ctime_buf[32];
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +
 +              if (name && name->len) {
 +                      n = (char *)name->name;
 +                      nl = name->len;
 +              }
 +              ctime_r(&info->atime.tv_sec, atime_buf);
 +              ctime_r(&info->mtime.tv_sec, mtime_buf);
 +              ctime_r(&info->ctime.tv_sec, ctime_buf);
 +              DbgPrint("inode: 0x%p; name: %.*s; ino: %d; size: %lld; "
 +                       "atime: %s; mtime: %s; ctime: %s", i, nl, n, ino, info->size, atime_buf, mtime_buf, ctime_buf);
 +
 +              dc = novfs_lookup_inode_cache(i, name, ino);
 +              if (dc) {
 +                      retVal = 0;
 +                      dc->flags = ENTRY_VALID;
 +                      dc->jiffies = get_jiffies_64();
 +                      dc->size = info->size;
 +                      dc->mode = info->mode;
 +                      dc->atime = info->atime;
 +                      dc->mtime = info->mtime;
 +                      dc->ctime = info->ctime;
 +
 +                      ctime_r(&dc->atime.tv_sec, atime_buf);
 +                      ctime_r(&dc->mtime.tv_sec, mtime_buf);
 +                      ctime_r(&dc->ctime.tv_sec, ctime_buf);
 +                      DbgPrint("entry: 0x%p; flags: 0x%x; jiffies: %lld; "
 +                               "ino: %d; size: %lld; mode: 0%o; atime: %s; "
 +                               "mtime: %s %d; ctime: %s; hash: 0x%x; "
 +                               " nameLen: %d; name: %s",
 +                               dc, dc->flags, dc->jiffies, dc->ino, dc->size,
 +                               dc->mode, atime_buf, mtime_buf, dc->mtime.tv_nsec, ctime_buf, dc->hash, dc->nameLen, dc->name);
 +              }
 +      }
 +      DbgPrint("return %d", retVal);
 +      return (retVal);
 +}
 +
 +/*
 + *  Removes entry from directory cache.  You can specify a name
 + *  or an inode number.
 + *
 + *  DirCacheLock should be held before calling this routine.
 + */
 +void novfs_remove_inode_entry(struct inode *i, struct qstr *name, ino_t ino)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      char *n = "<NULL>";
 +      int nl = 6;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              dc = novfs_lookup_inode_cache(i, name, ino);
 +              if (dc) {
 +                      if (name && name->name) {
 +                              nl = name->len;
 +                              n = (char *)name->name;
 +                      }
 +                      DbgPrint("inode: 0x%p; id: 0x%p; DC: 0x%p; "
 +                               "name: %.*s; ino: %d entry: 0x%p "
 +                               "[name: %.*s; ino: %d; next: 0x%p; "
 +                               "prev: 0x%p]",
 +                               i, id, &id->DirCache, nl, n, ino, dc,
 +                               dc->nameLen, dc->name, dc->ino, dc->list.next, dc->list.prev);
 +                      list_del(&dc->list);
 +                      kfree(dc);
 +                      DCCount--;
 +
 +                      id->cntDC--;
 +              }
 +      }
 +}
 +
 +/*
 + * Frees all invalid entries in the directory cache.
 + *
 + * DirCacheLock should be held before calling this routine.
 + */
 +void novfs_free_invalid_entries(struct inode *i)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *l;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              list_for_each(l, &id->DirCache) {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +                      if (0 == (dc->flags & ENTRY_VALID)) {
 +                              DbgPrint("inode: 0x%p; id: 0x%p; entry: 0x%p; "
 +                                       "name: %.*s; ino: %d", i, id, dc, dc->nameLen, dc->name, dc->ino);
 +                              l = l->prev;
 +                              list_del(&dc->list);
 +                              kfree(dc);
 +                              DCCount--;
 +
 +                              id->cntDC--;
 +                      }
 +              }
 +      }
 +}
 +
 +/*
 + *  Frees all entries in the inode cache.
 + *
 + *  DirCacheLock should be held before calling this routine.
 + */
 +void novfs_free_inode_cache(struct inode *i)
 +{
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *l;
 +
 +      if (i && (id = i->i_private) && id->DirCache.next) {
 +              list_for_each(l, &id->DirCache) {
 +                      dc = list_entry(l, struct novfs_dir_cache, list);
 +                      l = l->prev;
 +                      list_del(&dc->list);
 +                      kfree(dc);
 +                      DCCount--;
 +
 +                      id->cntDC--;
 +              }
 +      }
 +}
 +
 +void novfs_dump_inode(void *pf)
 +{
 +      struct inode *inode;
 +      void (*pfunc) (char *Fmt, ...) = pf;
 +      struct inode_data *id;
 +      struct novfs_dir_cache *dc;
 +      struct list_head *il, *l;
 +      char atime_buf[32];
 +      char mtime_buf[32];
 +      char ctime_buf[32];
 +      unsigned long icnt = 0, dccnt = 0;
 +
 +      mutex_lock(&InodeList_lock);
 +      list_for_each(il, &InodeList) {
 +              id = list_entry(il, struct inode_data, IList);
 +              inode = id->Inode;
 +              if (inode) {
 +                      icnt++;
 +
 +                      pfunc("Inode=0x%p I_ino=%d\n", inode, inode->i_ino);
 +
 +                      pfunc("   atime=%s\n", ctime_r(&inode->i_atime.tv_sec, atime_buf));
 +                      pfunc("   ctime=%s\n", ctime_r(&inode->i_mtime.tv_sec, atime_buf));
 +                      pfunc("   mtime=%s\n", ctime_r(&inode->i_ctime.tv_sec, atime_buf));
 +                      pfunc("   size=%lld\n", inode->i_size);
 +                      pfunc("   mode=0%o\n", inode->i_mode);
 +                      pfunc("   count=0%o\n", atomic_read(&inode->i_count));
 +              }
 +
 +              pfunc("   nofs_inode_data: 0x%p Name=%s Scope=0x%p\n", id, id->Name, id->Scope);
 +
 +              if (id->DirCache.next) {
 +                      list_for_each(l, &id->DirCache) {
 +                              dccnt++;
 +                              dc = list_entry(l, struct novfs_dir_cache, list);
 +                              ctime_r(&dc->atime.tv_sec, atime_buf);
 +                              ctime_r(&dc->mtime.tv_sec, mtime_buf);
 +                              ctime_r(&dc->ctime.tv_sec, ctime_buf);
 +
 +                              pfunc("   Cache Entry: 0x%p\n"
 +                                    "      flags:   0x%x\n"
 +                                    "      jiffies: %llu\n"
 +                                    "      ino:     %u\n"
 +                                    "      size:    %llu\n"
 +                                    "      mode:    0%o\n"
 +                                    "      atime:   %s\n"
 +                                    "      mtime:   %s\n"
 +                                    "      ctime:   %s\n"
 +                                    "      hash:    0x%x\n"
 +                                    "      len:     %d\n"
 +                                    "      name:    %s\n",
 +                                    dc, dc->flags, dc->jiffies,
 +                                    dc->ino, dc->size, dc->mode,
 +                                    atime_buf, mtime_buf, ctime_buf, dc->hash, dc->nameLen, dc->name);
 +                      }
 +              }
 +      }
 +      mutex_unlock(&InodeList_lock);
 +
 +      pfunc("Inodes: %d(%d) DirCache: %d(%d)\n", InodeCount, icnt, DCCount, dccnt);
 +
 +}
 +
 +module_init(init_novfs);
 +module_exit(exit_novfs);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Novell Inc.");
 +MODULE_DESCRIPTION("Novell NetWare Client for Linux");
 +MODULE_VERSION(NOVFS_VERSION_STRING);
Simple merge
@@@ -319,6 -319,6 +319,15 @@@ static int is_gpt_valid(struct parsed_p
                goto fail;
        }
  
++      /* Check the GUID Partition Table header size */
++      if (le32_to_cpu((*gpt)->header_size) >
++                      bdev_logical_block_size(state->bdev)) {
++              pr_debug("GUID Partition Table Header size is wrong: %u > %u\n",
++                      le32_to_cpu((*gpt)->header_size),
++                      bdev_logical_block_size(state->bdev));
++              goto fail;
++      }
++
        /* Check the GUID Partition Table CRC */
        origcrc = le32_to_cpu((*gpt)->header_crc32);
        (*gpt)->header_crc32 = 0;
Simple merge
diff --cc fs/super.c
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -274,13 -274,8 +276,14 @@@ struct inodes_stat_t 
  #define IS_PRIVATE(inode)     ((inode)->i_flags & S_PRIVATE)
  #define IS_IMA(inode)         ((inode)->i_flags & S_IMA)
  #define IS_AUTOMOUNT(inode)   ((inode)->i_flags & S_AUTOMOUNT)
+ #define IS_NOSEC(inode)               ((inode)->i_flags & S_NOSEC)
  
 +/*
 + * IS_ACL() tells the VFS to not apply the umask
 + * and use iop->check_acl for acl permission checks when defined.
 + */
 +#define IS_ACL(inode)         __IS_FLG(inode, MS_POSIXACL | MS_RICHACL)
 +
  /* the read-only stuff doesn't really belong here, but any other place is
     probably as bad and I don't want to create yet another include file. */
  
@@@ -127,7 -126,7 +126,8 @@@ struct hd_struct 
  #define GENHD_FL_SUPPRESS_PARTITION_INFO      32
  #define GENHD_FL_EXT_DEVT                     64 /* allow extended devt */
  #define GENHD_FL_NATIVE_CAPACITY              128
- #define GENHD_FL_NO_PARTITION_SCAN            256
+ #define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE   256
++#define GENHD_FL_NO_PARTITION_SCAN            512
  
  enum {
        DISK_EVENT_MEDIA_CHANGE                 = 1 << 0, /* media changed */
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc init/Kconfig
Simple merge
diff --cc init/main.c
@@@ -489,8 -487,8 +489,9 @@@ asmlinkage void __init start_kernel(voi
        printk(KERN_NOTICE "%s", linux_banner);
        setup_arch(&command_line);
        mm_init_owner(&init_mm, &init_task);
+       mm_init_cpumask(&init_mm);
        setup_command_line(command_line);
 +      unwind_setup();
        setup_nr_cpu_ids();
        setup_per_cpu_areas();
        smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
Simple merge
diff --cc kernel/Makefile
Simple merge
   */
  
  const kernel_cap_t __cap_empty_set = CAP_EMPTY_SET;
- const kernel_cap_t __cap_full_set = CAP_FULL_SET;
- const kernel_cap_t __cap_init_eff_set = CAP_INIT_EFF_SET;
  
  EXPORT_SYMBOL(__cap_empty_set);
- EXPORT_SYMBOL(__cap_full_set);
- EXPORT_SYMBOL(__cap_init_eff_set);
  
 -int file_caps_enabled = 1;
 +int file_caps_enabled;
  
  static int __init file_caps_disable(char *str)
  {
diff --cc kernel/ksysfs.c
@@@ -157,31 -166,8 +166,32 @@@ static struct bin_attribute notes_attr 
  struct kobject *kernel_kobj;
  EXPORT_SYMBOL_GPL(kernel_kobj);
  
 +#ifdef CONFIG_ENTERPRISE_SUPPORT
 +const char *supported_printable(int taint)
 +{
 +      int mask = TAINT_PROPRIETARY_MODULE|TAINT_NO_SUPPORT;
 +      if ((taint & mask) == mask)
 +              return "No, Proprietary and Unsupported modules are loaded";
 +      else if (taint & TAINT_PROPRIETARY_MODULE)
 +              return "No, Proprietary modules are loaded";
 +      else if (taint & TAINT_NO_SUPPORT)
 +              return "No, Unsupported modules are loaded";
 +      else if (taint & TAINT_EXTERNAL_SUPPORT)
 +              return "Yes, External";
 +      else
 +              return "Yes";
 +}
 +
 +static ssize_t supported_show(struct kobject *kobj,
 +                            struct kobj_attribute *attr, char *buf)
 +{
 +      return sprintf(buf, "%s\n", supported_printable(get_taint()));
 +}
 +KERNEL_ATTR_RO(supported);
 +#endif
 +
  static struct attribute * kernel_attrs[] = {
+       &fscaps_attr.attr,
  #if defined(CONFIG_HOTPLUG)
        &uevent_seqnum_attr.attr,
        &uevent_helper_attr.attr,
diff --cc kernel/module.c
Simple merge
diff --cc kernel/printk.c
Simple merge
diff --cc kernel/sysctl.c
Simple merge
Simple merge
Simple merge
diff --cc mm/page_alloc.c
@@@ -1747,6 -1737,45 +1737,51 @@@ static inline bool should_suppress_show
        return ret;
  }
  
+ static DEFINE_RATELIMIT_STATE(nopage_rs,
+               DEFAULT_RATELIMIT_INTERVAL,
+               DEFAULT_RATELIMIT_BURST);
+ void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...)
+ {
+       va_list args;
+       unsigned int filter = SHOW_MEM_FILTER_NODES;
+       if ((gfp_mask & __GFP_NOWARN) || !__ratelimit(&nopage_rs))
+               return;
+       /*
+        * This documents exceptions given to allocations in certain
+        * contexts that are allowed to allocate outside current's set
+        * of allowed nodes.
+        */
+       if (!(gfp_mask & __GFP_NOMEMALLOC))
+               if (test_thread_flag(TIF_MEMDIE) ||
+                   (current->flags & (PF_MEMALLOC | PF_EXITING)))
+                       filter &= ~SHOW_MEM_FILTER_NODES;
+       if (in_interrupt() || !(gfp_mask & __GFP_WAIT))
+               filter &= ~SHOW_MEM_FILTER_NODES;
+       if (fmt) {
+               printk(KERN_WARNING);
+               va_start(args, fmt);
+               vprintk(fmt, args);
+               va_end(args);
+       }
 -      pr_warning("%s: page allocation failure: order:%d, mode:0x%x\n",
++      if (!(gfp_mask & __GFP_WAIT)) {
++              pr_info("The following is only an harmless informational message.\n");
++              pr_info("Unless you get a _continuous_flood_ of these messages it means\n");
++              pr_info("everything is working fine. Allocations from irqs cannot be\n");
++              pr_info("perfectly reliable and the kernel is designed to handle that.\n");
++      }
++      pr_info("%s: page allocation failure. order:%d, mode:0x%x\n",
+                  current->comm, order, gfp_mask);
+       dump_stack();
+       if (!should_suppress_show_mem())
+               show_mem(filter);
+ }
  static inline int
  should_alloc_retry(gfp_t gfp_mask, unsigned int order,
                                unsigned long pages_reclaimed)
diff --cc mm/truncate.c
@@@ -294,14 -294,7 +294,14 @@@ void truncate_inode_pages_range(struct 
                pagevec_release(&pvec);
                mem_cgroup_uncharge_end();
        }
+       cleancache_flush_inode(mapping);
 +      /*
 +       * Cycle the tree_lock to make sure all __delete_from_page_cache()
 +       * calls run from page reclaim have finished as well (this handles the
 +       * case when page reclaim took the last page from our range).
 +       */
 +      spin_lock_irq(&mapping->tree_lock);
 +      spin_unlock_irq(&mapping->tree_lock);
-       precache_flush_inode(mapping);
  }
  EXPORT_SYMBOL(truncate_inode_pages_range);
  
@@@ -277,49 -228,19 +228,28 @@@ static struct net_bridge_port *new_nbp(
        return p;
  }
  
- static struct device_type br_type = {
-       .name   = "bridge",
- };
  int br_add_bridge(struct net *net, const char *name)
  {
-       struct net_device *dev;
 +      int ret;
+       struct net_device *dev;
+       dev = alloc_netdev(sizeof(struct net_bridge), name,
+                          br_dev_setup);
  
        if (!dev)
                return -ENOMEM;
  
 +      if (!try_module_get(THIS_MODULE)) {
 +              free_netdev(dev);
 +              return -ENOENT;
 +      }
 +
-       rtnl_lock();
-       if (strchr(dev->name, '%')) {
-               ret = dev_alloc_name(dev, dev->name);
-               if (ret < 0)
-                       goto out_free;
-       }
-       SET_NETDEV_DEVTYPE(dev, &br_type);
-       ret = register_netdevice(dev);
-       if (ret)
-               goto out_free;
+       dev_net_set(dev, net);
  
-       ret = br_sysfs_addbr(dev);
-       if (ret)
-               unregister_netdevice(dev);
-  out:
-       rtnl_unlock();
 -      return register_netdev(dev);
++      ret = register_netdev(dev);
 +      if (ret)
 +              module_put(THIS_MODULE);
 +      return ret;
- out_free:
-       free_netdev(dev);
-       goto out;
  }
  
  int br_del_bridge(struct net *net, const char *name)
@@@ -295,16 -301,14 +302,16 @@@ define rule_cc_o_
  endef
  
  # Built-in and composite module parts
- $(obj)/%.o: $(src)/%.c FORCE
+ $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
        $(call cmd,force_checksrc)
 +      $(call cmd,force_check_kmsg)
        $(call if_changed_rule,cc_o_c)
  
  # Single-part modules are special since we need to mark them in $(MODVERDIR)
  
- $(single-used-m): $(obj)/%.o: $(src)/%.c FORCE
+ $(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
        $(call cmd,force_checksrc)
 +      $(call cmd,force_check_kmsg)
        $(call if_changed_rule,cc_o_c)
        @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
  
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge