reported either.
unknown_nmi_panic
- [X86]
- Set unknown_nmi_panic=1 early on boot.
+ [X86] Cause panic on unknown NMI.
+ unsupported Allow loading of unsupported kernel modules:
+ 0 = only allow supported modules,
+ 1 = warn when loading unsupported modules,
+ 2 = don't warn.
+
+ CONFIG_ENTERPRISE_SUPPORT must be enabled for this
+ to have any effect.
+
usbcore.autosuspend=
[USB] The autosuspend time delay (in seconds) used
for newly-detected USB devices (default 2). This
out:
/* Get the CPE error record and log it */
ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);
+ recover = (ia64_mca_ce_extension && ia64_mca_ce_extension(
+ IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_CPE)));
+ local_irq_disable();
+
return IRQ_HANDLED;
}
# Makefile for the linux s390-specific parts of the memory manager.
#
-COMPILE_VERSION := __linux_compile_version_id__`hostname | \
- tr -c '[0-9A-Za-z]' '_'`__`date | \
- tr -c '[0-9A-Za-z]' '_'`_t
+COMPILE_VERSION := __linux_compile_version_id__$(shell hostname | \
+ tr -c '[0-9A-Za-z]' '_')__$(shell date | \
+ tr -c '[0-9A-Za-z]' '_')_t
+
+chk-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; fi ;)
+
+# Remove possible '-g' from CFLAGS_KERNEL, since we want to use stabs
+# debug format.
+override CFLAGS_KERNEL := $(shell echo $(CFLAGS_KERNEL) | sed 's/-g//')
- EXTRA_CFLAGS := -DCOMPILE_VERSION=$(COMPILE_VERSION) -gstabs -I.
+ ccflags-y := -DCOMPILE_VERSION=$(COMPILE_VERSION) -gstabs -I.
+# Assume we don't need the flag if the compiler doesn't know about it
- EXTRA_CFLAGS += $(call chk-option,-fno-eliminate-unused-debug-types)
++ccflags-y += $(call chk-option,-fno-eliminate-unused-debug-types)
+
targets := image
targets += bzImage
extern void
show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *sp, char *log_lvl);
+ unsigned long *sp, unsigned long bp, char *log_lvl);
+int try_stack_unwind(struct task_struct *task, struct pt_regs *regs,
+ unsigned long **stack, unsigned long *bp,
+ const struct stacktrace_ops *ops, void *data);
+
extern unsigned int code_bytes;
/* The form of the top of the frame on the stack */
}
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
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
/*
This enables panic and oops messages to be logged to a circular
buffer in RAM where it can be read back at some later point.
+config CRASHER
+ tristate "Crasher Module"
+ help
+ Slab cache memory tester. Only use this as a module
+
+ config MSM_SMD_PKT
+ bool "Enable device interface for some SMD packet ports"
+ default n
+ depends on MSM_SMD
+ help
+ Enables userspace clients to read and write to some packet SMD
+ ports via device interface for MSM chipset.
+
endmenu
Support for N-Trig touch screen.
config HID_ORTEK
- tristate "Ortek WKB-2000/Skycable wireless keyboard and mouse trackpad"
- tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad"
++ tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad"
depends on USB_HID
---help---
- Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad.
+ There are certain devices which have LogicalMaximum wrong in the keyboard
+ usage page of their report descriptor. The most prevailing ones so far
+ are manufactured by Ortek, thus the name of the driver. Currently
+ supported devices by this driver are
++ - Ortek PKB-1700
+ - Ortek WKB-2000
+ - Skycable wireless presenter
config HID_PANTHERLORD
tristate "Pantherlord/GreenAsia game controller"
}
static const struct hid_device_id ortek_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
{ }
};
MODULE_DEVICE_TABLE(hid, ortek_devices);
obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
+ obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
-obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
+obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o dm-least-pending.o
obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o
obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
--- /dev/null
+/*
+ * 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));
+ 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));
+ 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);
+}
+
- /* Unplug: let any queued io role on the sets devices. */
- static void do_unplug(struct raid_set *rs)
- {
- struct raid_dev *dev = rs->dev + rs->set.raid_devs;
-
- while (dev-- > rs->dev) {
- /* Only call any device unplug function, if io got queued. */
- if (TestClearDevIoQueued(dev))
- blk_unplug(bdev_get_queue(dev->dev->bdev));
- }
- }
-
+/* 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);
- if (r)
- do_unplug(rs); /* Unplug the sets device queues. */
+
+ /* 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. */
- if (r)
- do_unplug(rs); /* Unplug the sets device queues. */
+
+ 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,
+ §ors_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");
#define DRV_VERSION "3.2.9-k2"
const char ixgbe_driver_version[] = DRV_VERSION;
- static char ixgbe_copyright[] = "Copyright (c) 1999-2010 Intel Corporation.";
+ static const char ixgbe_copyright[] =
+ "Copyright (c) 1999-2011 Intel Corporation.";
+static int entropy = 0;
+module_param(entropy, int, 0);
+MODULE_PARM_DESC(entropy, "Allow ixgbe to populate the /dev/random entropy pool");
+
+
static const struct ixgbe_info *ixgbe_info_tbl[] = {
[board_82598] = &ixgbe_82598_info,
[board_82599] = &ixgbe_82599_info,
#include "tg3.h"
+static int entropy = 0;
+module_param(entropy, int, 0);
+MODULE_PARM_DESC(entropy, "Allow tg3 to populate the /dev/random entropy pool");
+
#define DRV_MODULE_NAME "tg3"
#define TG3_MAJ_NUM 3
- #define TG3_MIN_NUM 116
+ #define TG3_MIN_NUM 117
#define DRV_MODULE_VERSION \
__stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
- #define DRV_MODULE_RELDATE "December 3, 2010"
+ #define DRV_MODULE_RELDATE "January 25, 2011"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
}
static const struct scsi_dh_devlist rdac_dev_list[] = {
- {"IBM", "1722", 0},
- {"IBM", "1724", 0},
- {"IBM", "1726", 0},
- {"IBM", "1742", 0},
- {"IBM", "1745", 0},
- {"IBM", "1746", 0},
- {"IBM", "1814", 0},
- {"IBM", "1815", 0},
- {"IBM", "1818", 0},
- {"IBM", "3526", 0},
- {"SGI", "TP9400", 0},
- {"SGI", "TP9500", 0},
- {"SGI", "IS", 0},
- {"STK", "OPENstorage D280", 0},
- {"STK", "FLEXLINE 380", 0},
- {"SUN", "STK6580_6780", 0},
- {"SUN", "CSM200_R", 0},
- {"SUN", "LCSM100_I", 0},
- {"SUN", "LCSM100_S", 0},
- {"SUN", "LCSM100_E", 0},
- {"SUN", "LCSM100_F", 0},
- {"DELL", "MD3000", 0},
- {"DELL", "MD3000i", 0},
- {"DELL", "MD32xx", 0},
- {"DELL", "MD32xxi", 0},
- {"DELL", "MD36xxi", 0},
- {"LSI", "INF-01-00", 0},
- {"ENGENIO", "INF-01-00", 0},
- {"STK", "FLEXLINE 380", 0},
- {"SUN", "CSM100_R_FC", 0},
- {"SUN", "STK6580_6780", 0},
- {"SUN", "SUN_6180", 0},
- {NULL, NULL, 0},
+ {"IBM", "1722"},
+ {"IBM", "1724"},
+ {"IBM", "1726"},
+ {"IBM", "1742"},
+ {"IBM", "1745"},
+ {"IBM", "1746"},
+ {"IBM", "1814"},
+ {"IBM", "1815"},
+ {"IBM", "1818"},
+ {"IBM", "3526"},
+ {"SGI", "TP9400"},
+ {"SGI", "TP9500"},
+ {"SGI", "IS"},
+ {"STK", "OPENstorage D280"},
++ {"STK", "FLEXLINE 380"},
++ {"SUN", "STK6580_6780"},
+ {"SUN", "CSM200_R"},
+ {"SUN", "LCSM100_I"},
+ {"SUN", "LCSM100_S"},
+ {"SUN", "LCSM100_E"},
+ {"SUN", "LCSM100_F"},
+ {"DELL", "MD3000"},
+ {"DELL", "MD3000i"},
+ {"DELL", "MD32xx"},
+ {"DELL", "MD32xxi"},
+ {"DELL", "MD36xxi"},
+ {"DELL", "MD36xxf"},
+ {"LSI", "INF-01-00"},
+ {"ENGENIO", "INF-01-00"},
+ {"STK", "FLEXLINE 380"},
+ {"SUN", "CSM100_R_FC"},
+ {"SUN", "STK6580_6780"},
+ {"SUN", "SUN_6180"},
+ {NULL, NULL},
};
static int rdac_bus_attach(struct scsi_device *sdev);
return NEEDS_RETRY;
}
/*
- * if the device is in the process of becoming ready, we
+ * if the device is in the process of becoming ready, we
* should retry.
*/
- if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
+ if ((sshdr.asc == 0x04) &&
+ (sshdr.ascq == 0x01 || sshdr.ascq == 0x0a))
return NEEDS_RETRY;
/*
* if the device is not started, we need to wake
--- /dev/null
+ /*
+ * Copyright (c) 2009, Citrix Systems, Inc.
+ * Copyright (c) 2010, Microsoft Corporation.
+ * Copyright (c) 2011, Novell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/device.h>
+ #include <linux/workqueue.h>
+ #include <linux/sched.h>
+ #include <linux/wait.h>
+ #include <linux/input.h>
+ #include <linux/hid.h>
+ #include <linux/hiddev.h>
+ #include <linux/pci.h>
+ #include <linux/dmi.h>
++#include <linux/delay.h>
+
+ #include "hv_api.h"
+ #include "logging.h"
+ #include "version_info.h"
+ #include "vmbus.h"
+ #include "vmbus_api.h"
+ #include "channel.h"
+ #include "vmbus_packet_format.h"
+
+
+ /*
+ * Data types
+ */
+ struct hv_input_dev_info {
+ unsigned short vendor;
+ unsigned short product;
+ unsigned short version;
+ char name[128];
+ };
+
+ /* Represents the input vsc driver */
+ /* FIXME - can be removed entirely */
+ struct mousevsc_drv_obj {
+ struct hv_driver Base;
+ };
+
+
+ /* The maximum size of a synthetic input message. */
+ #define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
+
+ /*
+ * Current version
+ *
+ * History:
+ * Beta, RC < 2008/1/22 1,0
+ * RC > 2008/1/22 2,0
+ */
+ #define SYNTHHID_INPUT_VERSION_MAJOR 2
+ #define SYNTHHID_INPUT_VERSION_MINOR 0
+ #define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \
+ (SYNTHHID_INPUT_VERSION_MAJOR << 16))
+
+
+ #pragma pack(push,1)
+ /*
+ * Message types in the synthetic input protocol
+ */
+ enum synthhid_msg_type {
+ SynthHidProtocolRequest,
+ SynthHidProtocolResponse,
+ SynthHidInitialDeviceInfo,
+ SynthHidInitialDeviceInfoAck,
+ SynthHidInputReport,
+ SynthHidMax
+ };
+
+ /*
+ * Basic message structures.
+ */
+ struct synthhid_msg_hdr {
+ enum synthhid_msg_type type;
+ u32 size;
+ };
+
+ struct synthhid_msg {
+ struct synthhid_msg_hdr header;
+ char data[1]; /* Enclosed message */
+ };
+
+ union synthhid_version {
+ struct {
+ u16 minor_version;
+ u16 major_version;
+ };
+ u32 version;
+ };
+
+ /*
+ * Protocol messages
+ */
+ struct synthhid_protocol_request {
+ struct synthhid_msg_hdr header;
+ union synthhid_version version_requested;
+ };
+
+ struct synthhid_protocol_response {
+ struct synthhid_msg_hdr header;
+ union synthhid_version version_requested;
+ unsigned char approved;
+ };
+
+ struct synthhid_device_info {
+ struct synthhid_msg_hdr header;
+ struct hv_input_dev_info hid_dev_info;
+ struct hid_descriptor hid_descriptor;
+ };
+
+ struct synthhid_device_info_ack {
+ struct synthhid_msg_hdr header;
+ unsigned char reserved;
+ };
+
+ struct synthhid_input_report {
+ struct synthhid_msg_hdr header;
+ char buffer[1];
+ };
+
+ #pragma pack(pop)
+
+ #define INPUTVSC_SEND_RING_BUFFER_SIZE 10*PAGE_SIZE
+ #define INPUTVSC_RECV_RING_BUFFER_SIZE 10*PAGE_SIZE
+
+ #define NBITS(x) (((x)/BITS_PER_LONG)+1)
+
+ enum pipe_prot_msg_type {
+ PipeMessageInvalid = 0,
+ PipeMessageData,
+ PipeMessageMaximum
+ };
+
+
+ struct pipe_prt_msg {
+ enum pipe_prot_msg_type type;
+ u32 size;
+ char data[1];
+ };
+
+ /*
+ * Data types
+ */
+ struct mousevsc_prt_msg {
+ enum pipe_prot_msg_type type;
+ u32 size;
+ union {
+ struct synthhid_protocol_request request;
+ struct synthhid_protocol_response response;
+ struct synthhid_device_info_ack ack;
+ };
+ };
+
+ /*
+ * Represents an mousevsc device
+ */
+ struct mousevsc_dev {
+ struct hv_device *Device;
+ /* 0 indicates the device is being destroyed */
+ atomic_t RefCount;
+ int NumOutstandingRequests;
+ unsigned char bInitializeComplete;
+ struct mousevsc_prt_msg ProtocolReq;
+ struct mousevsc_prt_msg ProtocolResp;
+ /* Synchronize the request/response if needed */
+ wait_queue_head_t ProtocolWaitEvent;
+ wait_queue_head_t DeviceInfoWaitEvent;
+ int protocol_wait_condition;
+ int device_wait_condition;
+ int DeviceInfoStatus;
+
+ struct hid_descriptor *HidDesc;
+ unsigned char *ReportDesc;
+ u32 ReportDescSize;
+ struct hv_input_dev_info hid_dev_info;
+ };
+
+
+ static const char *driver_name = "mousevsc";
+
+ /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
+ static const struct hv_guid mouse_guid = {
+ .data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
+ 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A}
+ };
+
+ static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info);
+ static void inputreport_callback(struct hv_device *dev, void *packet, u32 len);
+ static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len);
+
+ static struct mousevsc_dev *AllocInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL);
+
+ if (!inputDevice)
+ return NULL;
+
+ /*
+ * Set to 2 to allow both inbound and outbound traffics
+ * (ie GetInputDevice() and MustGetInputDevice()) to proceed.
+ */
+ atomic_cmpxchg(&inputDevice->RefCount, 0, 2);
+
+ inputDevice->Device = Device;
+ Device->ext = inputDevice;
+
+ return inputDevice;
+ }
+
+ static void FreeInputDevice(struct mousevsc_dev *Device)
+ {
+ WARN_ON(atomic_read(&Device->RefCount) == 0);
+ kfree(Device);
+ }
+
+ /*
+ * Get the inputdevice object if exists and its refcount > 1
+ */
+ static struct mousevsc_dev *GetInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = (struct mousevsc_dev *)Device->ext;
+
+ /*
+ * FIXME
+ * This sure isn't a valid thing to print for debugging, no matter
+ * what the intention is...
+ *
+ * printk(KERN_ERR "-------------------------> REFCOUNT = %d",
+ * inputDevice->RefCount);
+ */
+
+ if (inputDevice && atomic_read(&inputDevice->RefCount) > 1)
+ atomic_inc(&inputDevice->RefCount);
+ else
+ inputDevice = NULL;
+
+ return inputDevice;
+ }
+
+ /*
+ * Get the inputdevice object iff exists and its refcount > 0
+ */
+ static struct mousevsc_dev *MustGetInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = (struct mousevsc_dev *)Device->ext;
+
+ if (inputDevice && atomic_read(&inputDevice->RefCount))
+ atomic_inc(&inputDevice->RefCount);
+ else
+ inputDevice = NULL;
+
+ return inputDevice;
+ }
+
+ static void PutInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = (struct mousevsc_dev *)Device->ext;
+
+ atomic_dec(&inputDevice->RefCount);
+ }
+
+ /*
+ * Drop ref count to 1 to effectively disable GetInputDevice()
+ */
+ static struct mousevsc_dev *ReleaseInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = (struct mousevsc_dev *)Device->ext;
+
+ /* Busy wait until the ref drop to 2, then set it to 1 */
+ while (atomic_cmpxchg(&inputDevice->RefCount, 2, 1) != 2)
+ udelay(100);
+
+ return inputDevice;
+ }
+
+ /*
+ * Drop ref count to 0. No one can use InputDevice object.
+ */
+ static struct mousevsc_dev *FinalReleaseInputDevice(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = (struct mousevsc_dev *)Device->ext;
+
+ /* Busy wait until the ref drop to 1, then set it to 0 */
+ while (atomic_cmpxchg(&inputDevice->RefCount, 1, 0) != 1)
+ udelay(100);
+
+ Device->ext = NULL;
+ return inputDevice;
+ }
+
+ static void MousevscOnSendCompletion(struct hv_device *Device, struct vmpacket_descriptor *Packet)
+ {
+ struct mousevsc_dev *inputDevice;
+ void *request;
+
+ inputDevice = MustGetInputDevice(Device);
+ if (!inputDevice) {
+ pr_err("unable to get input device...device being destroyed?");
+ return;
+ }
+
+ request = (void *)(unsigned long)Packet->trans_id;
+
+ if (request == &inputDevice->ProtocolReq) {
+ /* FIXME */
+ /* Shouldn't we be doing something here? */
+ }
+
+ PutInputDevice(Device);
+ }
+
+ static void MousevscOnReceiveDeviceInfo(struct mousevsc_dev *InputDevice, struct synthhid_device_info *DeviceInfo)
+ {
+ int ret = 0;
+ struct hid_descriptor *desc;
+ struct mousevsc_prt_msg ack;
+
+ /* Assume success for now */
+ InputDevice->DeviceInfoStatus = 0;
+
+ /* Save the device attr */
+ memcpy(&InputDevice->hid_dev_info, &DeviceInfo->hid_dev_info, sizeof(struct hv_input_dev_info));
+
+ /* Save the hid desc */
+ desc = &DeviceInfo->hid_descriptor;
+ WARN_ON(desc->bLength > 0);
+
+ InputDevice->HidDesc = kzalloc(desc->bLength, GFP_KERNEL);
+
+ if (!InputDevice->HidDesc) {
+ pr_err("unable to allocate hid descriptor - size %d", desc->bLength);
+ goto Cleanup;
+ }
+
+ memcpy(InputDevice->HidDesc, desc, desc->bLength);
+
+ /* Save the report desc */
+ InputDevice->ReportDescSize = desc->desc[0].wDescriptorLength;
+ InputDevice->ReportDesc = kzalloc(InputDevice->ReportDescSize,
+ GFP_KERNEL);
+
+ if (!InputDevice->ReportDesc) {
+ pr_err("unable to allocate report descriptor - size %d",
+ InputDevice->ReportDescSize);
+ goto Cleanup;
+ }
+
+ memcpy(InputDevice->ReportDesc,
+ ((unsigned char *)desc) + desc->bLength,
+ desc->desc[0].wDescriptorLength);
+
+ /* Send the ack */
+ memset(&ack, sizeof(struct mousevsc_prt_msg), 0);
+
+ ack.type = PipeMessageData;
+ ack.size = sizeof(struct synthhid_device_info_ack);
+
+ ack.ack.header.type = SynthHidInitialDeviceInfoAck;
+ ack.ack.header.size = 1;
+ ack.ack.reserved = 0;
+
+ ret = vmbus_sendpacket(InputDevice->Device->channel,
+ &ack,
+ sizeof(struct pipe_prt_msg) - sizeof(unsigned char) +
+ sizeof(struct synthhid_device_info_ack),
+ (unsigned long)&ack,
+ VM_PKT_DATA_INBAND,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ pr_err("unable to send synthhid device info ack - ret %d",
+ ret);
+ goto Cleanup;
+ }
+
+ InputDevice->device_wait_condition = 1;
+ wake_up(&InputDevice->DeviceInfoWaitEvent);
+
+ return;
+
+ Cleanup:
+ kfree(InputDevice->HidDesc);
+ InputDevice->HidDesc = NULL;
+
+ kfree(InputDevice->ReportDesc);
+ InputDevice->ReportDesc = NULL;
+
+ InputDevice->DeviceInfoStatus = -1;
+ InputDevice->device_wait_condition = 1;
+ wake_up(&InputDevice->DeviceInfoWaitEvent);
+ }
+
+ static void MousevscOnReceiveInputReport(struct mousevsc_dev *InputDevice, struct synthhid_input_report *InputReport)
+ {
+ struct mousevsc_drv_obj *inputDriver;
+
+ if (!InputDevice->bInitializeComplete) {
+ pr_info("Initialization incomplete...ignoring InputReport msg");
+ return;
+ }
+
+ inputDriver = (struct mousevsc_drv_obj *)InputDevice->Device->drv;
+
+ inputreport_callback(InputDevice->Device,
+ InputReport->buffer,
+ InputReport->header.size);
+ }
+
+ static void MousevscOnReceive(struct hv_device *Device, struct vmpacket_descriptor *Packet)
+ {
+ struct pipe_prt_msg *pipeMsg;
+ struct synthhid_msg *hidMsg;
+ struct mousevsc_dev *inputDevice;
+
+ inputDevice = MustGetInputDevice(Device);
+ if (!inputDevice) {
+ pr_err("unable to get input device...device being destroyed?");
+ return;
+ }
+
+ pipeMsg = (struct pipe_prt_msg *)((unsigned long)Packet + (Packet->offset8 << 3));
+
+ if (pipeMsg->type != PipeMessageData) {
+ pr_err("unknown pipe msg type - type %d len %d",
+ pipeMsg->type, pipeMsg->size);
+ PutInputDevice(Device);
+ return ;
+ }
+
+ hidMsg = (struct synthhid_msg *)&pipeMsg->data[0];
+
+ switch (hidMsg->header.type) {
+ case SynthHidProtocolResponse:
+ memcpy(&inputDevice->ProtocolResp, pipeMsg,
+ pipeMsg->size + sizeof(struct pipe_prt_msg) -
+ sizeof(unsigned char));
+ inputDevice->protocol_wait_condition = 1;
+ wake_up(&inputDevice->ProtocolWaitEvent);
+ break;
+
+ case SynthHidInitialDeviceInfo:
+ WARN_ON(pipeMsg->size >= sizeof(struct hv_input_dev_info));
+
+ /*
+ * Parse out the device info into device attr,
+ * hid desc and report desc
+ */
+ MousevscOnReceiveDeviceInfo(inputDevice,
+ (struct synthhid_device_info *)&pipeMsg->data[0]);
+ break;
+ case SynthHidInputReport:
+ MousevscOnReceiveInputReport(inputDevice,
+ (struct synthhid_input_report *)&pipeMsg->data[0]);
+
+ break;
+ default:
+ pr_err("unsupported hid msg type - type %d len %d",
+ hidMsg->header.type, hidMsg->header.size);
+ break;
+ }
+
+ PutInputDevice(Device);
+ }
+
+ static void MousevscOnChannelCallback(void *Context)
+ {
+ const int packetSize = 0x100;
+ int ret = 0;
+ struct hv_device *device = (struct hv_device *)Context;
+ struct mousevsc_dev *inputDevice;
+
+ u32 bytesRecvd;
+ u64 requestId;
+ unsigned char packet[packetSize];
+ struct vmpacket_descriptor *desc;
+ unsigned char *buffer = packet;
+ int bufferlen = packetSize;
+
+ inputDevice = MustGetInputDevice(device);
+
+ if (!inputDevice) {
+ pr_err("unable to get input device...device being destroyed?");
+ return;
+ }
+
+ do {
+ ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, &bytesRecvd, &requestId);
+
+ if (ret == 0) {
+ if (bytesRecvd > 0) {
+ desc = (struct vmpacket_descriptor *)buffer;
+
+ switch (desc->type) {
+ case VM_PKT_COMP:
+ MousevscOnSendCompletion(device,
+ desc);
+ break;
+
+ case VM_PKT_DATA_INBAND:
+ MousevscOnReceive(device, desc);
+ break;
+
+ default:
+ pr_err("unhandled packet type %d, tid %llx len %d\n",
+ desc->type,
+ requestId,
+ bytesRecvd);
+ break;
+ }
+
+ /* reset */
+ if (bufferlen > packetSize) {
+ kfree(buffer);
+
+ buffer = packet;
+ bufferlen = packetSize;
+ }
+ } else {
+ /*
+ * pr_debug("nothing else to read...");
+ * reset
+ */
+ if (bufferlen > packetSize) {
+ kfree(buffer);
+
+ buffer = packet;
+ bufferlen = packetSize;
+ }
+ break;
+ }
+ } else if (ret == -2) {
+ /* Handle large packet */
+ bufferlen = bytesRecvd;
+ buffer = kzalloc(bytesRecvd, GFP_KERNEL);
+
+ if (buffer == NULL) {
+ buffer = packet;
+ bufferlen = packetSize;
+
+ /* Try again next time around */
+ pr_err("unable to allocate buffer of size %d!",
+ bytesRecvd);
+ break;
+ }
+ }
+ } while (1);
+
+ PutInputDevice(device);
+
+ return;
+ }
+
+ static int MousevscConnectToVsp(struct hv_device *Device)
+ {
+ int ret = 0;
+ struct mousevsc_dev *inputDevice;
+ struct mousevsc_prt_msg *request;
+ struct mousevsc_prt_msg *response;
+
+ inputDevice = GetInputDevice(Device);
+
+ if (!inputDevice) {
+ pr_err("unable to get input device...device being destroyed?");
+ return -1;
+ }
+
+ init_waitqueue_head(&inputDevice->ProtocolWaitEvent);
+ init_waitqueue_head(&inputDevice->DeviceInfoWaitEvent);
+
+ request = &inputDevice->ProtocolReq;
+
+ /*
+ * Now, initiate the vsc/vsp initialization protocol on the open channel
+ */
+ memset(request, sizeof(struct mousevsc_prt_msg), 0);
+
+ request->type = PipeMessageData;
+ request->size = sizeof(struct synthhid_protocol_request);
+
+ request->request.header.type = SynthHidProtocolRequest;
+ request->request.header.size = sizeof(unsigned long);
+ request->request.version_requested.version = SYNTHHID_INPUT_VERSION;
+
+ pr_info("synthhid protocol request...");
+
+ ret = vmbus_sendpacket(Device->channel, request,
+ sizeof(struct pipe_prt_msg) -
+ sizeof(unsigned char) +
+ sizeof(struct synthhid_protocol_request),
+ (unsigned long)request,
+ VM_PKT_DATA_INBAND,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (ret != 0) {
+ pr_err("unable to send synthhid protocol request.");
+ goto Cleanup;
+ }
+
+ inputDevice->protocol_wait_condition = 0;
+ wait_event_timeout(inputDevice->ProtocolWaitEvent, inputDevice->protocol_wait_condition, msecs_to_jiffies(1000));
+ if (inputDevice->protocol_wait_condition == 0) {
+ ret = -ETIMEDOUT;
+ goto Cleanup;
+ }
+
+ response = &inputDevice->ProtocolResp;
+
+ if (!response->response.approved) {
+ pr_err("synthhid protocol request failed (version %d)",
+ SYNTHHID_INPUT_VERSION);
+ ret = -1;
+ goto Cleanup;
+ }
+
+ inputDevice->device_wait_condition = 0;
+ wait_event_timeout(inputDevice->DeviceInfoWaitEvent, inputDevice->device_wait_condition, msecs_to_jiffies(1000));
+ if (inputDevice->device_wait_condition == 0) {
+ ret = -ETIMEDOUT;
+ goto Cleanup;
+ }
+
+ /*
+ * We should have gotten the device attr, hid desc and report
+ * desc at this point
+ */
+ if (!inputDevice->DeviceInfoStatus)
+ pr_info("**** input channel up and running!! ****");
+ else
+ ret = -1;
+
+ Cleanup:
+ PutInputDevice(Device);
+
+ return ret;
+ }
+
+ static int MousevscOnDeviceAdd(struct hv_device *Device, void *AdditionalInfo)
+ {
+ int ret = 0;
+ struct mousevsc_dev *inputDevice;
+ struct mousevsc_drv_obj *inputDriver;
+ struct hv_input_dev_info dev_info;
+
+ inputDevice = AllocInputDevice(Device);
+
+ if (!inputDevice) {
+ ret = -1;
+ goto Cleanup;
+ }
+
+ inputDevice->bInitializeComplete = false;
+
+ /* Open the channel */
+ ret = vmbus_open(Device->channel,
+ INPUTVSC_SEND_RING_BUFFER_SIZE,
+ INPUTVSC_RECV_RING_BUFFER_SIZE,
+ NULL,
+ 0,
+ MousevscOnChannelCallback,
+ Device
+ );
+
+ if (ret != 0) {
+ pr_err("unable to open channel: %d", ret);
+ FreeInputDevice(inputDevice);
+ return -1;
+ }
+
+ pr_info("InputVsc channel open: %d", ret);
+
+ ret = MousevscConnectToVsp(Device);
+
+ if (ret != 0) {
+ pr_err("unable to connect channel: %d", ret);
+
+ vmbus_close(Device->channel);
+ FreeInputDevice(inputDevice);
+ return ret;
+ }
+
+ inputDriver = (struct mousevsc_drv_obj *)inputDevice->Device->drv;
+
+ dev_info.vendor = inputDevice->hid_dev_info.vendor;
+ dev_info.product = inputDevice->hid_dev_info.product;
+ dev_info.version = inputDevice->hid_dev_info.version;
+ strcpy(dev_info.name, "Microsoft Vmbus HID-compliant Mouse");
+
+ /* Send the device info back up */
+ deviceinfo_callback(Device, &dev_info);
+
+ /* Send the report desc back up */
+ /* workaround SA-167 */
+ if (inputDevice->ReportDesc[14] == 0x25)
+ inputDevice->ReportDesc[14] = 0x29;
+
+ reportdesc_callback(Device, inputDevice->ReportDesc,
+ inputDevice->ReportDescSize);
+
+ inputDevice->bInitializeComplete = true;
+
+ Cleanup:
+ return ret;
+ }
+
+ static int MousevscOnDeviceRemove(struct hv_device *Device)
+ {
+ struct mousevsc_dev *inputDevice;
+ int ret = 0;
+
+ pr_info("disabling input device (%p)...",
+ Device->ext);
+
+ inputDevice = ReleaseInputDevice(Device);
+
+
+ /*
+ * At this point, all outbound traffic should be disable. We only
+ * allow inbound traffic (responses) to proceed
+ *
+ * so that outstanding requests can be completed.
+ */
+ while (inputDevice->NumOutstandingRequests) {
+ pr_info("waiting for %d requests to complete...", inputDevice->NumOutstandingRequests);
+
+ udelay(100);
+ }
+
+ pr_info("removing input device (%p)...", Device->ext);
+
+ inputDevice = FinalReleaseInputDevice(Device);
+
+ pr_info("input device (%p) safe to remove", inputDevice);
+
+ /* Close the channel */
+ vmbus_close(Device->channel);
+
+ FreeInputDevice(inputDevice);
+
+ return ret;
+ }
+
+ static void MousevscOnCleanup(struct hv_driver *drv)
+ {
+ }
+
+ /*
+ * Data types
+ */
+ struct input_device_context {
+ struct hv_device *device_ctx;
+ struct hid_device *hid_device;
+ struct hv_input_dev_info device_info;
+ int connected;
+ };
+
+
+ static struct mousevsc_drv_obj g_mousevsc_drv;
+
+ static void deviceinfo_callback(struct hv_device *dev, struct hv_input_dev_info *info)
+ {
+ struct input_device_context *input_device_ctx =
+ dev_get_drvdata(&dev->device);
+
+ memcpy(&input_device_ctx->device_info, info,
+ sizeof(struct hv_input_dev_info));
+
+ DPRINT_INFO(INPUTVSC_DRV, "%s", __func__);
+ }
+
+ static void inputreport_callback(struct hv_device *dev, void *packet, u32 len)
+ {
+ int ret = 0;
+
+ struct input_device_context *input_dev_ctx =
+ dev_get_drvdata(&dev->device);
+
+ ret = hid_input_report(input_dev_ctx->hid_device,
+ HID_INPUT_REPORT, packet, len, 1);
+
+ DPRINT_DBG(INPUTVSC_DRV, "hid_input_report (ret %d)", ret);
+ }
+
+ static int mousevsc_hid_open(struct hid_device *hid)
+ {
+ return 0;
+ }
+
+ static void mousevsc_hid_close(struct hid_device *hid)
+ {
+ }
+
+ static int mousevsc_probe(struct device *device)
+ {
+ int ret = 0;
+
+ struct hv_driver *drv =
+ drv_to_hv_drv(device->driver);
+ struct mousevsc_drv_obj *mousevsc_drv_obj = drv->priv;
+
+ struct hv_device *device_obj = device_to_hv_device(device);
+ struct input_device_context *input_dev_ctx;
+
+ input_dev_ctx = kmalloc(sizeof(struct input_device_context),
+ GFP_KERNEL);
+
+ dev_set_drvdata(device, input_dev_ctx);
+
+ /* Call to the vsc driver to add the device */
+ ret = mousevsc_drv_obj->Base.dev_add(device_obj, NULL);
+
+ if (ret != 0) {
+ DPRINT_ERR(INPUTVSC_DRV, "unable to add input vsc device");
+
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static int mousevsc_remove(struct device *device)
+ {
+ int ret = 0;
+
+ struct hv_driver *drv =
+ drv_to_hv_drv(device->driver);
+ struct mousevsc_drv_obj *mousevsc_drv_obj = drv->priv;
+
+ struct hv_device *device_obj = device_to_hv_device(device);
+ struct input_device_context *input_dev_ctx;
+
+ input_dev_ctx = kmalloc(sizeof(struct input_device_context),
+ GFP_KERNEL);
+
+ dev_set_drvdata(device, input_dev_ctx);
+
+ if (input_dev_ctx->connected) {
+ hidinput_disconnect(input_dev_ctx->hid_device);
+ input_dev_ctx->connected = 0;
+ }
+
+ if (!mousevsc_drv_obj->Base.dev_rm)
+ return -1;
+
+ /*
+ * Call to the vsc driver to let it know that the device
+ * is being removed
+ */
+ ret = mousevsc_drv_obj->Base.dev_rm(device_obj);
+
+ if (ret != 0) {
+ DPRINT_ERR(INPUTVSC_DRV,
+ "unable to remove vsc device (ret %d)", ret);
+ }
+
+ kfree(input_dev_ctx);
+
+ return ret;
+ }
+
+ static void reportdesc_callback(struct hv_device *dev, void *packet, u32 len)
+ {
+ struct input_device_context *input_device_ctx =
+ dev_get_drvdata(&dev->device);
+ struct hid_device *hid_dev;
+
+ /* hid_debug = -1; */
+ hid_dev = kmalloc(sizeof(struct hid_device), GFP_KERNEL);
+
+ if (hid_parse_report(hid_dev, packet, len)) {
+ DPRINT_INFO(INPUTVSC_DRV, "Unable to call hd_parse_report");
+ return;
+ }
+
+ if (hid_dev) {
+ DPRINT_INFO(INPUTVSC_DRV, "hid_device created");
+
+ hid_dev->ll_driver->open = mousevsc_hid_open;
+ hid_dev->ll_driver->close = mousevsc_hid_close;
+
+ hid_dev->bus = BUS_VIRTUAL;
+ hid_dev->vendor = input_device_ctx->device_info.vendor;
+ hid_dev->product = input_device_ctx->device_info.product;
+ hid_dev->version = input_device_ctx->device_info.version;
+ hid_dev->dev = dev->device;
+
+ sprintf(hid_dev->name, "%s",
+ input_device_ctx->device_info.name);
+
+ /*
+ * HJ Do we want to call it with a 0
+ */
+ if (!hidinput_connect(hid_dev, 0)) {
+ hid_dev->claimed |= HID_CLAIMED_INPUT;
+
+ input_device_ctx->connected = 1;
+
+ DPRINT_INFO(INPUTVSC_DRV,
+ "HID device claimed by input\n");
+ }
+
+ if (!hid_dev->claimed) {
+ DPRINT_ERR(INPUTVSC_DRV,
+ "HID device not claimed by "
+ "input or hiddev\n");
+ }
+
+ input_device_ctx->hid_device = hid_dev;
+ }
+
+ kfree(hid_dev);
+ }
+
+ static int mousevsc_drv_exit_cb(struct device *dev, void *data)
+ {
+ struct device **curr = (struct device **)data;
+ *curr = dev;
+
+ return 1;
+ }
+
+ static void mousevsc_drv_exit(void)
+ {
+ struct mousevsc_drv_obj *mousevsc_drv_obj = &g_mousevsc_drv;
+ struct hv_driver *drv = &g_mousevsc_drv.Base;
+ int ret;
+
+ struct device *current_dev = NULL;
+
+ while (1) {
+ current_dev = NULL;
+
+ /* Get the device */
+ ret = driver_for_each_device(&drv->driver, NULL,
+ (void *)¤t_dev,
+ mousevsc_drv_exit_cb);
+ if (ret)
+ printk(KERN_ERR "Can't find mouse device!\n");
+
+ if (current_dev == NULL)
+ break;
+
+ /* Initiate removal from the top-down */
+ device_unregister(current_dev);
+ }
+
+ if (mousevsc_drv_obj->Base.cleanup)
+ mousevsc_drv_obj->Base.cleanup(&mousevsc_drv_obj->Base);
+
+ vmbus_child_driver_unregister(&drv->driver);
+
+ return;
+ }
+
+ static int mouse_vsc_initialize(struct hv_driver *Driver)
+ {
+ struct mousevsc_drv_obj *inputDriver =
+ (struct mousevsc_drv_obj *)Driver;
+ int ret = 0;
+
+ Driver->name = driver_name;
+ memcpy(&Driver->dev_type, &mouse_guid,
+ sizeof(struct hv_guid));
+
+ /* Setup the dispatch table */
+ inputDriver->Base.dev_add = MousevscOnDeviceAdd;
+ inputDriver->Base.dev_rm = MousevscOnDeviceRemove;
+ inputDriver->Base.cleanup = MousevscOnCleanup;
+
+ return ret;
+ }
+
+
+ static int __init mousevsc_init(void)
+ {
+ struct mousevsc_drv_obj *input_drv_obj = &g_mousevsc_drv;
+ struct hv_driver *drv = &g_mousevsc_drv.Base;
+
+ DPRINT_INFO(INPUTVSC_DRV, "Hyper-V Mouse driver initializing.");
+
+ /* Callback to client driver to complete the initialization */
+ mouse_vsc_initialize(&input_drv_obj->Base);
+
+ drv->driver.name = input_drv_obj->Base.name;
+ drv->priv = input_drv_obj;
+
+ drv->driver.probe = mousevsc_probe;
+ drv->driver.remove = mousevsc_remove;
+
+ /* The driver belongs to vmbus */
+ vmbus_child_driver_register(&drv->driver);
+
+ return 0;
+ }
+
+ static void __exit mousevsc_exit(void)
+ {
+ mousevsc_drv_exit();
+ }
+
+ /*
+ * We don't want to automatically load this driver just yet, it's quite
+ * broken. It's safe if you want to load it yourself manually, but
+ * don't inflict it on unsuspecting users, that's just mean.
+ */
+ #if 0
+
+ /*
+ * We use a PCI table to determine if we should autoload this driver This is
+ * needed by distro tools to determine if the hyperv drivers should be
+ * installed and/or configured. We don't do anything else with the table, but
+ * it needs to be present.
+ */
+ const static struct pci_device_id microsoft_hv_pci_table[] = {
+ { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */
+ { 0 }
+ };
+ MODULE_DEVICE_TABLE(pci, microsoft_hv_pci_table);
+ #endif
+
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(HV_DRV_VERSION);
+ module_init(mousevsc_init);
+ module_exit(mousevsc_exit);
+
obj-$(CONFIG_NFS_COMMON) += nfs_common/
obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
+obj-$(CONFIG_FS_RICHACL) += richacl.o
+richacl-y := richacl_base.o richacl_inode.o richacl_xattr.o
+
+ obj-$(CONFIG_FHANDLE) += fhandle.o
+
obj-y += quota/
obj-$(CONFIG_PROC_FS) += proc/
obj-$(CONFIG_BTRFS_FS) += btrfs/
obj-$(CONFIG_GFS2_FS) += gfs2/
obj-$(CONFIG_EXOFS_FS) += exofs/
+obj-$(CONFIG_NOVFS) += novfs/
obj-$(CONFIG_CEPH_FS) += ceph/
+ obj-$(CONFIG_PSTORE) += pstore/
if (strcmp(name, "") != 0)
return -EINVAL;
- if (!test_opt(inode->i_sb, POSIX_ACL))
+ if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
- if (!is_owner_or_cap(inode))
+ if (!inode_owner_or_capable(inode))
return -EPERM;
if (value) {
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"
+ #include <trace/events/ext4.h>
/*
* define how far ahead to read directories while searching them.
*/
if (test_opt(sb, OLDALLOC))
seq_puts(seq, ",oldalloc");
#ifdef CONFIG_EXT4_FS_XATTR
- if (test_opt(sb, XATTR_USER) &&
- !(def_mount_opts & EXT4_DEFM_XATTR_USER))
+ if (test_opt(sb, XATTR_USER))
seq_puts(seq, ",user_xattr");
- if (!test_opt(sb, XATTR_USER) &&
- (def_mount_opts & EXT4_DEFM_XATTR_USER)) {
+ if (!test_opt(sb, XATTR_USER))
seq_puts(seq, ",nouser_xattr");
- }
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
- if (test_opt(sb, POSIX_ACL) && !(def_mount_opts & EXT4_DEFM_ACL))
- seq_puts(seq, ",acl");
- if (!test_opt(sb, POSIX_ACL) && (def_mount_opts & EXT4_DEFM_ACL))
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+ if (sb->s_flags & MS_POSIXACL) {
+ if (!(def_mount_opts & EXT4_DEFM_ACL))
+ seq_puts(seq, ",acl");
+ } else if (sb->s_flags & MS_RICHACL)
+ seq_puts(seq, ",richacl");
+ else if (def_mount_opts & EXT4_DEFM_ACL)
seq_puts(seq, ",noacl");
#endif
if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {
}
if (def_mount_opts & EXT4_DEFM_UID16)
set_opt(sb, NO_UID32);
+ /* xattr user namespace & acls are now defaulted on */
#ifdef CONFIG_EXT4_FS_XATTR
- if (def_mount_opts & EXT4_DEFM_XATTR_USER)
- set_opt(sb, XATTR_USER);
+ set_opt(sb, XATTR_USER);
#endif
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
- set_opt(sb, POSIX_ACL);
+#if defined(CONFIG_EXT4_FS_POSIX_ACL)
- if (def_mount_opts & EXT4_DEFM_ACL)
- sb->s_flags |= MS_POSIXACL;
++ sb->s_flags |= MS_POSIXACL;
#endif
+ set_opt(sb, MBLK_IO_SUBMIT);
if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
set_opt(sb, JOURNAL_DATA);
else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_ORDERED)
goto exit;
}
- if (IS_ERR(nd->intent.open.file)) {
- error = PTR_ERR(nd->intent.open.file);
- goto exit_mutex_unlock;
- }
+ path->dentry = dentry;
+ path->mnt = nd->path.mnt;
/* Negative dentry, just create the file */
- if (!path->dentry->d_inode) {
+ if (!dentry->d_inode) {
+ int mode = op->mode;
- if (!IS_POSIXACL(dir->d_inode))
++ if (!IS_ACL(dir->d_inode))
+ mode &= ~current_umask();
/*
* This write is needed to ensure that a
- * ro->rw transition does not occur between
+ * rw->ro transition does not occur between
* the time when the file is created and when
* a permanent write count is taken through
* the 'struct file' in nameidata_to_filp().
memset(desc, 0, sizeof(*desc));
desc->file = filp;
- desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie;
+ desc->dir_cookie = &dir_ctx->dir_cookie;
desc->decode = NFS_PROTO(inode)->decode_dirent;
desc->plus = NFS_USE_READDIRPLUS(inode);
+ if (filp->f_pos > 0 && !test_bit(NFS_INO_SEEN_GETATTR, &NFS_I(inode)->flags))
+ desc->plus = 0;
+ clear_bit(NFS_INO_SEEN_GETATTR, &NFS_I(inode)->flags);
nfs_block_sillyrename(dentry);
res = nfs_revalidate_mapping(inode, filp->f_mapping);
--- /dev/null
+/*
+ * Novell NCP Redirector for Linux
+ * Author: James Turner
+ *
+ * This file contains all the functions necessary for sending commands to our
+ * daemon module.
+ *
+ * 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/fs.h>
+#include <linux/mount.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+#include <linux/pagemap.h>
- #include <linux/smp_lock.h>
+#include <linux/semaphore.h>
++#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/time.h>
+
+#include "vfs.h"
+#include "nwcapi.h"
+#include "commands.h"
+#include "nwerror.h"
+
+#define QUEUE_SENDING 0
+#define QUEUE_WAITING 1
+#define QUEUE_TIMEOUT 2
+#define QUEUE_ACKED 3
+#define QUEUE_DONE 4
+
+#define TIMEOUT_VALUE 10
+
+#define DH_TYPE_UNDEFINED 0
+#define DH_TYPE_STREAM 1
+#define DH_TYPE_CONNECTION 2
+
+struct daemon_queue {
+ struct list_head list; /* Must be first entry */
+ spinlock_t lock; /* Used to control access to list */
+ struct semaphore semaphore; /* Used to signal when data is available */
+};
+
+struct daemon_cmd {
+ struct list_head list; /* Must be first entry */
+ atomic_t reference;
+ unsigned int status;
+ unsigned int flags;
+ struct semaphore semaphore;
+ unsigned long sequence;
+ struct timer_list timer;
+ void *request;
+ unsigned long reqlen;
+ void *data;
+ int datalen;
+ void *reply;
+ unsigned long replen;
+};
+
+struct daemon_handle {
+ struct list_head list;
+ rwlock_t lock;
+ struct novfs_schandle session;
+};
+
+struct daemon_resource {
+ struct list_head list;
+ int type;
+ void *connection;
+ unsigned char handle[6];
+ mode_t mode;
+ loff_t size;
+};
+
+struct drive_map {
+ struct list_head list; /* Must be first item */
+ struct novfs_schandle session;
+ unsigned long hash;
+ int namelen;
+ char name[1];
+};
+
+static void Queue_get(struct daemon_cmd *Que);
+static void Queue_put(struct daemon_cmd *Que);
+static void RemoveDriveMaps(void);
+static int NwdConvertLocalHandle(struct novfs_xplat *pdata, struct daemon_handle *DHandle);
+static int NwdConvertNetwareHandle(struct novfs_xplat *pdata, struct daemon_handle *DHandle);
+static int set_map_drive(struct novfs_xplat *pdata, struct novfs_schandle Session);
+static int unmap_drive(struct novfs_xplat *pdata, struct novfs_schandle Session);
+static int NwdGetMountPath(struct novfs_xplat *pdata);
+static long local_unlink(const char *pathname);
+
+/*===[ Global variables ]=================================================*/
+static struct daemon_queue Daemon_Queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(Read_waitqueue);
+
+static atomic_t Sequence = ATOMIC_INIT(-1);
+static atomic_t Daemon_Open_Count = ATOMIC_INIT(0);
+
+static unsigned long Daemon_Command_Timeout = TIMEOUT_VALUE;
+
+static DEFINE_MUTEX(DriveMapLock);
+static LIST_HEAD(DriveMapList);
+
+int novfs_max_iosize = PAGE_SIZE;
+
+void novfs_daemon_queue_init()
+{
+ INIT_LIST_HEAD(&Daemon_Queue.list);
+ spin_lock_init(&Daemon_Queue.lock);
+ sema_init(&Daemon_Queue.semaphore, 0);
+}
+
+void novfs_daemon_queue_exit(void)
+{
+ /* Does nothing for now but we maybe should clear the queue. */
+}
+
+/*++======================================================================*/
+static void novfs_daemon_timer(unsigned long data)
+{
+ struct daemon_cmd *que = (struct daemon_cmd *)data;
+
+ if (QUEUE_ACKED != que->status) {
+ que->status = QUEUE_TIMEOUT;
+ }
+ up(&que->semaphore);
+}
+
+/*++======================================================================*/
+int Queue_Daemon_Command(void *request,
+ unsigned long reqlen, void *data, int dlen, void **reply, unsigned long *replen, int interruptible)
+{
+ struct daemon_cmd *que;
+ int retCode = 0;
+ uint64_t ts1, ts2;
+
+ ts1 = get_nanosecond_time();
+
+ DbgPrint("0x%p %d", request, reqlen);
+
+ if (atomic_read(&Daemon_Open_Count)) {
+
+ que = kmalloc(sizeof(*que), GFP_KERNEL);
+
+ DbgPrint("que=0x%p", que);
+ if (que) {
+ atomic_set(&que->reference, 0);
+ que->status = QUEUE_SENDING;
+ que->flags = 0;
+
+ sema_init(&que->semaphore, 0);
+
+ que->sequence = atomic_inc_return(&Sequence);
+
+ ((struct novfs_command_request_header *)request)->SequenceNumber = que->sequence;
+
+ /*
+ * Setup and start que timer
+ */
+ init_timer(&que->timer);
+ que->timer.expires = jiffies + (HZ * Daemon_Command_Timeout);
+ que->timer.data = (unsigned long)que;
+ que->timer.function = novfs_daemon_timer;
+ add_timer(&que->timer);
+
+ /*
+ * Setup request
+ */
+ que->request = request;
+ que->reqlen = reqlen;
+ que->data = data;
+ que->datalen = dlen;
+ que->reply = NULL;
+ que->replen = 0;
+
+ /*
+ * Added entry to queue.
+ */
+ /*
+ * Check to see if interruptible and set flags.
+ */
+ if (interruptible) {
+ que->flags |= INTERRUPTIBLE;
+ }
+
+ Queue_get(que);
+
+ spin_lock(&Daemon_Queue.lock);
+ list_add_tail(&que->list, &Daemon_Queue.list);
+ spin_unlock(&Daemon_Queue.lock);
+
+ /*
+ * Signal that there is data to be read
+ */
+ up(&Daemon_Queue.semaphore);
+
+ /*
+ * Give a change to the other processes.
+ */
+ yield();
+
+ /*
+ * Block waiting for reply or timeout
+ */
+ down(&que->semaphore);
+
+ if (QUEUE_ACKED == que->status) {
+ que->status = QUEUE_WAITING;
+ mod_timer(&que->timer, jiffies + (HZ * 2 * Daemon_Command_Timeout));
+ if (interruptible) {
+ retCode = down_interruptible(&que->semaphore);
+ } else {
+ down(&que->semaphore);
+ }
+ }
+
+ /*
+ * Delete timer
+ */
+ del_timer(&que->timer);
+
+ /*
+ * Check for timeout
+ */
+ if ((QUEUE_TIMEOUT == que->status)
+ && (NULL == que->reply)) {
+ DbgPrint("Timeout");
+ retCode = -ETIME;
+ }
+ *reply = que->reply;
+ *replen = que->replen;
+
+ /*
+ * Remove item from queue
+ */
+ Queue_put(que);
+
+ } else { /* Error case with no memory */
+
+ retCode = -ENOMEM;
+ *reply = NULL;
+ *replen = 0;
+ }
+ } else {
+ retCode = -EIO;
+ *reply = NULL;
+ *replen = 0;
+
+ }
+ ts2 = get_nanosecond_time();
+ ts2 = ts2 - ts1;
+
+ DbgPrint("%llu retCode=%d", ts2, retCode);
+ return (retCode);
+}
+
+static void Queue_get(struct daemon_cmd *Que)
+{
+ DbgPrint("que=0x%p %d", Que, atomic_read(&Que->reference));
+ atomic_inc(&Que->reference);
+}
+
+static void Queue_put(struct daemon_cmd *Que)
+{
+
+ DbgPrint("que=0x%p %d", Que, atomic_read(&Que->reference));
+ spin_lock(&Daemon_Queue.lock);
+
+ if (atomic_dec_and_test(&Que->reference)) {
+ /*
+ * Remove item from queue
+ */
+ list_del(&Que->list);
+ spin_unlock(&Daemon_Queue.lock);
+
+ /*
+ * Free item memory
+ */
+ kfree(Que);
+ } else {
+ spin_unlock(&Daemon_Queue.lock);
+ }
+}
+
+struct daemon_cmd *get_next_queue(int Set_Queue_Waiting)
+{
+ struct daemon_cmd *que;
+
+ DbgPrint("que=0x%p", Daemon_Queue.list.next);
+
+ spin_lock(&Daemon_Queue.lock);
+ que = (struct daemon_cmd *)Daemon_Queue.list.next;
+
+ while (que && (que != (struct daemon_cmd *)&Daemon_Queue.list.next)
+ && (que->status != QUEUE_SENDING)) {
+ que = (struct daemon_cmd *)que->list.next;
+ }
+
+ if ((NULL == que) || (que == (struct daemon_cmd *)&Daemon_Queue.list)
+ || (que->status != QUEUE_SENDING)) {
+ que = NULL;
+ } else if (Set_Queue_Waiting) {
+ que->status = QUEUE_WAITING;
+ }
+
+ if (que) {
+ atomic_inc(&que->reference);
+ }
+
+ spin_unlock(&Daemon_Queue.lock);
+
+ DbgPrint("return=0x%p", que);
+ return (que);
+}
+
+static struct daemon_cmd *find_queue(unsigned long sequence)
+{
+ struct daemon_cmd *que;
+
+ DbgPrint("0x%x", sequence);
+
+ spin_lock(&Daemon_Queue.lock);
+ que = (struct daemon_cmd *)Daemon_Queue.list.next;
+
+ while (que && (que != (struct daemon_cmd *)&Daemon_Queue.list.next)
+ && (que->sequence != sequence)) {
+ que = (struct daemon_cmd *)que->list.next;
+ }
+
+ if ((NULL == que)
+ || (que == (struct daemon_cmd *)&Daemon_Queue.list.next)
+ || (que->sequence != sequence)) {
+ que = NULL;
+ }
+
+ if (que) {
+ atomic_inc(&que->reference);
+ }
+
+ spin_unlock(&Daemon_Queue.lock);
+
+ DbgPrint("return 0x%p", que);
+ return (que);
+}
+
+int novfs_daemon_open_control(struct inode *Inode, struct file *File)
+{
+ DbgPrint("pid=%d Count=%d", current->pid, atomic_read(&Daemon_Open_Count));
+ atomic_inc(&Daemon_Open_Count);
+
+ return (0);
+}
+
+int novfs_daemon_close_control(struct inode *Inode, struct file *File)
+{
+ struct daemon_cmd *que;
+
+ DbgPrint("pid=%d Count=%d", current->pid, atomic_read(&Daemon_Open_Count));
+
+ if (atomic_dec_and_test(&Daemon_Open_Count)) {
+ /*
+ * Signal any pending que itmes.
+ */
+
+ spin_lock(&Daemon_Queue.lock);
+ que = (struct daemon_cmd *)Daemon_Queue.list.next;
+
+ while (que && (que != (struct daemon_cmd *)&Daemon_Queue.list.next)
+ && (que->status != QUEUE_DONE)) {
+ que->status = QUEUE_TIMEOUT;
+ up(&que->semaphore);
+
+ que = (struct daemon_cmd *)que->list.next;
+ }
+ spin_unlock(&Daemon_Queue.lock);
+
+ RemoveDriveMaps();
+
+ novfs_scope_cleanup();
+ }
+
+ return (0);
+}
+
+ssize_t novfs_daemon_cmd_send(struct file * file, char *buf, size_t len, loff_t * off)
+{
+ struct daemon_cmd *que;
+ size_t retValue = 0;
+ int Finished = 0;
+ struct novfs_data_list *dlist;
+ int i, dcnt, bcnt, ccnt, error;
+ char *vadr;
+ unsigned long cpylen;
+
+ DbgPrint("%u %lld", len, *off);
+ if (len > novfs_max_iosize) {
+ novfs_max_iosize = len;
+ }
+
+ while (!Finished) {
+ que = get_next_queue(1);
+ DbgPrint("0x%p", que);
+ if (que) {
+ retValue = que->reqlen;
+ if (retValue > len) {
+ retValue = len;
+ }
+ if (retValue > 0x80)
+ novfs_dump(0x80, que->request);
+ else
+ novfs_dump(retValue, que->request);
+
+ cpylen = copy_to_user(buf, que->request, retValue);
+ if (que->datalen && (retValue < len)) {
+ buf += retValue;
+ dlist = que->data;
+ dcnt = que->datalen;
+ for (i = 0; i < dcnt; i++, dlist++) {
+ if (DLREAD == dlist->rwflag) {
+ bcnt = dlist->len;
+ DbgPrint("page=0x%p "
+ "offset=0x%p len=%d", i, dlist->page, dlist->offset, dlist->len);
+ if ((bcnt + retValue) <= len) {
+ void *km_adr = NULL;
+
+ if (dlist->page) {
+ km_adr = kmap(dlist->page);
+ vadr = km_adr;
+ vadr += (unsigned long)
+ dlist->offset;
+ } else {
+ vadr = dlist->offset;
+ }
+
+ ccnt = copy_to_user(buf, vadr, bcnt);
+
+ DbgPrint("Copy %d from 0x%p to 0x%p.", bcnt, vadr, buf);
+ if (bcnt > 0x80)
+ novfs_dump(0x80, vadr);
+ else
+ novfs_dump(bcnt, vadr);
+
+ if (km_adr) {
+ kunmap(dlist->page);
+ }
+
+ retValue += bcnt;
+ buf += bcnt;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ Queue_put(que);
+ break;
+ }
+
+ if (O_NONBLOCK & file->f_flags) {
+ retValue = -EAGAIN;
+ break;
+ } else {
+ if ((error = down_interruptible(&Daemon_Queue.semaphore))) {
+ DbgPrint("after down_interruptible error...%d", error);
+ retValue = -EINTR;
+ break;
+ }
+ DbgPrint("after down_interruptible");
+ }
+ }
+
+ *off = *off;
+
+ DbgPrint("return 0x%x", retValue);
+
+ return (retValue);
+}
+
+ssize_t novfs_daemon_recv_reply(struct file * file, const char *buf, size_t nbytes, loff_t * ppos)
+{
+ struct daemon_cmd *que;
+ size_t retValue = 0;
+ void *reply;
+ unsigned long sequence, cpylen;
+
+ struct novfs_data_list *dlist;
+ char *vadr;
+ int i;
+
+ DbgPrint("buf=0x%p nbytes=%d ppos=%llx", buf, nbytes, *ppos);
+
+ /*
+ * Get sequence number from reply buffer
+ */
+
+ cpylen = copy_from_user(&sequence, buf, sizeof(sequence));
+
+ /*
+ * Find item based on sequence number
+ */
+ que = find_queue(sequence);
+
+ DbgPrint("0x%x 0x%p %d", sequence, que, nbytes);
+ if (que) {
+ do {
+ retValue = nbytes;
+ /*
+ * Ack packet from novfsd. Remove timer and
+ * return
+ */
+ if (nbytes == sizeof(sequence)) {
+ que->status = QUEUE_ACKED;
+ break;
+ }
+
+ if (NULL != (dlist = que->data)) {
+ int thiscopy, left = nbytes;
+ retValue = 0;
+
+ DbgPrint("dlist=0x%p count=%d", dlist, que->datalen);
+ for (i = 0; (i < que->datalen) && (retValue < nbytes); i++, dlist++) {
+ __DbgPrint("\n"
+ " dlist[%d].page: 0x%p\n"
+ " dlist[%d].offset: 0x%p\n"
+ " dlist[%d].len: 0x%x\n"
+ " dlist[%d].rwflag: 0x%x\n",
+ i, dlist->page, i, dlist->offset, i, dlist->len, i, dlist->rwflag);
+
+ if (DLWRITE == dlist->rwflag) {
+ void *km_adr = NULL;
+
+ if (dlist->page) {
+ km_adr = kmap(dlist->page);
+ vadr = km_adr;
+ vadr += (unsigned long)dlist->offset;
+ } else {
+ vadr = dlist->offset;
+ }
+
+ thiscopy = dlist->len;
+ if (thiscopy > left) {
+ thiscopy = left;
+ dlist->len = left;
+ }
+ cpylen = copy_from_user(vadr, buf, thiscopy);
+
+ if (thiscopy > 0x80)
+ novfs_dump(0x80, vadr);
+ else
+ novfs_dump(thiscopy, vadr);
+
+ if (km_adr) {
+ kunmap(dlist->page);
+ }
+
+ left -= thiscopy;
+ retValue += thiscopy;
+ buf += thiscopy;
+ }
+ }
+ que->replen = retValue;
+ } else {
+ reply = kmalloc(nbytes, GFP_KERNEL);
+ DbgPrint("reply=0x%p", reply);
+ if (reply) {
+ retValue = nbytes;
+ que->reply = reply;
+ que->replen = nbytes;
+
+ retValue -= copy_from_user(reply, buf, retValue);
+ if (retValue > 0x80)
+ novfs_dump(0x80, reply);
+ else
+ novfs_dump(retValue, reply);
+
+ } else {
+ retValue = -ENOMEM;
+ }
+ }
+
+ /*
+ * Set status that packet is done.
+ */
+ que->status = QUEUE_DONE;
+
+ } while (0);
+ up(&que->semaphore);
+ Queue_put(que);
+ }
+
+ DbgPrint("return 0x%x", retValue);
+
+ return (retValue);
+}
+
+int novfs_do_login(struct ncl_string *Server, struct ncl_string *Username,
+ struct ncl_string *Password, void **lgnId, struct novfs_schandle *Session)
+{
+ struct novfs_login_user_request *cmd;
+ struct novfs_login_user_reply *reply;
+ unsigned long replylen = 0;
+ int retCode, cmdlen, datalen;
+ unsigned char *data;
+
+ datalen = Server->len + Username->len + Password->len;
+ cmdlen = sizeof(*cmd) + datalen;
+ cmd = kmalloc(cmdlen, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ data = (unsigned char *)cmd + sizeof(*cmd);
+ cmd->Command.CommandType = VFS_COMMAND_LOGIN_USER;
+ cmd->Command.SequenceNumber = 0;
+ memcpy(&cmd->Command.SessionId, Session, sizeof(*Session));
+
+ cmd->srvNameType = Server->type;
+ cmd->serverLength = Server->len;
+ cmd->serverOffset = (unsigned long)(data - (unsigned char *)cmd);
+ memcpy(data, Server->buffer, Server->len);
+ data += Server->len;
+
+ cmd->usrNameType = Username->type;
+ cmd->userNameLength = Username->len;
+ cmd->userNameOffset = (unsigned long)(data - (unsigned char *)cmd);
+ memcpy(data, Username->buffer, Username->len);
+ data += Username->len;
+
+ cmd->pwdNameType = Password->type;
+ cmd->passwordLength = Password->len;
+ cmd->passwordOffset = (unsigned long)(data - (unsigned char *)cmd);
+ memcpy(data, Password->buffer, Password->len);
+ data += Password->len;
+
+ retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (reply->Reply.ErrorCode) {
+ retCode = reply->Reply.ErrorCode;
+ } else {
+ retCode = 0;
+ if (lgnId) {
+ *lgnId = reply->loginIdentity;
+ }
+ }
+ kfree(reply);
+ }
+ memset(cmd, 0, cmdlen);
+ kfree(cmd);
+ return (retCode);
+
+}
+
+int novfs_daemon_logout(struct qstr *Server, struct novfs_schandle *Session)
+{
+ struct novfs_logout_request *cmd;
+ struct novfs_logout_reply *reply;
+ unsigned long replylen = 0;
+ int retCode, cmdlen;
+
+ cmdlen = offsetof(struct novfs_logout_request, Name) + Server->len;
+ cmd = kmalloc(cmdlen, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->Command.CommandType = VFS_COMMAND_LOGOUT_USER;
+ cmd->Command.SequenceNumber = 0;
+ memcpy(&cmd->Command.SessionId, Session, sizeof(*Session));
+ cmd->length = Server->len;
+ memcpy(cmd->Name, Server->name, Server->len);
+
+ retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (reply->Reply.ErrorCode) {
+ retCode = -EIO;
+ }
+ kfree(reply);
+ }
+ kfree(cmd);
+ return (retCode);
+
+}
+
+int novfs_daemon_getpwuid(uid_t uid, int unamelen, char *uname)
+{
+ struct novfs_getpwuid_request cmd;
+ struct novfs_getpwuid_reply *reply;
+ unsigned long replylen = 0;
+ int retCode;
+
+ cmd.Command.CommandType = VFS_COMMAND_GETPWUD;
+ cmd.Command.SequenceNumber = 0;
+ SC_INITIALIZE(cmd.Command.SessionId);
+ cmd.uid = uid;
+
+ retCode = Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (reply->Reply.ErrorCode) {
+ retCode = -EIO;
+ } else {
+ retCode = 0;
+ memset(uname, 0, unamelen);
+ replylen = replylen - offsetof(struct
+ novfs_getpwuid_reply, UserName);
+ if (replylen) {
+ if (replylen > unamelen) {
+ retCode = -EINVAL;
+ replylen = unamelen - 1;
+ }
+ memcpy(uname, reply->UserName, replylen);
+ }
+ }
+ kfree(reply);
+ }
+ return (retCode);
+
+}
+
+int novfs_daemon_getversion(char *Buf, int length)
+{
+ struct novfs_get_version_request cmd;
+ struct novfs_get_version_reply *reply;
+ unsigned long replylen = 0;
+ int retVal = 0;
+
+ cmd.Command.CommandType = VFS_COMMAND_GET_VERSION;
+ cmd.Command.SequenceNumber = 0;
+ SC_INITIALIZE(cmd.Command.SessionId);
+
+ Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (reply->Reply.ErrorCode) {
+ retVal = -EIO;
+ } else {
+ retVal = replylen - offsetof(struct
+ novfs_get_version_reply, Version);
+ if (retVal < length) {
+ memcpy(Buf, reply->Version, retVal);
+ Buf[retVal] = '\0';
+ }
+ }
+ kfree(reply);
+ }
+ return (retVal);
+
+}
+
+static int daemon_login(struct novfs_login *Login, struct novfs_schandle *Session)
+{
+ int retCode = -ENOMEM;
+ struct novfs_login lLogin;
+ struct ncl_string server;
+ struct ncl_string username;
+ struct ncl_string password;
+
+ if (!copy_from_user(&lLogin, Login, sizeof(lLogin))) {
+ if (lLogin.Server.length > MAX_SERVER_NAME_LENGTH || lLogin.UserName.length > MAX_NAME_LEN ||
+ lLogin.Password.length > MAX_PASSWORD_LENGTH)
+ return -EINVAL;
+ server.buffer = kmalloc(lLogin.Server.length, GFP_KERNEL);
+ if (server.buffer) {
+ server.len = lLogin.Server.length;
+ server.type = NWC_STRING_TYPE_ASCII;
+ if (!copy_from_user((void *)server.buffer, lLogin.Server.data, server.len)) {
+ username.buffer = kmalloc(lLogin.UserName.length, GFP_KERNEL);
+ if (username.buffer) {
+ username.len = lLogin.UserName.length;
+ username.type = NWC_STRING_TYPE_ASCII;
+ if (!copy_from_user((void *)username.buffer, lLogin.UserName.data, username.len)) {
+ password.buffer = kmalloc(lLogin.Password.length, GFP_KERNEL);
+ if (password.buffer) {
+ password.len = lLogin.Password.length;
+ password.type = NWC_STRING_TYPE_ASCII;
+ if (!copy_from_user
+ ((void *)password.buffer, lLogin.Password.data, password.len)) {
+ retCode =
+ novfs_do_login(&server, &username, &password, NULL, Session);
+ if (!retCode) {
+ char *username;
+ username = novfs_scope_get_username();
+ if (username) {
+ novfs_add_to_root(username);
+ }
+ }
+ }
+ kfree(password.buffer);
+ }
+ }
+ kfree(username.buffer);
+ }
+ }
+ kfree(server.buffer);
+ }
+ }
+
+ return (retCode);
+}
+
+static int daemon_logout(struct novfs_logout *Logout, struct novfs_schandle *Session)
+{
+ struct novfs_logout lLogout;
+ struct qstr server;
+ int retCode = 0;
+
+ if (copy_from_user(&lLogout, Logout, sizeof(lLogout)))
+ return -EFAULT;
+ if (lLogout.Server.length > MAX_SERVER_NAME_LENGTH)
+ return -EINVAL;
+ server.name = kmalloc(lLogout.Server.length, GFP_KERNEL);
+ if (!server.name)
+ return -ENOMEM;
+ server.len = lLogout.Server.length;
+ if (copy_from_user((void *)server.name, lLogout.Server.data, server.len))
+ goto exit;
+ retCode = novfs_daemon_logout(&server, Session);
+exit:
+ kfree(server.name);
+ return (retCode);
+}
+
+int novfs_daemon_create_sessionId(struct novfs_schandle *SessionId)
+{
+ struct novfs_create_context_request cmd;
+ struct novfs_create_context_reply *reply;
+ unsigned long replylen = 0;
+ int retCode = 0;
+
+ DbgPrint("%d", current->pid);
+
+ cmd.Command.CommandType = VFS_COMMAND_CREATE_CONTEXT;
+ cmd.Command.SequenceNumber = 0;
+ SC_INITIALIZE(cmd.Command.SessionId);
+
+ retCode = Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (!reply->Reply.ErrorCode && replylen > sizeof(struct novfs_command_reply_header)) {
+ *SessionId = reply->SessionId;
+ retCode = 0;
+ } else {
+ SessionId->hTypeId = 0;
+ SessionId->hId = 0;
+ retCode = -EIO;
+ }
+ kfree(reply);
+ }
+ DbgPrint("SessionId=0x%llx", *SessionId);
+ return (retCode);
+}
+
+int novfs_daemon_destroy_sessionId(struct novfs_schandle SessionId)
+{
+ struct novfs_destroy_context_request cmd;
+ struct novfs_destroy_context_reply *reply;
+ unsigned long replylen = 0;
+ int retCode = 0;
+
+ DbgPrint("0x%p:%p", SessionId.hTypeId, SessionId.hId);
+
+ cmd.Command.CommandType = VFS_COMMAND_DESTROY_CONTEXT;
+ cmd.Command.SequenceNumber = 0;
+ cmd.Command.SessionId = SessionId;
+
+ retCode = Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (!reply->Reply.ErrorCode) {
+ struct drive_map *dm;
+ struct list_head *list;
+
+ retCode = 0;
+
+ /*
+ * When destroying the session check to see if there are any
+ * mapped drives. If there are then remove them.
+ */
+ mutex_lock(&DriveMapLock);
+ list_for_each(list, &DriveMapList) {
+ dm = list_entry(list, struct drive_map, list);
+ if (SC_EQUAL(SessionId, dm->session)) {
+ local_unlink(dm->name);
+ list = list->prev;
+ list_del(&dm->list);
+ kfree(dm);
+ }
+
+ }
+ mutex_unlock(&DriveMapLock);
+
+ } else {
+ retCode = -EIO;
+ }
+ kfree(reply);
+ }
+ return (retCode);
+}
+
+int novfs_daemon_get_userspace(struct novfs_schandle SessionId, uint64_t * TotalSize,
+ uint64_t * Free, uint64_t * TotalEnties, uint64_t * FreeEnties)
+{
+ struct novfs_get_user_space cmd;
+ struct novfs_get_user_space_reply *reply;
+ unsigned long replylen = 0;
+ int retCode = 0;
+
+ DbgPrint("0x%p:%p", SessionId.hTypeId, SessionId.hId);
+
+ cmd.Command.CommandType = VFS_COMMAND_GET_USER_SPACE;
+ cmd.Command.SequenceNumber = 0;
+ cmd.Command.SessionId = SessionId;
+
+ retCode = Queue_Daemon_Command(&cmd, sizeof(cmd), NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (!reply->Reply.ErrorCode) {
+
+ __DbgPrint("TotalSpace: %llu\n", reply->TotalSpace);
+ __DbgPrint("FreeSpace: %llu\n", reply->FreeSpace);
+ __DbgPrint("TotalEnties: %llu\n", reply->TotalEnties);
+ __DbgPrint("FreeEnties: %llu\n", reply->FreeEnties);
+
+ if (TotalSize)
+ *TotalSize = reply->TotalSpace;
+ if (Free)
+ *Free = reply->FreeSpace;
+ if (TotalEnties)
+ *TotalEnties = reply->TotalEnties;
+ if (FreeEnties)
+ *FreeEnties = reply->FreeEnties;
+ retCode = 0;
+ } else {
+ retCode = -EIO;
+ }
+ kfree(reply);
+ }
+ return (retCode);
+}
+
+int novfs_daemon_set_mnt_point(char *Path)
+{
+ struct novfs_set_mount_path *cmd;
+ struct novfs_set_mount_path_reply *reply;
+ unsigned long replylen, cmdlen;
+ int retCode = -ENOMEM;
+
+ DbgPrint("%s", Path);
+
+ replylen = strlen(Path);
+
+ cmdlen = sizeof(struct novfs_set_mount_path) + replylen;
+
+ cmd = kmalloc(cmdlen, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+ cmd->Command.CommandType = VFS_COMMAND_SET_MOUNT_PATH;
+ cmd->Command.SequenceNumber = 0;
+ SC_INITIALIZE(cmd->Command.SessionId);
+ cmd->PathLength = replylen;
+
+ strcpy(cmd->Path, Path);
+
+ replylen = 0;
+
+ retCode = Queue_Daemon_Command(cmd, cmdlen, NULL, 0, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ if (!reply->Reply.ErrorCode) {
+ retCode = 0;
+ } else {
+ retCode = -EIO;
+ }
+ kfree(reply);
+ }
+ kfree(cmd);
+ return retCode;
+}
+
+int novfs_daemon_debug_cmd_send(char *Command)
+{
+ struct novfs_debug_request cmd;
+ struct novfs_debug_reply *reply;
+ struct novfs_debug_reply lreply;
+ unsigned long replylen, cmdlen;
+ struct novfs_data_list dlist[2];
+
+ int retCode = -ENOMEM;
+
+ DbgPrint("%s", Command);
+
+ dlist[0].page = NULL;
+ dlist[0].offset = (char *)Command;
+ dlist[0].len = strlen(Command);
+ dlist[0].rwflag = DLREAD;
+
+ dlist[1].page = NULL;
+ dlist[1].offset = (char *)&lreply;
+ dlist[1].len = sizeof(lreply);
+ dlist[1].rwflag = DLWRITE;
+
+ cmdlen = offsetof(struct novfs_debug_request, dbgcmd);
+
+ cmd.Command.CommandType = VFS_COMMAND_DBG;
+ cmd.Command.SequenceNumber = 0;
+ SC_INITIALIZE(cmd.Command.SessionId);
+ cmd.cmdlen = strlen(Command);
+
+ replylen = 0;
+
+ retCode = Queue_Daemon_Command(&cmd, cmdlen, dlist, 2, (void *)&reply, &replylen, INTERRUPTIBLE);
+ if (reply) {
+ kfree(reply);
+ }
+ if (0 == retCode) {
+ retCode = lreply.Reply.ErrorCode;
+ }
+
+ return (retCode);
+}
+
+long novfs_daemon_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retCode = -ENOSYS;
+ unsigned long cpylen;
+ struct novfs_schandle session_id;
+
- lock_kernel(); /* needed? */
-
+ session_id = novfs_scope_get_sessionId(NULL);
+
+ switch (cmd) {
+ case IOC_LOGIN:
+ retCode = daemon_login((struct novfs_login *)arg, &session_id);
+ break;
+
+ case IOC_LOGOUT:
+ retCode = daemon_logout((struct novfs_logout *)arg, &session_id);
+ break;
+ case IOC_DEBUGPRINT:
+ {
+ struct Ioctl_Debug {
+ int length;
+ char *data;
+ } io;
+ char *buf;
+ io.length = 0;
+ cpylen = copy_from_user(&io, (char *)arg, sizeof(io));
- if (io.length <= 0 || io.length > 1024) {
- unlock_kernel();
++ if (io.length <= 0 || io.length > 1024)
+ return -EINVAL;
- }
+ if (io.length) {
+ buf = kmalloc(io.length + 1, GFP_KERNEL);
+ if (buf) {
+ buf[0] = 0;
+ cpylen = copy_from_user(buf, io.data, io.length);
+ buf[io.length] = '\0';
+ DbgPrint("%s", buf);
+ kfree(buf);
+ retCode = 0;
+ }
+ }
+ break;
+ }
+
+ case IOC_XPLAT:
+ {
+ struct novfs_xplat data;
+
+ cpylen = copy_from_user(&data, (void *)arg, sizeof(data));
+ retCode = ((data.xfunction & 0x0000FFFF) | 0xCC000000);
+
+ switch (data.xfunction) {
+ case NWC_GET_MOUNT_PATH:
+ DbgPrint("Call NwdGetMountPath");
+ retCode = NwdGetMountPath(&data);
+ break;
+ }
+
+ DbgPrint("[NOVFS XPLAT] status Code = %X\n", retCode);
+ break;
+ }
+
+ }
-
- unlock_kernel();
-
+ return (retCode);
+}
+
+static int daemon_added_resource(struct daemon_handle *DHandle, int Type, void *CHandle,
+ unsigned char *FHandle, unsigned long Mode, u_long Size)
+{
+ struct daemon_resource *resource;
+
+ if (FHandle)
+ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p FHandle=0x%x "
+ "Mode=0x%x Size=%d", DHandle, Type, CHandle, *(u32 *) & FHandle[2], Mode, Size);
+ else
+ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p\n", DHandle, Type, CHandle);
+
+ resource = kmalloc(sizeof(struct daemon_resource), GFP_KERNEL);
+ if (!resource)
+ return -ENOMEM;
+
+ resource->type = Type;
+ resource->connection = CHandle;
+ if (FHandle)
+ memcpy(resource->handle, FHandle, sizeof(resource->handle));
+ else
+ memset(resource->handle, 0, sizeof(resource->handle));
+ resource->mode = Mode;
+ resource->size = Size;
+ write_lock(&DHandle->lock);
+ list_add(&resource->list, &DHandle->list);
+ write_unlock(&DHandle->lock);
+ DbgPrint("Adding resource=0x%p", resource);
+ return 0;
+}
+
+static int daemon_remove_resource(struct daemon_handle *DHandle, int Type, void *CHandle, unsigned long FHandle)
+{
+ struct daemon_resource *resource;
+ struct list_head *l;
+ int retVal = -ENOMEM;
+
+ DbgPrint("DHandle=0x%p Type=%d CHandle=0x%p FHandle=0x%x", DHandle, Type, CHandle, FHandle);
+
+ write_lock(&DHandle->lock);
+
+ list_for_each(l, &DHandle->list) {
+ resource = list_entry(l, struct daemon_resource, list);
+
+ if ((Type == resource->type) && (resource->connection == CHandle)) {
+ DbgPrint("Found resource=0x%p", resource);
+ l = l->prev;
+ list_del(&resource->list);
+ kfree(resource);
+ break;
+ }
+ }
+
+ write_unlock(&DHandle->lock);
+
+ return (retVal);
+}
+
+int novfs_daemon_lib_open(struct inode *inode, struct file *file)
+{
+ struct daemon_handle *dh;
+
+ DbgPrint("inode=0x%p file=0x%p", inode, file);
+ dh = kmalloc(sizeof(struct daemon_handle), GFP_KERNEL);
+ if (!dh)
+ return -ENOMEM;
+ file->private_data = dh;
+ INIT_LIST_HEAD(&dh->list);
+ rwlock_init(&dh->lock);
+ dh->session = novfs_scope_get_sessionId(NULL);
+ return 0;
+}
+
+int novfs_daemon_lib_close(struct inode *inode, struct file *file)
+{
+ struct daemon_handle *dh;
+ struct daemon_resource *resource;
+ struct list_head *l;
+
+ char commanddata[sizeof(struct novfs_xplat_call_request) + sizeof(struct nwd_close_conn)];
+ struct novfs_xplat_call_request *cmd;
+ struct xplat_call_reply *reply;
+ struct nwd_close_conn *nwdClose;
+ unsigned long cmdlen, replylen;
+
+ DbgPrint("inode=0x%p file=0x%p", inode, file);
+ if (file->private_data) {
+ dh = (struct daemon_handle *)file->private_data;
+
+ list_for_each(l, &dh->list) {
+ resource = list_entry(l, struct daemon_resource, list);
+
+ if (DH_TYPE_STREAM == resource->type) {
+ novfs_close_stream(resource->connection, resource->handle, dh->session);
+ } else if (DH_TYPE_CONNECTION == resource->type) {
+ cmd = (struct novfs_xplat_call_request *)commanddata;
+ cmdlen = offsetof(struct novfs_xplat_call_request, data) + sizeof(struct nwd_close_conn);
+ cmd->Command.CommandType = VFS_COMMAND_XPLAT_CALL;
+ cmd->Command.SequenceNumber = 0;
+ cmd->Command.SessionId = dh->session;
+ cmd->NwcCommand = NWC_CLOSE_CONN;
+
+ cmd->dataLen = sizeof(struct nwd_close_conn);
+ nwdClose = (struct nwd_close_conn *)cmd->data;
+ nwdClose->ConnHandle = (void *)resource->connection;
+
+ Queue_Daemon_Command((void *)cmd, cmdlen, NULL, 0, (void **)&reply, &replylen, 0);
+ if (reply)
+ kfree(reply);
+ }
+ l = l->prev;
+ list_del(&resource->list);
+ kfree(resource);
+ }
+ kfree(dh);
+ file->private_data = NULL;
+ }
+
+ return (0);
+}
+
+ssize_t novfs_daemon_lib_read(struct file * file, char *buf, size_t len, loff_t * off)
+{
+ struct daemon_handle *dh;
+ struct daemon_resource *resource;
+
+ size_t thisread, totalread = 0;
+ loff_t offset = *off;
+
+ DbgPrint("file=0x%p len=%d off=%lld", file, len, *off);
+
+ if (file->private_data) {
+ dh = file->private_data;
+ read_lock(&dh->lock);
+ if (&dh->list != dh->list.next) {
+ resource = list_entry(dh->list.next, struct daemon_resource, list);
+
+ if (DH_TYPE_STREAM == resource->type) {
+ while (len > 0 && (offset < resource->size)) {
+ thisread = len;
+ if (novfs_read_stream
+ (resource->connection, resource->handle, buf, &thisread, &offset, 1, dh->session)
+ || !thisread) {
+ break;
+ }
+ len -= thisread;
+ buf += thisread;
+ offset += thisread;
+ totalread += thisread;
+ }
+ }
+ }
+ read_unlock(&dh->lock);
+ }
+ *off = offset;
+ DbgPrint("return = 0x%x", totalread);
+ return (totalread);
+}
+
+ssize_t novfs_daemon_lib_write(struct file * file, const char *buf, size_t len, loff_t * off)
+{
+ struct daemon_handle *dh;
+ struct daemon_resource *resource;
+
+ size_t thiswrite, totalwrite = -EINVAL;
+ loff_t offset = *off;
+ int status;
+
+ DbgPrint("file=0x%p len=%d off=%lld", file, len, *off);
+
+ if (file->private_data) {
+ dh = file->private_data;
+ write_lock(&dh->lock);
+ if (&dh->list != dh->list.next) {
+ resource = list_entry(dh->list.next, struct daemon_resource, list);
+
+ if ((DH_TYPE_STREAM == resource->type) && (len >= 0)) {
+ totalwrite = 0;
+ do {
+ thiswrite = len;
+ status =
+ novfs_write_stream(resource->connection,
+ resource->handle, (void *)buf, &thiswrite, &offset, dh->session);
+ if (status || !thiswrite) {
+ /*
+ * If len is zero then the file will have just been
+ * truncated to offset. Update size.
+ */
+ if (!status && !len) {
+ resource->size = offset;
+ }
+ totalwrite = status;
+ break;
+ }
+ len -= thiswrite;
+ buf += thiswrite;
+ offset += thiswrite;
+ totalwrite += thiswrite;
+ if (offset > resource->size) {
+ resource->size = offset;
+ }
+ } while (len > 0);
+ }
+ }
+ write_unlock(&dh->lock);
+ }
+ *off = offset;
+ DbgPrint("return = 0x%x", totalwrite);
+
+ return (totalwrite);
+}
+
+loff_t novfs_daemon_lib_llseek(struct file * file, loff_t offset, int origin)
+{
+ struct daemon_handle *dh;
+ struct daemon_resource *resource;
+
+ loff_t retVal = -EINVAL;
+
+ DbgPrint("file=0x%p offset=%lld origin=%d", file, offset, origin);
+
+ if (file->private_data) {
+ dh = file->private_data;
+ read_lock(&dh->lock);
+ if (&dh->list != dh->list.next) {
+ resource = list_entry(dh->list.next, struct daemon_resource, list);
+
+ if (DH_TYPE_STREAM == resource->type) {
+ switch (origin) {
+ case 2:
+ offset += resource->size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ if (offset >= 0) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_version = 0;
+ }
+ retVal = offset;
+ }
+ }
+ }
+ read_unlock(&dh->lock);
+ }
+
+ DbgPrint("ret %lld", retVal);
+
+ return retVal;
+}
+
+#define DbgIocCall(str) __DbgPrint("[VFS XPLAT] Call " str "\n")
+
+long novfs_daemon_lib_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retCode = -ENOSYS;
+ struct daemon_handle *dh;
+ void *handle = NULL;
+ unsigned long cpylen;
+
- lock_kernel(); /* needed? */
-
+ dh = file->private_data;
+
+ DbgPrint("file=0x%p 0x%x 0x%p dh=0x%p", file, cmd, arg, dh);
+
+ if (dh) {
+
+ switch (cmd) {
+ case IOC_LOGIN:
+ retCode = daemon_login((struct novfs_login *)arg, &dh->session);
+ break;
+
+ case IOC_LOGOUT:
+ retCode = daemon_logout((struct novfs_logout *)arg, &dh->session);
+ break;
+
+ case IOC_DEBUGPRINT:
+ {
+ struct Ioctl_Debug {
+ int length;
+ char *data;
+ } io;
+ char *buf;
+ io.length = 0;
+ cpylen = copy_from_user(&io, (void *)arg, sizeof(io));
- if (io.length <= 0 || io.length > 1024) {
- unlock_kernel();
++ if (io.length <= 0 || io.length > 1024)
+ return -EINVAL;
- }
+ if (io.length) {
+ buf = kmalloc(io.length + 1, GFP_KERNEL);
+ if (buf) {
+ buf[0] = 0;
+ cpylen = copy_from_user(buf, io.data, io.length);
+ buf[io.length] = '\0';
+ __DbgPrint("%s", buf);
+ kfree(buf);
+ retCode = 0;
+ }
+ }
+ break;
+ }
+
+ case IOC_XPLAT:
+ {
+ struct novfs_xplat data;
+
+ cpylen = copy_from_user(&data, (void *)arg, sizeof(data));
+ retCode = ((data.xfunction & 0x0000FFFF) | 0xCC000000);
+
+ switch (data.xfunction) {
+ case NWC_OPEN_CONN_BY_NAME:
+ DbgIocCall("NwOpenConnByName");
+ retCode = novfs_open_conn_by_name(&data, &handle, dh->session);
+ if (!retCode)
+ daemon_added_resource(dh, DH_TYPE_CONNECTION, handle, 0, 0, 0);
+ break;
+
+ case NWC_OPEN_CONN_BY_ADDRESS:
+ DbgIocCall("NwOpenConnByAddress");
+ retCode = novfs_open_conn_by_addr(&data, &handle, dh->session);
+ if (!retCode)
+ daemon_added_resource(dh, DH_TYPE_CONNECTION, handle, 0, 0, 0);
+ break;
+
+ case NWC_OPEN_CONN_BY_REFERENCE:
+
+ DbgIocCall("NwOpenConnByReference");
+ retCode = novfs_open_conn_by_ref(&data, &handle, dh->session);
+ if (!retCode)
+ daemon_added_resource(dh, DH_TYPE_CONNECTION, handle, 0, 0, 0);
+ break;
+
+ case NWC_SYS_CLOSE_CONN:
+ DbgIocCall("NwSysCloseConn");
+ retCode = novfs_sys_conn_close(&data, (unsigned long *)&handle, dh->session);
+ daemon_remove_resource(dh, DH_TYPE_CONNECTION, handle, 0);
+ break;
+
+ case NWC_CLOSE_CONN:
+ DbgIocCall("NwCloseConn");
+ retCode = novfs_conn_close(&data, &handle, dh->session);
+ daemon_remove_resource(dh, DH_TYPE_CONNECTION, handle, 0);
+ break;
+
+ case NWC_LOGIN_IDENTITY:
+ DbgIocCall("" "NwLoginIdentity");
+ retCode = novfs_login_id(&data, dh->session);
+ break;
+
+ case NWC_RAW_NCP_REQUEST:
+ DbgIocCall("[VFS XPLAT] Send Raw " "NCP Request");
+ retCode = novfs_raw_send(&data, dh->session);
+ break;
+
+ case NWC_AUTHENTICATE_CONN_WITH_ID:
+ DbgIocCall("[VFS XPLAT] Authenticate " "Conn With ID");
+ retCode = novfs_auth_conn(&data, dh->session);
+ break;
+
+ case NWC_UNAUTHENTICATE_CONN:
+ DbgIocCall("[VFS XPLAT] UnAuthenticate " "Conn With ID");
+ retCode = novfs_unauthenticate(&data, dh->session);
+ break;
+
+ case NWC_LICENSE_CONN:
+ DbgIocCall("Call NwLicenseConn");
+ retCode = novfs_license_conn(&data, dh->session);
+ break;
+
+ case NWC_LOGOUT_IDENTITY:
+ DbgIocCall("NwLogoutIdentity");
+ retCode = novfs_logout_id(&data, dh->session);
+ break;
+
+ case NWC_UNLICENSE_CONN:
+ DbgIocCall("NwUnlicense");
+ retCode = novfs_unlicense_conn(&data, dh->session);
+ break;
+
+ case NWC_GET_CONN_INFO:
+ DbgIocCall("NwGetConnInfo");
+ retCode = novfs_get_conn_info(&data, dh->session);
+ break;
+
+ case NWC_SET_CONN_INFO:
+ DbgIocCall("NwSetConnInfo");
+ retCode = novfs_set_conn_info(&data, dh->session);
+ break;
+
+ case NWC_SCAN_CONN_INFO:
+ DbgIocCall("NwScanConnInfo");
+ retCode = novfs_scan_conn_info(&data, dh->session);
+ break;
+
+ case NWC_GET_IDENTITY_INFO:
+ DbgIocCall("NwGetIdentityInfo");
+ retCode = novfs_get_id_info(&data, dh->session);
+ break;
+
+ case NWC_GET_REQUESTER_VERSION:
+ DbgIocCall("NwGetDaemonVersion");
+ retCode = novfs_get_daemon_ver(&data, dh->session);
+ break;
+
+ case NWC_GET_PREFERRED_DS_TREE:
+ DbgIocCall("NwcGetPreferredDsTree");
+ retCode = novfs_get_preferred_DS_tree(&data, dh->session);
+ break;
+
+ case NWC_SET_PREFERRED_DS_TREE:
+ DbgIocCall("NwcSetPreferredDsTree");
+ retCode = novfs_set_preferred_DS_tree(&data, dh->session);
+ break;
+
+ case NWC_GET_DEFAULT_NAME_CONTEXT:
+ DbgIocCall("NwcGetDefaultNameContext");
+ retCode = novfs_get_default_ctx(&data, dh->session);
+ break;
+
+ case NWC_SET_DEFAULT_NAME_CONTEXT:
+ DbgIocCall("NwcSetDefaultNameContext");
+ retCode = novfs_set_default_ctx(&data, dh->session);
+ break;
+
+ case NWC_QUERY_FEATURE:
+ DbgIocCall("NwQueryFeature");
+ retCode = novfs_query_feature(&data, dh->session);
+ break;
+
+ case NWC_GET_TREE_MONITORED_CONN_REF:
+ DbgIocCall("NwcGetTreeMonitoredConn");
+ retCode = novfs_get_tree_monitored_conn(&data, dh->session);
+ break;
+
+ case NWC_ENUMERATE_IDENTITIES:
+ DbgIocCall("NwcEnumerateIdentities");
+ retCode = novfs_enum_ids(&data, dh->session);
+ break;
+
+ case NWC_CHANGE_KEY:
+ DbgIocCall("NwcChangeAuthKey");
+ retCode = novfs_change_auth_key(&data, dh->session);
+ break;
+
+ case NWC_CONVERT_LOCAL_HANDLE:
+ DbgIocCall("NwdConvertLocalHandle");
+ retCode = NwdConvertLocalHandle(&data, dh);
+ break;
+
+ case NWC_CONVERT_NETWARE_HANDLE:
+ DbgIocCall("NwdConvertNetwareHandle");
+ retCode = NwdConvertNetwareHandle(&data, dh);
+ break;
+
+ case NWC_SET_PRIMARY_CONN:
+ DbgIocCall("NwcSetPrimaryConn");
+ retCode = novfs_set_pri_conn(&data, dh->session);
+ break;
+
+ case NWC_GET_PRIMARY_CONN:
+ DbgIocCall("NwcGetPrimaryConn");
+ retCode = novfs_get_pri_conn(&data, dh->session);
+ break;
+
+ case NWC_MAP_DRIVE:
+ DbgIocCall("NwcMapDrive");
+ retCode = set_map_drive(&data, dh->session);
+ break;
+
+ case NWC_UNMAP_DRIVE:
+ DbgIocCall("NwcUnMapDrive");
+ retCode = unmap_drive(&data, dh->session);
+ break;
+
+ case NWC_ENUMERATE_DRIVES:
+ DbgIocCall("NwcEnumerateDrives");
+ retCode = novfs_enum_drives(&data, dh->session);
+ break;
+
+ case NWC_GET_MOUNT_PATH:
+ DbgIocCall("NwdGetMountPath");
+ retCode = NwdGetMountPath(&data);
+ break;
+
+ case NWC_GET_BROADCAST_MESSAGE:
+ DbgIocCall("NwdGetBroadcastMessage");
+ retCode = novfs_get_bcast_msg(&data, dh->session);
+ break;
+
+ case NWC_SET_KEY:
+ DbgIocCall("NwdSetKey");
+ retCode = novfs_set_key_value(&data, dh->session);
+ break;
+
+ case NWC_VERIFY_KEY:
+ DbgIocCall("NwdVerifyKey");
+ retCode = novfs_verify_key_value(&data, dh->session);
+ break;
+
+ case NWC_RAW_NCP_REQUEST_ALL:
+ case NWC_NDS_RESOLVE_NAME_TO_ID:
+ case NWC_FRAGMENT_REQUEST:
+ case NWC_GET_CONFIGURED_NSPS:
+ default:
+ break;
+
+ }
+
+ DbgPrint("[NOVFS XPLAT] status Code = %X\n", retCode);
+ break;
+ }
+ }
+ }
+
- unlock_kernel();
-
+ return (retCode);
+}
+
+unsigned int novfs_daemon_poll(struct file *file, struct poll_table_struct *poll_table)
+{
+ struct daemon_cmd *que;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ que = get_next_queue(0);
+ if (que)
+ mask |= (POLLIN | POLLRDNORM);
+ return mask;
+}
+
+static int NwdConvertNetwareHandle(struct novfs_xplat *pdata, struct daemon_handle *DHandle)
+{
+ int retVal;
+ struct nwc_convert_netware_handle nh;
+ unsigned long cpylen;
+
+ DbgPrint("DHandle=0x%p", DHandle);
+
+ cpylen = copy_from_user(&nh, pdata->reqData, sizeof(struct nwc_convert_netware_handle));
+
+ retVal =
+ daemon_added_resource(DHandle, DH_TYPE_STREAM,
+ Uint32toHandle(nh.ConnHandle), nh.NetWareHandle, nh.uAccessMode, nh.uFileSize);
+
+ return (retVal);
+}
+
+static int NwdConvertLocalHandle(struct novfs_xplat *pdata, struct daemon_handle *DHandle)
+{
+ int retVal = NWE_REQUESTER_FAILURE;
+ struct daemon_resource *resource;
+ struct nwc_convert_local_handle lh;
+ struct list_head *l;
+ unsigned long cpylen;
+
+ DbgPrint("DHandle=0x%p", DHandle);
+
+ read_lock(&DHandle->lock);
+
+ list_for_each(l, &DHandle->list) {
+ resource = list_entry(l, struct daemon_resource, list);
+
+ if (DH_TYPE_STREAM == resource->type) {
+ lh.uConnReference = HandletoUint32(resource->connection);
+
+//sgled memcpy(lh.NwWareHandle, resource->handle, sizeof(resource->handle));
+ memcpy(lh.NetWareHandle, resource->handle, sizeof(resource->handle)); //sgled
+ if (pdata->repLen >= sizeof(struct nwc_convert_local_handle)) {
+ cpylen = copy_to_user(pdata->repData, &lh, sizeof(struct nwc_convert_local_handle));
+ retVal = 0;
+ } else {
+ retVal = NWE_BUFFER_OVERFLOW;
+ }
+ break;
+ }
+ }
+
+ read_unlock(&DHandle->lock);
+
+ return (retVal);
+}
+
+static int NwdGetMountPath(struct novfs_xplat *pdata)
+{
+ int retVal = NWE_REQUESTER_FAILURE;
+ int len;
+ unsigned long cpylen;
+ struct nwc_get_mount_path mp;
+
+ if (pdata->reqLen != sizeof(mp))
+ return -EINVAL;
+ cpylen = copy_from_user(&mp, pdata->reqData, pdata->reqLen);
+
+ if (novfs_current_mnt) {
+
+ len = strlen(novfs_current_mnt) + 1;
+ if ((len > mp.MountPathLen) && mp.pMountPath) {
+ retVal = NWE_BUFFER_OVERFLOW;
+ } else {
+ if (mp.pMountPath) {
+ cpylen = copy_to_user(mp.pMountPath, novfs_current_mnt, len);
+ }
+ retVal = 0;
+ }
+
+ mp.MountPathLen = len;
+
+ if (pdata->repData && (pdata->repLen >= sizeof(mp))) {
+ cpylen = copy_to_user(pdata->repData, &mp, sizeof(mp));
+ }
+ }
+
+ return (retVal);
+}
+
+static int set_map_drive(struct novfs_xplat *pdata, struct novfs_schandle Session)
+{
+ int retVal;
+ unsigned long cpylen;
+ struct nwc_map_drive_ex symInfo;
+ char *path;
+ struct drive_map *drivemap, *dm;
+ struct list_head *list;
+
+ retVal = novfs_set_map_drive(pdata, Session);
+ if (retVal)
+ return retVal;
+ if (copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo)))
+ return -EFAULT;
+ if (symInfo.linkOffsetLength > MAX_NAME_LEN)
+ return -EINVAL;
+ drivemap = kmalloc(sizeof(struct drive_map) + symInfo.linkOffsetLength, GFP_KERNEL);
+ if (!drivemap)
+ return -ENOMEM;
+
+ path = (char *)pdata->reqData;
+ path += symInfo.linkOffset;
+ cpylen = copy_from_user(drivemap->name, path, symInfo.linkOffsetLength);
+
+ drivemap->session = Session;
+ drivemap->hash = full_name_hash(drivemap->name, symInfo.linkOffsetLength - 1);
+ drivemap->namelen = symInfo.linkOffsetLength - 1;
+ DbgPrint("hash=0x%lx path=%s", drivemap->hash, drivemap->name);
+
+ dm = (struct drive_map *)&DriveMapList.next;
+
+ mutex_lock(&DriveMapLock);
+
+ list_for_each(list, &DriveMapList) {
+ dm = list_entry(list, struct drive_map, list);
+ __DbgPrint("%s: dm=0x%p\n"
+ " hash: 0x%lx\n"
+ " namelen: %d\n" " name: %s\n", __func__, dm, dm->hash, dm->namelen, dm->name);
+
+ if (drivemap->hash == dm->hash) {
+ if (0 == strcmp(dm->name, drivemap->name)) {
+ dm = NULL;
+ break;
+ }
+ } else if (drivemap->hash < dm->hash) {
+ break;
+ }
+ }
+
+ if (dm) {
+ if ((dm == (struct drive_map *)&DriveMapList) || (dm->hash < drivemap->hash)) {
+ list_add(&drivemap->list, &dm->list);
+ } else {
+ list_add_tail(&drivemap->list, &dm->list);
+ }
+ } else
+ kfree(drivemap);
+ mutex_unlock(&DriveMapLock);
+ return (retVal);
+}
+
+static int unmap_drive(struct novfs_xplat *pdata, struct novfs_schandle Session)
+{
+ int retVal = NWE_REQUESTER_FAILURE;
+ struct nwc_unmap_drive_ex symInfo;
+ char *path;
+ struct drive_map *dm;
+ struct list_head *list;
+ unsigned long hash;
+
+ retVal = novfs_unmap_drive(pdata, Session);
+ if (retVal)
+ return retVal;
+ if (copy_from_user(&symInfo, pdata->reqData, sizeof(symInfo)))
+ return -EFAULT;
+ if (symInfo.linkLen > MAX_NAME_LEN || symInfo.linkLen == 0)
+ return -EINVAL;
+ path = kmalloc(symInfo.linkLen, GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+ if (copy_from_user(path, ((struct nwc_unmap_drive_ex *)pdata->reqData)->linkData, symInfo.linkLen)) {
+ kfree(path);
+ return -EFAULT;
+ }
+
+ hash = full_name_hash(path, symInfo.linkLen - 1);
+ DbgPrint("hash=0x%x path=%s", hash, path);
+
+ dm = NULL;
+
+ mutex_lock(&DriveMapLock);
+
+ list_for_each(list, &DriveMapList) {
+ dm = list_entry(list, struct drive_map, list);
+ __DbgPrint("%s: dm=0x%p %s\n"
+ " hash: 0x%x\n" " namelen: %d\n", __func__, dm, dm->name, dm->hash, dm->namelen);
+
+ if (hash == dm->hash) {
+ if (0 == strcmp(dm->name, path)) {
+ break;
+ }
+ } else if (hash < dm->hash) {
+ dm = NULL;
+ break;
+ }
+ }
+
+ if (dm) {
+ __DbgPrint("%s: Remove dm=0x%p %s\n"
+ " hash: 0x%x\n" " namelen: %d\n", __func__, dm, dm->name, dm->hash, dm->namelen);
+ list_del(&dm->list);
+ kfree(dm);
+ }
+
+ mutex_unlock(&DriveMapLock);
+ return (retVal);
+}
+
+static void RemoveDriveMaps(void)
+{
+ struct drive_map *dm;
+ struct list_head *list;
+
+ mutex_lock(&DriveMapLock);
+ list_for_each(list, &DriveMapList) {
+ dm = list_entry(list, struct drive_map, list);
+
+ __DbgPrint("%s: dm=0x%p\n"
+ " hash: 0x%x\n"
+ " namelen: %d\n" " name: %s\n", __func__, dm, dm->hash, dm->namelen, dm->name);
+ local_unlink(dm->name);
+ list = list->prev;
+ list_del(&dm->list);
+ kfree(dm);
+ }
+ mutex_unlock(&DriveMapLock);
+}
+
+/* As picked from do_unlinkat() */
+
+static long local_unlink(const char *pathname)
+{
+ int error;
+ struct dentry *dentry;
+ char *name, *c;
+ struct nameidata nd;
+ struct inode *inode = NULL;
+
- error = path_lookup(pathname, LOOKUP_PARENT, &nd);
- DbgPrint("path_lookup %s error: %d\n", pathname, error);
++ error = kern_path_parent(pathname, &nd);
++ DbgPrint("kern_path_parent %s error: %d\n", pathname, error);
+ if (error)
+ return error;
+
+ error = -EISDIR;
+ if (nd.last_type != LAST_NORM)
+ goto exit1;
+ mutex_lock(&nd.path.dentry->d_inode->i_mutex);
+ /* Get the filename of pathname */
+ name = c = (char *)pathname;
+ while (*c != '\0') {
+ if (*c == '/')
+ name = ++c;
+ else
+ c++;
+ }
+ dentry = lookup_one_len(name, nd.path.dentry, strlen(name));
+ error = PTR_ERR(dentry);
+
+ if (!IS_ERR(dentry)) {
+ DbgPrint("dentry %p", dentry);
+ if (!(dentry->d_inode) || !(dentry->d_inode->i_mode & S_IFLNK)) {
+ DbgPrint("%s not a link", name);
+ error = -ENOENT;
+ goto exit1;
+ }
+ /* Why not before? Because we want correct error value */
+ if (nd.last.name[nd.last.len])
+ goto slashes;
+ inode = dentry->d_inode;
+ if (inode)
+ atomic_inc(&inode->i_count);
+ error = mnt_want_write(nd.path.mnt);
+ DbgPrint("inode %p mnt_want_write error %d", inode, error);
+ if (error)
+ goto exit2;
+ error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+ mnt_drop_write(nd.path.mnt);
+exit2:
+ dput(dentry);
+ }
+ mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ if (inode)
+ iput(inode); /* truncate the inode here */
+exit1:
+ path_put(&nd.path);
+ DbgPrint("returning error %d", error);
+ return error;
+
+slashes:
+ error = !dentry->d_inode ? -ENOENT : S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ goto exit2;
+}
--- /dev/null
+/*
+ * 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/smp_lock.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 int novfs_get_sb(struct file_system_type *Fstype, int Flags, const char *Dev_name, void *Data, struct vfsmount *Mnt);
-
+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,
- .unplug_io_fn = default_unplug_io_fn,
+};
+
+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 | 0777, 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 | 0777, 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 | 0777, 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 int novfs_get_sb(struct file_system_type *Fstype, int Flags, const char *Dev_name, void *Data, struct vfsmount *Mnt)
++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", Fstype, Dev_name);
- return get_sb_nodev(Fstype, Flags, Data, novfs_fill_super, Mnt);
++ 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",
- .get_sb = novfs_get_sb,
++ .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);
--- /dev/null
+/*
+ * Novell NCP Redirector for Linux
+ * Author: James Turner
+ *
+ * This module contains functions that create the interface to the proc
+ * filesystem.
+ *
+ * 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/kernel.h>
+#include <linux/proc_fs.h>
- #include <linux/smp_lock.h>
+
+#include "vfs.h"
+
+struct proc_dir_entry *novfs_procfs_dir;
+struct proc_dir_entry *Novfs_Control;
+struct proc_dir_entry *Novfs_Library;
+struct proc_dir_entry *Novfs_Version;
+
+static struct file_operations novfs_daemon_proc_fops;
+static struct file_operations novfs_lib_proc_fops;
+
+/*===[ Code ]=============================================================*/
+
+static int Novfs_Get_Version(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ char *buf, tbuf[48];
+ int len = 0, i;
+
+ if (!off) {
+ buf = page + off;
+ *start = buf;
+ len = sprintf(buf, "Novfs Version=%s\n", NOVFS_VERSION_STRING);
+ i = novfs_daemon_getversion(tbuf, sizeof(tbuf));
+ if ((i > 0) && i < (count - len)) {
+ len += sprintf(buf + len, "Novfsd Version=%s\n", tbuf);
+ }
+
+ if (novfs_current_mnt) {
+ i = strlen(novfs_current_mnt);
+ if ((i > 0) && i < (count - len)) {
+ len += sprintf(buf + len, "Novfs mount=%s\n", novfs_current_mnt);
+ }
+ }
+ DbgPrint("%s", buf);
+ }
+ *eof = 1;
+ return (len);
+}
+
+int novfs_proc_init(void)
+{
+ int retCode = 0;
+
+ novfs_procfs_dir = proc_mkdir(MODULE_NAME, NULL);
+ if (novfs_procfs_dir) {
+
+ Novfs_Control = create_proc_entry("Control", 0600, novfs_procfs_dir);
+
+ if (Novfs_Control) {
+ Novfs_Control->size = 0;
+ memcpy(&novfs_daemon_proc_fops, Novfs_Control->proc_fops, sizeof(struct file_operations));
+
+ /*
+ * Setup our functions
+ */
+ novfs_daemon_proc_fops.owner = THIS_MODULE;
+ novfs_daemon_proc_fops.open = novfs_daemon_open_control;
+ novfs_daemon_proc_fops.release = novfs_daemon_close_control;
+ novfs_daemon_proc_fops.read = novfs_daemon_cmd_send;
+ novfs_daemon_proc_fops.write = novfs_daemon_recv_reply;
+ novfs_daemon_proc_fops.unlocked_ioctl = novfs_daemon_ioctl;
+
+ Novfs_Control->proc_fops = &novfs_daemon_proc_fops;
+ } else {
+ remove_proc_entry(MODULE_NAME, NULL);
+ return (-ENOENT);
+ }
+
+ Novfs_Library = create_proc_entry("Library", 0666, novfs_procfs_dir);
+ if (Novfs_Library) {
+ Novfs_Library->size = 0;
+
+ /*
+ * Setup our file functions
+ */
+ memcpy(&novfs_lib_proc_fops, Novfs_Library->proc_fops, sizeof(struct file_operations));
+ novfs_lib_proc_fops.owner = THIS_MODULE;
+ novfs_lib_proc_fops.open = novfs_daemon_lib_open;
+ novfs_lib_proc_fops.release = novfs_daemon_lib_close;
+ novfs_lib_proc_fops.read = novfs_daemon_lib_read;
+ novfs_lib_proc_fops.write = novfs_daemon_lib_write;
+ novfs_lib_proc_fops.llseek = novfs_daemon_lib_llseek;
+ novfs_lib_proc_fops.unlocked_ioctl = novfs_daemon_lib_ioctl;
+ Novfs_Library->proc_fops = &novfs_lib_proc_fops;
+ } else {
+ remove_proc_entry("Control", novfs_procfs_dir);
+ remove_proc_entry(MODULE_NAME, NULL);
+ return (-ENOENT);
+ }
+
+ Novfs_Version = create_proc_read_entry("Version", 0444, novfs_procfs_dir, Novfs_Get_Version, NULL);
+ if (Novfs_Version) {
+ Novfs_Version->size = 0;
+ } else {
+ remove_proc_entry("Library", novfs_procfs_dir);
+ remove_proc_entry("Control", novfs_procfs_dir);
+ remove_proc_entry(MODULE_NAME, NULL);
+ retCode = -ENOENT;
+ }
+ } else {
+ retCode = -ENOENT;
+ }
+ return (retCode);
+}
+
+void novfs_proc_exit(void)
+{
+
+ DbgPrint("remove_proc_entry(Version, NULL)\n");
+ remove_proc_entry("Version", novfs_procfs_dir);
+
+ DbgPrint("remove_proc_entry(Control, NULL)\n");
+ remove_proc_entry("Control", novfs_procfs_dir);
+
+ DbgPrint("remove_proc_entry(Library, NULL)\n");
+ remove_proc_entry("Library", novfs_procfs_dir);
+
+ DbgPrint("remove_proc_entry(%s, NULL)\n", MODULE_NAME);
+ remove_proc_entry(MODULE_NAME, NULL);
+
+ DbgPrint("done\n");
+}
--- /dev/null
+/*
+ * Novell NCP Redirector for Linux
+ * Author: James Turner
+ *
+ * This file contains a debugging code for the novfs VFS.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+
+#include <linux/profile.h>
+#include <linux/notifier.h>
+
+#include "vfs.h"
+
+/*===[ Manifest constants ]===============================================*/
+#define DBGBUFFERSIZE (1024*1024*32)
+
+/*===[ Type definitions ]=================================================*/
+struct local_rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+char *DbgPrintBuffer = NULL;
+char DbgPrintOn = 0;
+char DbgSyslogOn = 0;
+char DbgProfileOn = 0;
+
+static unsigned long DbgPrintBufferOffset = 0;
+static unsigned long DbgPrintBufferReadOffset = 0;
+static unsigned long DbgPrintBufferSize = DBGBUFFERSIZE;
+
+static struct file_operations Dbg_proc_file_operations;
+static struct file_operations dentry_proc_file_ops;
+static struct file_operations inode_proc_file_ops;
+
+static struct proc_dir_entry *dbg_dir = NULL;
+static struct proc_dir_entry *dbg_file = NULL;
+static struct proc_dir_entry *dentry_file = NULL;
+static struct proc_dir_entry *inode_file = NULL;
+
+static DEFINE_MUTEX(LocalPrint_lock);
+
+static ssize_t User_proc_write_DbgBuffer(struct file *file, const char __user * buf, size_t nbytes, loff_t * ppos)
+{
+ ssize_t retval = nbytes;
+ u_char *lbuf, *p;
+ int i;
+ u_long cpylen;
+
+ lbuf = kmalloc(nbytes + 1, GFP_KERNEL);
+ if (lbuf) {
+ cpylen = copy_from_user(lbuf, buf, nbytes);
+
+ lbuf[nbytes] = 0;
+ DbgPrint("%s", lbuf);
+
+ for (i = 0; lbuf[i] && lbuf[i] != '\n'; i++) ;
+
+ if ('\n' == lbuf[i]) {
+ lbuf[i] = '\0';
+ }
+
+ if (!strcmp("on", lbuf)) {
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ DbgPrintOn = 1;
+ } else if (!strcmp("off", lbuf)) {
+ DbgPrintOn = 0;
+ } else if (!strcmp("reset", lbuf)) {
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ } else if (NULL != (p = strchr(lbuf, ' '))) {
+ *p++ = '\0';
+ if (!strcmp("syslog", lbuf)) {
+
+ if (!strcmp("on", p)) {
+ DbgSyslogOn = 1;
+ } else if (!strcmp("off", p)) {
+ DbgSyslogOn = 0;
+ }
+ } else if (!strcmp("novfsd", lbuf)) {
+ novfs_daemon_debug_cmd_send(p);
+ } else if (!strcmp("file_update_timeout", lbuf)) {
+ novfs_update_timeout = simple_strtoul(p, NULL, 0);
+ } else if (!strcmp("cache", lbuf)) {
+ if (!strcmp("on", p)) {
+ novfs_page_cache = 1;
+ } else if (!strcmp("off", p)) {
+ novfs_page_cache = 0;
+ }
+ } else if (!strcmp("profile", lbuf)) {
+ if (!strcmp("on", p)) {
+ DbgProfileOn = 1;
+ } else if (!strcmp("off", p)) {
+ DbgProfileOn = 0;
+ }
+ }
+ }
+ kfree(lbuf);
+ }
+
+ return (retval);
+}
+
+static ssize_t User_proc_read_DbgBuffer(struct file *file, char *buf, size_t nbytes, loff_t * ppos)
+{
+ ssize_t retval = 0;
+ size_t count;
+
+ if (0 != (count = DbgPrintBufferOffset - DbgPrintBufferReadOffset)) {
+
+ if (count > nbytes) {
+ count = nbytes;
+ }
+
+ count -= copy_to_user(buf, &DbgPrintBuffer[DbgPrintBufferReadOffset], count);
+
+ if (count == 0) {
+ if (retval == 0)
+ retval = -EFAULT;
+ } else {
+ DbgPrintBufferReadOffset += count;
+ if (DbgPrintBufferReadOffset >= DbgPrintBufferOffset) {
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ }
+ retval = count;
+ }
+ }
+
+ return retval;
+}
+
+static int proc_read_DbgBuffer(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int len;
+
+ printk(KERN_ALERT "proc_read_DbgBuffer: off=%ld count=%d DbgPrintBufferOffset=%lu DbgPrintBufferReadOffset=%lu\n", off,
+ count, DbgPrintBufferOffset, DbgPrintBufferReadOffset);
+
+ len = DbgPrintBufferOffset - DbgPrintBufferReadOffset;
+
+ if ((int)(DbgPrintBufferOffset - DbgPrintBufferReadOffset) > count)
+ len = count;
+
+ if (len) {
+ memcpy(page, &DbgPrintBuffer[DbgPrintBufferReadOffset], len);
+ DbgPrintBufferReadOffset += len;
+ }
+
+ if (DbgPrintBufferReadOffset >= DbgPrintBufferOffset)
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+
+ printk(KERN_ALERT "proc_read_DbgBuffer: return %d\n", len);
+
+ return len;
+}
+
+#define DBG_BUFFER_SIZE (2*1024)
+
+static int LocalPrint(char *Fmt, ...)
+{
+ int len = 0;
+ va_list args;
+
+ if (DbgPrintBuffer) {
+ va_start(args, Fmt);
+ len += vsnprintf(DbgPrintBuffer + DbgPrintBufferOffset, DbgPrintBufferSize - DbgPrintBufferOffset, Fmt, args);
+ DbgPrintBufferOffset += len;
+ }
+
+ return (len);
+}
+
+int ___DbgPrint(const char *site, const char *Fmt, ...)
+{
+ char *buf;
+ int len = 0;
+ unsigned long offset;
+ va_list args;
+
+ if ((DbgPrintBuffer && DbgPrintOn) || DbgSyslogOn) {
+ buf = kmalloc(DBG_BUFFER_SIZE, GFP_KERNEL);
+
+ if (buf) {
+ va_start(args, Fmt);
+ len = snprintf(buf, DBG_BUFFER_SIZE, "[%d] %s ", current->pid, site);
+ len += vsnprintf(buf + len, DBG_BUFFER_SIZE - len, Fmt, args);
+ if (-1 == len) {
+ len = DBG_BUFFER_SIZE - 1;
+ buf[len] = '\0';
+ }
+ /*
+ len = sprintf(&DbgPrintBuffer[offset], "[%llu] ", ts);
+ len += vsprintf(&DbgPrintBuffer[offset+len], Fmt, args);
+ */
+
+ if (len) {
+ if (DbgSyslogOn) {
+ printk("<6>%s", buf);
+ }
+
+ if (DbgPrintBuffer && DbgPrintOn) {
+ if ((DbgPrintBufferOffset + len) > DbgPrintBufferSize) {
+ offset = DbgPrintBufferOffset;
+ DbgPrintBufferOffset = 0;
+ memset(&DbgPrintBuffer[offset], 0, DbgPrintBufferSize - offset);
+ }
+
+ mb();
+
+ if ((DbgPrintBufferOffset + len) < DbgPrintBufferSize) {
+ DbgPrintBufferOffset += len;
+ offset = DbgPrintBufferOffset - len;
+ memcpy(&DbgPrintBuffer[offset], buf, len + 1);
+ }
+ }
+ }
+ kfree(buf);
+ }
+ }
+
+ return (len);
+}
+
+static void doline(unsigned char *b, unsigned char *e, unsigned char *l)
+{
+ unsigned char c;
+
+ *b++ = ' ';
+
+ while (l < e) {
+ c = *l++;
+ if ((c < ' ') || (c > '~')) {
+ c = '.';
+ }
+ *b++ = c;
+ *b = '\0';
+ }
+}
+
+void novfs_dump(int size, void *dumpptr)
+{
+ unsigned char *ptr = (unsigned char *)dumpptr;
+ unsigned char *line = NULL, buf[100], *bptr = buf;
+ int i;
+
+ if (DbgPrintBuffer || DbgSyslogOn) {
+ if (size) {
+ for (i = 0; i < size; i++) {
+ if (0 == (i % 16)) {
+ if (line) {
+ doline(bptr, ptr, line);
+ __DbgPrint("%s\n", buf);
+ bptr = buf;
+ }
+ bptr += sprintf(bptr, "0x%p: ", ptr);
+ line = ptr;
+ }
+ bptr += sprintf(bptr, "%02x ", *ptr++);
+ }
+ doline(bptr, ptr, line);
+ __DbgPrint("%s\n", buf);
+ }
+ }
+}
+
+#define FEBRUARY 2
+#define STARTOFTIME 1970
+#define SECDAY 86400L
+#define SECYR (SECDAY * 365)
+#define leapyear(year) ((year) % 4 == 0)
+#define days_in_year(a) (leapyear(a) ? 366 : 365)
+#define days_in_month(a) (month_days[(a) - 1])
+
+static int month_days[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
+ */
+static void NovfsGregorianDay(struct local_rtc_time *tm)
+{
+ int leapsToDate;
+ int lastYear;
+ int day;
+ int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+ lastYear = tm->tm_year - 1;
+
+ /*
+ * Number of leap corrections to apply up to end of last year
+ */
+ leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400;
+
+ /*
+ * This year is a leap year if it is divisible by 4 except when it is
+ * divisible by 100 unless it is divisible by 400
+ *
+ * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
+ */
+ if ((tm->tm_year % 4 == 0) && ((tm->tm_year % 100 != 0) || (tm->tm_year % 400 == 0)) && (tm->tm_mon > 2)) {
+ /*
+ * We are past Feb. 29 in a leap year
+ */
+ day = 1;
+ } else {
+ day = 0;
+ }
+
+ day += lastYear * 365 + leapsToDate + MonthOffset[tm->tm_mon - 1] + tm->tm_mday;
+
+ tm->tm_wday = day % 7;
+}
+
+static void private_to_tm(int tim, struct local_rtc_time *tm)
+{
+ register int i;
+ register long hms, day;
+
+ day = tim / SECDAY;
+ hms = tim % SECDAY;
+
+ /* Hours, minutes, seconds are easy */
+ tm->tm_hour = hms / 3600;
+ tm->tm_min = (hms % 3600) / 60;
+ tm->tm_sec = (hms % 3600) % 60;
+
+ /* Number of years in days */
+ for (i = STARTOFTIME; day >= days_in_year(i); i++)
+ day -= days_in_year(i);
+ tm->tm_year = i;
+
+ /* Number of months in days left */
+ if (leapyear(tm->tm_year))
+ days_in_month(FEBRUARY) = 29;
+ for (i = 1; day >= days_in_month(i); i++)
+ day -= days_in_month(i);
+ days_in_month(FEBRUARY) = 28;
+ tm->tm_mon = i;
+
+ /* Days are what is left over (+1) from all that. */
+ tm->tm_mday = day + 1;
+
+ /*
+ * Determine the day of week
+ */
+ NovfsGregorianDay(tm);
+}
+
+char *ctime_r(time_t * clock, char *buf)
+{
+ struct local_rtc_time tm;
+ static char *DAYOFWEEK[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static char *MONTHOFYEAR[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
+
+ private_to_tm(*clock, &tm);
+
+ sprintf(buf, "%s %s %d %d:%02d:%02d %d", DAYOFWEEK[tm.tm_wday],
+ MONTHOFYEAR[tm.tm_mon - 1], tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year);
+ return (buf);
+}
+
+static void dump(struct dentry *parent, void *pf)
+{
+ void (*pfunc) (char *Fmt, ...) = pf;
+ struct l {
+ struct l *next;
+ struct dentry *dentry;
+ } *l, *n, *start;
+ struct list_head *p;
+ struct dentry *d;
+ char *buf, *path, *sd;
+ char inode_number[16];
+
+ buf = (char *)kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
+
+ if (NULL == buf) {
+ return;
+ }
+
+ if (parent) {
+ pfunc("starting 0x%p %.*s\n", parent, parent->d_name.len, parent->d_name.name);
+ if (parent->d_subdirs.next == &parent->d_subdirs) {
+ pfunc("No children...\n");
+ } else {
+ start = kmalloc(sizeof(*start), GFP_KERNEL);
+ if (start) {
+ start->next = NULL;
+ start->dentry = parent;
+ l = start;
+ while (l) {
+ p = l->dentry->d_subdirs.next;
+ while (p != &l->dentry->d_subdirs) {
+ d = list_entry(p, struct dentry, d_u.d_child);
+ p = p->next;
+
+ if (d->d_subdirs.next != &d->d_subdirs) {
+ n = kmalloc(sizeof(*n), GFP_KERNEL);
+ if (n) {
+ n->next = l->next;
+ l->next = n;
+ n->dentry = d;
+ }
+ } else {
+ path = novfs_scope_dget_path(d, buf, PATH_LENGTH_BUFFER, 1);
+ if (path) {
+ pfunc
+ ("1-0x%p %s\n"
+ " d_name: %.*s\n"
+ " d_parent: 0x%p\n"
+ " d_count: %d\n"
+ " d_flags: 0x%x\n"
+ " d_subdirs: 0x%p\n"
+ " d_inode: 0x%p\n",
+ d, path,
+ d->d_name.len,
+ d->d_name.name,
+ d->d_parent,
- atomic_read
- (&d->d_count), d->d_flags, d->d_subdirs.next, d->d_inode);
++ d->d_count, d->d_flags, d->d_subdirs.next, d->d_inode);
+ }
+ }
+ }
+ l = l->next;
+ }
+ l = start;
+ while (l) {
+ d = l->dentry;
+ path = novfs_scope_dget_path(d, buf, PATH_LENGTH_BUFFER, 1);
+ if (path) {
+ sd = " (None)";
+ if (&d->d_subdirs != d->d_subdirs.next) {
+ sd = "";
+ }
+ inode_number[0] = '\0';
+ if (d->d_inode) {
+ sprintf(inode_number, " (%lu)", d->d_inode->i_ino);
+ }
+ pfunc("0x%p %s\n"
+ " d_parent: 0x%p\n"
+ " d_count: %d\n"
+ " d_flags: 0x%x\n"
+ " d_subdirs: 0x%p%s\n"
+ " d_inode: 0x%p%s\n",
+ d, path, d->d_parent,
- atomic_read(&d->d_count),
- d->d_flags, d->d_subdirs.next, sd, d->d_inode, inode_number);
++ d->d_count, d->d_flags,
++ d->d_subdirs.next, sd,
++ d->d_inode, inode_number);
+ }
+
+ n = l;
+ l = l->next;
+ kfree(n);
+ }
+ }
+ }
+ }
+
+ kfree(buf);
+
+}
+
+static ssize_t common_read(char *buf, size_t len, loff_t * off)
+{
+ ssize_t retval = 0;
+ size_t count;
+ unsigned long offset = *off;
+
+ if (0 != (count = DbgPrintBufferOffset - offset)) {
+ if (count > len) {
+ count = len;
+ }
+
+ count -= copy_to_user(buf, &DbgPrintBuffer[offset], count);
+
+ if (count == 0) {
+ retval = -EFAULT;
+ } else {
+ *off += (loff_t) count;
+ retval = count;
+ }
+ }
+ return retval;
+
+}
+
+static ssize_t novfs_profile_read_inode(struct file *file, char *buf, size_t len, loff_t * off)
+{
+ ssize_t retval = 0;
+ unsigned long offset = *off;
+ static char save_DbgPrintOn;
+
+ if (offset == 0) {
+ mutex_lock(&LocalPrint_lock);
+ save_DbgPrintOn = DbgPrintOn;
+ DbgPrintOn = 0;
+
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ novfs_dump_inode(LocalPrint);
+ }
+
+ retval = common_read(buf, len, off);
+
+ if (0 == retval) {
+ DbgPrintOn = save_DbgPrintOn;
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+
+ mutex_unlock(&LocalPrint_lock);
+ }
+
+ return retval;
+
+}
+
+static ssize_t novfs_profile_dentry_read(struct file *file, char *buf, size_t len, loff_t * off)
+{
+ ssize_t retval = 0;
+ unsigned long offset = *off;
+ static char save_DbgPrintOn;
+
+ if (offset == 0) {
+ mutex_lock(&LocalPrint_lock);
+ save_DbgPrintOn = DbgPrintOn;
+ DbgPrintOn = 0;
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ dump(novfs_root, LocalPrint);
+ }
+
+ retval = common_read(buf, len, off);
+
+ if (0 == retval) {
+ DbgPrintBufferOffset = DbgPrintBufferReadOffset = 0;
+ DbgPrintOn = save_DbgPrintOn;
+
+ mutex_unlock(&LocalPrint_lock);
+ }
+
+ return retval;
+
+}
+
+uint64_t get_nanosecond_time()
+{
+ struct timespec ts;
+ uint64_t retVal;
+
+ ts = current_kernel_time();
+
+ retVal = (uint64_t) NSEC_PER_SEC;
+ retVal *= (uint64_t) ts.tv_sec;
+ retVal += (uint64_t) ts.tv_nsec;
+
+ return (retVal);
+}
+
+void novfs_profile_init()
+{
+ if (novfs_procfs_dir)
+ dbg_dir = novfs_procfs_dir;
+ else
+ dbg_dir = proc_mkdir(MODULE_NAME, NULL);
+
+ if (dbg_dir) {
+ dbg_file = create_proc_read_entry("Debug", 0600, dbg_dir, proc_read_DbgBuffer, NULL);
+ if (dbg_file) {
+ dbg_file->size = DBGBUFFERSIZE;
+ memcpy(&Dbg_proc_file_operations, dbg_file->proc_fops, sizeof(struct file_operations));
+ Dbg_proc_file_operations.read = User_proc_read_DbgBuffer;
+ Dbg_proc_file_operations.write = User_proc_write_DbgBuffer;
+ dbg_file->proc_fops = &Dbg_proc_file_operations;
+ } else {
+ remove_proc_entry(MODULE_NAME, NULL);
+ vfree(DbgPrintBuffer);
+ DbgPrintBuffer = NULL;
+ }
+ }
+
+ if (DbgPrintBuffer) {
+ if (dbg_dir) {
+ inode_file = create_proc_entry("inode", 0600, dbg_dir);
+ if (inode_file) {
+ inode_file->size = 0;
+ memcpy(&inode_proc_file_ops, inode_file->proc_fops, sizeof(struct file_operations));
+ inode_proc_file_ops.owner = THIS_MODULE;
+ inode_proc_file_ops.read = novfs_profile_read_inode;
+ inode_file->proc_fops = &inode_proc_file_ops;
+ }
+
+ dentry_file = create_proc_entry("dentry", 0600, dbg_dir);
+ if (dentry_file) {
+ dentry_file->size = 0;
+ memcpy(&dentry_proc_file_ops, dentry_file->proc_fops, sizeof(struct file_operations));
+ dentry_proc_file_ops.owner = THIS_MODULE;
+ dentry_proc_file_ops.read = novfs_profile_dentry_read;
+ dentry_file->proc_fops = &dentry_proc_file_ops;
+ }
+
+ } else {
+ vfree(DbgPrintBuffer);
+ DbgPrintBuffer = NULL;
+ }
+ }
+}
+
+void novfs_profile_exit(void)
+{
+ if (dbg_file)
+ DbgPrint("Calling remove_proc_entry(Debug, NULL)\n"), remove_proc_entry("Debug", dbg_dir);
+ if (inode_file)
+ DbgPrint("Calling remove_proc_entry(inode, NULL)\n"), remove_proc_entry("inode", dbg_dir);
+ if (dentry_file)
+ DbgPrint("Calling remove_proc_entry(dentry, NULL)\n"), remove_proc_entry("dentry", dbg_dir);
+
+ if (dbg_dir && (dbg_dir != novfs_procfs_dir)) {
+ DbgPrint("Calling remove_proc_entry(%s, NULL)\n", MODULE_NAME);
+ remove_proc_entry(MODULE_NAME, NULL);
+ }
+}
--- /dev/null
+/*
+ * Novell NCP Redirector for Linux
+ * Author: James Turner
+ *
+ * This file contains functions used to scope users.
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+#include <linux/personality.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/synclink.h>
- #include <linux/smp_lock.h>
+#include <linux/semaphore.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+
+#include "vfs.h"
+
+#define SHUTDOWN_INTERVAL 5
+#define CLEANUP_INTERVAL 10
+#define MAX_USERNAME_LENGTH 32
+
+static struct list_head Scope_List;
+static struct semaphore Scope_Lock;
+static struct semaphore Scope_Thread_Delay;
+static int Scope_Thread_Terminate = 0;
+static struct timer_list Scope_Timer;
+static unsigned int Scope_Hash_Val = 1;
+
+static struct novfs_scope_list *Scope_Search4Scope(struct novfs_schandle Id, int Session, int Locked)
+{
+ struct novfs_scope_list *scope, *rscope = NULL;
+ struct novfs_schandle cur_scope;
+ struct list_head *sl;
+ int offset;
+
+ DbgPrint("Scope_Search4Scope: 0x%p:%p 0x%x 0x%x\n", Id.hTypeId, Id.hId, Session, Locked);
+
+ if (Session)
+ offset = offsetof(struct novfs_scope_list, SessionId);
+ else
+ offset = offsetof(struct novfs_scope_list, ScopeId);
+
+ if (!Locked) {
+ down(&Scope_Lock);
+ }
+
+ sl = Scope_List.next;
+ DbgPrint("Scope_Search4Scope: 0x%p\n", sl);
+ while (sl != &Scope_List) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+
+ cur_scope = *(struct novfs_schandle *)((char *)scope + offset);
+ if (SC_EQUAL(Id, cur_scope)) {
+ rscope = scope;
+ break;
+ }
+
+ sl = sl->next;
+ }
+
+ if (!Locked) {
+ up(&Scope_Lock);
+ }
+
+ DbgPrint("Scope_Search4Scope: return 0x%p\n", rscope);
+ return (rscope);
+}
+
+static struct novfs_scope_list *Scope_Find_Scope(int Create)
+{
+ struct novfs_scope_list *scope = NULL, *pscope = NULL;
+ struct task_struct *task;
+ struct novfs_schandle scopeId;
+ int addscope = 0;
+
+ task = current;
+
+ DbgPrint("Scope_Find_Scope: %d %d %d %d\n", current_uid(), current_euid(), current_suid(), current_fsuid());
+
+ //scopeId = task->euid;
+ UID_TO_SCHANDLE(scopeId, current_euid());
+
+ scope = Scope_Search4Scope(scopeId, 0, 0);
+
+ if (!scope && Create) {
+ scope = kmalloc(sizeof(*pscope), GFP_KERNEL);
+ if (scope) {
+ scope->ScopeId = scopeId;
+ SC_INITIALIZE(scope->SessionId);
+ scope->ScopePid = task->pid;
+ scope->ScopeTask = task;
+ scope->ScopeHash = 0;
+ scope->ScopeUid = current_euid();
+ scope->ScopeUserName[0] = '\0';
+
+ if (!novfs_daemon_create_sessionId(&scope->SessionId)) {
+ DbgPrint("Scope_Find_Scope2: %d %d %d %d\n",
+ current_uid(), current_euid(), current_suid(), current_fsuid());
+ memset(scope->ScopeUserName, 0, sizeof(scope->ScopeUserName));
+ scope->ScopeUserNameLength = 0;
+ novfs_daemon_getpwuid(current_euid(), sizeof(scope->ScopeUserName), scope->ScopeUserName);
+ scope->ScopeUserNameLength = strlen(scope->ScopeUserName);
+ addscope = 1;
+ }
+
+ scope->ScopeHash = Scope_Hash_Val++;
+ DbgPrint("Scope_Find_Scope: Adding 0x%p\n"
+ " ScopeId: 0x%p:%p\n"
+ " SessionId: 0x%p:%p\n"
+ " ScopePid: %d\n"
+ " ScopeTask: 0x%p\n"
+ " ScopeHash: %u\n"
+ " ScopeUid: %u\n"
+ " ScopeUserNameLength: %u\n"
+ " ScopeUserName: %s\n",
+ scope,
+ scope->ScopeId.hTypeId, scope->ScopeId.hId,
+ scope->SessionId.hTypeId, scope->SessionId.hId,
+ scope->ScopePid,
+ scope->ScopeTask,
+ scope->ScopeHash, scope->ScopeUid, scope->ScopeUserNameLength, scope->ScopeUserName);
+
+ if (SC_PRESENT(scope->SessionId)) {
+ down(&Scope_Lock);
+ pscope = Scope_Search4Scope(scopeId, 0, 1);
+
+ if (!pscope) {
+ list_add(&scope->ScopeList, &Scope_List);
+ }
+ up(&Scope_Lock);
+
+ if (pscope) {
+ printk("<6>Scope_Find_Scope scope not added because it was already there...\n");
+ novfs_daemon_destroy_sessionId(scope->SessionId);
+ kfree(scope);
+ scope = pscope;
+ addscope = 0;
+ }
+ } else {
+ kfree(scope);
+ scope = NULL;
+ }
+
+ if (scope && addscope)
+ novfs_add_to_root(scope->ScopeUserName);
+ }
+ }
+
+ return (scope);
+}
+
+static int Scope_Validate_Scope(struct novfs_scope_list *Scope)
+{
+ struct novfs_scope_list *s;
+ struct list_head *sl;
+ int retVal = 0;
+
+ DbgPrint("Scope_Validate_Scope: 0x%p\n", Scope);
+
+ down(&Scope_Lock);
+
+ sl = Scope_List.next;
+ while (sl != &Scope_List) {
+ s = list_entry(sl, struct novfs_scope_list, ScopeList);
+
+ if (s == Scope) {
+ retVal = 1;
+ break;
+ }
+
+ sl = sl->next;
+ }
+
+ up(&Scope_Lock);
+
+ return (retVal);
+}
+
+uid_t novfs_scope_get_uid(struct novfs_scope_list * scope)
+{
+ uid_t uid = 0;
+ if (!scope)
+ scope = Scope_Find_Scope(1);
+
+ if (scope && Scope_Validate_Scope(scope))
+ uid = scope->ScopeUid;
+ return uid;
+}
+
+char *novfs_scope_get_username(void)
+{
+ char *name = NULL;
+ struct novfs_scope_list *Scope;
+
+ Scope = Scope_Find_Scope(1);
+
+ if (Scope && Scope_Validate_Scope(Scope))
+ name = Scope->ScopeUserName;
+
+ return name;
+}
+
+struct novfs_schandle novfs_scope_get_sessionId(struct novfs_scope_list
+ *Scope)
+{
+ struct novfs_schandle sessionId;
+ DbgPrint("Scope_Get_SessionId: 0x%p\n", Scope);
+ SC_INITIALIZE(sessionId);
+ if (!Scope)
+ Scope = Scope_Find_Scope(1);
+
+ if (Scope && Scope_Validate_Scope(Scope))
+ sessionId = Scope->SessionId;
+ DbgPrint("Scope_Get_SessionId: return 0x%p:%p\n", sessionId.hTypeId, sessionId.hId);
+ return (sessionId);
+}
+
+struct novfs_scope_list *novfs_get_scope_from_name(struct qstr *Name)
+{
+ struct novfs_scope_list *scope, *rscope = NULL;
+ struct list_head *sl;
+
+ DbgPrint("Scope_Get_ScopefromName: %.*s\n", Name->len, Name->name);
+
+ down(&Scope_Lock);
+
+ sl = Scope_List.next;
+ while (sl != &Scope_List) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+
+ if ((Name->len == scope->ScopeUserNameLength) && (0 == strncmp(scope->ScopeUserName, Name->name, Name->len))) {
+ rscope = scope;
+ break;
+ }
+
+ sl = sl->next;
+ }
+
+ up(&Scope_Lock);
+
+ return (rscope);
+}
+
+int novfs_scope_set_userspace(uint64_t * TotalSize, uint64_t * Free, uint64_t * TotalEnties, uint64_t * FreeEnties)
+{
+ struct novfs_scope_list *scope;
+ int retVal = 0;
+
+ scope = Scope_Find_Scope(1);
+
+ if (scope) {
+ if (TotalSize)
+ scope->ScopeUSize = *TotalSize;
+ if (Free)
+ scope->ScopeUFree = *Free;
+ if (TotalEnties)
+ scope->ScopeUTEnties = *TotalEnties;
+ if (FreeEnties)
+ scope->ScopeUAEnties = *FreeEnties;
+ }
+
+ return (retVal);
+}
+
+int novfs_scope_get_userspace(uint64_t * TotalSize, uint64_t * Free, uint64_t * TotalEnties, uint64_t * FreeEnties)
+{
+ struct novfs_scope_list *scope;
+ int retVal = 0;
+
+ uint64_t td, fd, te, fe;
+
+ scope = Scope_Find_Scope(1);
+
+ td = fd = te = fe = 0;
+ if (scope) {
+
+ retVal = novfs_daemon_get_userspace(scope->SessionId, &td, &fd, &te, &fe);
+
+ scope->ScopeUSize = td;
+ scope->ScopeUFree = fd;
+ scope->ScopeUTEnties = te;
+ scope->ScopeUAEnties = fe;
+ }
+
+ if (TotalSize)
+ *TotalSize = td;
+ if (Free)
+ *Free = fd;
+ if (TotalEnties)
+ *TotalEnties = te;
+ if (FreeEnties)
+ *FreeEnties = fe;
+
+ return (retVal);
+}
+
+struct novfs_scope_list *novfs_get_scope(struct dentry *Dentry)
+{
+ struct novfs_scope_list *scope = NULL;
+ char *buf, *path, *cp;
+ struct qstr name;
+
+ buf = (char *)kmalloc(PATH_LENGTH_BUFFER, GFP_KERNEL);
+ if (buf) {
+ path = novfs_scope_dget_path(Dentry, buf, PATH_LENGTH_BUFFER, 0);
+ if (path) {
+ DbgPrint("Scope_Get_ScopefromPath: %s\n", path);
+
+ if (*path == '/')
+ path++;
+
+ cp = path;
+ if (*cp) {
+ while (*cp && (*cp != '/'))
+ cp++;
+
+ *cp = '\0';
+ name.hash = 0;
+ name.len = (int)(cp - path);
+ name.name = path;
+ scope = novfs_get_scope_from_name(&name);
+ }
+ }
+ kfree(buf);
+ }
+
+ return (scope);
+}
+
+static char *add_to_list(char *Name, char *List, char *EndOfList)
+{
+ while (*Name && (List < EndOfList)) {
+ *List++ = *Name++;
+ }
+
+ if (List < EndOfList) {
+ *List++ = '\0';
+ }
+ return (List);
+}
+
+char *novfs_get_scopeusers(void)
+{
+ struct novfs_scope_list *scope;
+ struct list_head *sl;
+ int asize = 8 * MAX_USERNAME_LENGTH;
+ char *list, *cp, *ep;
+
+ DbgPrint("Scope_Get_ScopeUsers\n");
+
+ do { /* Copy list until done or out of memory */
+ list = kmalloc(asize, GFP_KERNEL);
+
+ DbgPrint("Scope_Get_ScopeUsers list=0x%p\n", list);
+ if (list) {
+ cp = list;
+ ep = cp + asize;
+
+ /*
+ * Add the tree and server entries
+ */
+ cp = add_to_list(TREE_DIRECTORY_NAME, cp, ep);
+ cp = add_to_list(SERVER_DIRECTORY_NAME, cp, ep);
+
+ down(&Scope_Lock);
+
+ sl = Scope_List.next;
+ while ((sl != &Scope_List) && (cp < ep)) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+
+ DbgPrint("Scope_Get_ScopeUsers found 0x%p %s\n", scope, scope->ScopeUserName);
+
+ cp = add_to_list(scope->ScopeUserName, cp, ep);
+
+ sl = sl->next;
+ }
+
+ up(&Scope_Lock);
+
+ if (cp < ep) {
+ *cp++ = '\0';
+ asize = 0;
+ } else { /* Allocation was to small, up size */
+
+ asize *= 4;
+ kfree(list);
+ list = NULL;
+ }
+ } else { /* if allocation fails return an empty list */
+
+ break;
+ }
+ } while (!list); /* List was to small try again */
+
+ return (list);
+}
+
+void *novfs_scope_lookup(void)
+{
+ return Scope_Find_Scope(1);
+}
+
+static void Scope_Timer_Function(unsigned long context)
+{
+ up(&Scope_Thread_Delay);
+}
+
+static int Scope_Cleanup_Thread(void *Args)
+{
+ struct novfs_scope_list *scope, *rscope;
+ struct list_head *sl, cleanup;
+ struct task_struct *task;
+
+ DbgPrint("Scope_Cleanup_Thread: %d\n", current->pid);
+
+ /*
+ * Setup and start que timer
+ */
+ init_timer(&Scope_Timer);
+
+ while (0 == Scope_Thread_Terminate) {
+ DbgPrint("Scope_Cleanup_Thread: looping\n");
+ if (Scope_Thread_Terminate) {
+ break;
+ }
+
+ /*
+ * Check scope list for any terminated processes
+ */
+ down(&Scope_Lock);
+
+ sl = Scope_List.next;
+ INIT_LIST_HEAD(&cleanup);
+
+ while (sl != &Scope_List) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+ sl = sl->next;
+
+ rscope = NULL;
+ rcu_read_lock();
+ for_each_process(task) {
+ if ((task->cred->uid == scope->ScopeUid)
+ || (task->cred->euid == scope->ScopeUid)) {
+ rscope = scope;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!rscope) {
+ list_move(&scope->ScopeList, &cleanup);
+ DbgPrint("Scope_Cleanup_Thread: Scope=0x%p\n", rscope);
+ }
+ }
+
+ up(&Scope_Lock);
+
+ sl = cleanup.next;
+ while (sl != &cleanup) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+ sl = sl->next;
+
+ DbgPrint("Scope_Cleanup_Thread: Removing 0x%p\n"
+ " ScopeId: 0x%p:%p\n"
+ " SessionId: 0x%p:%p\n"
+ " ScopePid: %d\n"
+ " ScopeTask: 0x%p\n"
+ " ScopeHash: %u\n"
+ " ScopeUid: %u\n"
+ " ScopeUserName: %s\n",
+ scope,
+ scope->ScopeId,
+ scope->SessionId,
+ scope->ScopePid, scope->ScopeTask, scope->ScopeHash, scope->ScopeUid, scope->ScopeUserName);
+ if (!Scope_Search4Scope(scope->SessionId, 1, 0)) {
+ novfs_remove_from_root(scope->ScopeUserName);
+ novfs_daemon_destroy_sessionId(scope->SessionId);
+ }
+ kfree(scope);
+ }
+
+ Scope_Timer.expires = jiffies + HZ * CLEANUP_INTERVAL;
+ Scope_Timer.data = (unsigned long)0;
+ Scope_Timer.function = Scope_Timer_Function;
+ add_timer(&Scope_Timer);
+ DbgPrint("Scope_Cleanup_Thread: sleeping\n");
+
+ if (down_interruptible(&Scope_Thread_Delay)) {
+ break;
+ }
+ del_timer(&Scope_Timer);
+ }
+ Scope_Thread_Terminate = 0;
+
+ printk(KERN_INFO "Scope_Cleanup_Thread: Exit\n");
+ DbgPrint("Scope_Cleanup_Thread: Exit\n");
+ return (0);
+}
+
+void novfs_scope_cleanup(void)
+{
+ struct novfs_scope_list *scope;
+ struct list_head *sl;
+
+ DbgPrint("Scope_Cleanup:\n");
+
+ /*
+ * Check scope list for any terminated processes
+ */
+ down(&Scope_Lock);
+
+ sl = Scope_List.next;
+
+ while (sl != &Scope_List) {
+ scope = list_entry(sl, struct novfs_scope_list, ScopeList);
+ sl = sl->next;
+
+ list_del(&scope->ScopeList);
+
+ DbgPrint("Scope_Cleanup: Removing 0x%p\n"
+ " ScopeId: 0x%p:%p\n"
+ " SessionId: 0x%p:%p\n"
+ " ScopePid: %d\n"
+ " ScopeTask: 0x%p\n"
+ " ScopeHash: %u\n"
+ " ScopeUid: %u\n"
+ " ScopeUserName: %s\n",
+ scope,
+ scope->ScopeId,
+ scope->SessionId,
+ scope->ScopePid, scope->ScopeTask, scope->ScopeHash, scope->ScopeUid, scope->ScopeUserName);
+ if (!Scope_Search4Scope(scope->SessionId, 1, 1)) {
+ novfs_remove_from_root(scope->ScopeUserName);
+ novfs_daemon_destroy_sessionId(scope->SessionId);
+ }
+ kfree(scope);
+ }
+
+ up(&Scope_Lock);
+
+}
+
+/*
+ * Walks the dentry chain building a path.
+ */
+char *novfs_scope_dget_path(struct dentry *Dentry, char *Buf, unsigned int Buflen, int Flags)
+{
+ char *retval = &Buf[Buflen];
+ struct dentry *p = Dentry;
+ int len;
+
+ *(--retval) = '\0';
+ Buflen--;
+
+ do {
+ 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;
+ }
+ } while (!IS_ROOT(p));
+
+ if (IS_ROOT(Dentry)) {
+ retval++;
+ }
+
+ if (Flags) {
+ len = strlen(p->d_sb->s_type->name);
+ if (Buflen - len > 0) {
+ retval -= len;
+ Buflen -= len;
+ memcpy(retval, p->d_sb->s_type->name, len);
+ *(--retval) = '/';
+ Buflen--;
+ }
+ }
+
+ return (retval);
+}
+
+void novfs_scope_init(void)
+{
+ INIT_LIST_HEAD(&Scope_List);
+ sema_init(&Scope_Lock, 1);
+ sema_init(&Scope_Thread_Delay, 0);
+ kthread_run(Scope_Cleanup_Thread, NULL, "novfs_ST");
+}
+
+void novfs_scope_exit(void)
+{
+ unsigned long expires = jiffies + HZ * SHUTDOWN_INTERVAL;
+
+ printk(KERN_INFO "Scope_Uninit: Start\n");
+
+ Scope_Thread_Terminate = 1;
+
+ up(&Scope_Thread_Delay);
+
+ mb();
+ while (Scope_Thread_Terminate && (jiffies < expires))
+ yield();
+ /* down(&Scope_Thread_Delay); */
+ printk(KERN_INFO "Scope_Uninit: Exit\n");
+
+}
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
#define NFS_INO_COMMIT (7) /* inode is committing unstable writes */
- #define NFS_INO_SEEN_GETATTR (8) /* flag to track if app is calling
+ #define NFS_INO_PNFS_COMMIT (8) /* use pnfs code for commit */
+ #define NFS_INO_LAYOUTCOMMIT (9) /* layoutcommit required */
++#define NFS_INO_SEEN_GETATTR (10) /* flag to track if app is calling
+ * getattr in a directory during
+ * readdir
+ */
static inline struct nfs_inode *NFS_I(const struct inode *inode)
{
nopage:
if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
+ unsigned int filter = SHOW_MEM_FILTER_NODES;
+
+ /*
+ * 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() || !wait)
+ filter &= ~SHOW_MEM_FILTER_NODES;
+
- pr_warning("%s: page allocation failure. order:%d, mode:0x%x\n",
+ if (!wait) {
- printk(KERN_INFO "The following is only an harmless informational message.\n");
- printk(KERN_INFO "Unless you get a _continuous_flood_ of these messages it means\n");
- printk(KERN_INFO "everything is working fine. Allocations from irqs cannot be\n");
- printk(KERN_INFO "perfectly reliable and the kernel is designed to handle that.\n");
++ 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");
+ }
- printk(KERN_INFO "%s: page allocation failure."
- " order:%d, mode:0x%x, alloc_flags:0x%x pflags:0x%x\n",
- current->comm, order, gfp_mask, alloc_flags,
- current->flags);
++ pr_info("%s: page allocation failure. order:%d, mode:0x%x\n",
+ current->comm, order, gfp_mask);
dump_stack();
- show_mem();
+ if (!should_suppress_show_mem())
+ show_mem(filter);
}
return page;
got_pg:
xprt = kzalloc(size, GFP_KERNEL);
if (xprt == NULL)
goto out;
- kref_init(&xprt->kref);
+ atomic_set(&xprt->count, 1);
xprt->max_reqs = max_req;
- xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
+ xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL | __GFP_REPEAT);
if (xprt->slot == NULL)
goto out_free;
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o sid.o file.o
+ resource.o sid.o file.o net.o
-clean-files := capability_names.h rlim_names.h
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
+
- clean-files: capability_names.h af_names.h
++clean-files := capability_names.h rlim_names.h af_names.h
+
+ # Build a lower case string table of capability names
+ # Transforms lines from
+ # #define CAP_DAC_OVERRIDE 1
+ # to
+ # [1] = "dac_override",
quiet_cmd_make-caps = GEN $@
- cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@
+ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
+ sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
+ -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
+ echo "};" >> $@
+quiet_cmd_make-af = GEN $@
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@
+
+
+ # Build a lower case string table of rlimit names.
+ # Transforms lines from
+ # #define RLIMIT_STACK 3 /* max stack size */
+ # to
+ # [RLIMIT_STACK] = "stack",
+ #
+ # and build a second integer table (with the second sed cmd), that maps
+ # RLIMIT defines to the order defined in asm-generic/resource.h Thi is
+ # required by policy load to map policy ordering of RLIMITs to internal
+ # ordering for architectures that redefine an RLIMIT.
+ # Transforms lines from
+ # #define RLIMIT_STACK 3 /* max stack size */
+ # to
+ # RLIMIT_STACK,
quiet_cmd_make-rlim = GEN $@
- cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@
+ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
+ sed $< >> $@ -r -n \
+ -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\
+ echo "};" >> $@ ;\
+ echo "static const int rlim_map[] = {" >> $@ ;\
+ sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
+ echo "};" >> $@
$(obj)/capability.o : $(obj)/capability_names.h
+$(obj)/net.o : $(obj)/af_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
- $(obj)/af_names.h : $(srctree)/include/linux/socket.h
- $(call cmd,make-af)
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
$(call cmd,make-rlim)
++$(obj)/af_names.h : $(srctree)/include/linux/socket.h
++ $(call cmd,make-af)