Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Apr 2015 22:01:29 +0000 (18:01 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Apr 2015 22:01:29 +0000 (18:01 -0400)
Pull sparc fixes from David Miller
 "Unfortunately, I brown paper bagged the generic iommu pool allocator
  by applying the wrong revision of the patch series.

  This reverts the bad one, and puts the right one in"

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc:
  iommu-common: Fix PARISC compile-time warnings
  sparc: Make LDC use common iommu poll management functions
  sparc: Make sparc64 use scalable lib/iommu-common.c functions
  Break up monolithic iommu table/lock into finer graularity pools and lock
  sparc: Revert generic IOMMU allocator.

252 files changed:
Documentation/ABI/testing/sysfs-block-dm
Documentation/CodingStyle
Documentation/DocBook/drm.tmpl
Documentation/DocBook/media/v4l/biblio.xml
Documentation/DocBook/media/v4l/dev-sliced-vbi.xml
Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml
Documentation/PCI/MSI-HOWTO.txt
Documentation/PCI/pci-error-recovery.txt
Documentation/PCI/pcieaer-howto.txt
Documentation/arm/Booting
Documentation/arm/README
Documentation/blackfin/Makefile
Documentation/block/biodoc.txt
Documentation/cgroups/memory.txt
Documentation/device-mapper/dm-crypt.txt
Documentation/device-mapper/log-writes.txt [new file with mode: 0644]
Documentation/device-mapper/switch.txt
Documentation/device-mapper/thin-provisioning.txt
Documentation/device-mapper/verity.txt
Documentation/devicetree/bindings/arm/bcm/bcm11351.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/bcm21664.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/bcm63138.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method [deleted file]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm21664.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm4708.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,bcm63138.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/brcm,cygnus.txt [new file with mode: 0644]
Documentation/devicetree/bindings/arm/bcm/cygnus.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/kona-timer.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm/kona-wdt.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm2835.txt [deleted file]
Documentation/devicetree/bindings/arm/bcm4708.txt [deleted file]
Documentation/devicetree/bindings/arm/brcm-brcmstb.txt [deleted file]
Documentation/devicetree/bindings/bus/bcma.txt [deleted file]
Documentation/devicetree/bindings/bus/brcm,bus-axi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/bcm-kona-clock.txt [deleted file]
Documentation/devicetree/bindings/clock/brcm,kona-ccu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/dma/bcm2835-dma.txt [deleted file]
Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-altera.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt [deleted file]
Documentation/devicetree/bindings/gpio/gpio.txt
Documentation/devicetree/bindings/gpio/mrvl-gpio.txt
Documentation/devicetree/bindings/i2c/brcm,kona-i2c.txt [new file with mode: 0644]
Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt [deleted file]
Documentation/devicetree/bindings/mfd/bcm590xx.txt [deleted file]
Documentation/devicetree/bindings/mfd/brcm,bcm59056.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/brcm/bmips.txt [deleted file]
Documentation/devicetree/bindings/mips/brcm/brcm,bmips.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mips/brcm/usb.txt [deleted file]
Documentation/devicetree/bindings/misc/brcm,kona-smc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/misc/smc.txt [deleted file]
Documentation/devicetree/bindings/mmc/brcm,kona-sdhci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/kona-sdhci.txt [deleted file]
Documentation/devicetree/bindings/net/brcm,bcm7445-switch-v4.0.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/brcm,bcmgenet.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/brcm,systemport.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt [deleted file]
Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt [deleted file]
Documentation/devicetree/bindings/net/broadcom-sf2.txt [deleted file]
Documentation/devicetree/bindings/net/broadcom-systemport.txt [deleted file]
Documentation/devicetree/bindings/phy/bcm-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/brcm,kona-usb2-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt [deleted file]
Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt [new file with mode: 0644]
Documentation/devicetree/bindings/serial/bcm63xx-uart.txt [deleted file]
Documentation/devicetree/bindings/serial/brcm,bcm6345-uart.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/bcm2835-i2s.txt [deleted file]
Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt [new file with mode: 0644]
Documentation/devicetree/bindings/timer/brcm,kona-timer.txt [new file with mode: 0644]
Documentation/devicetree/bindings/unittest.txt
Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/brcm,kona-wdt.txt [new file with mode: 0644]
Documentation/devicetree/of_selftest.txt [deleted file]
Documentation/devicetree/of_unittest.txt [new file with mode: 0644]
Documentation/email-clients.txt
Documentation/filesystems/f2fs.txt
Documentation/filesystems/proc.txt
Documentation/gpio/board.txt
Documentation/gpio/consumer.txt
Documentation/i2o/README [deleted file]
Documentation/i2o/ioctl [deleted file]
Documentation/input/alps.txt
Documentation/input/event-codes.txt
Documentation/input/gpio-tilt.txt
Documentation/input/iforce-protocol.txt
Documentation/input/walkera0701.txt
Documentation/input/yealink.txt
Documentation/kernel-parameters.txt
Documentation/kmemcheck.txt
Documentation/kprobes.txt
Documentation/memory-barriers.txt
Documentation/memory-hotplug.txt
Documentation/printk-formats.txt
Documentation/scheduler/completion.txt
Documentation/vm/pagemap.txt
Documentation/vm/transhuge.txt
Documentation/zh_CN/arm64/booting.txt
Documentation/zh_CN/arm64/legacy_instructions.txt [new file with mode: 0644]
Documentation/zh_CN/arm64/memory.txt
MAINTAINERS
README
arch/mips/Kconfig
arch/mips/configs/lemote2f_defconfig
arch/mips/configs/loongson3_defconfig
arch/mips/include/asm/mach-loongson/gpio.h
arch/mips/loongson/common/Makefile
arch/mips/loongson/common/gpio.c [deleted file]
arch/x86/Kconfig
arch/x86/include/uapi/asm/e820.h
arch/x86/kernel/Makefile
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_intel_pt.c
arch/x86/kernel/cpu/perf_event_intel_rapl.c
arch/x86/kernel/e820.c
arch/x86/kernel/i387.c
arch/x86/kernel/pmem.c [new file with mode: 0644]
arch/x86/kernel/signal.c
drivers/block/Kconfig
drivers/block/Makefile
drivers/block/pmem.c [new file with mode: 0644]
drivers/coresight/of_coresight.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/devres.c
drivers/gpio/gpio-adp5588.c
drivers/gpio/gpio-altera.c [new file with mode: 0644]
drivers/gpio/gpio-arizona.c
drivers/gpio/gpio-crystalcove.c
drivers/gpio/gpio-da9052.c
drivers/gpio/gpio-da9055.c
drivers/gpio/gpio-f7188x.c
drivers/gpio/gpio-ich.c
drivers/gpio/gpio-kempld.c
drivers/gpio/gpio-loongson.c [new file with mode: 0644]
drivers/gpio/gpio-max7300.c
drivers/gpio/gpio-max732x.c
drivers/gpio/gpio-mb86s7x.c
drivers/gpio/gpio-mc33880.c
drivers/gpio/gpio-mcp23s08.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-pcf857x.c
drivers/gpio/gpio-pxa.c
drivers/gpio/gpio-rcar.c
drivers/gpio/gpio-tb10x.c
drivers/gpio/gpio-vf610.c
drivers/gpio/gpio-xgene-sb.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/i2c/busses/i2c-jz4780.c
drivers/i2c/i2c-core.c
drivers/input/keyboard/gpio_keys_polled.c
drivers/leds/leds-gpio.c
drivers/md/Kconfig
drivers/md/Makefile
drivers/md/dm-cache-policy-mq.c
drivers/md/dm-crypt.c
drivers/md/dm-delay.c
drivers/md/dm-log-userspace-base.c
drivers/md/dm-log-userspace-transfer.c
drivers/md/dm-log-writes.c [new file with mode: 0644]
drivers/md/dm-mpath.c
drivers/md/dm-sysfs.c
drivers/md/dm-table.c
drivers/md/dm-verity.c
drivers/md/dm.c
drivers/md/dm.h
drivers/media/platform/am437x/am437x-vpfe.c
drivers/media/platform/soc_camera/soc_camera.c
drivers/of/Kconfig
drivers/of/Makefile
drivers/of/base.c
drivers/of/of_net.c
drivers/of/unittest-data/.gitignore [new file with mode: 0644]
drivers/of/unittest-data/Makefile [new file with mode: 0644]
drivers/of/unittest-data/tests-overlay.dtsi
drivers/of/unittest.c
drivers/video/fbdev/omap2/dss/omapdss-boot-init.c
fs/9p/v9fs.h
fs/9p/vfs_addr.c
fs/9p/vfs_file.c
fs/Kconfig
fs/f2fs/Kconfig
fs/f2fs/acl.c
fs/f2fs/checkpoint.c
fs/f2fs/data.c
fs/f2fs/debug.c
fs/f2fs/dir.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/gc.c
fs/f2fs/inline.c
fs/f2fs/inode.c
fs/f2fs/namei.c
fs/f2fs/node.c
fs/f2fs/node.h
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/segment.h
fs/f2fs/super.c
fs/f2fs/xattr.c
fs/fs_pin.c
fs/namespace.c
fs/pnode.c
fs/pnode.h
include/asm-generic/gpio.h
include/linux/device-mapper.h
include/linux/f2fs_fs.h
include/linux/fs_pin.h
include/linux/gpio/consumer.h
include/linux/gpio/driver.h
include/linux/hsi/hsi.h
include/linux/mount.h
include/linux/of.h
include/linux/of_graph.h
include/trace/events/f2fs.h
include/trace/events/filemap.h
include/trace/events/kmem.h
include/trace/events/vmscan.h
include/uapi/linux/dm-ioctl.h
init/Kconfig
init/do_mounts.c
kernel/locking/lockdep.c
kernel/smp.c
net/9p/protocol.c
net/9p/trans_fd.c
net/9p/trans_rdma.c
net/9p/trans_virtio.c
net/bluetooth/hidp/core.c
tools/perf/Documentation/perf-kmem.txt
tools/perf/builtin-kmem.c
tools/perf/util/probe-event.c
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/run_x86_tests.sh
tools/testing/selftests/x86/single_step_syscall.c [new file with mode: 0644]

index 87ca5691e29b1d92726e01bbe5296cde6932d08d..f9f2339b9a0a88e485d91bae022b1e6a3d213f07 100644 (file)
@@ -23,3 +23,25 @@ Description: Device-mapper device suspend state.
                Contains the value 1 while the device is suspended.
                Otherwise it contains 0. Read-only attribute.
 Users:         util-linux, device-mapper udev rules
+
+What:          /sys/block/dm-<num>/dm/rq_based_seq_io_merge_deadline
+Date:          March 2015
+KernelVersion: 4.1
+Contact:       dm-devel@redhat.com
+Description:   Allow control over how long a request that is a
+               reasonable merge candidate can be queued on the request
+               queue.  The resolution of this deadline is in
+               microseconds (ranging from 1 to 100000 usecs).
+               Setting this attribute to 0 (the default) will disable
+               request-based DM's merge heuristic and associated extra
+               accounting.  This attribute is not applicable to
+               bio-based DM devices so it will only ever report 0 for
+               them.
+
+What:          /sys/block/dm-<num>/dm/use_blk_mq
+Date:          March 2015
+KernelVersion: 4.1
+Contact:       dm-devel@redhat.com
+Description:   Request-based Device-mapper blk-mq I/O path mode.
+               Contains the value 1 if the device is using blk-mq.
+               Otherwise it contains 0. Read-only attribute.
index 4d4f06d47e0620325016bbda0af537995a8bfead..f4b78eafd92a2e76e439f168fff97e1d41374b59 100644 (file)
@@ -13,7 +13,7 @@ and NOT read it.  Burn them, it's a great symbolic gesture.
 Anyway, here goes:
 
 
-               Chapter 1: Indentation
+               Chapter 1: Indentation
 
 Tabs are 8 characters, and thus indentations are also 8 characters.
 There are heretic movements that try to make indentations 4 (or even 2!)
@@ -56,7 +56,6 @@ instead of "double-indenting" the "case" labels.  E.g.:
                break;
        }
 
-
 Don't put multiple statements on a single line unless you have
 something to hide:
 
@@ -156,25 +155,25 @@ comments on.
 
 Do not unnecessarily use braces where a single statement will do.
 
-if (condition)
-       action();
+       if (condition)
+               action();
 
 and
 
-if (condition)
-       do_this();
-else
-       do_that();
+       if (condition)
+               do_this();
+       else
+               do_that();
 
 This does not apply if only one branch of a conditional statement is a single
 statement; in the latter case use braces in both branches:
 
-if (condition) {
-       do_this();
-       do_that();
-} else {
-       otherwise();
-}
+       if (condition) {
+               do_this();
+               do_that();
+       } else {
+               otherwise();
+       }
 
                3.1:  Spaces
 
@@ -186,8 +185,11 @@ although they are not required in the language, as in: "sizeof info" after
 "struct fileinfo info;" is declared).
 
 So use a space after these keywords:
+
        if, switch, case, for, do, while
+
 but not with sizeof, typeof, alignof, or __attribute__.  E.g.,
+
        s = sizeof(struct file);
 
 Do not add spaces around (inside) parenthesized expressions.  This example is
@@ -209,12 +211,15 @@ such as any of these:
        =  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :
 
 but no space after unary operators:
+
        &  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined
 
 no space before the postfix increment & decrement unary operators:
+
        ++  --
 
 no space after the prefix increment & decrement unary operators:
+
        ++  --
 
 and no space around the '.' and "->" structure member operators.
@@ -268,13 +273,11 @@ See chapter 6 (Functions).
                Chapter 5: Typedefs
 
 Please don't use things like "vps_t".
-
 It's a _mistake_ to use typedef for structures and pointers. When you see a
 
        vps_t a;
 
 in the source, what does it mean?
-
 In contrast, if it says
 
        struct virtual_container *a;
@@ -372,11 +375,11 @@ In source files, separate functions with one blank line.  If the function is
 exported, the EXPORT* macro for it should follow immediately after the closing
 function brace line.  E.g.:
 
-int system_is_up(void)
-{
-       return system_state == SYSTEM_RUNNING;
-}
-EXPORT_SYMBOL(system_is_up);
+       int system_is_up(void)
+       {
+               return system_state == SYSTEM_RUNNING;
+       }
+       EXPORT_SYMBOL(system_is_up);
 
 In function prototypes, include parameter names with their data types.
 Although this is not required by the C language, it is preferred in Linux
@@ -405,34 +408,34 @@ The rationale for using gotos is:
     modifications are prevented
 - saves the compiler work to optimize redundant code away ;)
 
-int fun(int a)
-{
-       int result = 0;
-       char *buffer;
-
-       buffer = kmalloc(SIZE, GFP_KERNEL);
-       if (!buffer)
-               return -ENOMEM;
-
-       if (condition1) {
-               while (loop1) {
-                       ...
+       int fun(int a)
+       {
+               int result = 0;
+               char *buffer;
+
+               buffer = kmalloc(SIZE, GFP_KERNEL);
+               if (!buffer)
+                       return -ENOMEM;
+
+               if (condition1) {
+                       while (loop1) {
+                               ...
+                       }
+                       result = 1;
+                       goto out_buffer;
                }
-               result = 1;
-               goto out_buffer;
+               ...
+       out_buffer:
+               kfree(buffer);
+               return result;
        }
-       ...
-out_buffer:
-       kfree(buffer);
-       return result;
-}
 
 A common type of bug to be aware of it "one err bugs" which look like this:
 
-err:
-       kfree(foo->bar);
-       kfree(foo);
-       return ret;
+       err:
+               kfree(foo->bar);
+               kfree(foo);
+               return ret;
 
 The bug in this code is that on some exit paths "foo" is NULL.  Normally the
 fix for this is to split it up into two error labels "err_bar:" and "err_foo:".
@@ -503,9 +506,9 @@ values.  To do the latter, you can stick the following in your .emacs file:
 (defun c-lineup-arglist-tabs-only (ignored)
   "Line up argument lists by tabs, not spaces"
   (let* ((anchor (c-langelem-pos c-syntactic-element))
-        (column (c-langelem-2nd-pos c-syntactic-element))
-        (offset (- (1+ column) anchor))
-        (steps (floor offset c-basic-offset)))
+         (column (c-langelem-2nd-pos c-syntactic-element))
+         (offset (- (1+ column) anchor))
+         (steps (floor offset c-basic-offset)))
     (* (max steps 1)
        c-basic-offset)))
 
@@ -612,7 +615,7 @@ have a reference count on it, you almost certainly have a bug.
 
 Names of macros defining constants and labels in enums are capitalized.
 
-#define CONSTANT 0x12345
+       #define CONSTANT 0x12345
 
 Enums are preferred when defining several related constants.
 
@@ -623,28 +626,28 @@ Generally, inline functions are preferable to macros resembling functions.
 
 Macros with multiple statements should be enclosed in a do - while block:
 
-#define macrofun(a, b, c)                      \
-       do {                                    \
-               if (a == 5)                     \
-                       do_this(b, c);          \
-       } while (0)
+       #define macrofun(a, b, c)                       \
+               do {                                    \
+                       if (a == 5)                     \
+                               do_this(b, c);          \
+               } while (0)
 
 Things to avoid when using macros:
 
 1) macros that affect control flow:
 
-#define FOO(x)                                 \
-       do {                                    \
-               if (blah(x) < 0)                \
-                       return -EBUGGERED;      \
-       } while(0)
+       #define FOO(x)                                  \
+               do {                                    \
+                       if (blah(x) < 0)                \
+                               return -EBUGGERED;      \
+               } while(0)
 
 is a _very_ bad idea.  It looks like a function call but exits the "calling"
 function; don't break the internal parsers of those who will read the code.
 
 2) macros that depend on having a local variable with a magic name:
 
-#define FOO(val) bar(index, val)
+       #define FOO(val) bar(index, val)
 
 might look like a good thing, but it's confusing as hell when one reads the
 code and it's prone to breakage from seemingly innocent changes.
@@ -656,8 +659,8 @@ bite you if somebody e.g. turns FOO into an inline function.
 must enclose the expression in parentheses. Beware of similar issues with
 macros using parameters.
 
-#define CONSTANT 0x4000
-#define CONSTEXP (CONSTANT | 3)
+       #define CONSTANT 0x4000
+       #define CONSTEXP (CONSTANT | 3)
 
 5) namespace collisions when defining local variables in macros resembling
 functions:
@@ -809,11 +812,11 @@ you should use, rather than explicitly coding some variant of them yourself.
 For example, if you need to calculate the length of an array, take advantage
 of the macro
 
-  #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+       #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 Similarly, if you need to calculate the size of some structure member, use
 
-  #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
+       #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
 
 There are also min() and max() macros that do strict type checking if you
 need them.  Feel free to peruse that header file to see what else is already
@@ -826,19 +829,19 @@ Some editors can interpret configuration information embedded in source files,
 indicated with special markers.  For example, emacs interprets lines marked
 like this:
 
--*- mode: c -*-
+       -*- mode: c -*-
 
 Or like this:
 
-/*
-Local Variables:
-compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
-End:
-*/
+       /*
+       Local Variables:
+       compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
+       End:
+       */
 
 Vim interprets markers that look like this:
 
-/* vim:set sw=8 noet */
+       /* vim:set sw=8 noet */
 
 Do not include any of these in source files.  People have their own personal
 editor configurations, and your source files should not override them.  This
@@ -915,9 +918,9 @@ At the end of any non-trivial #if or #ifdef block (more than a few lines),
 place a comment after the #endif on the same line, noting the conditional
 expression used.  For instance:
 
-#ifdef CONFIG_SOMETHING
-...
-#endif /* CONFIG_SOMETHING */
+       #ifdef CONFIG_SOMETHING
+       ...
+       #endif /* CONFIG_SOMETHING */
 
 
                Appendix I: References
index 03f1985a4bd1876d7b3e78d70b0c6939ef709eab..0cad3ce957ffe160277186b91a69c32b70cdf88f 100644 (file)
@@ -1293,7 +1293,7 @@ int max_width, max_height;</synopsis>
           </para>
           <para>
             If a page flip can be successfully scheduled the driver must set the
-            <code>drm_crtc-&lt;fb</code> field to the new framebuffer pointed to
+            <code>drm_crtc-&gt;fb</code> field to the new framebuffer pointed to
             by <code>fb</code>. This is important so that the reference counting
             on framebuffers stays balanced.
           </para>
index 7ff01a23c2fe4835d848d6353e8bb78d66dc56eb..fdee6b3f3ecaaaabbbf7d3cd5b62cd8b3d1be257 100644 (file)
@@ -1,14 +1,13 @@
   <bibliography>
     <title>References</title>
 
-    <biblioentry id="eia608">
-      <abbrev>EIA&nbsp;608-B</abbrev>
+    <biblioentry id="cea608">
+      <abbrev>CEA&nbsp;608-E</abbrev>
       <authorgroup>
-       <corpauthor>Electronic Industries Alliance (<ulink
-url="http://www.eia.org">http://www.eia.org</ulink>)</corpauthor>
+       <corpauthor>Consumer Electronics Association (<ulink
+url="http://www.ce.org">http://www.ce.org</ulink>)</corpauthor>
       </authorgroup>
-      <title>EIA 608-B "Recommended Practice for Line 21 Data
-Service"</title>
+      <title>CEA-608-E R-2014 "Line 21 Data Services"</title>
     </biblioentry>
 
     <biblioentry id="en300294">
index 7a8bf3011ee999861b3c338f1b47316052f99a85..0aec62ed2bf8e1a00e40b8025316c1fac00b6613 100644 (file)
@@ -254,7 +254,7 @@ ETS&nbsp;300&nbsp;231, lsb first transmitted.</entry>
          <row>
            <entry><constant>V4L2_SLICED_CAPTION_525</constant></entry>
            <entry>0x1000</entry>
-           <entry><xref linkend="eia608" /></entry>
+           <entry><xref linkend="cea608" /></entry>
            <entry>NTSC line 21, 284 (second field 21)</entry>
            <entry>Two bytes in transmission order, including parity
 bit, lsb first transmitted.</entry>
index bd015d1563ffe45b8137eabeb9e463db2078cdcd..d05623c554037c6471b1acf38f3d32d4a55c289e 100644 (file)
@@ -205,7 +205,7 @@ ETS&nbsp;300&nbsp;231, lsb first transmitted.</entry>
          <row>
            <entry><constant>V4L2_SLICED_CAPTION_525</constant></entry>
            <entry>0x1000</entry>
-           <entry><xref linkend="eia608" /></entry>
+           <entry><xref linkend="cea608" /></entry>
            <entry>NTSC line 21, 284 (second field 21)</entry>
            <entry>Two bytes in transmission order, including parity
 bit, lsb first transmitted.</entry>
index 0d920d54536df88b07f5b46637cfa32b5970100e..1179850f453c66849c1808f83b0955a63cd33ad1 100644 (file)
@@ -353,7 +353,7 @@ retry:
        rc = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
                                   maxvec, maxvec);
        /*
-        * -ENOSPC is the only error code allowed to be analized
+        * -ENOSPC is the only error code allowed to be analyzed
         */
        if (rc == -ENOSPC) {
                if (maxvec == 1)
@@ -370,7 +370,7 @@ retry:
        return rc;
 }
 
-Note how pci_enable_msix_range() return value is analized for a fallback -
+Note how pci_enable_msix_range() return value is analyzed for a fallback -
 any error code other than -ENOSPC indicates a fatal error and should not
 be retried.
 
@@ -486,7 +486,7 @@ during development.
 If your device supports both MSI-X and MSI capabilities, you should use
 the MSI-X facilities in preference to the MSI facilities.  As mentioned
 above, MSI-X supports any number of interrupts between 1 and 2048.
-In constrast, MSI is restricted to a maximum of 32 interrupts (and
+In contrast, MSI is restricted to a maximum of 32 interrupts (and
 must be a power of two).  In addition, the MSI interrupt vectors must
 be allocated consecutively, so the system might not be able to allocate
 as many vectors for MSI as it could for MSI-X.  On some platforms, MSI
@@ -501,18 +501,9 @@ necessary to disable interrupts (Linux guarantees the same interrupt will
 not be re-entered).  If a device uses multiple interrupts, the driver
 must disable interrupts while the lock is held.  If the device sends
 a different interrupt, the driver will deadlock trying to recursively
-acquire the spinlock.
-
-There are two solutions.  The first is to take the lock with
-spin_lock_irqsave() or spin_lock_irq() (see
-Documentation/DocBook/kernel-locking).  The second is to specify
-IRQF_DISABLED to request_irq() so that the kernel runs the entire
-interrupt routine with interrupts disabled.
-
-If your MSI interrupt routine does not hold the lock for the whole time
-it is running, the first solution may be best.  The second solution is
-normally preferred as it avoids making two transitions from interrupt
-disabled to enabled and back again.
+acquire the spinlock.  Such deadlocks can be avoided by using
+spin_lock_irqsave() or spin_lock_irq() which disable local interrupts
+and acquire the lock (see Documentation/DocBook/kernel-locking).
 
 4.6 How to tell whether MSI/MSI-X is enabled on a device
 
index 898ded24510de978c287116e03094e4a052d48da..ac26869c7db450dc2861fb0b617ef8045610c3b2 100644 (file)
@@ -256,7 +256,7 @@ STEP 4: Slot Reset
 ------------------
 
 In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
-the platform will peform a slot reset on the requesting PCI device(s). 
+the platform will perform a slot reset on the requesting PCI device(s). 
 The actual steps taken by a platform to perform a slot reset
 will be platform-dependent. Upon completion of slot reset, the
 platform will call the device slot_reset() callback.
index 26d3d945c3c21aa965cd9b48c49cc42abf09df3f..b4987c0bcb20fd86e6fe8b989fab15d8b6fbed51 100644 (file)
@@ -66,8 +66,8 @@ hardware (mostly chipsets) has root ports that cannot obtain the reporting
 source ID. nosourceid=n by default.
 
 2.3 AER error output
-When a PCI-E AER error is captured, an error message will be outputed to
-console. If it's a correctable error, it is outputed as a warning.
+When a PCI-E AER error is captured, an error message will be outputted to
+console. If it's a correctable error, it is outputted as a warning.
 Otherwise, it is printed as an error. So users could choose different
 log level to filter out correctable error messages.
 
index 371814a36719f7979402f41c63ca3609fcdae6a9..83c1df2fc758ba4b92dd900785ed7f0920ad9619 100644 (file)
@@ -58,13 +58,18 @@ serial format options as described in
 --------------------------
 
 Existing boot loaders:         OPTIONAL
-New boot loaders:              MANDATORY
+New boot loaders:              MANDATORY except for DT-only platforms
 
 The boot loader should detect the machine type its running on by some
 method.  Whether this is a hard coded value or some algorithm that
 looks at the connected hardware is beyond the scope of this document.
 The boot loader must ultimately be able to provide a MACH_TYPE_xxx
-value to the kernel. (see linux/arch/arm/tools/mach-types).
+value to the kernel. (see linux/arch/arm/tools/mach-types).  This
+should be passed to the kernel in register r1.
+
+For DT-only platforms, the machine type will be determined by device
+tree.  set the machine type to all ones (~0).  This is not strictly
+necessary, but assures that it will not match any existing types.
 
 4. Setup boot data
 ------------------
index aea34095cdcfcc5c61a8fe7254cb2e8761992e90..9d1e5b2c92e62b7554492b27516611b4adedc2dd 100644 (file)
@@ -185,13 +185,20 @@ Kernel entry (head.S)
   board devices are used, or the device is setup, and provides that
   machine specific "personality."
 
-  This fine-grained machine specific selection is controlled by the machine
-  type ID, which acts both as a run-time and a compile-time code selection
-  method.
+  For platforms that support device tree (DT), the machine selection is
+  controlled at runtime by passing the device tree blob to the kernel.  At
+  compile-time, support for the machine type must be selected.  This allows for
+  a single multiplatform kernel build to be used for several machine types.
 
-  You can register a new machine via the web site at:
+  For platforms that do not use device tree, this machine selection is
+  controlled by the machine type ID, which acts both as a run-time and a
+  compile-time code selection method.  You can register a new machine via the
+  web site at:
 
     <http://www.arm.linux.org.uk/developer/machines/>
 
+  Note: Please do not register a machine type for DT-only platforms.  If your
+  platform is DT-only, you do not need a registered machine type.
+
 ---
 Russell King (15/03/2004)
index 03f78059d6f5849b7914d50a2983fb6f9d678eb9..6782c58fbc297d830fdf5a1a034b7b28ce0bce02 100644 (file)
@@ -1,5 +1,5 @@
 ifneq ($(CONFIG_BLACKFIN),)
-ifneq ($(CONFIG_BFIN_GPTIMERS,)
+ifneq ($(CONFIG_BFIN_GPTIMERS),)
 obj-m := gptimers-example.o
 endif
 endif
index 5aabc08de811d49ba898d550a0c2ff98ab3964a5..fd12c0d835fd1eca07f9c3bf8ef28eb7e877d3f2 100644 (file)
@@ -48,8 +48,7 @@ Description of Contents:
        - Highmem I/O support
        - I/O scheduler modularization
   1.2 Tuning based on high level requirements/capabilities
-       1.2.1 I/O Barriers
-       1.2.2 Request Priority/Latency
+       1.2.1 Request Priority/Latency
   1.3 Direct access/bypass to lower layers for diagnostics and special
       device operations
        1.3.1 Pre-built commands
@@ -255,29 +254,12 @@ some control over i/o ordering.
 What kind of support exists at the generic block layer for this ?
 
 The flags and rw fields in the bio structure can be used for some tuning
-from above e.g indicating that an i/o is just a readahead request, or for
-marking  barrier requests (discussed next), or priority settings (currently
-unused). As far as user applications are concerned they would need an
-additional mechanism either via open flags or ioctls, or some other upper
-level mechanism to communicate such settings to block.
-
-1.2.1 I/O Barriers
-
-There is a way to enforce strict ordering for i/os through barriers.
-All requests before a barrier point must be serviced before the barrier
-request and any other requests arriving after the barrier will not be
-serviced until after the barrier has completed. This is useful for higher
-level control on write ordering, e.g flushing a log of committed updates
-to disk before the corresponding updates themselves.
-
-A flag in the bio structure, BIO_BARRIER is used to identify a barrier i/o.
-The generic i/o scheduler would make sure that it places the barrier request and
-all other requests coming after it after all the previous requests in the
-queue. Barriers may be implemented in different ways depending on the
-driver. For more details regarding I/O barriers, please read barrier.txt
-in this directory.
-
-1.2.2 Request Priority/Latency
+from above e.g indicating that an i/o is just a readahead request, or priority
+settings (currently unused). As far as user applications are concerned they
+would need an additional mechanism either via open flags or ioctls, or some
+other upper level mechanism to communicate such settings to block.
+
+1.2.1 Request Priority/Latency
 
 Todo/Under discussion:
 Arjan's proposed request priority scheme allows higher levels some broad
@@ -906,8 +888,8 @@ queue and specific I/O schedulers.  Unless stated otherwise, elevator is used
 to refer to both parts and I/O scheduler to specific I/O schedulers.
 
 Block layer implements generic dispatch queue in block/*.c.
-The generic dispatch queue is responsible for properly ordering barrier
-requests, requeueing, handling non-fs requests and all other subtleties.
+The generic dispatch queue is responsible for requeueing, handling non-fs
+requests and all other subtleties.
 
 Specific I/O schedulers are responsible for ordering normal filesystem
 requests.  They can also choose to delay certain requests to improve
index a22df3ad35fff8a43d78517f790a9822aa5d8d55..f456b4315e86d80abe8d4d0b4fbb68eeca5b4e4a 100644 (file)
@@ -275,11 +275,6 @@ When oom event notifier is registered, event will be delivered.
 
 2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM)
 
-WARNING: Current implementation lacks reclaim support. That means allocation
-        attempts will fail when close to the limit even if there are plenty of
-        kmem available for reclaim. That makes this option unusable in real
-        life so DO NOT SELECT IT unless for development purposes.
-
 With the Kernel memory extension, the Memory Controller is able to limit
 the amount of kernel memory used by the system. Kernel memory is fundamentally
 different than user memory, since it can't be swapped out, which makes it
@@ -345,6 +340,9 @@ set:
     In this case, the admin could set up K so that the sum of all groups is
     never greater than the total memory, and freely set U at the cost of his
     QoS.
+    WARNING: In the current implementation, memory reclaim will NOT be
+    triggered for a cgroup when it hits K while staying below U, which makes
+    this setup impractical.
 
     U != 0, K >= U:
     Since kmem charges will also be fed to the user counter and reclaim will be
index ad697781f9ac478477cfed76978b047685eda2b6..692171fe9da0307732b96327e10848e1d12e2e26 100644 (file)
@@ -5,7 +5,7 @@ Device-Mapper's "crypt" target provides transparent encryption of block devices
 using the kernel crypto API.
 
 For a more detailed description of supported parameters see:
-http://code.google.com/p/cryptsetup/wiki/DMCrypt
+https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
 
 Parameters: <cipher> <key> <iv_offset> <device path> \
              <offset> [<#opt_params> <opt_params>]
@@ -80,7 +80,7 @@ Example scripts
 ===============
 LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
 encryption with dm-crypt using the 'cryptsetup' utility, see
-http://code.google.com/p/cryptsetup/
+https://gitlab.com/cryptsetup/cryptsetup
 
 [[
 #!/bin/sh
diff --git a/Documentation/device-mapper/log-writes.txt b/Documentation/device-mapper/log-writes.txt
new file mode 100644 (file)
index 0000000..c10f30c
--- /dev/null
@@ -0,0 +1,140 @@
+dm-log-writes
+=============
+
+This target takes 2 devices, one to pass all IO to normally, and one to log all
+of the write operations to.  This is intended for file system developers wishing
+to verify the integrity of metadata or data as the file system is written to.
+There is a log_write_entry written for every WRITE request and the target is
+able to take arbitrary data from userspace to insert into the log.  The data
+that is in the WRITE requests is copied into the log to make the replay happen
+exactly as it happened originally.
+
+Log Ordering
+============
+
+We log things in order of completion once we are sure the write is no longer in
+cache.  This means that normal WRITE requests are not actually logged until the
+next REQ_FLUSH request.  This is to make it easier for userspace to replay the
+log in a way that correlates to what is on disk and not what is in cache, to
+make it easier to detect improper waiting/flushing.
+
+This works by attaching all WRITE requests to a list once the write completes.
+Once we see a REQ_FLUSH request we splice this list onto the request and once
+the FLUSH request completes we log all of the WRITEs and then the FLUSH.  Only
+completed WRITEs, at the time the REQ_FLUSH is issued, are added in order to
+simulate the worst case scenario with regard to power failures.  Consider the
+following example (W means write, C means complete):
+
+W1,W2,W3,C3,C2,Wflush,C1,Cflush
+
+The log would show the following
+
+W3,W2,flush,W1....
+
+Again this is to simulate what is actually on disk, this allows us to detect
+cases where a power failure at a particular point in time would create an
+inconsistent file system.
+
+Any REQ_FUA requests bypass this flushing mechanism and are logged as soon as
+they complete as those requests will obviously bypass the device cache.
+
+Any REQ_DISCARD requests are treated like WRITE requests.  Otherwise we would
+have all the DISCARD requests, and then the WRITE requests and then the FLUSH
+request.  Consider the following example:
+
+WRITE block 1, DISCARD block 1, FLUSH
+
+If we logged DISCARD when it completed, the replay would look like this
+
+DISCARD 1, WRITE 1, FLUSH
+
+which isn't quite what happened and wouldn't be caught during the log replay.
+
+Target interface
+================
+
+i) Constructor
+
+   log-writes <dev_path> <log_dev_path>
+
+   dev_path    : Device that all of the IO will go to normally.
+   log_dev_path : Device where the log entries are written to.
+
+ii) Status
+
+    <#logged entries> <highest allocated sector>
+
+    #logged entries           : Number of logged entries
+    highest allocated sector   : Highest allocated sector
+
+iii) Messages
+
+    mark <description>
+
+       You can use a dmsetup message to set an arbitrary mark in a log.
+       For example say you want to fsck a file system after every
+       write, but first you need to replay up to the mkfs to make sure
+       we're fsck'ing something reasonable, you would do something like
+       this:
+
+         mkfs.btrfs -f /dev/mapper/log
+         dmsetup message log 0 mark mkfs
+         <run test>
+
+         This would allow you to replay the log up to the mkfs mark and
+         then replay from that point on doing the fsck check in the
+         interval that you want.
+
+       Every log has a mark at the end labeled "dm-log-writes-end".
+
+Userspace component
+===================
+
+There is a userspace tool that will replay the log for you in various ways.
+It can be found here: https://github.com/josefbacik/log-writes
+
+Example usage
+=============
+
+Say you want to test fsync on your file system.  You would do something like
+this:
+
+TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
+dmsetup create log --table "$TABLE"
+mkfs.btrfs -f /dev/mapper/log
+dmsetup message log 0 mark mkfs
+
+mount /dev/mapper/log /mnt/btrfs-test
+<some test that does fsync at the end>
+dmsetup message log 0 mark fsync
+md5sum /mnt/btrfs-test/foo
+umount /mnt/btrfs-test
+
+dmsetup remove log
+replay-log --log /dev/sdc --replay /dev/sdb --end-mark fsync
+mount /dev/sdb /mnt/btrfs-test
+md5sum /mnt/btrfs-test/foo
+<verify md5sum's are correct>
+
+Another option is to do a complicated file system operation and verify the file
+system is consistent during the entire operation.  You could do this with:
+
+TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
+dmsetup create log --table "$TABLE"
+mkfs.btrfs -f /dev/mapper/log
+dmsetup message log 0 mark mkfs
+
+mount /dev/mapper/log /mnt/btrfs-test
+<fsstress to dirty the fs>
+btrfs filesystem balance /mnt/btrfs-test
+umount /mnt/btrfs-test
+dmsetup remove log
+
+replay-log --log /dev/sdc --replay /dev/sdb --end-mark mkfs
+btrfsck /dev/sdb
+replay-log --log /dev/sdc --replay /dev/sdb --start-mark mkfs \
+       --fsck "btrfsck /dev/sdb" --check fua
+
+And that will replay the log until it sees a FUA request, run the fsck command
+and if the fsck passes it will replay to the next FUA, until it is completed or
+the fsck command exists abnormally.
index 8897d04948384289b3fca54801be9676c15ce0e5..424835e57f2713df8e349883c02bce0771aa7012 100644 (file)
@@ -47,8 +47,8 @@ consume far too much memory.
 Using this device-mapper switch target we can now build a two-layer
 device hierarchy:
 
-    Upper Tier  Determine which array member the I/O should be sent to.
-    Lower Tier  Load balance amongst paths to a particular member.
+    Upper Tier - Determine which array member the I/O should be sent to.
+    Lower Tier - Load balance amongst paths to a particular member.
 
 The lower tier consists of a single dm multipath device for each member.
 Each of these multipath devices contains the set of paths directly to
index 2f5173500bd953b32e55134012af50ac93e46ad8..4f67578b295483bcc14d48f069c6ded3581e3f94 100644 (file)
@@ -380,9 +380,6 @@ then you'll have no access to blocks mapped beyond the end.  If you
 load a target that is bigger than before, then extra blocks will be
 provisioned as and when needed.
 
-If you wish to reduce the size of your thin device and potentially
-regain some space then send the 'trim' message to the pool.
-
 ii) Status
 
      <nr mapped sectors> <highest mapped sector>
index 9884681535ee36bf03a7d7baaa54abb36360d53d..e15bc1a0fb98ab23563681210cc6ed1865234816 100644 (file)
@@ -11,6 +11,7 @@ Construction Parameters
     <data_block_size> <hash_block_size>
     <num_data_blocks> <hash_start_block>
     <algorithm> <digest> <salt>
+    [<#opt_params> <opt_params>]
 
 <version>
     This is the type of the on-disk hash format.
@@ -62,6 +63,22 @@ Construction Parameters
 <salt>
     The hexadecimal encoding of the salt value.
 
+<#opt_params>
+    Number of optional parameters. If there are no optional parameters,
+    the optional paramaters section can be skipped or #opt_params can be zero.
+    Otherwise #opt_params is the number of following arguments.
+
+    Example of optional parameters section:
+        1 ignore_corruption
+
+ignore_corruption
+    Log corrupted blocks, but allow read operations to proceed normally.
+
+restart_on_corruption
+    Restart the system when a corrupted block is discovered. This option is
+    not compatible with ignore_corruption and requires user space support to
+    avoid restart loops.
+
 Theory of operation
 ===================
 
@@ -125,7 +142,7 @@ block boundary) are the hash blocks which are stored a depth at a time
 
 The full specification of kernel parameters and on-disk metadata format
 is available at the cryptsetup project's wiki page
-  http://code.google.com/p/cryptsetup/wiki/DMVerity
+  https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
 
 Status
 ======
@@ -142,7 +159,7 @@ Set up a device:
 
 A command line tool veritysetup is available to compute or verify
 the hash tree or activate the kernel device. This is available from
-the cryptsetup upstream repository http://code.google.com/p/cryptsetup/
+the cryptsetup upstream repository https://gitlab.com/cryptsetup/cryptsetup/
 (as a libcryptsetup extension).
 
 Create hash on the device:
diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt b/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt
deleted file mode 100644 (file)
index 0ff6560..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-Broadcom BCM11351 device tree bindings
--------------------------------------------
-
-Boards with the bcm281xx SoC family (which includes bcm11130, bcm11140,
-bcm11351, bcm28145, bcm28155 SoCs) shall have the following properties:
-
-Required root node property:
-
-compatible = "brcm,bcm11351";
-DEPRECATED: compatible = "bcm,bcm11351";
diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm21664.txt b/Documentation/devicetree/bindings/arm/bcm/bcm21664.txt
deleted file mode 100644 (file)
index e077425..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Broadcom BCM21664 device tree bindings
---------------------------------------
-
-This document describes the device tree bindings for boards with the BCM21664
-SoC.
-
-Required root node property:
-  - compatible: brcm,bcm21664
-
-Example:
-       / {
-               model = "BCM21664 SoC";
-               compatible = "brcm,bcm21664";
-               [...]
-       }
diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm63138.txt b/Documentation/devicetree/bindings/arm/bcm/bcm63138.txt
deleted file mode 100644 (file)
index bd49987..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Broadcom BCM63138 DSL System-on-a-Chip device tree bindings
------------------------------------------------------------
-
-Boards compatible with the BCM63138 DSL System-on-a-Chip should have the
-following properties:
-
-Required root node property:
-
-compatible: should be "brcm,bcm63138"
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method
deleted file mode 100644 (file)
index 8240c02..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-Broadcom Kona Family CPU Enable Method
---------------------------------------
-This binding defines the enable method used for starting secondary
-CPUs in the following Broadcom SoCs:
-  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155, BCM21664
-
-The enable method is specified by defining the following required
-properties in the "cpus" device tree node:
-  - enable-method = "brcm,bcm11351-cpu-method";
-  - secondary-boot-reg = <...>;
-
-The secondary-boot-reg property is a u32 value that specifies the
-physical address of the register used to request the ROM holding pen
-code release a secondary CPU.  The value written to the register is
-formed by encoding the target CPU id into the low bits of the
-physical start address it should jump to.
-
-Example:
-       cpus {
-               #address-cells = <1>;
-               #size-cells = <0>;
-               enable-method = "brcm,bcm11351-cpu-method";
-               secondary-boot-reg = <0x3500417c>;
-
-               cpu0: cpu@0 {
-                       device_type = "cpu";
-                       compatible = "arm,cortex-a9";
-                       reg = <0>;
-               };
-
-               cpu1: cpu@1 {
-                       device_type = "cpu";
-                       compatible = "arm,cortex-a9";
-                       reg = <1>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351-cpu-method.txt
new file mode 100644 (file)
index 0000000..8240c02
--- /dev/null
@@ -0,0 +1,36 @@
+Broadcom Kona Family CPU Enable Method
+--------------------------------------
+This binding defines the enable method used for starting secondary
+CPUs in the following Broadcom SoCs:
+  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155, BCM21664
+
+The enable method is specified by defining the following required
+properties in the "cpus" device tree node:
+  - enable-method = "brcm,bcm11351-cpu-method";
+  - secondary-boot-reg = <...>;
+
+The secondary-boot-reg property is a u32 value that specifies the
+physical address of the register used to request the ROM holding pen
+code release a secondary CPU.  The value written to the register is
+formed by encoding the target CPU id into the low bits of the
+physical start address it should jump to.
+
+Example:
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               enable-method = "brcm,bcm11351-cpu-method";
+               secondary-boot-reg = <0x3500417c>;
+
+               cpu0: cpu@0 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a9";
+                       reg = <0>;
+               };
+
+               cpu1: cpu@1 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a9";
+                       reg = <1>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm11351.txt
new file mode 100644 (file)
index 0000000..0ff6560
--- /dev/null
@@ -0,0 +1,10 @@
+Broadcom BCM11351 device tree bindings
+-------------------------------------------
+
+Boards with the bcm281xx SoC family (which includes bcm11130, bcm11140,
+bcm11351, bcm28145, bcm28155 SoCs) shall have the following properties:
+
+Required root node property:
+
+compatible = "brcm,bcm11351";
+DEPRECATED: compatible = "bcm,bcm11351";
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm21664.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm21664.txt
new file mode 100644 (file)
index 0000000..e077425
--- /dev/null
@@ -0,0 +1,15 @@
+Broadcom BCM21664 device tree bindings
+--------------------------------------
+
+This document describes the device tree bindings for boards with the BCM21664
+SoC.
+
+Required root node property:
+  - compatible: brcm,bcm21664
+
+Example:
+       / {
+               model = "BCM21664 SoC";
+               compatible = "brcm,bcm21664";
+               [...]
+       }
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt
new file mode 100644 (file)
index 0000000..ac68348
--- /dev/null
@@ -0,0 +1,8 @@
+Broadcom BCM2835 device tree bindings
+-------------------------------------------
+
+Boards with the BCM2835 SoC shall have the following properties:
+
+Required root node property:
+
+compatible = "brcm,bcm2835";
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm4708.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm4708.txt
new file mode 100644 (file)
index 0000000..6b0f49f
--- /dev/null
@@ -0,0 +1,8 @@
+Broadcom BCM4708 device tree bindings
+-------------------------------------------
+
+Boards with the BCM4708 SoC shall have the following properties:
+
+Required root node property:
+
+compatible = "brcm,bcm4708";
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm63138.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm63138.txt
new file mode 100644 (file)
index 0000000..bd49987
--- /dev/null
@@ -0,0 +1,9 @@
+Broadcom BCM63138 DSL System-on-a-Chip device tree bindings
+-----------------------------------------------------------
+
+Boards compatible with the BCM63138 DSL System-on-a-Chip should have the
+following properties:
+
+Required root node property:
+
+compatible: should be "brcm,bcm63138"
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,brcmstb.txt
new file mode 100644 (file)
index 0000000..430608e
--- /dev/null
@@ -0,0 +1,97 @@
+ARM Broadcom STB platforms Device Tree Bindings
+-----------------------------------------------
+Boards with Broadcom Brahma15 ARM-based BCMxxxx (generally BCM7xxx variants)
+SoC shall have the following DT organization:
+
+Required root node properties:
+    - compatible: "brcm,bcm<chip_id>", "brcm,brcmstb"
+
+example:
+/ {
+    #address-cells = <2>;
+    #size-cells = <2>;
+    model = "Broadcom STB (bcm7445)";
+    compatible = "brcm,bcm7445", "brcm,brcmstb";
+
+Further, syscon nodes that map platform-specific registers used for general
+system control is required:
+
+    - compatible: "brcm,bcm<chip_id>-sun-top-ctrl", "syscon"
+    - compatible: "brcm,bcm<chip_id>-hif-cpubiuctrl", "syscon"
+    - compatible: "brcm,bcm<chip_id>-hif-continuation", "syscon"
+
+example:
+    rdb {
+        #address-cells = <1>;
+        #size-cells = <1>;
+        compatible = "simple-bus";
+        ranges = <0 0x00 0xf0000000 0x1000000>;
+
+        sun_top_ctrl: syscon@404000 {
+            compatible = "brcm,bcm7445-sun-top-ctrl", "syscon";
+            reg = <0x404000 0x51c>;
+        };
+
+        hif_cpubiuctrl: syscon@3e2400 {
+            compatible = "brcm,bcm7445-hif-cpubiuctrl", "syscon";
+            reg = <0x3e2400 0x5b4>;
+        };
+
+        hif_continuation: syscon@452000 {
+            compatible = "brcm,bcm7445-hif-continuation", "syscon";
+            reg = <0x452000 0x100>;
+        };
+    };
+
+Lastly, nodes that allow for support of SMP initialization and reboot are
+required:
+
+smpboot
+-------
+Required properties:
+
+    - compatible
+        The string "brcm,brcmstb-smpboot".
+
+    - syscon-cpu
+        A phandle / integer array property which lets the BSP know the location
+        of certain CPU power-on registers.
+
+        The layout of the property is as follows:
+            o a phandle to the "hif_cpubiuctrl" syscon node
+            o offset to the base CPU power zone register
+            o offset to the base CPU reset register
+
+    - syscon-cont
+        A phandle pointing to the syscon node which describes the CPU boot
+        continuation registers.
+            o a phandle to the "hif_continuation" syscon node
+
+example:
+    smpboot {
+        compatible = "brcm,brcmstb-smpboot";
+        syscon-cpu = <&hif_cpubiuctrl 0x88 0x178>;
+        syscon-cont = <&hif_continuation>;
+    };
+
+reboot
+-------
+Required properties
+
+    - compatible
+        The string property "brcm,brcmstb-reboot" for 40nm/28nm chips with
+        the new SYS_CTRL interface, or "brcm,bcm7038-reboot" for 65nm
+        chips with the old SUN_TOP_CTRL interface.
+
+    - syscon
+        A phandle / integer array that points to the syscon node which describes
+        the general system reset registers.
+            o a phandle to "sun_top_ctrl"
+            o offset to the "reset source enable" register
+            o offset to the "software master reset" register
+
+example:
+    reboot {
+        compatible = "brcm,brcmstb-reboot";
+        syscon = <&sun_top_ctrl 0x304 0x308>;
+    };
diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,cygnus.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,cygnus.txt
new file mode 100644 (file)
index 0000000..4c77169
--- /dev/null
@@ -0,0 +1,31 @@
+Broadcom Cygnus device tree bindings
+------------------------------------
+
+
+Boards with Cygnus SoCs shall have the following properties:
+
+Required root node property:
+
+BCM11300
+compatible = "brcm,bcm11300", "brcm,cygnus";
+
+BCM11320
+compatible = "brcm,bcm11320", "brcm,cygnus";
+
+BCM11350
+compatible = "brcm,bcm11350", "brcm,cygnus";
+
+BCM11360
+compatible = "brcm,bcm11360", "brcm,cygnus";
+
+BCM58300
+compatible = "brcm,bcm58300", "brcm,cygnus";
+
+BCM58302
+compatible = "brcm,bcm58302", "brcm,cygnus";
+
+BCM58303
+compatible = "brcm,bcm58303", "brcm,cygnus";
+
+BCM58305
+compatible = "brcm,bcm58305", "brcm,cygnus";
diff --git a/Documentation/devicetree/bindings/arm/bcm/cygnus.txt b/Documentation/devicetree/bindings/arm/bcm/cygnus.txt
deleted file mode 100644 (file)
index 4c77169..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-Broadcom Cygnus device tree bindings
-------------------------------------
-
-
-Boards with Cygnus SoCs shall have the following properties:
-
-Required root node property:
-
-BCM11300
-compatible = "brcm,bcm11300", "brcm,cygnus";
-
-BCM11320
-compatible = "brcm,bcm11320", "brcm,cygnus";
-
-BCM11350
-compatible = "brcm,bcm11350", "brcm,cygnus";
-
-BCM11360
-compatible = "brcm,bcm11360", "brcm,cygnus";
-
-BCM58300
-compatible = "brcm,bcm58300", "brcm,cygnus";
-
-BCM58302
-compatible = "brcm,bcm58302", "brcm,cygnus";
-
-BCM58303
-compatible = "brcm,bcm58303", "brcm,cygnus";
-
-BCM58305
-compatible = "brcm,bcm58305", "brcm,cygnus";
diff --git a/Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt b/Documentation/devicetree/bindings/arm/bcm/kona-resetmgr.txt
deleted file mode 100644 (file)
index 93f31ca..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Broadcom Kona Family Reset Manager
-----------------------------------
-
-The reset manager is used on the Broadcom BCM21664 SoC.
-
-Required properties:
-  - compatible: brcm,bcm21664-resetmgr
-  - reg: memory address & range
-
-Example:
-       brcm,resetmgr@35001f00 {
-               compatible = "brcm,bcm21664-resetmgr";
-               reg = <0x35001f00 0x24>;
-       };
diff --git a/Documentation/devicetree/bindings/arm/bcm/kona-timer.txt b/Documentation/devicetree/bindings/arm/bcm/kona-timer.txt
deleted file mode 100644 (file)
index 39adf54..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-Broadcom Kona Family timer
------------------------------------------------------
-This timer is used in the following Broadcom SoCs:
- BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
-
-Required properties:
-- compatible : "brcm,kona-timer"
-- DEPRECATED: compatible : "bcm,kona-timer"
-- reg : Register range for the timer
-- interrupts : interrupt for the timer
-- clocks: phandle + clock specifier pair of the external clock
-- clock-frequency: frequency that the clock operates
-
-Only one of clocks or clock-frequency should be specified.
-
-Refer to clocks/clock-bindings.txt for generic clock consumer properties.
-
-Example:
-       timer@35006000 {
-               compatible = "brcm,kona-timer";
-               reg = <0x35006000 0x1000>;
-               interrupts = <0x0 7 0x4>;
-               clocks = <&hub_timer_clk>;
-       };
-
diff --git a/Documentation/devicetree/bindings/arm/bcm/kona-wdt.txt b/Documentation/devicetree/bindings/arm/bcm/kona-wdt.txt
deleted file mode 100644 (file)
index 2b86a00..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Broadcom Kona Family Watchdog Timer
------------------------------------
-
-This watchdog timer is used in the following Broadcom SoCs:
-  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
-
-Required properties:
-  - compatible = "brcm,bcm11351-wdt", "brcm,kona-wdt";
-  - reg: memory address & range
-
-Example:
-       watchdog@35002f40 {
-               compatible = "brcm,bcm11351-wdt", "brcm,kona-wdt";
-               reg = <0x35002f40 0x6c>;
-       };
diff --git a/Documentation/devicetree/bindings/arm/bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm2835.txt
deleted file mode 100644 (file)
index ac68348..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Broadcom BCM2835 device tree bindings
--------------------------------------------
-
-Boards with the BCM2835 SoC shall have the following properties:
-
-Required root node property:
-
-compatible = "brcm,bcm2835";
diff --git a/Documentation/devicetree/bindings/arm/bcm4708.txt b/Documentation/devicetree/bindings/arm/bcm4708.txt
deleted file mode 100644 (file)
index 6b0f49f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-Broadcom BCM4708 device tree bindings
--------------------------------------------
-
-Boards with the BCM4708 SoC shall have the following properties:
-
-Required root node property:
-
-compatible = "brcm,bcm4708";
diff --git a/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt b/Documentation/devicetree/bindings/arm/brcm-brcmstb.txt
deleted file mode 100644 (file)
index 430608e..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-ARM Broadcom STB platforms Device Tree Bindings
------------------------------------------------
-Boards with Broadcom Brahma15 ARM-based BCMxxxx (generally BCM7xxx variants)
-SoC shall have the following DT organization:
-
-Required root node properties:
-    - compatible: "brcm,bcm<chip_id>", "brcm,brcmstb"
-
-example:
-/ {
-    #address-cells = <2>;
-    #size-cells = <2>;
-    model = "Broadcom STB (bcm7445)";
-    compatible = "brcm,bcm7445", "brcm,brcmstb";
-
-Further, syscon nodes that map platform-specific registers used for general
-system control is required:
-
-    - compatible: "brcm,bcm<chip_id>-sun-top-ctrl", "syscon"
-    - compatible: "brcm,bcm<chip_id>-hif-cpubiuctrl", "syscon"
-    - compatible: "brcm,bcm<chip_id>-hif-continuation", "syscon"
-
-example:
-    rdb {
-        #address-cells = <1>;
-        #size-cells = <1>;
-        compatible = "simple-bus";
-        ranges = <0 0x00 0xf0000000 0x1000000>;
-
-        sun_top_ctrl: syscon@404000 {
-            compatible = "brcm,bcm7445-sun-top-ctrl", "syscon";
-            reg = <0x404000 0x51c>;
-        };
-
-        hif_cpubiuctrl: syscon@3e2400 {
-            compatible = "brcm,bcm7445-hif-cpubiuctrl", "syscon";
-            reg = <0x3e2400 0x5b4>;
-        };
-
-        hif_continuation: syscon@452000 {
-            compatible = "brcm,bcm7445-hif-continuation", "syscon";
-            reg = <0x452000 0x100>;
-        };
-    };
-
-Lastly, nodes that allow for support of SMP initialization and reboot are
-required:
-
-smpboot
--------
-Required properties:
-
-    - compatible
-        The string "brcm,brcmstb-smpboot".
-
-    - syscon-cpu
-        A phandle / integer array property which lets the BSP know the location
-        of certain CPU power-on registers.
-
-        The layout of the property is as follows:
-            o a phandle to the "hif_cpubiuctrl" syscon node
-            o offset to the base CPU power zone register
-            o offset to the base CPU reset register
-
-    - syscon-cont
-        A phandle pointing to the syscon node which describes the CPU boot
-        continuation registers.
-            o a phandle to the "hif_continuation" syscon node
-
-example:
-    smpboot {
-        compatible = "brcm,brcmstb-smpboot";
-        syscon-cpu = <&hif_cpubiuctrl 0x88 0x178>;
-        syscon-cont = <&hif_continuation>;
-    };
-
-reboot
--------
-Required properties
-
-    - compatible
-        The string property "brcm,brcmstb-reboot" for 40nm/28nm chips with
-        the new SYS_CTRL interface, or "brcm,bcm7038-reboot" for 65nm
-        chips with the old SUN_TOP_CTRL interface.
-
-    - syscon
-        A phandle / integer array that points to the syscon node which describes
-        the general system reset registers.
-            o a phandle to "sun_top_ctrl"
-            o offset to the "reset source enable" register
-            o offset to the "software master reset" register
-
-example:
-    reboot {
-        compatible = "brcm,brcmstb-reboot";
-        syscon = <&sun_top_ctrl 0x304 0x308>;
-    };
diff --git a/Documentation/devicetree/bindings/bus/bcma.txt b/Documentation/devicetree/bindings/bus/bcma.txt
deleted file mode 100644 (file)
index edd44d8..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-Driver for ARM AXI Bus with Broadcom Plugins (bcma)
-
-Required properties:
-
-- compatible : brcm,bus-axi
-
-- reg : iomem address range of chipcommon core
-
-The cores on the AXI bus are automatically detected by bcma with the
-memory ranges they are using and they get registered afterwards.
-Automatic detection of the IRQ number is not working on
-BCM47xx/BCM53xx ARM SoCs. To assign IRQ numbers to the cores, provide
-them manually through device tree. Use an interrupt-map to specify the
-IRQ used by the devices on the bus. The first address is just an index,
-because we do not have any special register.
-
-The top-level axi bus may contain children representing attached cores
-(devices). This is needed since some hardware details can't be auto
-detected (e.g. IRQ numbers). Also some of the cores may be responsible
-for extra things, e.g. ChipCommon providing access to the GPIO chip.
-
-Example:
-
-       axi@18000000 {
-               compatible = "brcm,bus-axi";
-               reg = <0x18000000 0x1000>;
-               ranges = <0x00000000 0x18000000 0x00100000>;
-               #address-cells = <1>;
-               #size-cells = <1>;
-               #interrupt-cells = <1>;
-               interrupt-map-mask = <0x000fffff 0xffff>;
-               interrupt-map =
-                       /* Ethernet Controller 0 */
-                       <0x00024000 0 &gic GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
-
-                       /* Ethernet Controller 1 */
-                       <0x00025000 0 &gic GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
-
-                       /* PCIe Controller 0 */
-                       <0x00012000 0 &gic GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
-                       <0x00012000 1 &gic GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>,
-                       <0x00012000 2 &gic GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
-                       <0x00012000 3 &gic GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
-                       <0x00012000 4 &gic GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
-                       <0x00012000 5 &gic GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
-
-               chipcommon {
-                       reg = <0x00000000 0x1000>;
-
-                       gpio-controller;
-                       #gpio-cells = <2>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/bus/brcm,bus-axi.txt b/Documentation/devicetree/bindings/bus/brcm,bus-axi.txt
new file mode 100644 (file)
index 0000000..edd44d8
--- /dev/null
@@ -0,0 +1,53 @@
+Driver for ARM AXI Bus with Broadcom Plugins (bcma)
+
+Required properties:
+
+- compatible : brcm,bus-axi
+
+- reg : iomem address range of chipcommon core
+
+The cores on the AXI bus are automatically detected by bcma with the
+memory ranges they are using and they get registered afterwards.
+Automatic detection of the IRQ number is not working on
+BCM47xx/BCM53xx ARM SoCs. To assign IRQ numbers to the cores, provide
+them manually through device tree. Use an interrupt-map to specify the
+IRQ used by the devices on the bus. The first address is just an index,
+because we do not have any special register.
+
+The top-level axi bus may contain children representing attached cores
+(devices). This is needed since some hardware details can't be auto
+detected (e.g. IRQ numbers). Also some of the cores may be responsible
+for extra things, e.g. ChipCommon providing access to the GPIO chip.
+
+Example:
+
+       axi@18000000 {
+               compatible = "brcm,bus-axi";
+               reg = <0x18000000 0x1000>;
+               ranges = <0x00000000 0x18000000 0x00100000>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0x000fffff 0xffff>;
+               interrupt-map =
+                       /* Ethernet Controller 0 */
+                       <0x00024000 0 &gic GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
+
+                       /* Ethernet Controller 1 */
+                       <0x00025000 0 &gic GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+
+                       /* PCIe Controller 0 */
+                       <0x00012000 0 &gic GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
+                       <0x00012000 1 &gic GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>,
+                       <0x00012000 2 &gic GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
+                       <0x00012000 3 &gic GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
+                       <0x00012000 4 &gic GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
+                       <0x00012000 5 &gic GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+
+               chipcommon {
+                       reg = <0x00000000 0x1000>;
+
+                       gpio-controller;
+                       #gpio-cells = <2>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt b/Documentation/devicetree/bindings/clock/bcm-kona-clock.txt
deleted file mode 100644 (file)
index 5286e26..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-Broadcom Kona Family Clocks
-
-This binding is associated with Broadcom SoCs having "Kona" style
-clock control units (CCUs).  A CCU is a clock provider that manages
-a set of clock signals.  Each CCU is represented by a node in the
-device tree.
-
-This binding uses the common clock binding:
-    Documentation/devicetree/bindings/clock/clock-bindings.txt
-
-Required properties:
-- compatible
-       Shall have a value of the form "brcm,<model>-<which>-ccu",
-       where <model> is a Broadcom SoC model number and <which> is
-       the name of a defined CCU.  For example:
-           "brcm,bcm11351-root-ccu"
-       The compatible strings used for each supported SoC family
-       are defined below.
-- reg
-       Shall define the base and range of the address space
-       containing clock control registers
-- #clock-cells
-       Shall have value <1>.  The permitted clock-specifier values
-       are defined below.
-- clock-output-names
-       Shall be an ordered list of strings defining the names of
-       the clocks provided by the CCU.
-
-Device tree example:
-
-       slave_ccu: slave_ccu {
-               compatible = "brcm,bcm11351-slave-ccu";
-               reg = <0x3e011000 0x0f00>;
-               #clock-cells = <1>;
-               clock-output-names = "uartb",
-                                    "uartb2",
-                                    "uartb3",
-                                    "uartb4";
-       };
-
-       ref_crystal_clk: ref_crystal {
-               #clock-cells = <0>;
-               compatible = "fixed-clock";
-               clock-frequency = <26000000>;
-       };
-
-       uart@3e002000 {
-               compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
-               status = "disabled";
-               reg = <0x3e002000 0x1000>;
-               clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>;
-               interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
-               reg-shift = <2>;
-               reg-io-width = <4>;
-       };
-
-BCM281XX family
----------------
-CCU compatible string values for SoCs in the BCM281XX family are:
-    "brcm,bcm11351-root-ccu"
-    "brcm,bcm11351-aon-ccu"
-    "brcm,bcm11351-hub-ccu"
-    "brcm,bcm11351-master-ccu"
-    "brcm,bcm11351-slave-ccu"
-
-The following table defines the set of CCUs and clock specifiers for
-BCM281XX family clocks.  When a clock consumer references a clocks,
-its symbolic specifier (rather than its numeric index value) should
-be used.  These specifiers are defined in:
-    "include/dt-bindings/clock/bcm281xx.h"
-
-    CCU     Clock           Type    Index   Specifier
-    ---     -----           ----    -----   ---------
-    root    frac_1m         peri      0     BCM281XX_ROOT_CCU_FRAC_1M
-
-    aon     hub_timer       peri      0     BCM281XX_AON_CCU_HUB_TIMER
-    aon     pmu_bsc         peri      1     BCM281XX_AON_CCU_PMU_BSC
-    aon     pmu_bsc_var     peri      2     BCM281XX_AON_CCU_PMU_BSC_VAR
-
-    hub     tmon_1m         peri      0     BCM281XX_HUB_CCU_TMON_1M
-
-    master  sdio1           peri      0     BCM281XX_MASTER_CCU_SDIO1
-    master  sdio2           peri      1     BCM281XX_MASTER_CCU_SDIO2
-    master  sdio3           peri      2     BCM281XX_MASTER_CCU_SDIO3
-    master  sdio4           peri      3     BCM281XX_MASTER_CCU_SDIO4
-    master  dmac            peri      4     BCM281XX_MASTER_CCU_DMAC
-    master  usb_ic          peri      5     BCM281XX_MASTER_CCU_USB_IC
-    master  hsic2_48m       peri      6     BCM281XX_MASTER_CCU_HSIC_48M
-    master  hsic2_12m       peri      7     BCM281XX_MASTER_CCU_HSIC_12M
-
-    slave   uartb           peri      0     BCM281XX_SLAVE_CCU_UARTB
-    slave   uartb2          peri      1     BCM281XX_SLAVE_CCU_UARTB2
-    slave   uartb3          peri      2     BCM281XX_SLAVE_CCU_UARTB3
-    slave   uartb4          peri      3     BCM281XX_SLAVE_CCU_UARTB4
-    slave   ssp0            peri      4     BCM281XX_SLAVE_CCU_SSP0
-    slave   ssp2            peri      5     BCM281XX_SLAVE_CCU_SSP2
-    slave   bsc1            peri      6     BCM281XX_SLAVE_CCU_BSC1
-    slave   bsc2            peri      7     BCM281XX_SLAVE_CCU_BSC2
-    slave   bsc3            peri      8     BCM281XX_SLAVE_CCU_BSC3
-    slave   pwm             peri      9     BCM281XX_SLAVE_CCU_PWM
-
-
-BCM21664 family
----------------
-CCU compatible string values for SoCs in the BCM21664 family are:
-    "brcm,bcm21664-root-ccu"
-    "brcm,bcm21664-aon-ccu"
-    "brcm,bcm21664-master-ccu"
-    "brcm,bcm21664-slave-ccu"
-
-The following table defines the set of CCUs and clock specifiers for
-BCM21664 family clocks.  When a clock consumer references a clocks,
-its symbolic specifier (rather than its numeric index value) should
-be used.  These specifiers are defined in:
-    "include/dt-bindings/clock/bcm21664.h"
-
-    CCU     Clock           Type    Index   Specifier
-    ---     -----           ----    -----   ---------
-    root    frac_1m         peri      0     BCM21664_ROOT_CCU_FRAC_1M
-
-    aon     hub_timer       peri      0     BCM21664_AON_CCU_HUB_TIMER
-
-    master  sdio1           peri      0     BCM21664_MASTER_CCU_SDIO1
-    master  sdio2           peri      1     BCM21664_MASTER_CCU_SDIO2
-    master  sdio3           peri      2     BCM21664_MASTER_CCU_SDIO3
-    master  sdio4           peri      3     BCM21664_MASTER_CCU_SDIO4
-    master  sdio1_sleep     peri      4     BCM21664_MASTER_CCU_SDIO1_SLEEP
-    master  sdio2_sleep     peri      5     BCM21664_MASTER_CCU_SDIO2_SLEEP
-    master  sdio3_sleep     peri      6     BCM21664_MASTER_CCU_SDIO3_SLEEP
-    master  sdio4_sleep     peri      7     BCM21664_MASTER_CCU_SDIO4_SLEEP
-
-    slave   uartb           peri      0     BCM21664_SLAVE_CCU_UARTB
-    slave   uartb2          peri      1     BCM21664_SLAVE_CCU_UARTB2
-    slave   uartb3          peri      2     BCM21664_SLAVE_CCU_UARTB3
-    slave   uartb4          peri      3     BCM21664_SLAVE_CCU_UARTB4
-    slave   bsc1            peri      4     BCM21664_SLAVE_CCU_BSC1
-    slave   bsc2            peri      5     BCM21664_SLAVE_CCU_BSC2
-    slave   bsc3            peri      6     BCM21664_SLAVE_CCU_BSC3
-    slave   bsc4            peri      7     BCM21664_SLAVE_CCU_BSC4
diff --git a/Documentation/devicetree/bindings/clock/brcm,kona-ccu.txt b/Documentation/devicetree/bindings/clock/brcm,kona-ccu.txt
new file mode 100644 (file)
index 0000000..5286e26
--- /dev/null
@@ -0,0 +1,139 @@
+Broadcom Kona Family Clocks
+
+This binding is associated with Broadcom SoCs having "Kona" style
+clock control units (CCUs).  A CCU is a clock provider that manages
+a set of clock signals.  Each CCU is represented by a node in the
+device tree.
+
+This binding uses the common clock binding:
+    Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible
+       Shall have a value of the form "brcm,<model>-<which>-ccu",
+       where <model> is a Broadcom SoC model number and <which> is
+       the name of a defined CCU.  For example:
+           "brcm,bcm11351-root-ccu"
+       The compatible strings used for each supported SoC family
+       are defined below.
+- reg
+       Shall define the base and range of the address space
+       containing clock control registers
+- #clock-cells
+       Shall have value <1>.  The permitted clock-specifier values
+       are defined below.
+- clock-output-names
+       Shall be an ordered list of strings defining the names of
+       the clocks provided by the CCU.
+
+Device tree example:
+
+       slave_ccu: slave_ccu {
+               compatible = "brcm,bcm11351-slave-ccu";
+               reg = <0x3e011000 0x0f00>;
+               #clock-cells = <1>;
+               clock-output-names = "uartb",
+                                    "uartb2",
+                                    "uartb3",
+                                    "uartb4";
+       };
+
+       ref_crystal_clk: ref_crystal {
+               #clock-cells = <0>;
+               compatible = "fixed-clock";
+               clock-frequency = <26000000>;
+       };
+
+       uart@3e002000 {
+               compatible = "brcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart";
+               status = "disabled";
+               reg = <0x3e002000 0x1000>;
+               clocks = <&slave_ccu BCM281XX_SLAVE_CCU_UARTB3>;
+               interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+               reg-shift = <2>;
+               reg-io-width = <4>;
+       };
+
+BCM281XX family
+---------------
+CCU compatible string values for SoCs in the BCM281XX family are:
+    "brcm,bcm11351-root-ccu"
+    "brcm,bcm11351-aon-ccu"
+    "brcm,bcm11351-hub-ccu"
+    "brcm,bcm11351-master-ccu"
+    "brcm,bcm11351-slave-ccu"
+
+The following table defines the set of CCUs and clock specifiers for
+BCM281XX family clocks.  When a clock consumer references a clocks,
+its symbolic specifier (rather than its numeric index value) should
+be used.  These specifiers are defined in:
+    "include/dt-bindings/clock/bcm281xx.h"
+
+    CCU     Clock           Type    Index   Specifier
+    ---     -----           ----    -----   ---------
+    root    frac_1m         peri      0     BCM281XX_ROOT_CCU_FRAC_1M
+
+    aon     hub_timer       peri      0     BCM281XX_AON_CCU_HUB_TIMER
+    aon     pmu_bsc         peri      1     BCM281XX_AON_CCU_PMU_BSC
+    aon     pmu_bsc_var     peri      2     BCM281XX_AON_CCU_PMU_BSC_VAR
+
+    hub     tmon_1m         peri      0     BCM281XX_HUB_CCU_TMON_1M
+
+    master  sdio1           peri      0     BCM281XX_MASTER_CCU_SDIO1
+    master  sdio2           peri      1     BCM281XX_MASTER_CCU_SDIO2
+    master  sdio3           peri      2     BCM281XX_MASTER_CCU_SDIO3
+    master  sdio4           peri      3     BCM281XX_MASTER_CCU_SDIO4
+    master  dmac            peri      4     BCM281XX_MASTER_CCU_DMAC
+    master  usb_ic          peri      5     BCM281XX_MASTER_CCU_USB_IC
+    master  hsic2_48m       peri      6     BCM281XX_MASTER_CCU_HSIC_48M
+    master  hsic2_12m       peri      7     BCM281XX_MASTER_CCU_HSIC_12M
+
+    slave   uartb           peri      0     BCM281XX_SLAVE_CCU_UARTB
+    slave   uartb2          peri      1     BCM281XX_SLAVE_CCU_UARTB2
+    slave   uartb3          peri      2     BCM281XX_SLAVE_CCU_UARTB3
+    slave   uartb4          peri      3     BCM281XX_SLAVE_CCU_UARTB4
+    slave   ssp0            peri      4     BCM281XX_SLAVE_CCU_SSP0
+    slave   ssp2            peri      5     BCM281XX_SLAVE_CCU_SSP2
+    slave   bsc1            peri      6     BCM281XX_SLAVE_CCU_BSC1
+    slave   bsc2            peri      7     BCM281XX_SLAVE_CCU_BSC2
+    slave   bsc3            peri      8     BCM281XX_SLAVE_CCU_BSC3
+    slave   pwm             peri      9     BCM281XX_SLAVE_CCU_PWM
+
+
+BCM21664 family
+---------------
+CCU compatible string values for SoCs in the BCM21664 family are:
+    "brcm,bcm21664-root-ccu"
+    "brcm,bcm21664-aon-ccu"
+    "brcm,bcm21664-master-ccu"
+    "brcm,bcm21664-slave-ccu"
+
+The following table defines the set of CCUs and clock specifiers for
+BCM21664 family clocks.  When a clock consumer references a clocks,
+its symbolic specifier (rather than its numeric index value) should
+be used.  These specifiers are defined in:
+    "include/dt-bindings/clock/bcm21664.h"
+
+    CCU     Clock           Type    Index   Specifier
+    ---     -----           ----    -----   ---------
+    root    frac_1m         peri      0     BCM21664_ROOT_CCU_FRAC_1M
+
+    aon     hub_timer       peri      0     BCM21664_AON_CCU_HUB_TIMER
+
+    master  sdio1           peri      0     BCM21664_MASTER_CCU_SDIO1
+    master  sdio2           peri      1     BCM21664_MASTER_CCU_SDIO2
+    master  sdio3           peri      2     BCM21664_MASTER_CCU_SDIO3
+    master  sdio4           peri      3     BCM21664_MASTER_CCU_SDIO4
+    master  sdio1_sleep     peri      4     BCM21664_MASTER_CCU_SDIO1_SLEEP
+    master  sdio2_sleep     peri      5     BCM21664_MASTER_CCU_SDIO2_SLEEP
+    master  sdio3_sleep     peri      6     BCM21664_MASTER_CCU_SDIO3_SLEEP
+    master  sdio4_sleep     peri      7     BCM21664_MASTER_CCU_SDIO4_SLEEP
+
+    slave   uartb           peri      0     BCM21664_SLAVE_CCU_UARTB
+    slave   uartb2          peri      1     BCM21664_SLAVE_CCU_UARTB2
+    slave   uartb3          peri      2     BCM21664_SLAVE_CCU_UARTB3
+    slave   uartb4          peri      3     BCM21664_SLAVE_CCU_UARTB4
+    slave   bsc1            peri      4     BCM21664_SLAVE_CCU_BSC1
+    slave   bsc2            peri      5     BCM21664_SLAVE_CCU_BSC2
+    slave   bsc3            peri      6     BCM21664_SLAVE_CCU_BSC3
+    slave   bsc4            peri      7     BCM21664_SLAVE_CCU_BSC4
diff --git a/Documentation/devicetree/bindings/dma/bcm2835-dma.txt b/Documentation/devicetree/bindings/dma/bcm2835-dma.txt
deleted file mode 100644 (file)
index 1396078..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-* BCM2835 DMA controller
-
-The BCM2835 DMA controller has 16 channels in total.
-Only the lower 13 channels have an associated IRQ.
-Some arbitrary channels are used by the firmware
-(1,3,6,7 in the current firmware version).
-The channels 0,2 and 3 have special functionality
-and should not be used by the driver.
-
-Required properties:
-- compatible: Should be "brcm,bcm2835-dma".
-- reg: Should contain DMA registers location and length.
-- interrupts: Should contain the DMA interrupts associated
-               to the DMA channels in ascending order.
-- #dma-cells: Must be <1>, the cell in the dmas property of the
-               client device represents the DREQ number.
-- brcm,dma-channel-mask: Bit mask representing the channels
-                        not used by the firmware in ascending order,
-                        i.e. first channel corresponds to LSB.
-
-Example:
-
-dma: dma@7e007000 {
-       compatible = "brcm,bcm2835-dma";
-       reg = <0x7e007000 0xf00>;
-       interrupts = <1 16>,
-                    <1 17>,
-                    <1 18>,
-                    <1 19>,
-                    <1 20>,
-                    <1 21>,
-                    <1 22>,
-                    <1 23>,
-                    <1 24>,
-                    <1 25>,
-                    <1 26>,
-                    <1 27>,
-                    <1 28>;
-
-       #dma-cells = <1>;
-       brcm,dma-channel-mask = <0x7f35>;
-};
-
-DMA clients connected to the BCM2835 DMA controller must use the format
-described in the dma.txt file, using a two-cell specifier for each channel.
-
-Example:
-
-bcm2835_i2s: i2s@7e203000 {
-       compatible = "brcm,bcm2835-i2s";
-       reg = < 0x7e203000 0x20>,
-             < 0x7e101098 0x02>;
-
-       dmas = <&dma 2>,
-              <&dma 3>;
-       dma-names = "tx", "rx";
-};
diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt
new file mode 100644 (file)
index 0000000..1396078
--- /dev/null
@@ -0,0 +1,57 @@
+* BCM2835 DMA controller
+
+The BCM2835 DMA controller has 16 channels in total.
+Only the lower 13 channels have an associated IRQ.
+Some arbitrary channels are used by the firmware
+(1,3,6,7 in the current firmware version).
+The channels 0,2 and 3 have special functionality
+and should not be used by the driver.
+
+Required properties:
+- compatible: Should be "brcm,bcm2835-dma".
+- reg: Should contain DMA registers location and length.
+- interrupts: Should contain the DMA interrupts associated
+               to the DMA channels in ascending order.
+- #dma-cells: Must be <1>, the cell in the dmas property of the
+               client device represents the DREQ number.
+- brcm,dma-channel-mask: Bit mask representing the channels
+                        not used by the firmware in ascending order,
+                        i.e. first channel corresponds to LSB.
+
+Example:
+
+dma: dma@7e007000 {
+       compatible = "brcm,bcm2835-dma";
+       reg = <0x7e007000 0xf00>;
+       interrupts = <1 16>,
+                    <1 17>,
+                    <1 18>,
+                    <1 19>,
+                    <1 20>,
+                    <1 21>,
+                    <1 22>,
+                    <1 23>,
+                    <1 24>,
+                    <1 25>,
+                    <1 26>,
+                    <1 27>,
+                    <1 28>;
+
+       #dma-cells = <1>;
+       brcm,dma-channel-mask = <0x7f35>;
+};
+
+DMA clients connected to the BCM2835 DMA controller must use the format
+described in the dma.txt file, using a two-cell specifier for each channel.
+
+Example:
+
+bcm2835_i2s: i2s@7e203000 {
+       compatible = "brcm,bcm2835-i2s";
+       reg = < 0x7e203000 0x20>,
+             < 0x7e101098 0x02>;
+
+       dmas = <&dma 2>,
+              <&dma 3>;
+       dma-names = "tx", "rx";
+};
diff --git a/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt
new file mode 100644 (file)
index 0000000..4a63bc9
--- /dev/null
@@ -0,0 +1,52 @@
+Broadcom Kona Family GPIO
+=========================
+
+This GPIO driver is used in the following Broadcom SoCs:
+  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
+
+The Broadcom GPIO Controller IP can be configured prior to synthesis to
+support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The
+GPIO controller only supports edge, not level, triggering of interrupts.
+
+Required properties
+-------------------
+
+- compatible: "brcm,bcm11351-gpio", "brcm,kona-gpio"
+- reg: Physical base address and length of the controller's registers.
+- interrupts: The interrupt outputs from the controller. There is one GPIO
+  interrupt per GPIO bank. The number of interrupts listed depends on the
+  number of GPIO banks on the SoC. The interrupts must be ordered by bank,
+  starting with bank 0. There is always a 1:1 mapping between banks and
+  IRQs.
+- #gpio-cells: Should be <2>. The first cell is the pin number, the second
+  cell is used to specify optional parameters:
+  - bit 0 specifies polarity (0 for normal, 1 for inverted)
+  See also "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
+- #interrupt-cells: Should be <2>. The first cell is the GPIO number. The
+  second cell is used to specify flags. The following subset of flags is
+  supported:
+  - trigger type (bits[1:0]):
+      1 = low-to-high edge triggered.
+      2 = high-to-low edge triggered.
+      3 = low-to-high or high-to-low edge triggered
+      Valid values are 1, 2, 3
+  See also .../devicetree/bindings/interrupt-controller/interrupts.txt.
+- gpio-controller: Marks the device node as a GPIO controller.
+- interrupt-controller: Marks the device node as an interrupt controller.
+
+Example:
+       gpio: gpio@35003000 {
+               compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio";
+               reg = <0x35003000 0x800>;
+               interrupts =
+                      <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH
+                       GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH
+                       GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH
+                       GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH
+                       GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH
+                       GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
+               #gpio-cells = <2>;
+               #interrupt-cells = <2>;
+               gpio-controller;
+               interrupt-controller;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt
new file mode 100644 (file)
index 0000000..12f5014
--- /dev/null
@@ -0,0 +1,43 @@
+Altera GPIO controller bindings
+
+Required properties:
+- compatible:
+  - "altr,pio-1.0"
+- reg: Physical base address and length of the controller's registers.
+- #gpio-cells : Should be 2
+  - The first cell is the gpio offset number.
+  - The second cell is reserved and is currently unused.
+- gpio-controller : Marks the device node as a GPIO controller.
+- interrupt-controller: Mark the device node as an interrupt controller
+- #interrupt-cells : Should be 1. The interrupt type is fixed in the hardware.
+  - The first cell is the GPIO offset number within the GPIO controller.
+- interrupts: Specify the interrupt.
+- altr,interrupt-trigger: Specifies the interrupt trigger type the GPIO
+  hardware is synthesized. This field is required if the Altera GPIO controller
+  used has IRQ enabled as the interrupt type is not software controlled,
+  but hardware synthesized. Required if GPIO is used as an interrupt
+  controller. The value is defined in <dt-bindings/interrupt-controller/irq.h>
+  Only the following flags are supported:
+    IRQ_TYPE_EDGE_RISING
+    IRQ_TYPE_EDGE_FALLING
+    IRQ_TYPE_EDGE_BOTH
+    IRQ_TYPE_LEVEL_HIGH
+
+Optional properties:
+- altr,ngpio: Width of the GPIO bank. This defines how many pins the
+  GPIO device has. Ranges between 1-32. Optional and defaults to 32 if not
+  specified.
+
+Example:
+
+gpio_altr: gpio@0xff200000 {
+       compatible = "altr,pio-1.0";
+       reg = <0xff200000 0x10>;
+       interrupts = <0 45 4>;
+       altr,ngpio = <32>;
+       altr,interrupt-trigger = <IRQ_TYPE_EDGE_RISING>;
+       #gpio-cells = <2>;
+       gpio-controller;
+       #interrupt-cells = <1>;
+       interrupt-controller;
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt b/Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt
deleted file mode 100644 (file)
index 4a63bc9..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-Broadcom Kona Family GPIO
-=========================
-
-This GPIO driver is used in the following Broadcom SoCs:
-  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
-
-The Broadcom GPIO Controller IP can be configured prior to synthesis to
-support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The
-GPIO controller only supports edge, not level, triggering of interrupts.
-
-Required properties
--------------------
-
-- compatible: "brcm,bcm11351-gpio", "brcm,kona-gpio"
-- reg: Physical base address and length of the controller's registers.
-- interrupts: The interrupt outputs from the controller. There is one GPIO
-  interrupt per GPIO bank. The number of interrupts listed depends on the
-  number of GPIO banks on the SoC. The interrupts must be ordered by bank,
-  starting with bank 0. There is always a 1:1 mapping between banks and
-  IRQs.
-- #gpio-cells: Should be <2>. The first cell is the pin number, the second
-  cell is used to specify optional parameters:
-  - bit 0 specifies polarity (0 for normal, 1 for inverted)
-  See also "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
-- #interrupt-cells: Should be <2>. The first cell is the GPIO number. The
-  second cell is used to specify flags. The following subset of flags is
-  supported:
-  - trigger type (bits[1:0]):
-      1 = low-to-high edge triggered.
-      2 = high-to-low edge triggered.
-      3 = low-to-high or high-to-low edge triggered
-      Valid values are 1, 2, 3
-  See also .../devicetree/bindings/interrupt-controller/interrupts.txt.
-- gpio-controller: Marks the device node as a GPIO controller.
-- interrupt-controller: Marks the device node as an interrupt controller.
-
-Example:
-       gpio: gpio@35003000 {
-               compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio";
-               reg = <0x35003000 0x800>;
-               interrupts =
-                      <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH
-                       GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH
-                       GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH
-                       GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH
-                       GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH
-                       GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
-               #gpio-cells = <2>;
-               #interrupt-cells = <2>;
-               gpio-controller;
-               interrupt-controller;
-       };
index f7a158d858620f73133aded14765f042742a3fb4..5788d5cf12524b95c556ec0451204c3b735f6d43 100644 (file)
@@ -116,6 +116,29 @@ Every GPIO controller node must contain both an empty "gpio-controller"
 property, and a #gpio-cells integer property, which indicates the number of
 cells in a gpio-specifier.
 
+The GPIO chip may contain GPIO hog definitions. GPIO hogging is a mechanism
+providing automatic GPIO request and configuration as part of the
+gpio-controller's driver probe function.
+
+Each GPIO hog definition is represented as a child node of the GPIO controller.
+Required properties:
+- gpio-hog:   A property specifying that this child node represent a GPIO hog.
+- gpios:      Store the GPIO information (id, flags, ...). Shall contain the
+             number of cells specified in its parent node (GPIO controller
+             node).
+Only one of the following properties scanned in the order shown below.
+This means that when multiple properties are present they will be searched
+in the order presented below and the first match is taken as the intended
+configuration.
+- input:      A property specifying to set the GPIO direction as input.
+- output-low  A property specifying to set the GPIO direction as output with
+             the value low.
+- output-high A property specifying to set the GPIO direction as output with
+             the value high.
+
+Optional properties:
+- line-name:  The GPIO label name. If not present the node name is used.
+
 Example of two SOC GPIO banks defined as gpio-controller nodes:
 
        qe_pio_a: gpio-controller@1400 {
@@ -123,6 +146,13 @@ Example of two SOC GPIO banks defined as gpio-controller nodes:
                reg = <0x1400 0x18>;
                gpio-controller;
                #gpio-cells = <2>;
+
+               line_b {
+                       gpio-hog;
+                       gpios = <6 0>;
+                       output-low;
+                       line-name = "foo-bar-gpio";
+               };
        };
 
        qe_pio_e: gpio-controller@1460 {
index 67a2e4e414a5eb93409807b9e837cda236abd977..98d1983969566a0278b4544a5a24cef3c14fe85c 100644 (file)
@@ -12,7 +12,7 @@ Required properties:
   gpio_mux.
 - interrupt-names : Should be the names of irq resources. Each interrupt
   uses its own interrupt name, so there should be as many interrupt names
-  as referenced interrups.
+  as referenced interrupts.
 - interrupt-controller : Identifies the node as an interrupt controller.
 - #interrupt-cells: Specifies the number of cells needed to encode an
   interrupt source.
diff --git a/Documentation/devicetree/bindings/i2c/brcm,kona-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,kona-i2c.txt
new file mode 100644 (file)
index 0000000..1b87b74
--- /dev/null
@@ -0,0 +1,35 @@
+Broadcom Kona Family I2C
+=========================
+
+This I2C controller is used in the following Broadcom SoCs:
+
+  BCM11130
+  BCM11140
+  BCM11351
+  BCM28145
+  BCM28155
+
+Required Properties
+-------------------
+- compatible: "brcm,bcm11351-i2c", "brcm,kona-i2c"
+- reg: Physical base address and length of controller registers
+- interrupts: The interrupt number used by the controller
+- clocks: clock specifier for the kona i2c external clock
+- clock-frequency: The I2C bus frequency in Hz
+- #address-cells: Should be <1>
+- #size-cells: Should be <0>
+
+Refer to clocks/clock-bindings.txt for generic clock consumer
+properties.
+
+Example:
+
+i2c@3e016000 {
+       compatible = "brcm,bcm11351-i2c","brcm,kona-i2c";
+       reg = <0x3e016000 0x80>;
+       interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&bsc1_clk>;
+       clock-frequency = <400000>;
+       #address-cells = <1>;
+       #size-cells = <0>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt b/Documentation/devicetree/bindings/i2c/i2c-bcm-kona.txt
deleted file mode 100644 (file)
index 1b87b74..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-Broadcom Kona Family I2C
-=========================
-
-This I2C controller is used in the following Broadcom SoCs:
-
-  BCM11130
-  BCM11140
-  BCM11351
-  BCM28145
-  BCM28155
-
-Required Properties
--------------------
-- compatible: "brcm,bcm11351-i2c", "brcm,kona-i2c"
-- reg: Physical base address and length of controller registers
-- interrupts: The interrupt number used by the controller
-- clocks: clock specifier for the kona i2c external clock
-- clock-frequency: The I2C bus frequency in Hz
-- #address-cells: Should be <1>
-- #size-cells: Should be <0>
-
-Refer to clocks/clock-bindings.txt for generic clock consumer
-properties.
-
-Example:
-
-i2c@3e016000 {
-       compatible = "brcm,bcm11351-i2c","brcm,kona-i2c";
-       reg = <0x3e016000 0x80>;
-       interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
-       clocks = <&bsc1_clk>;
-       clock-frequency = <400000>;
-       #address-cells = <1>;
-       #size-cells = <0>;
-};
diff --git a/Documentation/devicetree/bindings/mfd/bcm590xx.txt b/Documentation/devicetree/bindings/mfd/bcm590xx.txt
deleted file mode 100644 (file)
index be51a15..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
--------------------------------
-BCM590xx Power Management Units
--------------------------------
-
-Required properties:
-- compatible: "brcm,bcm59056"
-- reg: I2C slave address
-- interrupts: interrupt for the PMU. Generic interrupt client node bindings
-  are described in interrupt-controller/interrupts.txt
-
-------------------
-Voltage Regulators
-------------------
-
-Optional child nodes:
-- regulators: container node for regulators following the generic
-  regulator binding in regulator/regulator.txt
-
-  The valid regulator node names for BCM59056 are:
-       rfldo, camldo1, camldo2, simldo1, simldo2, sdldo, sdxldo,
-       mmcldo1, mmcldo2, audldo, micldo, usbldo, vibldo,
-       csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr,
-       gpldo1, gpldo2, gpldo3, gpldo4, gpldo5, gpldo6,
-       vbus
-
-Example:
-       pmu: bcm59056@8 {
-               compatible = "brcm,bcm59056";
-               reg = <0x08>;
-               interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
-               regulators {
-                       rfldo_reg: rfldo {
-                               regulator-min-microvolt = <1200000>;
-                               regulator-max-microvolt = <3300000>;
-                       };
-
-                       ...
-               };
-       };
diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm59056.txt b/Documentation/devicetree/bindings/mfd/brcm,bcm59056.txt
new file mode 100644 (file)
index 0000000..be51a15
--- /dev/null
@@ -0,0 +1,39 @@
+-------------------------------
+BCM590xx Power Management Units
+-------------------------------
+
+Required properties:
+- compatible: "brcm,bcm59056"
+- reg: I2C slave address
+- interrupts: interrupt for the PMU. Generic interrupt client node bindings
+  are described in interrupt-controller/interrupts.txt
+
+------------------
+Voltage Regulators
+------------------
+
+Optional child nodes:
+- regulators: container node for regulators following the generic
+  regulator binding in regulator/regulator.txt
+
+  The valid regulator node names for BCM59056 are:
+       rfldo, camldo1, camldo2, simldo1, simldo2, sdldo, sdxldo,
+       mmcldo1, mmcldo2, audldo, micldo, usbldo, vibldo,
+       csr, iosr1, iosr2, msr, sdsr1, sdsr2, vsr,
+       gpldo1, gpldo2, gpldo3, gpldo4, gpldo5, gpldo6,
+       vbus
+
+Example:
+       pmu: bcm59056@8 {
+               compatible = "brcm,bcm59056";
+               reg = <0x08>;
+               interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_HIGH>;
+               regulators {
+                       rfldo_reg: rfldo {
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <3300000>;
+                       };
+
+                       ...
+               };
+       };
diff --git a/Documentation/devicetree/bindings/mips/brcm/bmips.txt b/Documentation/devicetree/bindings/mips/brcm/bmips.txt
deleted file mode 100644 (file)
index 8ef71b4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-* Broadcom MIPS (BMIPS) CPUs
-
-Required properties:
-- compatible: "brcm,bmips3300", "brcm,bmips4350", "brcm,bmips4380",
-  "brcm,bmips5000"
-
-- mips-hpt-frequency: This is common to all CPUs in the system so it lives
-  under the "cpus" node.
diff --git a/Documentation/devicetree/bindings/mips/brcm/brcm,bmips.txt b/Documentation/devicetree/bindings/mips/brcm/brcm,bmips.txt
new file mode 100644 (file)
index 0000000..8ef71b4
--- /dev/null
@@ -0,0 +1,8 @@
+* Broadcom MIPS (BMIPS) CPUs
+
+Required properties:
+- compatible: "brcm,bmips3300", "brcm,bmips4350", "brcm,bmips4380",
+  "brcm,bmips5000"
+
+- mips-hpt-frequency: This is common to all CPUs in the system so it lives
+  under the "cpus" node.
diff --git a/Documentation/devicetree/bindings/mips/brcm/usb.txt b/Documentation/devicetree/bindings/mips/brcm/usb.txt
deleted file mode 100644 (file)
index 452c45c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-* Broadcom USB controllers
-
-Required properties:
-- compatible: "brcm,bcm3384-ohci", "brcm,bcm3384-ehci"
-
-  These currently use the generic-ohci and generic-ehci drivers.  On some
-  systems, special handling may be needed in the following cases:
-
-  - Restoring state after systemwide power save modes
-  - Sharing PHYs with the USBD (UDC) hardware
-  - Figuring out which controllers are disabled on ASIC bondout variants
diff --git a/Documentation/devicetree/bindings/misc/brcm,kona-smc.txt b/Documentation/devicetree/bindings/misc/brcm,kona-smc.txt
new file mode 100644 (file)
index 0000000..6c9f176
--- /dev/null
@@ -0,0 +1,15 @@
+Broadcom Secure Monitor Bounce buffer
+-----------------------------------------------------
+This binding defines the location of the bounce buffer
+used for non-secure to secure communications.
+
+Required properties:
+- compatible : "brcm,kona-smc"
+- DEPRECATED: compatible : "bcm,kona-smc"
+- reg : Location and size of bounce buffer
+
+Example:
+       smc@0x3404c000 {
+               compatible = "brcm,bcm11351-smc", "brcm,kona-smc";
+               reg = <0x3404c000 0x400>; //1 KiB in SRAM
+       };
diff --git a/Documentation/devicetree/bindings/misc/smc.txt b/Documentation/devicetree/bindings/misc/smc.txt
deleted file mode 100644 (file)
index 6c9f176..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Broadcom Secure Monitor Bounce buffer
------------------------------------------------------
-This binding defines the location of the bounce buffer
-used for non-secure to secure communications.
-
-Required properties:
-- compatible : "brcm,kona-smc"
-- DEPRECATED: compatible : "bcm,kona-smc"
-- reg : Location and size of bounce buffer
-
-Example:
-       smc@0x3404c000 {
-               compatible = "brcm,bcm11351-smc", "brcm,kona-smc";
-               reg = <0x3404c000 0x400>; //1 KiB in SRAM
-       };
diff --git a/Documentation/devicetree/bindings/mmc/brcm,kona-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,kona-sdhci.txt
new file mode 100644 (file)
index 0000000..aaba248
--- /dev/null
@@ -0,0 +1,21 @@
+Broadcom BCM281xx SDHCI
+
+This file documents differences between the core properties in mmc.txt
+and the properties present in the bcm281xx SDHCI
+
+Required properties:
+- compatible : Should be "brcm,kona-sdhci"
+- DEPRECATED: compatible : Should be "bcm,kona-sdhci"
+- clocks: phandle + clock specifier pair of the external clock
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+
+sdio2: sdio@0x3f1a0000 {
+       compatible = "brcm,kona-sdhci";
+       reg = <0x3f1a0000 0x10000>;
+       clocks = <&sdio3_clk>;
+       interrupts = <0x0 74 0x4>;
+};
+
diff --git a/Documentation/devicetree/bindings/mmc/kona-sdhci.txt b/Documentation/devicetree/bindings/mmc/kona-sdhci.txt
deleted file mode 100644 (file)
index aaba248..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-Broadcom BCM281xx SDHCI
-
-This file documents differences between the core properties in mmc.txt
-and the properties present in the bcm281xx SDHCI
-
-Required properties:
-- compatible : Should be "brcm,kona-sdhci"
-- DEPRECATED: compatible : Should be "bcm,kona-sdhci"
-- clocks: phandle + clock specifier pair of the external clock
-
-Refer to clocks/clock-bindings.txt for generic clock consumer properties.
-
-Example:
-
-sdio2: sdio@0x3f1a0000 {
-       compatible = "brcm,kona-sdhci";
-       reg = <0x3f1a0000 0x10000>;
-       clocks = <&sdio3_clk>;
-       interrupts = <0x0 74 0x4>;
-};
-
diff --git a/Documentation/devicetree/bindings/net/brcm,bcm7445-switch-v4.0.txt b/Documentation/devicetree/bindings/net/brcm,bcm7445-switch-v4.0.txt
new file mode 100644 (file)
index 0000000..30d4875
--- /dev/null
@@ -0,0 +1,78 @@
+* Broadcom Starfighter 2 integrated swich
+
+Required properties:
+
+- compatible: should be "brcm,bcm7445-switch-v4.0"
+- reg: addresses and length of the register sets for the device, must be 6
+  pairs of register addresses and lengths
+- interrupts: interrupts for the devices, must be two interrupts
+- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
+- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
+- #size-cells: must be 0
+- #address-cells: must be 2, see dsa/dsa.txt
+
+Subnodes:
+
+The integrated switch subnode should be specified according to the binding
+described in dsa/dsa.txt.
+
+Optional properties:
+
+- reg-names: litteral names for the device base register addresses, when present
+  must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
+
+- interrupt-names: litternal names for the device interrupt lines, when present
+  must be: "switch_0" and "switch_1"
+
+- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
+  switch
+
+- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
+  by the switch
+
+- brcm,fcb-pause-override: boolean property, if present indicates that the switch
+  supports Failover Control Block pause override capability
+
+- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
+  Admission Control Block supports reporting the number of packets in-flight in a
+  switch queue
+
+Example:
+
+switch_top@f0b00000 {
+       compatible = "simple-bus";
+       #size-cells = <1>;
+       #address-cells = <1>;
+       ranges = <0 0xf0b00000 0x40804>;
+
+       ethernet_switch@0 {
+               compatible = "brcm,bcm7445-switch-v4.0";
+               #size-cells = <0>;
+               #address-cells = <2>;
+               reg = <0x0 0x40000
+                       0x40000 0x110
+                       0x40340 0x30
+                       0x40380 0x30
+                       0x40400 0x34
+                       0x40600 0x208>;
+               interrupts = <0 0x18 0
+                               0 0x19 0>;
+               brcm,num-gphy = <1>;
+               brcm,num-rgmii-ports = <2>;
+               brcm,fcb-pause-override;
+               brcm,acb-packets-inflight;
+
+               ...
+               switch@0 {
+                       reg = <0 0>;
+                       #size-cells = <0>;
+                       #address-cells <1>;
+
+                       port@0 {
+                               label = "gphy";
+                               reg = <0>;
+                       };
+                       ...
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt b/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt
new file mode 100644 (file)
index 0000000..451fef2
--- /dev/null
@@ -0,0 +1,121 @@
+* Broadcom BCM7xxx Ethernet Controller (GENET)
+
+Required properties:
+- compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2",
+  "brcm,genet-v3", "brcm,genet-v4".
+- reg: address and length of the register set for the device
+- interrupts: must be two cells, the first cell is the general purpose
+  interrupt line, while the second cell is the interrupt for the ring
+  RX and TX queues operating in ring mode
+- phy-mode: see ethernet.txt file in the same directory
+- #address-cells: should be 1
+- #size-cells: should be 1
+
+Optional properties:
+- clocks: When provided, must be two phandles to the functional clocks nodes
+  of the GENET block. The first phandle is the main GENET clock used during
+  normal operation, while the second phandle is the Wake-on-LAN clock.
+- clock-names: When provided, names of the functional clock phandles, first
+  name should be "enet" and second should be "enet-wol".
+
+- phy-handle: See ethernet.txt file in the same directory; used to describe
+  configurations where a PHY (internal or external) is used.
+
+- fixed-link: When the GENET interface is connected to a MoCA hardware block or
+  when operating in a RGMII to RGMII type of connection, or when the MDIO bus is
+  voluntarily disabled, this property should be used to describe the "fixed link".
+  See Documentation/devicetree/bindings/net/fixed-link.txt for information on
+  the property specifics
+
+Required child nodes:
+
+- mdio bus node: this node should always be present regarless of the PHY
+  configuration of the GENET instance
+
+MDIO bus node required properties:
+
+- compatible: should contain one of "brcm,genet-mdio-v1", "brcm,genet-mdio-v2"
+  "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", the version has to match the
+  parent node compatible property (e.g: brcm,genet-v4 pairs with
+  brcm,genet-mdio-v4)
+- reg: address and length relative to the parent node base register address
+- #address-cells: address cell for MDIO bus addressing, should be 1
+- #size-cells: size of the cells for MDIO bus addressing, should be 0
+
+Ethernet PHY node properties:
+
+See Documentation/devicetree/bindings/net/phy.txt for the list of required and
+optional properties.
+
+Internal Gigabit PHY example:
+
+ethernet@f0b60000 {
+       phy-mode = "internal";
+       phy-handle = <&phy1>;
+       mac-address = [ 00 10 18 36 23 1a ];
+       compatible = "brcm,genet-v4";
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       reg = <0xf0b60000 0xfc4c>;
+       interrupts = <0x0 0x14 0x0>, <0x0 0x15 0x0>;
+
+       mdio@e14 {
+               compatible = "brcm,genet-mdio-v4";
+               #address-cells = <0x1>;
+               #size-cells = <0x0>;
+               reg = <0xe14 0x8>;
+
+               phy1: ethernet-phy@1 {
+                       max-speed = <1000>;
+                       reg = <0x1>;
+                       compatible = "brcm,28nm-gphy", "ethernet-phy-ieee802.3-c22";
+               };
+       };
+};
+
+MoCA interface / MAC to MAC example:
+
+ethernet@f0b80000 {
+       phy-mode = "moca";
+       fixed-link = <1 0 1000 0 0>;
+       mac-address = [ 00 10 18 36 24 1a ];
+       compatible = "brcm,genet-v4";
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       reg = <0xf0b80000 0xfc4c>;
+       interrupts = <0x0 0x16 0x0>, <0x0 0x17 0x0>;
+
+       mdio@e14 {
+               compatible = "brcm,genet-mdio-v4";
+               #address-cells = <0x1>;
+               #size-cells = <0x0>;
+               reg = <0xe14 0x8>;
+       };
+};
+
+
+External MDIO-connected Gigabit PHY/switch:
+
+ethernet@f0ba0000 {
+       phy-mode = "rgmii";
+       phy-handle = <&phy0>;
+       mac-address = [ 00 10 18 36 26 1a ];
+       compatible = "brcm,genet-v4";
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       reg = <0xf0ba0000 0xfc4c>;
+       interrupts = <0x0 0x18 0x0>, <0x0 0x19 0x0>;
+
+       mdio@0e14 {
+               compatible = "brcm,genet-mdio-v4";
+               #address-cells = <0x1>;
+               #size-cells = <0x0>;
+               reg = <0xe14 0x8>;
+
+               phy0: ethernet-phy@0 {
+                       max-speed = <1000>;
+                       reg = <0x0>;
+                       compatible = "brcm,bcm53125", "ethernet-phy-ieee802.3-c22";
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/brcm,systemport.txt b/Documentation/devicetree/bindings/net/brcm,systemport.txt
new file mode 100644 (file)
index 0000000..877da34
--- /dev/null
@@ -0,0 +1,30 @@
+* Broadcom BCM7xxx Ethernet Systemport Controller (SYSTEMPORT)
+
+Required properties:
+- compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport"
+- reg: address and length of the register set for the device.
+- interrupts: interrupts for the device, first cell must be for the rx
+  interrupts, and the second cell should be for the transmit queues. An
+  optional third interrupt cell for Wake-on-LAN can be specified
+- local-mac-address: Ethernet MAC address (48 bits) of this adapter
+- phy-mode: Should be a string describing the PHY interface to the
+  Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt
+- fixed-link: see Documentation/devicetree/bindings/net/fixed-link.txt for
+  the property specific details
+
+Optional properties:
+- systemport,num-tier2-arb: number of tier 2 arbiters, an integer
+- systemport,num-tier1-arb: number of tier 1 arbiters, an integer
+- systemport,num-txq: number of HW transmit queues, an integer
+- systemport,num-rxq: number of HW receive queues, an integer
+
+Example:
+ethernet@f04a0000 {
+       compatible = "brcm,systemport-v1.00";
+       reg = <0xf04a0000 0x4650>;
+       local-mac-address = [ 00 11 22 33 44 55 ];
+       fixed-link = <0 1 1000 0 0>;
+       phy-mode = "gmii";
+       interrupts = <0x0 0x16 0x0>,
+               <0x0 0x17 0x0>;
+};
diff --git a/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt b/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
new file mode 100644 (file)
index 0000000..ab0bb42
--- /dev/null
@@ -0,0 +1,39 @@
+* Broadcom UniMAC MDIO bus controller
+
+Required properties:
+- compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2",
+  "brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio"
+- reg: address and length of the regsiter set for the device, first one is the
+  base register, and the second one is optional and for indirect accesses to
+  larger than 16-bits MDIO transactions
+- reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw"
+- #size-cells: must be 1
+- #address-cells: must be 0
+
+Optional properties:
+- interrupts: must be one if the interrupt is shared with the Ethernet MAC or
+  Ethernet switch this MDIO block is integrated from, or must be two, if there
+  are two separate interrupts, first one must be "mdio done" and second must be
+  for "mdio error"
+- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
+  to this hardware block, or must be "mdio_done" for the first interrupt and
+  "mdio_error" for the second when there are separate interrupts
+
+Child nodes of this MDIO bus controller node are standard Ethernet PHY device
+nodes as described in Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+
+mdio@403c0 {
+       compatible = "brcm,unimac-mdio";
+       reg = <0x403c0 0x8 0x40300 0x18>;
+       reg-names = "mdio", "mdio_indir_rw";
+       #size-cells = <1>;
+       #address-cells = <0>;
+
+       ...
+       phy@0 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt b/Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt
deleted file mode 100644 (file)
index 451fef2..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-* Broadcom BCM7xxx Ethernet Controller (GENET)
-
-Required properties:
-- compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2",
-  "brcm,genet-v3", "brcm,genet-v4".
-- reg: address and length of the register set for the device
-- interrupts: must be two cells, the first cell is the general purpose
-  interrupt line, while the second cell is the interrupt for the ring
-  RX and TX queues operating in ring mode
-- phy-mode: see ethernet.txt file in the same directory
-- #address-cells: should be 1
-- #size-cells: should be 1
-
-Optional properties:
-- clocks: When provided, must be two phandles to the functional clocks nodes
-  of the GENET block. The first phandle is the main GENET clock used during
-  normal operation, while the second phandle is the Wake-on-LAN clock.
-- clock-names: When provided, names of the functional clock phandles, first
-  name should be "enet" and second should be "enet-wol".
-
-- phy-handle: See ethernet.txt file in the same directory; used to describe
-  configurations where a PHY (internal or external) is used.
-
-- fixed-link: When the GENET interface is connected to a MoCA hardware block or
-  when operating in a RGMII to RGMII type of connection, or when the MDIO bus is
-  voluntarily disabled, this property should be used to describe the "fixed link".
-  See Documentation/devicetree/bindings/net/fixed-link.txt for information on
-  the property specifics
-
-Required child nodes:
-
-- mdio bus node: this node should always be present regarless of the PHY
-  configuration of the GENET instance
-
-MDIO bus node required properties:
-
-- compatible: should contain one of "brcm,genet-mdio-v1", "brcm,genet-mdio-v2"
-  "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", the version has to match the
-  parent node compatible property (e.g: brcm,genet-v4 pairs with
-  brcm,genet-mdio-v4)
-- reg: address and length relative to the parent node base register address
-- #address-cells: address cell for MDIO bus addressing, should be 1
-- #size-cells: size of the cells for MDIO bus addressing, should be 0
-
-Ethernet PHY node properties:
-
-See Documentation/devicetree/bindings/net/phy.txt for the list of required and
-optional properties.
-
-Internal Gigabit PHY example:
-
-ethernet@f0b60000 {
-       phy-mode = "internal";
-       phy-handle = <&phy1>;
-       mac-address = [ 00 10 18 36 23 1a ];
-       compatible = "brcm,genet-v4";
-       #address-cells = <0x1>;
-       #size-cells = <0x1>;
-       reg = <0xf0b60000 0xfc4c>;
-       interrupts = <0x0 0x14 0x0>, <0x0 0x15 0x0>;
-
-       mdio@e14 {
-               compatible = "brcm,genet-mdio-v4";
-               #address-cells = <0x1>;
-               #size-cells = <0x0>;
-               reg = <0xe14 0x8>;
-
-               phy1: ethernet-phy@1 {
-                       max-speed = <1000>;
-                       reg = <0x1>;
-                       compatible = "brcm,28nm-gphy", "ethernet-phy-ieee802.3-c22";
-               };
-       };
-};
-
-MoCA interface / MAC to MAC example:
-
-ethernet@f0b80000 {
-       phy-mode = "moca";
-       fixed-link = <1 0 1000 0 0>;
-       mac-address = [ 00 10 18 36 24 1a ];
-       compatible = "brcm,genet-v4";
-       #address-cells = <0x1>;
-       #size-cells = <0x1>;
-       reg = <0xf0b80000 0xfc4c>;
-       interrupts = <0x0 0x16 0x0>, <0x0 0x17 0x0>;
-
-       mdio@e14 {
-               compatible = "brcm,genet-mdio-v4";
-               #address-cells = <0x1>;
-               #size-cells = <0x0>;
-               reg = <0xe14 0x8>;
-       };
-};
-
-
-External MDIO-connected Gigabit PHY/switch:
-
-ethernet@f0ba0000 {
-       phy-mode = "rgmii";
-       phy-handle = <&phy0>;
-       mac-address = [ 00 10 18 36 26 1a ];
-       compatible = "brcm,genet-v4";
-       #address-cells = <0x1>;
-       #size-cells = <0x1>;
-       reg = <0xf0ba0000 0xfc4c>;
-       interrupts = <0x0 0x18 0x0>, <0x0 0x19 0x0>;
-
-       mdio@0e14 {
-               compatible = "brcm,genet-mdio-v4";
-               #address-cells = <0x1>;
-               #size-cells = <0x0>;
-               reg = <0xe14 0x8>;
-
-               phy0: ethernet-phy@0 {
-                       max-speed = <1000>;
-                       reg = <0x0>;
-                       compatible = "brcm,bcm53125", "ethernet-phy-ieee802.3-c22";
-               };
-       };
-};
diff --git a/Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt b/Documentation/devicetree/bindings/net/broadcom-mdio-unimac.txt
deleted file mode 100644 (file)
index ab0bb42..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-* Broadcom UniMAC MDIO bus controller
-
-Required properties:
-- compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2",
-  "brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio"
-- reg: address and length of the regsiter set for the device, first one is the
-  base register, and the second one is optional and for indirect accesses to
-  larger than 16-bits MDIO transactions
-- reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw"
-- #size-cells: must be 1
-- #address-cells: must be 0
-
-Optional properties:
-- interrupts: must be one if the interrupt is shared with the Ethernet MAC or
-  Ethernet switch this MDIO block is integrated from, or must be two, if there
-  are two separate interrupts, first one must be "mdio done" and second must be
-  for "mdio error"
-- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
-  to this hardware block, or must be "mdio_done" for the first interrupt and
-  "mdio_error" for the second when there are separate interrupts
-
-Child nodes of this MDIO bus controller node are standard Ethernet PHY device
-nodes as described in Documentation/devicetree/bindings/net/phy.txt
-
-Example:
-
-mdio@403c0 {
-       compatible = "brcm,unimac-mdio";
-       reg = <0x403c0 0x8 0x40300 0x18>;
-       reg-names = "mdio", "mdio_indir_rw";
-       #size-cells = <1>;
-       #address-cells = <0>;
-
-       ...
-       phy@0 {
-               compatible = "ethernet-phy-ieee802.3-c22";
-               reg = <0>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/net/broadcom-sf2.txt b/Documentation/devicetree/bindings/net/broadcom-sf2.txt
deleted file mode 100644 (file)
index 30d4875..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-* Broadcom Starfighter 2 integrated swich
-
-Required properties:
-
-- compatible: should be "brcm,bcm7445-switch-v4.0"
-- reg: addresses and length of the register sets for the device, must be 6
-  pairs of register addresses and lengths
-- interrupts: interrupts for the devices, must be two interrupts
-- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
-- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
-- #size-cells: must be 0
-- #address-cells: must be 2, see dsa/dsa.txt
-
-Subnodes:
-
-The integrated switch subnode should be specified according to the binding
-described in dsa/dsa.txt.
-
-Optional properties:
-
-- reg-names: litteral names for the device base register addresses, when present
-  must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
-
-- interrupt-names: litternal names for the device interrupt lines, when present
-  must be: "switch_0" and "switch_1"
-
-- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
-  switch
-
-- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
-  by the switch
-
-- brcm,fcb-pause-override: boolean property, if present indicates that the switch
-  supports Failover Control Block pause override capability
-
-- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
-  Admission Control Block supports reporting the number of packets in-flight in a
-  switch queue
-
-Example:
-
-switch_top@f0b00000 {
-       compatible = "simple-bus";
-       #size-cells = <1>;
-       #address-cells = <1>;
-       ranges = <0 0xf0b00000 0x40804>;
-
-       ethernet_switch@0 {
-               compatible = "brcm,bcm7445-switch-v4.0";
-               #size-cells = <0>;
-               #address-cells = <2>;
-               reg = <0x0 0x40000
-                       0x40000 0x110
-                       0x40340 0x30
-                       0x40380 0x30
-                       0x40400 0x34
-                       0x40600 0x208>;
-               interrupts = <0 0x18 0
-                               0 0x19 0>;
-               brcm,num-gphy = <1>;
-               brcm,num-rgmii-ports = <2>;
-               brcm,fcb-pause-override;
-               brcm,acb-packets-inflight;
-
-               ...
-               switch@0 {
-                       reg = <0 0>;
-                       #size-cells = <0>;
-                       #address-cells <1>;
-
-                       port@0 {
-                               label = "gphy";
-                               reg = <0>;
-                       };
-                       ...
-               };
-       };
-};
diff --git a/Documentation/devicetree/bindings/net/broadcom-systemport.txt b/Documentation/devicetree/bindings/net/broadcom-systemport.txt
deleted file mode 100644 (file)
index 877da34..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-* Broadcom BCM7xxx Ethernet Systemport Controller (SYSTEMPORT)
-
-Required properties:
-- compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport"
-- reg: address and length of the register set for the device.
-- interrupts: interrupts for the device, first cell must be for the rx
-  interrupts, and the second cell should be for the transmit queues. An
-  optional third interrupt cell for Wake-on-LAN can be specified
-- local-mac-address: Ethernet MAC address (48 bits) of this adapter
-- phy-mode: Should be a string describing the PHY interface to the
-  Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt
-- fixed-link: see Documentation/devicetree/bindings/net/fixed-link.txt for
-  the property specific details
-
-Optional properties:
-- systemport,num-tier2-arb: number of tier 2 arbiters, an integer
-- systemport,num-tier1-arb: number of tier 1 arbiters, an integer
-- systemport,num-txq: number of HW transmit queues, an integer
-- systemport,num-rxq: number of HW receive queues, an integer
-
-Example:
-ethernet@f04a0000 {
-       compatible = "brcm,systemport-v1.00";
-       reg = <0xf04a0000 0x4650>;
-       local-mac-address = [ 00 11 22 33 44 55 ];
-       fixed-link = <0 1 1000 0 0>;
-       phy-mode = "gmii";
-       interrupts = <0x0 0x16 0x0>,
-               <0x0 0x17 0x0>;
-};
diff --git a/Documentation/devicetree/bindings/phy/bcm-phy.txt b/Documentation/devicetree/bindings/phy/bcm-phy.txt
deleted file mode 100644 (file)
index 3dc8b3d..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-BROADCOM KONA USB2 PHY
-
-Required properties:
- - compatible: brcm,kona-usb2-phy
- - reg: offset and length of the PHY registers
- - #phy-cells: must be 0
-Refer to phy/phy-bindings.txt for the generic PHY binding properties
-
-Example:
-
-       usbphy: usb-phy@3f130000 {
-               compatible = "brcm,kona-usb2-phy";
-               reg = <0x3f130000 0x28>;
-               #phy-cells = <0>;
-       };
diff --git a/Documentation/devicetree/bindings/phy/brcm,kona-usb2-phy.txt b/Documentation/devicetree/bindings/phy/brcm,kona-usb2-phy.txt
new file mode 100644 (file)
index 0000000..3dc8b3d
--- /dev/null
@@ -0,0 +1,15 @@
+BROADCOM KONA USB2 PHY
+
+Required properties:
+ - compatible: brcm,kona-usb2-phy
+ - reg: offset and length of the PHY registers
+ - #phy-cells: must be 0
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Example:
+
+       usbphy: usb-phy@3f130000 {
+               compatible = "brcm,kona-usb2-phy";
+               reg = <0x3f130000 0x28>;
+               #phy-cells = <0>;
+       };
diff --git a/Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt b/Documentation/devicetree/bindings/pwm/bcm-kona-pwm.txt
deleted file mode 100644 (file)
index 8eae9fe..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-Broadcom Kona PWM controller device tree bindings
-
-This controller has 6 channels.
-
-Required Properties :
-- compatible: should contain "brcm,kona-pwm"
-- reg: physical base address and length of the controller's registers
-- clocks: phandle + clock specifier pair for the external clock
-- #pwm-cells: Should be 3. See pwm.txt in this directory for a
-  description of the cells format.
-
-Refer to clocks/clock-bindings.txt for generic clock consumer properties.
-
-Example:
-
-pwm: pwm@3e01a000 {
-       compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm";
-       reg = <0x3e01a000 0xc4>;
-       clocks = <&pwm_clk>;
-       #pwm-cells = <3>;
-};
diff --git a/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt b/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt
new file mode 100644 (file)
index 0000000..8eae9fe
--- /dev/null
@@ -0,0 +1,21 @@
+Broadcom Kona PWM controller device tree bindings
+
+This controller has 6 channels.
+
+Required Properties :
+- compatible: should contain "brcm,kona-pwm"
+- reg: physical base address and length of the controller's registers
+- clocks: phandle + clock specifier pair for the external clock
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a
+  description of the cells format.
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+
+pwm: pwm@3e01a000 {
+       compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm";
+       reg = <0x3e01a000 0xc4>;
+       clocks = <&pwm_clk>;
+       #pwm-cells = <3>;
+};
diff --git a/Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt b/Documentation/devicetree/bindings/reset/brcm,bcm21664-resetmgr.txt
new file mode 100644 (file)
index 0000000..93f31ca
--- /dev/null
@@ -0,0 +1,14 @@
+Broadcom Kona Family Reset Manager
+----------------------------------
+
+The reset manager is used on the Broadcom BCM21664 SoC.
+
+Required properties:
+  - compatible: brcm,bcm21664-resetmgr
+  - reg: memory address & range
+
+Example:
+       brcm,resetmgr@35001f00 {
+               compatible = "brcm,bcm21664-resetmgr";
+               reg = <0x35001f00 0x24>;
+       };
diff --git a/Documentation/devicetree/bindings/serial/bcm63xx-uart.txt b/Documentation/devicetree/bindings/serial/bcm63xx-uart.txt
deleted file mode 100644 (file)
index 5c52e5e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-* BCM63xx UART
-
-Required properties:
-
-- compatible: "brcm,bcm6345-uart"
-
-- reg: The base address of the UART register bank.
-
-- interrupts: A single interrupt specifier.
-
-- clocks: Clock driving the hardware; used to figure out the baud rate
-  divisor.
-
-Example:
-
-       uart0: serial@14e00520 {
-               compatible = "brcm,bcm6345-uart";
-               reg = <0x14e00520 0x18>;
-               interrupt-parent = <&periph_intc>;
-               interrupts = <2>;
-               clocks = <&periph_clk>;
-       };
-
-       clocks {
-               periph_clk: periph_clk@0 {
-                       compatible = "fixed-clock";
-                       #clock-cells = <0>;
-                       clock-frequency = <54000000>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/serial/brcm,bcm6345-uart.txt b/Documentation/devicetree/bindings/serial/brcm,bcm6345-uart.txt
new file mode 100644 (file)
index 0000000..5c52e5e
--- /dev/null
@@ -0,0 +1,30 @@
+* BCM63xx UART
+
+Required properties:
+
+- compatible: "brcm,bcm6345-uart"
+
+- reg: The base address of the UART register bank.
+
+- interrupts: A single interrupt specifier.
+
+- clocks: Clock driving the hardware; used to figure out the baud rate
+  divisor.
+
+Example:
+
+       uart0: serial@14e00520 {
+               compatible = "brcm,bcm6345-uart";
+               reg = <0x14e00520 0x18>;
+               interrupt-parent = <&periph_intc>;
+               interrupts = <2>;
+               clocks = <&periph_clk>;
+       };
+
+       clocks {
+               periph_clk: periph_clk@0 {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <54000000>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/sound/bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/bcm2835-i2s.txt
deleted file mode 100644 (file)
index 65783de..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-* Broadcom BCM2835 SoC I2S/PCM module
-
-Required properties:
-- compatible: "brcm,bcm2835-i2s"
-- reg: A list of base address and size entries:
-       * The first entry should cover the PCM registers
-       * The second entry should cover the PCM clock registers
-- dmas: List of DMA controller phandle and DMA request line ordered pairs.
-- dma-names: Identifier string for each DMA request line in the dmas property.
-  These strings correspond 1:1 with the ordered pairs in dmas.
-
-  One of the DMA channels will be responsible for transmission (should be
-  named "tx") and one for reception (should be named "rx").
-
-Example:
-
-bcm2835_i2s: i2s@7e203000 {
-       compatible = "brcm,bcm2835-i2s";
-       reg = <0x7e203000 0x20>,
-             <0x7e101098 0x02>;
-
-       dmas = <&dma 2>,
-              <&dma 3>;
-       dma-names = "tx", "rx";
-};
diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt
new file mode 100644 (file)
index 0000000..65783de
--- /dev/null
@@ -0,0 +1,25 @@
+* Broadcom BCM2835 SoC I2S/PCM module
+
+Required properties:
+- compatible: "brcm,bcm2835-i2s"
+- reg: A list of base address and size entries:
+       * The first entry should cover the PCM registers
+       * The second entry should cover the PCM clock registers
+- dmas: List of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: Identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+
+  One of the DMA channels will be responsible for transmission (should be
+  named "tx") and one for reception (should be named "rx").
+
+Example:
+
+bcm2835_i2s: i2s@7e203000 {
+       compatible = "brcm,bcm2835-i2s";
+       reg = <0x7e203000 0x20>,
+             <0x7e101098 0x02>;
+
+       dmas = <&dma 2>,
+              <&dma 3>;
+       dma-names = "tx", "rx";
+};
diff --git a/Documentation/devicetree/bindings/timer/brcm,kona-timer.txt b/Documentation/devicetree/bindings/timer/brcm,kona-timer.txt
new file mode 100644 (file)
index 0000000..39adf54
--- /dev/null
@@ -0,0 +1,25 @@
+Broadcom Kona Family timer
+-----------------------------------------------------
+This timer is used in the following Broadcom SoCs:
+ BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
+
+Required properties:
+- compatible : "brcm,kona-timer"
+- DEPRECATED: compatible : "bcm,kona-timer"
+- reg : Register range for the timer
+- interrupts : interrupt for the timer
+- clocks: phandle + clock specifier pair of the external clock
+- clock-frequency: frequency that the clock operates
+
+Only one of clocks or clock-frequency should be specified.
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+       timer@35006000 {
+               compatible = "brcm,kona-timer";
+               reg = <0x35006000 0x1000>;
+               interrupts = <0x0 7 0x4>;
+               clocks = <&hub_timer_clk>;
+       };
+
index 8933211f32f945c70b568d35372342ab3baca3d3..3bf58c20fe94da71ecae272e7d86256bd42f0683 100644 (file)
@@ -1,60 +1,60 @@
-1) OF selftest platform device
+1) OF unittest platform device
 
-** selftest
+** unittest
 
 Required properties:
-- compatible: must be "selftest"
+- compatible: must be "unittest"
 
 All other properties are optional.
 
 Example:
-       selftest {
-               compatible = "selftest";
+       unittest {
+               compatible = "unittest";
                status = "okay";
        };
 
-2) OF selftest i2c adapter platform device
+2) OF unittest i2c adapter platform device
 
 ** platform device unittest adapter
 
 Required properties:
-- compatible: must be selftest-i2c-bus
+- compatible: must be unittest-i2c-bus
 
-Children nodes contain selftest i2c devices.
+Children nodes contain unittest i2c devices.
 
 Example:
-       selftest-i2c-bus {
-               compatible = "selftest-i2c-bus";
+       unittest-i2c-bus {
+               compatible = "unittest-i2c-bus";
                status = "okay";
        };
 
-3) OF selftest i2c device
+3) OF unittest i2c device
 
-** I2C selftest device
+** I2C unittest device
 
 Required properties:
-- compatible: must be selftest-i2c-dev
+- compatible: must be unittest-i2c-dev
 
 All other properties are optional
 
 Example:
-       selftest-i2c-dev {
-               compatible = "selftest-i2c-dev";
+       unittest-i2c-dev {
+               compatible = "unittest-i2c-dev";
                status = "okay";
        };
 
-4) OF selftest i2c mux device
+4) OF unittest i2c mux device
 
-** I2C selftest mux
+** I2C unittest mux
 
 Required properties:
-- compatible: must be selftest-i2c-mux
+- compatible: must be unittest-i2c-mux
 
-Children nodes contain selftest i2c bus nodes per channel.
+Children nodes contain unittest i2c bus nodes per channel.
 
 Example:
-       selftest-i2c-mux {
-               compatible = "selftest-i2c-mux";
+       unittest-i2c-mux {
+               compatible = "unittest-i2c-mux";
                status = "okay";
                #address-cells = <1>;
                #size-cells = <0>;
@@ -64,7 +64,7 @@ Example:
                        #size-cells = <0>;
                        i2c-dev {
                                reg = <8>;
-                               compatible = "selftest-i2c-dev";
+                               compatible = "unittest-i2c-dev";
                                status = "okay";
                        };
                };
diff --git a/Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt b/Documentation/devicetree/bindings/usb/brcm,bcm3384-usb.txt
new file mode 100644 (file)
index 0000000..452c45c
--- /dev/null
@@ -0,0 +1,11 @@
+* Broadcom USB controllers
+
+Required properties:
+- compatible: "brcm,bcm3384-ohci", "brcm,bcm3384-ehci"
+
+  These currently use the generic-ohci and generic-ehci drivers.  On some
+  systems, special handling may be needed in the following cases:
+
+  - Restoring state after systemwide power save modes
+  - Sharing PHYs with the USBD (UDC) hardware
+  - Figuring out which controllers are disabled on ASIC bondout variants
diff --git a/Documentation/devicetree/bindings/watchdog/brcm,kona-wdt.txt b/Documentation/devicetree/bindings/watchdog/brcm,kona-wdt.txt
new file mode 100644 (file)
index 0000000..2b86a00
--- /dev/null
@@ -0,0 +1,15 @@
+Broadcom Kona Family Watchdog Timer
+-----------------------------------
+
+This watchdog timer is used in the following Broadcom SoCs:
+  BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
+
+Required properties:
+  - compatible = "brcm,bcm11351-wdt", "brcm,kona-wdt";
+  - reg: memory address & range
+
+Example:
+       watchdog@35002f40 {
+               compatible = "brcm,bcm11351-wdt", "brcm,kona-wdt";
+               reg = <0x35002f40 0x6c>;
+       };
diff --git a/Documentation/devicetree/of_selftest.txt b/Documentation/devicetree/of_selftest.txt
deleted file mode 100644 (file)
index 57a808b..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-Open Firmware Device Tree Selftest
-----------------------------------
-
-Author: Gaurav Minocha <gaurav.minocha.os@gmail.com>
-
-1. Introduction
-
-This document explains how the test data required for executing OF selftest
-is attached to the live tree dynamically, independent of the machine's
-architecture.
-
-It is recommended to read the following documents before moving ahead.
-
-[1] Documentation/devicetree/usage-model.txt
-[2] http://www.devicetree.org/Device_Tree_Usage
-
-OF Selftest has been designed to test the interface (include/linux/of.h)
-provided to device driver developers to fetch the device information..etc.
-from the unflattened device tree data structure. This interface is used by
-most of the device drivers in various use cases.
-
-
-2. Test-data
-
-The Device Tree Source file (drivers/of/testcase-data/testcases.dts) contains
-the test data required for executing the unit tests automated in
-drivers/of/selftests.c. Currently, following Device Tree Source Include files
-(.dtsi) are included in testcase.dts:
-
-drivers/of/testcase-data/tests-interrupts.dtsi
-drivers/of/testcase-data/tests-platform.dtsi
-drivers/of/testcase-data/tests-phandle.dtsi
-drivers/of/testcase-data/tests-match.dtsi
-
-When the kernel is build with OF_SELFTEST enabled, then the following make rule
-
-$(obj)/%.dtb: $(src)/%.dts FORCE
-       $(call if_changed_dep, dtc)
-
-is used to compile the DT source file (testcase.dts) into a binary blob
-(testcase.dtb), also referred as flattened DT.
-
-After that, using the following rule the binary blob above is wrapped as an
-assembly file (testcase.dtb.S).
-
-$(obj)/%.dtb.S: $(obj)/%.dtb
-       $(call cmd, dt_S_dtb)
-
-The assembly file is compiled into an object file (testcase.dtb.o), and is
-linked into the kernel image.
-
-
-2.1. Adding the test data
-
-Un-flattened device tree structure:
-
-Un-flattened device tree consists of connected device_node(s) in form of a tree
-structure described below.
-
-// following struct members are used to construct the tree
-struct device_node {
-    ...
-    struct  device_node *parent;
-    struct  device_node *child;
-    struct  device_node *sibling;
-    ...
- };
-
-Figure 1, describes a generic structure of machine's un-flattened device tree
-considering only child and sibling pointers. There exists another pointer,
-*parent, that is used to traverse the tree in the reverse direction. So, at
-a particular level the child node and all the sibling nodes will have a parent
-pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4's
-parent points to root node)
-
-root ('/')
-   |
-child1 -> sibling2 -> sibling3 -> sibling4 -> null
-   |         |           |           |
-   |         |           |          null
-   |         |           |
-   |         |        child31 -> sibling32 -> null
-   |         |           |          |
-   |         |          null       null
-   |         |
-   |      child21 -> sibling22 -> sibling23 -> null
-   |         |          |            |
-   |        null       null         null
-   |
-child11 -> sibling12 -> sibling13 -> sibling14 -> null
-   |           |           |            |
-   |           |           |           null
-   |           |           |
-  null        null       child131 -> null
-                           |
-                          null
-
-Figure 1: Generic structure of un-flattened device tree
-
-
-Before executing OF selftest, it is required to attach the test data to
-machine's device tree (if present). So, when selftest_data_add() is called,
-at first it reads the flattened device tree data linked into the kernel image
-via the following kernel symbols:
-
-__dtb_testcases_begin - address marking the start of test data blob
-__dtb_testcases_end   - address marking the end of test data blob
-
-Secondly, it calls of_fdt_unflatten_tree() to unflatten the flattened
-blob. And finally, if the machine's device tree (i.e live tree) is present,
-then it attaches the unflattened test data tree to the live tree, else it
-attaches itself as a live device tree.
-
-attach_node_and_children() uses of_attach_node() to attach the nodes into the
-live tree as explained below. To explain the same, the test data tree described
- in Figure 2 is attached to the live tree described in Figure 1.
-
-root ('/')
-    |
- testcase-data
-    |
- test-child0 -> test-sibling1 -> test-sibling2 -> test-sibling3 -> null
-    |               |                |                |
- test-child01      null             null             null
-
-
-Figure 2: Example test data tree to be attached to live tree.
-
-According to the scenario above, the live tree is already present so it isn't
-required to attach the root('/') node. All other nodes are attached by calling
-of_attach_node() on each node.
-
-In the function of_attach_node(), the new node is attached as the child of the
-given parent in live tree. But, if parent already has a child then the new node
-replaces the current child and turns it into its sibling. So, when the testcase
-data node is attached to the live tree above (Figure 1), the final structure is
- as shown in Figure 3.
-
-root ('/')
-   |
-testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
-   |               |          |           |           |
- (...)             |          |           |          null
-                   |          |         child31 -> sibling32 -> null
-                   |          |           |           |
-                   |          |          null        null
-                   |          |
-                   |        child21 -> sibling22 -> sibling23 -> null
-                   |          |           |            |
-                   |         null        null         null
-                   |
-                child11 -> sibling12 -> sibling13 -> sibling14 -> null
-                   |          |            |            |
-                  null       null          |           null
-                                           |
-                                        child131 -> null
-                                           |
-                                          null
------------------------------------------------------------------------
-
-root ('/')
-   |
-testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
-   |               |          |           |           |
-   |             (...)      (...)       (...)        null
-   |
-test-sibling3 -> test-sibling2 -> test-sibling1 -> test-child0 -> null
-   |                |                   |                |
-  null             null                null         test-child01
-
-
-Figure 3: Live device tree structure after attaching the testcase-data.
-
-
-Astute readers would have noticed that test-child0 node becomes the last
-sibling compared to the earlier structure (Figure 2). After attaching first
-test-child0 the test-sibling1 is attached that pushes the child node
-(i.e. test-child0) to become a sibling and makes itself a child node,
- as mentioned above.
-
-If a duplicate node is found (i.e. if a node with same full_name property is
-already present in the live tree), then the node isn't attached rather its
-properties are updated to the live tree's node by calling the function
-update_node_properties().
-
-
-2.2. Removing the test data
-
-Once the test case execution is complete, selftest_data_remove is called in
-order to remove the device nodes attached initially (first the leaf nodes are
-detached and then moving up the parent nodes are removed, and eventually the
-whole tree). selftest_data_remove() calls detach_node_and_children() that uses
-of_detach_node() to detach the nodes from the live device tree.
-
-To detach a node, of_detach_node() either updates the child pointer of given
-node's parent to its sibling or attaches the previous sibling to the given
-node's sibling, as appropriate. That is it :)
diff --git a/Documentation/devicetree/of_unittest.txt b/Documentation/devicetree/of_unittest.txt
new file mode 100644 (file)
index 0000000..3e4e7d4
--- /dev/null
@@ -0,0 +1,197 @@
+Open Firmware Device Tree Unittest
+----------------------------------
+
+Author: Gaurav Minocha <gaurav.minocha.os@gmail.com>
+
+1. Introduction
+
+This document explains how the test data required for executing OF unittest
+is attached to the live tree dynamically, independent of the machine's
+architecture.
+
+It is recommended to read the following documents before moving ahead.
+
+[1] Documentation/devicetree/usage-model.txt
+[2] http://www.devicetree.org/Device_Tree_Usage
+
+OF Selftest has been designed to test the interface (include/linux/of.h)
+provided to device driver developers to fetch the device information..etc.
+from the unflattened device tree data structure. This interface is used by
+most of the device drivers in various use cases.
+
+
+2. Test-data
+
+The Device Tree Source file (drivers/of/unittest-data/testcases.dts) contains
+the test data required for executing the unit tests automated in
+drivers/of/unittest.c. Currently, following Device Tree Source Include files
+(.dtsi) are included in testcases.dts:
+
+drivers/of/unittest-data/tests-interrupts.dtsi
+drivers/of/unittest-data/tests-platform.dtsi
+drivers/of/unittest-data/tests-phandle.dtsi
+drivers/of/unittest-data/tests-match.dtsi
+
+When the kernel is build with OF_SELFTEST enabled, then the following make rule
+
+$(obj)/%.dtb: $(src)/%.dts FORCE
+       $(call if_changed_dep, dtc)
+
+is used to compile the DT source file (testcases.dts) into a binary blob
+(testcases.dtb), also referred as flattened DT.
+
+After that, using the following rule the binary blob above is wrapped as an
+assembly file (testcases.dtb.S).
+
+$(obj)/%.dtb.S: $(obj)/%.dtb
+       $(call cmd, dt_S_dtb)
+
+The assembly file is compiled into an object file (testcases.dtb.o), and is
+linked into the kernel image.
+
+
+2.1. Adding the test data
+
+Un-flattened device tree structure:
+
+Un-flattened device tree consists of connected device_node(s) in form of a tree
+structure described below.
+
+// following struct members are used to construct the tree
+struct device_node {
+    ...
+    struct  device_node *parent;
+    struct  device_node *child;
+    struct  device_node *sibling;
+    ...
+ };
+
+Figure 1, describes a generic structure of machine's un-flattened device tree
+considering only child and sibling pointers. There exists another pointer,
+*parent, that is used to traverse the tree in the reverse direction. So, at
+a particular level the child node and all the sibling nodes will have a parent
+pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4's
+parent points to root node)
+
+root ('/')
+   |
+child1 -> sibling2 -> sibling3 -> sibling4 -> null
+   |         |           |           |
+   |         |           |          null
+   |         |           |
+   |         |        child31 -> sibling32 -> null
+   |         |           |          |
+   |         |          null       null
+   |         |
+   |      child21 -> sibling22 -> sibling23 -> null
+   |         |          |            |
+   |        null       null         null
+   |
+child11 -> sibling12 -> sibling13 -> sibling14 -> null
+   |           |           |            |
+   |           |           |           null
+   |           |           |
+  null        null       child131 -> null
+                           |
+                          null
+
+Figure 1: Generic structure of un-flattened device tree
+
+
+Before executing OF unittest, it is required to attach the test data to
+machine's device tree (if present). So, when selftest_data_add() is called,
+at first it reads the flattened device tree data linked into the kernel image
+via the following kernel symbols:
+
+__dtb_testcases_begin - address marking the start of test data blob
+__dtb_testcases_end   - address marking the end of test data blob
+
+Secondly, it calls of_fdt_unflatten_tree() to unflatten the flattened
+blob. And finally, if the machine's device tree (i.e live tree) is present,
+then it attaches the unflattened test data tree to the live tree, else it
+attaches itself as a live device tree.
+
+attach_node_and_children() uses of_attach_node() to attach the nodes into the
+live tree as explained below. To explain the same, the test data tree described
+ in Figure 2 is attached to the live tree described in Figure 1.
+
+root ('/')
+    |
+ testcase-data
+    |
+ test-child0 -> test-sibling1 -> test-sibling2 -> test-sibling3 -> null
+    |               |                |                |
+ test-child01      null             null             null
+
+
+Figure 2: Example test data tree to be attached to live tree.
+
+According to the scenario above, the live tree is already present so it isn't
+required to attach the root('/') node. All other nodes are attached by calling
+of_attach_node() on each node.
+
+In the function of_attach_node(), the new node is attached as the child of the
+given parent in live tree. But, if parent already has a child then the new node
+replaces the current child and turns it into its sibling. So, when the testcase
+data node is attached to the live tree above (Figure 1), the final structure is
+ as shown in Figure 3.
+
+root ('/')
+   |
+testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
+   |               |          |           |           |
+ (...)             |          |           |          null
+                   |          |         child31 -> sibling32 -> null
+                   |          |           |           |
+                   |          |          null        null
+                   |          |
+                   |        child21 -> sibling22 -> sibling23 -> null
+                   |          |           |            |
+                   |         null        null         null
+                   |
+                child11 -> sibling12 -> sibling13 -> sibling14 -> null
+                   |          |            |            |
+                  null       null          |           null
+                                           |
+                                        child131 -> null
+                                           |
+                                          null
+-----------------------------------------------------------------------
+
+root ('/')
+   |
+testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
+   |               |          |           |           |
+   |             (...)      (...)       (...)        null
+   |
+test-sibling3 -> test-sibling2 -> test-sibling1 -> test-child0 -> null
+   |                |                   |                |
+  null             null                null         test-child01
+
+
+Figure 3: Live device tree structure after attaching the testcase-data.
+
+
+Astute readers would have noticed that test-child0 node becomes the last
+sibling compared to the earlier structure (Figure 2). After attaching first
+test-child0 the test-sibling1 is attached that pushes the child node
+(i.e. test-child0) to become a sibling and makes itself a child node,
+ as mentioned above.
+
+If a duplicate node is found (i.e. if a node with same full_name property is
+already present in the live tree), then the node isn't attached rather its
+properties are updated to the live tree's node by calling the function
+update_node_properties().
+
+
+2.2. Removing the test data
+
+Once the test case execution is complete, selftest_data_remove is called in
+order to remove the device nodes attached initially (first the leaf nodes are
+detached and then moving up the parent nodes are removed, and eventually the
+whole tree). selftest_data_remove() calls detach_node_and_children() that uses
+of_detach_node() to detach the nodes from the live device tree.
+
+To detach a node, of_detach_node() either updates the child pointer of given
+node's parent to its sibling or attaches the previous sibling to the given
+node's sibling, as appropriate. That is it :)
index eede6088f978c25f83463b6d741d627b642fbfd8..c7d49b8855593b2a75e263363a9360e905f50773 100644 (file)
@@ -211,7 +211,7 @@ Thunderbird (GUI)
 Thunderbird is an Outlook clone that likes to mangle text, but there are ways
 to coerce it into behaving.
 
-- Allows use of an external editor:
+- Allow use of an external editor:
   The easiest thing to do with Thunderbird and patches is to use an
   "external editor" extension and then just use your favorite $EDITOR
   for reading/merging patches into the body text.  To do this, download
@@ -219,6 +219,15 @@ to coerce it into behaving.
   View->Toolbars->Customize... and finally just click on it when in the
   Compose dialog.
 
+  Please note that "external editor" requires that your editor must not
+  fork, or in other words, the editor must not return before closing. 
+  You may have to pass additional flags or change the settings of your
+  editor. Most notably if you are using gvim then you must pass the -f
+  option to gvim by putting "/usr/bin/gvim -f" (if the binary is in
+  /usr/bin) to the text editor field in "external editor" settings. If you
+  are using some other editor then please read its manual to find out how
+  to do this.
+
 To beat some sense out of the internal editor, do this:
 
 - Edit your Thunderbird config settings so that it won't use format=flowed.
index dac11d7fef2744adb2b9ae185f9665b10d761be4..e9e750e59efc50c922122d22ba1ec7ca69775a54 100644 (file)
@@ -140,6 +140,12 @@ nobarrier              This option can be used if underlying storage guarantees
 fastboot               This option is used when a system wants to reduce mount
                        time as much as possible, even though normal performance
                       can be sacrificed.
+extent_cache           Enable an extent cache based on rb-tree, it can cache
+                       as many as extent which map between contiguous logical
+                       address and physical address per inode, resulting in
+                       increasing the cache hit ratio.
+noinline_data          Disable the inline data feature, inline data feature is
+                       enabled by default.
 
 ================================================================================
 DEBUGFS ENTRIES
index 8e36c7e3c3455b3dd78463e6e722b5f5946dabdb..c3b6b301d8b004778c990e315b6d37250d673223 100644 (file)
@@ -1260,9 +1260,9 @@ Various pieces   of  information about  kernel activity  are  available in the
 since the system first booted.  For a quick look, simply cat the file:
 
   > cat /proc/stat
-  cpu  2255 34 2290 22625563 6290 127 456 0 0
-  cpu0 1132 34 1441 11311718 3675 127 438 0 0
-  cpu1 1123 0 849 11313845 2614 0 18 0 0
+  cpu  2255 34 2290 22625563 6290 127 456 0 0 0
+  cpu0 1132 34 1441 11311718 3675 127 438 0 0 0
+  cpu1 1123 0 849 11313845 2614 0 18 0 0 0
   intr 114930548 113199788 3 0 5 263 0 4 [... lots more numbers ...]
   ctxt 1990473
   btime 1062191376
index 8b35f51fe7b6a3e8d11a2abc3104913794349cce..b80606de545ad809fb147688060b26b51af4d5e1 100644 (file)
@@ -50,10 +50,43 @@ gpiod_is_active_low(power) will be true).
 
 ACPI
 ----
-ACPI does not support function names for GPIOs. Therefore, only the "idx"
-argument of gpiod_get_index() is useful to discriminate between GPIOs assigned
-to a device. The "con_id" argument can still be set for debugging purposes (it
-will appear under error messages as well as debug and sysfs nodes).
+ACPI also supports function names for GPIOs in a similar fashion to DT.
+The above DT example can be converted to an equivalent ACPI description
+with the help of _DSD (Device Specific Data), introduced in ACPI 5.1:
+
+       Device (FOO) {
+               Name (_CRS, ResourceTemplate () {
+                       GpioIo (Exclusive, ..., IoRestrictionOutputOnly,
+                               "\\_SB.GPI0") {15} // red
+                       GpioIo (Exclusive, ..., IoRestrictionOutputOnly,
+                               "\\_SB.GPI0") {16} // green
+                       GpioIo (Exclusive, ..., IoRestrictionOutputOnly,
+                               "\\_SB.GPI0") {17} // blue
+                       GpioIo (Exclusive, ..., IoRestrictionOutputOnly,
+                               "\\_SB.GPI0") {1} // power
+               })
+
+               Name (_DSD, Package () {
+                       ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+                       Package () {
+                               Package () {
+                                       "led-gpios",
+                                       Package () {
+                                               ^FOO, 0, 0, 1,
+                                               ^FOO, 1, 0, 1,
+                                               ^FOO, 2, 0, 1,
+                                       }
+                               },
+                               Package () {
+                                       "power-gpios",
+                                       Package () {^FOO, 3, 0, 0},
+                               },
+                       }
+               })
+       }
+
+For more information about the ACPI GPIO bindings see
+Documentation/acpi/gpio-properties.txt.
 
 Platform Data
 -------------
index d85fbae451ea930ddd9729dda3b55c44e443814b..c21c1313f09e55bee448fbb62d5efcd93eabe848 100644 (file)
@@ -58,7 +58,6 @@ pattern where a GPIO is optional, the gpiod_get_optional() and
 gpiod_get_index_optional() functions can be used. These functions return NULL
 instead of -ENOENT if no GPIO has been assigned to the requested function:
 
-
        struct gpio_desc *gpiod_get_optional(struct device *dev,
                                             const char *con_id,
                                             enum gpiod_flags flags)
@@ -68,6 +67,27 @@ instead of -ENOENT if no GPIO has been assigned to the requested function:
                                                   unsigned int index,
                                                   enum gpiod_flags flags)
 
+For a function using multiple GPIOs all of those can be obtained with one call:
+
+       struct gpio_descs *gpiod_get_array(struct device *dev,
+                                          const char *con_id,
+                                          enum gpiod_flags flags)
+
+This function returns a struct gpio_descs which contains an array of
+descriptors:
+
+       struct gpio_descs {
+               unsigned int ndescs;
+               struct gpio_desc *desc[];
+       }
+
+The following function returns NULL instead of -ENOENT if no GPIOs have been
+assigned to the requested function:
+
+       struct gpio_descs *gpiod_get_array_optional(struct device *dev,
+                                                   const char *con_id,
+                                                   enum gpiod_flags flags)
+
 Device-managed variants of these functions are also defined:
 
        struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
@@ -82,20 +102,37 @@ Device-managed variants of these functions are also defined:
                                                  const char *con_id,
                                                  enum gpiod_flags flags)
 
-       struct gpio_desc * devm_gpiod_get_index_optional(struct device *dev,
+       struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev,
                                                        const char *con_id,
                                                        unsigned int index,
                                                        enum gpiod_flags flags)
 
+       struct gpio_descs *devm_gpiod_get_array(struct device *dev,
+                                               const char *con_id,
+                                               enum gpiod_flags flags)
+
+       struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev,
+                                                        const char *con_id,
+                                                        enum gpiod_flags flags)
+
 A GPIO descriptor can be disposed of using the gpiod_put() function:
 
        void gpiod_put(struct gpio_desc *desc)
 
-It is strictly forbidden to use a descriptor after calling this function. The
-device-managed variant is, unsurprisingly:
+For an array of GPIOs this function can be used:
+
+       void gpiod_put_array(struct gpio_descs *descs)
+
+It is strictly forbidden to use a descriptor after calling these functions.
+It is also not allowed to individually release descriptors (using gpiod_put())
+from an array acquired with gpiod_get_array().
+
+The device-managed variants are, unsurprisingly:
 
        void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
 
+       void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
+
 
 Using GPIOs
 ===========
@@ -222,6 +259,26 @@ GPIOs belonging to the same bank or chip simultaneously if supported by the
 corresponding chip driver. In that case a significantly improved performance
 can be expected. If simultaneous setting is not possible the GPIOs will be set
 sequentially.
+
+The gpiod_set_array() functions take three arguments:
+       * array_size    - the number of array elements
+       * desc_array    - an array of GPIO descriptors
+       * value_array   - an array of values to assign to the GPIOs
+
+The descriptor array can be obtained using the gpiod_get_array() function
+or one of its variants. If the group of descriptors returned by that function
+matches the desired group of GPIOs, those GPIOs can be set by simply using
+the struct gpio_descs returned by gpiod_get_array():
+
+       struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
+       gpiod_set_array(my_gpio_descs->ndescs, my_gpio_descs->desc,
+                       my_gpio_values);
+
+It is also possible to set a completely arbitrary array of descriptors. The
+descriptors may be obtained using any combination of gpiod_get() and
+gpiod_get_array(). Afterwards the array of descriptors has to be setup
+manually before it can be used with gpiod_set_array().
+
 Note that for optimal performance GPIOs belonging to the same chip should be
 contiguous within the array of descriptors.
 
diff --git a/Documentation/i2o/README b/Documentation/i2o/README
deleted file mode 100644 (file)
index ee91e26..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-
-       Linux I2O Support       (c) Copyright 1999 Red Hat Software
-                                       and others.
-
-       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.
-
-AUTHORS (so far)
-
-Alan Cox, Building Number Three Ltd.
-       Core code, SCSI and Block OSMs
-
-Steve Ralston, LSI Logic Corp.
-       Debugging SCSI and Block OSM
-
-Deepak Saxena, Intel Corp.
-       Various core/block extensions
-       /proc interface, bug fixes
-       Ioctl interfaces for control
-       Debugging LAN OSM
-
-Philip Rumpf
-       Fixed assorted dumb SMP locking bugs
-
-Juha Sievanen, University of Helsinki Finland
-       LAN OSM code
-       /proc interface to LAN class
-       Bug fixes
-       Core code extensions
-
-Auvo Häkkinen, University of Helsinki Finland
-       LAN OSM code
-       /Proc interface to LAN class
-       Bug fixes
-       Core code extensions
-
-Taneli Vähäkangas, University of Helsinki Finland
-       Fixes to i2o_config
-
-CREDITS
-
-       This work was made possible by
-
-Red Hat Software
-       Funding for the Building #3 part of the project
-
-Symbios Logic (Now LSI)
-       Host adapters, hints, known to work platforms when I hit
-       compatibility problems
-
-BoxHill Corporation
-       Loan of initial FibreChannel disk array used for development work.
-
-European Commission
-       Funding the work done by the University of Helsinki
-
-SysKonnect
-        Loan of FDDI and Gigabit Ethernet cards
-
-ASUSTeK
-        Loan of I2O motherboard
diff --git a/Documentation/i2o/ioctl b/Documentation/i2o/ioctl
deleted file mode 100644 (file)
index 27c3c54..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-
-Linux I2O User Space Interface
-rev 0.3 - 04/20/99
-
-=============================================================================
-Originally written by Deepak Saxena(deepak@plexity.net)
-Currently maintained by Deepak Saxena(deepak@plexity.net)
-=============================================================================
-
-I. Introduction
-
-The Linux I2O subsystem provides a set of ioctl() commands that can be
-utilized by user space applications to communicate with IOPs and devices
-on individual IOPs. This document defines the specific ioctl() commands
-that are available to the user and provides examples of their uses.
-
-This document assumes the reader is familiar with or has access to the
-I2O specification as no I2O message parameters are outlined.  For information
-on the specification, see http://www.i2osig.org
-
-This document and the I2O user space interface are currently maintained
-by Deepak Saxena.  Please send all comments, errata, and bug fixes to
-deepak@csociety.purdue.edu
-
-II. IOP Access
-
-Access to the I2O subsystem is provided through the device file named
-/dev/i2o/ctl.  This file is a character file with major number 10 and minor
-number 166.  It can be created through the following command:
-
-   mknod /dev/i2o/ctl c 10 166
-
-III. Determining the IOP Count
-
-   SYNOPSIS
-
-   ioctl(fd, I2OGETIOPS,  int *count);
-
-   u8 count[MAX_I2O_CONTROLLERS];
-
-   DESCRIPTION
-
-   This function returns the system's active IOP table.  count should
-   point to a buffer containing MAX_I2O_CONTROLLERS entries.  Upon
-   returning, each entry will contain a non-zero value if the given
-   IOP unit is active, and NULL if it is inactive or non-existent.
-
-   RETURN VALUE.
-
-   Returns 0 if no errors occur, and -1 otherwise.  If an error occurs,
-   errno is set appropriately:
-
-     EFAULT   Invalid user space pointer was passed
-
-IV. Getting Hardware Resource Table
-
-   SYNOPSIS
-
-   ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt);
-
-      struct i2o_cmd_hrtlct
-      {
-         u32   iop;      /* IOP unit number */
-         void  *resbuf;  /* Buffer for result */
-         u32   *reslen;  /* Buffer length in bytes */
-      };
-
-   DESCRIPTION
-
-   This function returns the Hardware Resource Table of the IOP specified
-   by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of
-   the data is written into *(hrt->reslen).
-
-   RETURNS
-
-   This function returns 0 if no errors occur. If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(hrt->reslen)
-
-V. Getting Logical Configuration Table
-
-   SYNOPSIS
-
-   ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct);
-
-      struct i2o_cmd_hrtlct
-      {
-         u32   iop;      /* IOP unit number */
-         void  *resbuf;  /* Buffer for result */
-         u32   *reslen;  /* Buffer length in bytes */
-      };
-
-   DESCRIPTION
-
-   This function returns the Logical Configuration Table of the IOP specified
-   by lct->iop in the buffer pointed to by lct->resbuf. The actual size of
-   the data is written into *(lct->reslen).
-
-   RETURNS
-
-   This function returns 0 if no errors occur. If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(lct->reslen)
-
-VI. Setting Parameters
-
-   SYNOPSIS
-
-   ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops);
-
-      struct i2o_cmd_psetget
-      {
-         u32   iop;      /* IOP unit number */
-         u32   tid;      /* Target device TID */
-         void  *opbuf;   /* Operation List buffer */
-         u32   oplen;    /* Operation List buffer length in bytes */
-         void  *resbuf;  /* Result List buffer */
-         u32   *reslen;  /* Result List buffer length in bytes */
-      };
-
-   DESCRIPTION
-
-   This function posts a UtilParamsSet message to the device identified
-   by ops->iop and ops->tid.  The operation list for the message is
-   sent through the ops->opbuf buffer, and the result list is written
-   into the buffer pointed to by ops->resbuf.  The number of bytes
-   written is placed into *(ops->reslen).
-
-   RETURNS
-
-   The return value is the size in bytes of the data written into
-   ops->resbuf if no errors occur.  If an error occurs, -1 is returned
-   and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-   A return value of 0 does not mean that the value was actually
-   changed properly on the IOP.  The user should check the result
-   list to determine the specific status of the transaction.
-
-VII. Getting Parameters
-
-   SYNOPSIS
-
-   ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops);
-
-      struct i2o_parm_setget
-      {
-         u32   iop;      /* IOP unit number */
-         u32   tid;      /* Target device TID */
-         void  *opbuf;   /* Operation List buffer */
-         u32   oplen;    /* Operation List buffer length in bytes */
-         void  *resbuf;  /* Result List buffer */
-         u32   *reslen;  /* Result List buffer length in bytes */
-      };
-
-   DESCRIPTION
-
-   This function posts a UtilParamsGet message to the device identified
-   by ops->iop and ops->tid.  The operation list for the message is
-   sent through the ops->opbuf buffer, and the result list is written
-   into the buffer pointed to by ops->resbuf.  The actual size of data
-   written is placed into *(ops->reslen).
-
-   RETURNS
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-   A return value of 0 does not mean that the value was actually
-   properly retrieved.  The user should check the result list
-   to determine the specific status of the transaction.
-
-VIII. Downloading Software
-
-   SYNOPSIS
-
-   ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw);
-
-      struct i2o_sw_xfer
-      {
-         u32   iop;       /* IOP unit number */
-         u8    flags;     /* DownloadFlags field */
-         u8    sw_type;   /* Software type */
-         u32   sw_id;     /* Software ID */
-         void  *buf;      /* Pointer to software buffer */
-         u32   *swlen;    /* Length of software buffer */
-         u32   *maxfrag;  /* Number of fragments */
-         u32   *curfrag;  /* Current fragment number */
-      };
-
-   DESCRIPTION
-
-   This function downloads a software fragment pointed by sw->buf
-   to the iop identified by sw->iop. The DownloadFlags, SwID, SwType
-   and SwSize fields of the ExecSwDownload message are filled in with
-   the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen).
-
-   The fragments _must_ be sent in order and be 8K in size. The last
-   fragment _may_ be shorter, however. The kernel will compute its
-   size based on information in the sw->swlen field.
-
-   Please note that SW transfers can take a long time.
-
-   RETURNS
-
-   This function returns 0 no errors occur. If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-IX. Uploading Software
-
-   SYNOPSIS
-
-   ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw);
-
-      struct i2o_sw_xfer
-      {
-         u32   iop;      /* IOP unit number */
-         u8    flags;   /* UploadFlags */
-         u8    sw_type;  /* Software type */
-         u32   sw_id;    /* Software ID */
-         void  *buf;     /* Pointer to software buffer */
-         u32   *swlen;   /* Length of software buffer */
-         u32   *maxfrag; /* Number of fragments */
-         u32   *curfrag; /* Current fragment number */
-      };
-
-   DESCRIPTION
-
-   This function uploads a software fragment from the IOP identified
-   by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields.
-   The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload
-   message are filled in with the values of sw->flags, sw->sw_id,
-   sw->sw_type and *(sw->swlen).
-
-   The fragments _must_ be requested in order and be 8K in size. The
-   user is responsible for allocating memory pointed by sw->buf. The
-   last fragment _may_ be shorter.
-
-   Please note that SW transfers can take a long time.
-
-   RETURNS
-
-   This function returns 0 if no errors occur.  If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-X. Removing Software
-
-   SYNOPSIS
-
-   ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw);
-
-      struct i2o_sw_xfer
-      {
-         u32   iop;      /* IOP unit number */
-         u8    flags;   /* RemoveFlags */
-         u8    sw_type;  /* Software type */
-         u32   sw_id;    /* Software ID */
-         void  *buf;     /* Unused */
-         u32   *swlen;   /* Length of the software data */
-         u32   *maxfrag; /* Unused */
-         u32   *curfrag; /* Unused */
-      };
-
-   DESCRIPTION
-
-   This function removes software from the IOP identified by sw->iop.
-   The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message
-   are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and
-   *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses
-   *(sw->swlen) value to verify correct identication of the module to remove.
-   The actual size of the module is written into *(sw->swlen).
-
-   RETURNS
-
-   This function returns 0 if no errors occur.  If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-X. Validating Configuration
-
-   SYNOPSIS
-
-   ioctl(fd, I2OVALIDATE, int *iop);
-       u32 iop;
-
-   DESCRIPTION
-
-   This function posts an ExecConfigValidate message to the controller
-   identified by iop. This message indicates that the current
-   configuration is accepted. The iop changes the status of suspect drivers
-   to valid and may delete old drivers from its store.
-
-   RETURNS
-
-   This function returns 0 if no erro occur.  If an error occurs, -1 is
-   returned and errno is set appropriately:
-
-      ETIMEDOUT   Timeout waiting for reply message
-      ENXIO       Invalid IOP number
-
-XI. Configuration Dialog
-
-   SYNOPSIS
-
-   ioctl(fd, I2OHTML, struct i2o_html *htquery);
-      struct i2o_html
-      {
-         u32   iop;      /* IOP unit number */
-         u32   tid;      /* Target device ID */
-         u32   page;     /* HTML page */
-         void  *resbuf;  /* Buffer for reply HTML page */
-         u32   *reslen;  /* Length in bytes of reply buffer */
-         void  *qbuf;    /* Pointer to HTTP query string */
-         u32   qlen;     /* Length in bytes of query string buffer */
-      };
-
-   DESCRIPTION
-
-   This function posts an UtilConfigDialog message to the device identified
-   by htquery->iop and htquery->tid.  The requested HTML page number is
-   provided by the htquery->page field, and the resultant data is stored
-   in the buffer pointed to by htquery->resbuf.  If there is an HTTP query
-   string that is to be sent to the device, it should be sent in the buffer
-   pointed to by htquery->qbuf.  If there is no query string, this field
-   should be set to NULL. The actual size of the reply received is written
-   into *(htquery->reslen).
-
-   RETURNS
-
-   This function returns 0 if no error occur. If an error occurs, -1
-   is returned and errno is set appropriately:
-
-      EFAULT      Invalid user space pointer was passed
-      ENXIO       Invalid IOP number
-      ENOBUFS     Buffer not large enough.  If this occurs, the required
-                  buffer length is written into *(ops->reslen)
-      ETIMEDOUT   Timeout waiting for reply message
-      ENOMEM      Kernel memory allocation error
-
-XII. Events
-
-    In the process of determining this.  Current idea is to have use
-    the select() interface to allow user apps to periodically poll
-    the /dev/i2o/ctl device for events.  When select() notifies the user
-    that an event is available, the user would call read() to retrieve
-    a list of all the events that are pending for the specific device.
-
-=============================================================================
-Revision History
-=============================================================================
-
-Rev 0.1 - 04/01/99
-- Initial revision
-
-Rev 0.2 - 04/06/99
-- Changed return values to match UNIX ioctl() standard.  Only return values
-  are 0 and -1.  All errors are reported through errno.
-- Added summary of proposed possible event interfaces
-
-Rev 0.3 - 04/20/99
-- Changed all ioctls() to use pointers to user data instead of actual data
-- Updated error values to match the code
index 92ae734c00c348ab810373e0dc838a92462c932f..b9d229fee6b95b7d4836b9781c6b5650cb013435 100644 (file)
@@ -58,7 +58,7 @@ To exit command mode, PSMOUSE_CMD_SETSTREAM (EA) is sent to the touchpad.
 While in command mode, register addresses can be set by first sending a
 specific command, either EC for v3 devices or F5 for v4 devices. Then the
 address is sent one nibble at a time, where each nibble is encoded as a
-command with optional data. This enoding differs slightly between the v3 and
+command with optional data. This encoding differs slightly between the v3 and
 v4 protocols.
 
 Once an address has been set, the addressed register can be read by sending
@@ -139,7 +139,7 @@ ALPS Absolute Mode - Protocol Version 3
 ---------------------------------------
 
 ALPS protocol version 3 has three different packet formats. The first two are
-associated with touchpad events, and the third is associatd with trackstick
+associated with touchpad events, and the third is associated with trackstick
 events.
 
 The first type is the touchpad position packet.
index 96705616f5820a6d48d6cfda497c7fcf30917e2a..3f0f5ce3338b63a8153410c9b08edba739989e22 100644 (file)
@@ -229,7 +229,7 @@ such device to feedback.
 EV_PWR:
 ----------
 EV_PWR events are a special type of event used specifically for power
-mangement. Its usage is not well defined. To be addressed later.
+management. Its usage is not well defined. To be addressed later.
 
 Device properties:
 =================
index 06d60c3ff5e732d22cea71f599298db6432deca4..2cdfd9bcb1afc1015b4f98201f6b38cdab1691b3 100644 (file)
@@ -28,7 +28,7 @@ Example:
 --------
 
 Example configuration for a single TS1003 tilt switch that rotates around
-one axis in 4 steps and emitts the current tilt via two GPIOs.
+one axis in 4 steps and emits the current tilt via two GPIOs.
 
 static int sg060_tilt_enable(struct device *dev) {
        /* code to enable the sensors */
index 2d5fbfd6023e8dffda80793e02c58b3f658bab0b..66287151c54a99fd76cab71888ac2b720f4c489b 100644 (file)
@@ -97,7 +97,7 @@ LEN= 0e
 *** Attack and fade ***
 OP=  02
 LEN= 08
-00-01 Address where to store the parameteres
+00-01 Address where to store the parameters
 02-03 Duration of attack (little endian encoding, in ms)
 04 Level at end of attack. Signed byte.
 05-06 Duration of fade.
index 561385d38482100d9c0a917b8647598aefd9f810..49e3ac60dcef57d045720f5d9f96fc2b522a0f62 100644 (file)
@@ -91,7 +91,7 @@ absolute binary value. (10 bits per channel). Next nibble is checksum for
 first ten nibbles.
 
 Next nibbles 12 .. 21 represents four channels (not all channels can be
-directly controlled from TX). Binary representations ar the same as in first
+directly controlled from TX). Binary representations are the same as in first
 four channels. In nibbles 22 and 23 is a special magic number. Nibble 24 is
 checksum for nibbles 12..23.
 
index 5360e434486ced9970c6c71454b085b3ff617061..8277b76ec50649f3a35f5f5407d135fc37312a0e 100644 (file)
@@ -93,7 +93,7 @@ Format description:
   Format specifier
     '8' :  Generic 7 segment digit with individual addressable segments
 
-    Reduced capability 7 segm digit, when segments are hard wired together.
+    Reduced capability 7 segment digit, when segments are hard wired together.
     '1' : 2 segments digit only able to produce a 1.
     'e' : Most significant day of the month digit,
           able to produce at least 1 2 3.
index 11a76df2e1f1ee837abb694b482def0e3467530e..274252f205b7073ad909d19e2628e466b6b76c0e 100644 (file)
@@ -928,6 +928,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Enable debug messages at boot time.  See
                        Documentation/dynamic-debug-howto.txt for details.
 
+       eagerfpu=       [X86]
+                       on      enable eager fpu restore
+                       off     disable eager fpu restore
+                       auto    selects the default scheme, which automatically
+                               enables eagerfpu restore for xsaveopt.
+
        early_ioremap_debug [KNL]
                        Enable debug messages in early_ioremap support. This
                        is useful for tracking down temporary early mappings
@@ -1966,6 +1972,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                                 or
                                 memmap=0x10000$0x18690000
 
+       memmap=nn[KMG]!ss[KMG]
+                       [KNL,X86] Mark specific memory as protected.
+                       Region of memory to be used, from ss to ss+nn.
+                       The memory region may be marked as e820 type 12 (0xc)
+                       and is NVDIMM or ADR memory.
+
        memory_corruption_check=0/1 [X86]
                        Some BIOSes seem to corrupt the first 64k of
                        memory when doing things like suspend/resume.
@@ -2344,12 +2356,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        parameter, xsave area per process might occupy more
                        memory on xsaves enabled systems.
 
-       eagerfpu=       [X86]
-                       on      enable eager fpu restore
-                       off     disable eager fpu restore
-                       auto    selects the default scheme, which automatically
-                               enables eagerfpu restore for xsaveopt.
-
        nohlt           [BUGS=ARM,SH] Tells the kernel that the sleep(SH) or
                        wfi(ARM) instruction doesn't work correctly and not to
                        use it. This is also useful when using JTAG debugger.
index a41bdebbe87bf459e3c07d1dfd093f9eb0e80c3e..80aae85d8da6c1b8476fd6824553ae7070e5c508 100644 (file)
@@ -82,8 +82,8 @@ menu to even appear in "menuconfig". These are:
 
   o CONFIG_DEBUG_PAGEALLOC=n
 
-       This option is located under "Kernel hacking" / "Debug page memory
-       allocations".
+       This option is located under "Kernel hacking" / "Memory Debugging"
+        / "Debug page memory allocations".
 
 In addition, I highly recommend turning on CONFIG_DEBUG_INFO=y. This is also
 located under "Kernel hacking". With this, you will be able to get line number
index 1488b6525eb68dbf8310bc1eeb7946fe815ad919..1f9b3e2b98aec9a6687ae14b4f85d7c143729c07 100644 (file)
@@ -305,8 +305,8 @@ architectures:
 3. Configuring Kprobes
 
 When configuring the kernel using make menuconfig/xconfig/oldconfig,
-ensure that CONFIG_KPROBES is set to "y".  Under "Instrumentation
-Support", look for "Kprobes".
+ensure that CONFIG_KPROBES is set to "y". Under "General setup", look
+for "Kprobes".
 
 So that you can load and unload Kprobes-based instrumentation modules,
 make sure "Loadable module support" (CONFIG_MODULES) and "Module
index 6974f1c2b4e1edea11b0d31185153d12ef25957c..f95746189b5ded41d9ae8bdbbcafec4e1da8820e 100644 (file)
@@ -1727,7 +1727,7 @@ There are some more advanced barrier functions:
        }
 
      The dma_rmb() allows us guarantee the device has released ownership
-     before we read the data from the descriptor, and he dma_wmb() allows
+     before we read the data from the descriptor, and the dma_wmb() allows
      us to guarantee the data is written to the descriptor before the device
      can see it now has ownership.  The wmb() is needed to guarantee that the
      cache coherent memory writes have completed before attempting a write to
index ea03abfc97e95dc660b8028559d940357bc1c8f7..ce2cfcf35c27a0d0972547e82f61fbc38c85b5ab 100644 (file)
@@ -149,7 +149,7 @@ For example, assume 1GiB memory block size. A device for a memory starting at
 (0x100000000 / 1Gib = 4)
 This device covers address range [0x100000000 ... 0x140000000)
 
-Under each memory block, you can see 4 files:
+Under each memory block, you can see 5 files:
 
 /sys/devices/system/memory/memoryXXX/phys_index
 /sys/devices/system/memory/memoryXXX/phys_device
@@ -359,38 +359,51 @@ Need more implementation yet....
 --------------------------------
 8. Memory hotplug event notifier
 --------------------------------
-Memory hotplug has event notifier. There are 6 types of notification.
+Hotplugging events are sent to a notification queue.
 
-MEMORY_GOING_ONLINE
+There are six types of notification defined in include/linux/memory.h:
+
+MEM_GOING_ONLINE
   Generated before new memory becomes available in order to be able to
   prepare subsystems to handle memory. The page allocator is still unable
   to allocate from the new memory.
 
-MEMORY_CANCEL_ONLINE
+MEM_CANCEL_ONLINE
   Generated if MEMORY_GOING_ONLINE fails.
 
-MEMORY_ONLINE
+MEM_ONLINE
   Generated when memory has successfully brought online. The callback may
   allocate pages from the new memory.
 
-MEMORY_GOING_OFFLINE
+MEM_GOING_OFFLINE
   Generated to begin the process of offlining memory. Allocations are no
   longer possible from the memory but some of the memory to be offlined
   is still in use. The callback can be used to free memory known to a
   subsystem from the indicated memory block.
 
-MEMORY_CANCEL_OFFLINE
+MEM_CANCEL_OFFLINE
   Generated if MEMORY_GOING_OFFLINE fails. Memory is available again from
   the memory block that we attempted to offline.
 
-MEMORY_OFFLINE
+MEM_OFFLINE
   Generated after offlining memory is complete.
 
-A callback routine can be registered by
+A callback routine can be registered by calling
+
   hotplug_memory_notifier(callback_func, priority)
 
-The second argument of callback function (action) is event types of above.
-The third argument is passed by pointer of struct memory_notify.
+Callback functions with higher values of priority are called before callback
+functions with lower values.
+
+A callback function must have the following prototype:
+
+  int callback_func(
+    struct notifier_block *self, unsigned long action, void *arg);
+
+The first argument of the callback function (self) is a pointer to the block
+of the notifier chain that points to the callback function itself.
+The second argument (action) is one of the event types described above.
+The third argument (arg) passes a pointer of struct memory_notify.
 
 struct memory_notify {
        unsigned long start_pfn;
@@ -412,6 +425,18 @@ node loses all memory. If this is -1, then nodemask status is not changed.
 If status_changed_nid* >= 0, callback should create/discard structures for the
 node if necessary.
 
+The callback routine shall return one of the values
+NOTIFY_DONE, NOTIFY_OK, NOTIFY_BAD, NOTIFY_STOP
+defined in include/linux/notifier.h
+
+NOTIFY_DONE and NOTIFY_OK have no effect on the further processing.
+
+NOTIFY_BAD is used as response to the MEM_GOING_ONLINE, MEM_GOING_OFFLINE,
+MEM_ONLINE, or MEM_OFFLINE action to cancel hotplugging. It stops
+further processing of the notification queue.
+
+NOTIFY_STOP stops further processing of the notification queue.
+
 --------------
 9. Future Work
 --------------
index cb6a596072bbe375aacbc6202d0eed7b6ab294cb..2216eb187c213b4c0c5140a760f9df3098150e41 100644 (file)
@@ -228,7 +228,7 @@ UUID/GUID addresses:
        lower ('l') or upper case ('L') hex characters - and big endian order
        in lower ('b') or upper case ('B') hex characters.
 
-       Where no additional specifiers are used the default little endian
+       Where no additional specifiers are used the default big endian
        order with lower case hex characters will be printed.
 
        Passed by reference.
@@ -273,6 +273,16 @@ struct clk:
 
        Passed by reference.
 
+bitmap and its derivatives such as cpumask and nodemask:
+
+       %*pb    0779
+       %*pbl   0,3-6,8-10
+
+       For printing bitmap and its derivatives such as cpumask and nodemask,
+       %*pb output the bitmap with field width as the number of bits and %*pbl
+       output the bitmap as range list with field width as the number of bits.
+
+       Passed by reference.
 
 Thank you for your cooperation and attention.
 
index f77651eca31ea0b32efea5ecdbf1f05f23e72eab..2622bc7a188b0519f87058773e6dc826c5e785a9 100644 (file)
@@ -7,24 +7,24 @@ Introduction:
 -------------
 
 If you have one or more threads of execution that must wait for some process
-to have reached a point or a specific state, completions can provide a race
-free solution to this problem. Semantically they are somewhat like a
-pthread_barriers and have similar use-cases.
+to have reached a point or a specific state, completions can provide a
+race-free solution to this problem. Semantically they are somewhat like a
+pthread_barrier and have similar use-cases.
 
-Completions are a code synchronization mechanism that is preferable to any
+Completions are a code synchronization mechanism which is preferable to any
 misuse of locks. Any time you think of using yield() or some quirky
-msleep(1); loop to allow something else to proceed, you probably want to
+msleep(1) loop to allow something else to proceed, you probably want to
 look into using one of the wait_for_completion*() calls instead. The
-advantage of using completions is clear intent of the code but also more
+advantage of using completions is clear intent of the code, but also more
 efficient code as both threads can continue until the result is actually
 needed.
 
 Completions are built on top of the generic event infrastructure in Linux,
-with the event reduced to a simple flag appropriately called "done" in
-struct completion, that tells the waiting threads of execution if they
+with the event reduced to a simple flag (appropriately called "done") in
+struct completion that tells the waiting threads of execution if they
 can continue safely.
 
-As completions are scheduling related the code is found in
+As completions are scheduling related, the code is found in
 kernel/sched/completion.c - for details on completion design and
 implementation see completions-design.txt
 
@@ -32,9 +32,9 @@ implementation see completions-design.txt
 Usage:
 ------
 
-There are three parts to the using completions, the initialization of the
+There are three parts to using completions, the initialization of the
 struct completion, the waiting part through a call to one of the variants of
-wait_for_completion() and the signaling side through a call to complete(),
+wait_for_completion() and the signaling side through a call to complete()
 or complete_all(). Further there are some helper functions for checking the
 state of completions.
 
@@ -50,7 +50,7 @@ handling of completions is:
 providing the wait queue to place tasks on for waiting and the flag for
 indicating the state of affairs.
 
-Completions should be named to convey the intent of the waiter.  A good
+Completions should be named to convey the intent of the waiter. A good
 example is:
 
        wait_for_completion(&early_console_added);
@@ -73,7 +73,7 @@ the default state to "not available", that is, "done" is set to 0.
 
 The re-initialization function, reinit_completion(), simply resets the
 done element to "not available", thus again to 0, without touching the
-wait queue. Calling init_completion() on the same completions object is
+wait queue. Calling init_completion() twice on the same completion object is
 most likely a bug as it re-initializes the queue to an empty queue and
 enqueued tasks could get "lost" - use reinit_completion() in that case.
 
@@ -87,10 +87,17 @@ initialization should always use:
        DECLARE_COMPLETION_ONSTACK(setup_done)
 
 suitable for automatic/local variables on the stack and will make lockdep
-happy. Note also that one needs to making *sure* the completion passt to
+happy. Note also that one needs to make *sure* the completion passed to
 work threads remains in-scope, and no references remain to on-stack data
 when the initiating function returns.
 
+Using on-stack completions for code that calls any of the _timeout or
+_interruptible/_killable variants is not advisable as they will require
+additional synchronization to prevent the on-stack completion object in
+the timeout/signal cases from going out of scope. Consider using dynamically
+allocated completions when intending to use the _interruptible/_killable
+or _timeout variants of wait_for_completion().
+
 
 Waiting for completions:
 ------------------------
@@ -99,34 +106,38 @@ For a thread of execution to wait for some concurrent work to finish, it
 calls wait_for_completion() on the initialized completion structure.
 A typical usage scenario is:
 
-       structure completion setup_done;
+       struct completion setup_done;
        init_completion(&setup_done);
-       initialze_work(...,&setup_done,...)
+       initialize_work(...,&setup_done,...)
 
        /* run non-dependent code */              /* do setup */
 
-       wait_for_completion(&seupt_done);         complete(setup_done)
+       wait_for_completion(&setup_done);         complete(setup_done)
 
-This is not implying any temporal order of wait_for_completion() and the
+This is not implying any temporal order on wait_for_completion() and the
 call to complete() - if the call to complete() happened before the call
 to wait_for_completion() then the waiting side simply will continue
-immediately as all dependencies are satisfied.
+immediately as all dependencies are satisfied if not it will block until
+completion is signaled by complete().
 
-Note that wait_for_completion() is calling spin_lock_irq/spin_unlock_irq
+Note that wait_for_completion() is calling spin_lock_irq()/spin_unlock_irq(),
 so it can only be called safely when you know that interrupts are enabled.
-Calling it from hard-irq context will result in hard to detect spurious
-enabling of interrupts.
+Calling it from hard-irq or irqs-off atomic contexts will result in
+hard-to-detect spurious enabling of interrupts.
 
 wait_for_completion():
 
        void wait_for_completion(struct completion *done):
 
-The default behavior is to wait without a timeout and mark the task as
+The default behavior is to wait without a timeout and to mark the task as
 uninterruptible. wait_for_completion() and its variants are only safe
-in soft-interrupt or process context but not in hard-irq context.
+in process context (as they can sleep) but not in atomic context,
+interrupt context, with disabled irqs. or preemption is disabled - see also
+try_wait_for_completion() below for handling completion in atomic/interrupt
+context.
+
 As all variants of wait_for_completion() can (obviously) block for a long
-time, you probably don't want to call this with held locks - see also
-try_wait_for_completion() below.
+time, you probably don't want to call this with held mutexes.
 
 
 Variants available:
@@ -141,43 +152,44 @@ A common problem that occurs is to have unclean assignment of return types,
 so care should be taken with assigning return-values to variables of proper
 type. Checking for the specific meaning of return values also has been found
 to be quite inaccurate e.g. constructs like
-if(!wait_for_completion_interruptible_timeout(...)) would execute the same
+if (!wait_for_completion_interruptible_timeout(...)) would execute the same
 code path for successful completion and for the interrupted case - which is
 probably not what you want.
 
        int wait_for_completion_interruptible(struct completion *done)
 
-marking the task TASK_INTERRUPTIBLE. If a signal was received while waiting.
-It will return -ERESTARTSYS and 0 otherwise.
+This function marks the task TASK_INTERRUPTIBLE. If a signal was received
+while waiting it will return -ERESTARTSYS; 0 otherwise.
 
        unsigned long wait_for_completion_timeout(struct completion *done,
                unsigned long timeout)
 
-The task is marked as TASK_UNINTERRUPTIBLE and will wait at most timeout
-(in jiffies). If timeout occurs it return 0 else the remaining time in
-jiffies (but at least 1). Timeouts are preferably passed by msecs_to_jiffies()
-or usecs_to_jiffies(). If the returned timeout value is deliberately ignored
-a comment should probably explain why (e.g. see drivers/mfd/wm8350-core.c
-wm8350_read_auxadc())
+The task is marked as TASK_UNINTERRUPTIBLE and will wait at most 'timeout'
+(in jiffies). If timeout occurs it returns 0 else the remaining time in
+jiffies (but at least 1). Timeouts are preferably calculated with
+msecs_to_jiffies() or usecs_to_jiffies(). If the returned timeout value is
+deliberately ignored a comment should probably explain why (e.g. see
+drivers/mfd/wm8350-core.c wm8350_read_auxadc())
 
        long wait_for_completion_interruptible_timeout(
                struct completion *done, unsigned long timeout)
 
-passing a timeout in jiffies and marking the task as TASK_INTERRUPTIBLE. If a
-signal was received it will return -ERESTARTSYS, 0 if completion timed-out and
-the remaining time in jiffies if completion occurred.
+This function passes a timeout in jiffies and marks the task as
+TASK_INTERRUPTIBLE. If a signal was received it will return -ERESTARTSYS;
+otherwise it returns 0 if the completion timed out or the remaining time in
+jiffies if completion occurred.
 
-Further variants include _killable which passes TASK_KILLABLE as the
-designated tasks state and will return a -ERESTARTSYS if interrupted or
-else 0 if completions was achieved as well as a _timeout variant.
+Further variants include _killable which uses TASK_KILLABLE as the
+designated tasks state and will return -ERESTARTSYS if it is interrupted or
+else 0 if completion was achieved.  There is a _timeout variant as well:
 
        long wait_for_completion_killable(struct completion *done)
        long wait_for_completion_killable_timeout(struct completion *done,
                unsigned long timeout)
 
-The _io variants wait_for_completion_io behave the same as the non-_io
+The _io variants wait_for_completion_io() behave the same as the non-_io
 variants, except for accounting waiting time as waiting on IO, which has
-an impact on how scheduling is calculated.
+an impact on how the task is accounted in scheduling stats.
 
        void wait_for_completion_io(struct completion *done)
        unsigned long wait_for_completion_io_timeout(struct completion *done
@@ -187,13 +199,13 @@ an impact on how scheduling is calculated.
 Signaling completions:
 ----------------------
 
-A thread of execution that wants to signal that the conditions for
-continuation have been achieved calls complete() to signal exactly one
-of the waiters that it can continue.
+A thread that wants to signal that the conditions for continuation have been
+achieved calls complete() to signal exactly one of the waiters that it can
+continue.
 
        void complete(struct completion *done)
 
-or calls complete_all to signal all current and future waiters.
+or calls complete_all() to signal all current and future waiters.
 
        void complete_all(struct completion *done)
 
@@ -205,32 +217,32 @@ wakeup order is the same in which they were enqueued (FIFO order).
 If complete() is called multiple times then this will allow for that number
 of waiters to continue - each call to complete() will simply increment the
 done element. Calling complete_all() multiple times is a bug though. Both
-complete() and complete_all() can be called in hard-irq context safely.
+complete() and complete_all() can be called in hard-irq/atomic context safely.
 
 There only can be one thread calling complete() or complete_all() on a
-particular struct completions at any time - serialized through the wait
+particular struct completion at any time - serialized through the wait
 queue spinlock. Any such concurrent calls to complete() or complete_all()
 probably are a design bug.
 
 Signaling completion from hard-irq context is fine as it will appropriately
-lock with spin_lock_irqsave/spin_unlock_irqrestore.
+lock with spin_lock_irqsave/spin_unlock_irqrestore and it will never sleep.
 
 
 try_wait_for_completion()/completion_done():
 --------------------------------------------
 
-The try_wait_for_completion will not put the thread on the wait queue but
-rather returns false if it would need to enqueue (block) the thread, else it
-consumes any posted completions and returns true.
+The try_wait_for_completion() function will not put the thread on the wait
+queue but rather returns false if it would need to enqueue (block) the thread,
+else it consumes one posted completion and returns true.
 
-     bool try_wait_for_completion(struct completion *done)
+       bool try_wait_for_completion(struct completion *done)
 
-Finally to check state of a completions without changing it in any way is
-provided by completion_done() returning false if there are any posted
-completion that was not yet consumed by waiters implying that there are
-waiters and true otherwise;
+Finally, to check the state of a completion without changing it in any way, 
+call completion_done(), which returns false if there are no posted
+completions that were not yet consumed by waiters (implying that there are
+waiters) and true otherwise;
 
-     bool completion_done(struct completion *done)
+       bool completion_done(struct completion *done)
 
 Both try_wait_for_completion() and completion_done() are safe to be called in
-hard-irq context.
+hard-irq or atomic context.
index 6fbd55ef6b45379f4e92c7ee009657c3c73bcd7c..6bfbc172cdb96b437728a34eadf3e257ab456799 100644 (file)
@@ -131,7 +131,8 @@ Short descriptions to the page flags:
 13. SWAPCACHE   page is mapped to swap space, ie. has an associated swap entry
 14. SWAPBACKED  page is backed by swap/RAM
 
-The page-types tool in this directory can be used to query the above flags.
+The page-types tool in the tools/vm directory can be used to query the
+above flags.
 
 Using pagemap to do something useful:
 
index 6b31cfbe2a9a1197b8d33d294e90bd6f64d3fcd9..8143b9e8373db41746330c468dede07acfd96b56 100644 (file)
@@ -159,6 +159,17 @@ for each pass:
 
 /sys/kernel/mm/transparent_hugepage/khugepaged/full_scans
 
+max_ptes_none specifies how many extra small pages (that are
+not already mapped) can be allocated when collapsing a group
+of small pages into one large page.
+
+/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none
+
+A higher value leads to use additional memory for programs.
+A lower value leads to gain less thp performance. Value of
+max_ptes_none can waste cpu time very little, you can
+ignore it.
+
 == Boot parameter ==
 
 You can change the sysfs boot time defaults of Transparent Hugepage
index 6f6d956ac1c9724bd47fd6a5a77d22f09fe1cd9b..7cd36af11e71c2964082dc9d37540a7566923c31 100644 (file)
@@ -15,6 +15,8 @@ Documentation/arm64/booting.txt 的中文翻译
 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
 译存在问题,请联系中文版维护者。
 
+本文翻译提交时的 Git 检出点为: bc465aa9d045feb0e13b4a8f32cc33c1943f62d6
+
 英文版维护者: Will Deacon <will.deacon@arm.com>
 中文版维护者: 傅炜  Fu Wei <wefu@redhat.com>
 中文版翻译者: 傅炜  Fu Wei <wefu@redhat.com>
@@ -88,22 +90,44 @@ AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内
 
   u32 code0;                   /* 可执行代码 */
   u32 code1;                   /* 可执行代码 */
-  u64 text_offset;             /* 映像装载偏移 */
-  u64 res0     = 0;            /* 保留 */
-  u64 res1     = 0;            /* 保留 */
+  u64 text_offset;             /* 映像装载偏移,小端模式 */
+  u64 image_size;              /* 映像实际大小, 小端模式 */
+  u64 flags;                   /* 内核旗标, 小端模式 *
   u64 res2     = 0;            /* 保留 */
   u64 res3     = 0;            /* 保留 */
   u64 res4     = 0;            /* 保留 */
   u32 magic    = 0x644d5241;   /* 魔数, 小端, "ARM\x64" */
-  u32 res5 = 0;                /* 保留 */
+  u32 res5;                    /* 保留 (用于 PE COFF 偏移) */
 
 
 映像头注释:
 
+- 自 v3.17 起,除非另有说明,所有域都是小端模式。
+
 - code0/code1 负责跳转到 stext.
 
-映像必须位于系统 RAM 起始处的特定偏移(当前是 0x80000)。系统 RAM
-的起始地址必须是以 2MB 对齐的。
+- 当通过 EFI 启动时, 最初 code0/code1 被跳过。
+  res5 是到 PE 文件头的偏移,而 PE 文件头含有 EFI 的启动入口点 (efi_stub_entry)。
+  当 stub 代码完成了它的使命,它会跳转到 code0 继续正常的启动流程。
+
+- v3.17 之前,未明确指定 text_offset 的字节序。此时,image_size 为零,
+  且 text_offset 依照内核字节序为 0x80000。
+  当 image_size 非零,text_offset 为小端模式且是有效值,应被引导加载程序使用。
+  当 image_size 为零,text_offset 可假定为 0x80000。
+
+- flags 域 (v3.17 引入) 为 64 位小端模式,其编码如下:
+  位 0:       内核字节序。 1 表示大端模式,0 表示小端模式。
+  位 1-63:    保留。
+
+- 当 image_size 为零时,引导装载程序应该试图在内核映像末尾之后尽可能多地保留空闲内存
+  供内核直接使用。对内存空间的需求量因所选定的内核特性而异, 且无实际限制。
+
+内核映像必须被放置在靠近可用系统内存起始的 2MB 对齐为基址的 text_offset 字节处,并从那里被调用。
+当前,对 Linux 来说在此基址以下的内存是无法使用的,因此强烈建议将系统内存的起始作为这个基址。
+从映像起始地址算起,最少必须为内核释放出 image_size 字节的空间。
+
+任何提供给内核的内存(甚至在 2MB 对齐的基地址之前),若未从内核中标记为保留
+(如在设备树(dtb)的 memreserve 区域),都将被认为对内核是可用。
 
 在跳转入内核前,必须符合以下状态:
 
@@ -124,8 +148,12 @@ AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内
 - 高速缓存、MMU
   MMU 必须关闭。
   指令缓存开启或关闭都可以。
-  数据缓存必须关闭且无效。
-  外部高速缓存(如果存在)必须配置并禁用。
+  已载入的内核映像的相应内存区必须被清理,以达到缓存一致性点(PoC)。
+  当存在系统缓存或其他使能缓存的一致性主控器时,通常需使用虚拟地址维护其缓存,而非 set/way 操作。
+  遵从通过虚拟地址操作维护构架缓存的系统缓存必须被配置,并可以被使能。
+  而不通过虚拟地址操作维护构架缓存的系统缓存(不推荐),必须被配置且禁用。
+
+  *译者注:对于 PoC 以及缓存相关内容,请参考 ARMv8 构架参考手册 ARM DDI 0487A
 
 - 架构计时器
   CNTFRQ 必须设定为计时器的频率,且 CNTVOFF 必须设定为对所有 CPU
@@ -141,6 +169,14 @@ AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内
   在进入内核映像的异常级中,所有构架中可写的系统寄存器必须通过软件
   在一个更高的异常级别下初始化,以防止在 未知 状态下运行。
 
+  对于拥有 GICv3 中断控制器的系统:
+  - 若当前在 EL3 :
+    ICC_SRE_EL3.Enable (位 3) 必须初始化为 0b1。
+    ICC_SRE_EL3.SRE (位 0) 必须初始化为 0b1。
+  - 若内核运行在 EL1:
+    ICC_SRE_EL2.Enable (位 3) 必须初始化为 0b1。
+    ICC_SRE_EL2.SRE (位 0) 必须初始化为 0b1。
+
 以上对于 CPU 模式、高速缓存、MMU、架构计时器、一致性、系统寄存器的
 必要条件描述适用于所有 CPU。所有 CPU 必须在同一异常级别跳入内核。
 
@@ -170,7 +206,7 @@ AArch64 内核当前没有提供自解压代码,因此如果使用了压缩内
   ARM DEN 0022A:用于 ARM 上的电源状态协调接口系统软件)中描述的
   CPU_ON 调用来将 CPU 带入内核。
 
-  *译者注:到文档翻译时,此文档已更新为 ARM DEN 0022B
+  *译者注: ARM DEN 0022A 已更新到 ARM DEN 0022C
 
   设备树必须包含一个 ‘psci’ 节点,请参考以下文档:
   Documentation/devicetree/bindings/arm/psci.txt
diff --git a/Documentation/zh_CN/arm64/legacy_instructions.txt b/Documentation/zh_CN/arm64/legacy_instructions.txt
new file mode 100644 (file)
index 0000000..68362a1
--- /dev/null
@@ -0,0 +1,72 @@
+Chinese translated version of Documentation/arm64/legacy_instructions.txt
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly.  However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help.  Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Punit Agrawal <punit.agrawal@arm.com>
+            Suzuki K. Poulose <suzuki.poulose@arm.com>
+Chinese maintainer: Fu Wei <wefu@redhat.com>
+---------------------------------------------------------------------
+Documentation/arm64/legacy_instructions.txt 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+
+本文翻译提交时的 Git 检出点为: bc465aa9d045feb0e13b4a8f32cc33c1943f62d6
+
+英文版维护者: Punit Agrawal <punit.agrawal@arm.com>
+            Suzuki K. Poulose <suzuki.poulose@arm.com>
+中文版维护者: 傅炜  Fu Wei <wefu@redhat.com>
+中文版翻译者: 傅炜  Fu Wei <wefu@redhat.com>
+中文版校译者: 傅炜  Fu Wei <wefu@redhat.com>
+
+以下为正文
+---------------------------------------------------------------------
+Linux 内核在 arm64 上的移植提供了一个基础框架,以支持构架中正在被淘汰或已废弃指令的模拟执行。
+这个基础框架的代码使用未定义指令钩子(hooks)来支持模拟。如果指令存在,它也允许在硬件中启用该指令。
+
+模拟模式可通过写 sysctl 节点(/proc/sys/abi)来控制。
+不同的执行方式及 sysctl 节点的相应值,解释如下:
+
+* Undef(未定义)
+  值: 0
+  产生未定义指令终止异常。它是那些构架中已废弃的指令,如 SWP,的默认处理方式。
+
+* Emulate(模拟)
+  值: 1
+  使用软件模拟方式。为解决软件迁移问题,这种模拟指令模式的使用是被跟踪的,并会发出速率限制警告。
+  它是那些构架中正在被淘汰的指令,如 CP15 barriers(隔离指令),的默认处理方式。
+
+* Hardware Execution(硬件执行)
+  值: 2
+  虽然标记为正在被淘汰,但一些实现可能提供硬件执行这些指令的使能/禁用操作。
+  使用硬件执行一般会有更好的性能,但将无法收集运行时对正被淘汰指令的使用统计数据。
+
+默认执行模式依赖于指令在构架中状态。正在被淘汰的指令应该以模拟(Emulate)作为默认模式,
+而已废弃的指令必须默认使用未定义(Undef)模式
+
+注意:指令模拟可能无法应对所有情况。更多详情请参考单独的指令注释。
+
+受支持的遗留指令
+-------------
+* SWP{B}
+节点: /proc/sys/abi/swp
+状态: 已废弃
+默认执行方式: Undef (0)
+
+* CP15 Barriers
+节点: /proc/sys/abi/cp15_barrier
+状态: 正被淘汰,不推荐使用
+默认执行方式: Emulate (1)
+
+* SETEND
+节点: /proc/sys/abi/setend
+状态: 正被淘汰,不推荐使用
+默认执行方式: Emulate (1)*
+注:为了使能这个特性,系统中的所有 CPU 必须在 EL0 支持混合字节序。
+如果一个新的 CPU (不支持混合字节序) 在使能这个特性后被热插入系统,
+在应用中可能会出现不可预期的结果。
index a782704c1cb59ab868de82d573291ac8780f6297..19b3a52d5d9479178604a0d73666c27a2ff17f07 100644 (file)
@@ -15,6 +15,8 @@ Documentation/arm64/memory.txt 的中文翻译
 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
 译存在问题,请联系中文版维护者。
 
+本文翻译提交时的 Git 检出点为: bc465aa9d045feb0e13b4a8f32cc33c1943f62d6
+
 英文版维护者: Catalin Marinas <catalin.marinas@arm.com>
 中文版维护者: 傅炜  Fu Wei <wefu@redhat.com>
 中文版翻译者: 傅炜  Fu Wei <wefu@redhat.com>
@@ -26,69 +28,53 @@ Documentation/arm64/memory.txt 的中文翻译
                     ===========================
 
 作者: Catalin Marinas <catalin.marinas@arm.com>
-日期: 2012 年 02 月 20 日
 
 本文档描述 AArch64 Linux 内核所使用的虚拟内存布局。此构架可以实现
 页大小为 4KB 的 4 级转换表和页大小为 64KB 的 3 级转换表。
 
-AArch64 Linux 使用页大小为 4KB 的 3 级转换表配置,对于用户和内核
-都有 39-bit (512GB) 的虚拟地址空间。对于页大小为 64KB的配置,仅
-使用 2 级转换表,但内存布局相同。
+AArch64 Linux 使用 3 级或 4 级转换表,其页大小配置为 4KB,对于用户和内核
+分别都有 39-bit (512GB) 或 48-bit (256TB) 的虚拟地址空间。
+对于页大小为 64KB的配置,仅使用 2 级转换表,有 42-bit (4TB) 的虚拟地址空间,但内存布局相同。
 
-用户地址空间的 63:39 位为 0,而内核地址空间的相应位为 1。TTBRx 的
+用户地址空间的 63:48 位为 0,而内核地址空间的相应位为 1。TTBRx 的
 选择由虚拟地址的 63 位给出。swapper_pg_dir 仅包含内核(全局)映射,
-而用户 pgd 仅包含用户(非全局)映射。swapper_pgd_dir 地址被写入
+而用户 pgd 仅包含用户(非全局)映射。swapper_pg_dir 地址被写入
 TTBR1 中,且从不写入 TTBR0。
 
 
-AArch64 Linux 在页大小为 4KB 时的内存布局:
+AArch64 Linux 在页大小为 4KB,并使用 3 级转换表时的内存布局:
 
 起始地址                   结束地址                    大小          用途
 -----------------------------------------------------------------------
 0000000000000000       0000007fffffffff         512GB          用户空间
+ffffff8000000000       ffffffffffffffff         512GB          内核空间
 
-ffffff8000000000       ffffffbbfffeffff        ~240GB          vmalloc
-
-ffffffbbffff0000       ffffffbbffffffff          64KB          [防护页]
-
-ffffffbc00000000       ffffffbdffffffff           8GB          vmemmap
-
-ffffffbe00000000       ffffffbffbbfffff          ~8GB          [防护页,未来用于 vmmemap]
 
-ffffffbffbc00000       ffffffbffbdfffff           2MB          earlyprintk 设备
+AArch64 Linux 在页大小为 4KB,并使用 4 级转换表时的内存布局:
 
-ffffffbffbe00000       ffffffbffbe0ffff          64KB          PCI I/O 空间
-
-ffffffbffbe10000       ffffffbcffffffff          ~2MB          [防护页]
-
-ffffffbffc000000       ffffffbfffffffff          64MB          模块
-
-ffffffc000000000       ffffffffffffffff         256GB          内核逻辑内存映射
+起始地址                   结束地址                    大小          用途
+-----------------------------------------------------------------------
+0000000000000000       0000ffffffffffff         256TB          用户空间
+ffff000000000000       ffffffffffffffff         256TB          内核空间
 
 
-AArch64 Linux 在页大小为 64KB 时的内存布局:
+AArch64 Linux 在页大小为 64KB,并使用 2 级转换表时的内存布局:
 
 起始地址                   结束地址                    大小          用途
 -----------------------------------------------------------------------
 0000000000000000       000003ffffffffff           4TB          用户空间
+fffffc0000000000       ffffffffffffffff           4TB          内核空间
 
-fffffc0000000000       fffffdfbfffeffff          ~2TB          vmalloc
-
-fffffdfbffff0000       fffffdfbffffffff          64KB          [防护页]
-
-fffffdfc00000000       fffffdfdffffffff           8GB          vmemmap
-
-fffffdfe00000000       fffffdfffbbfffff          ~8GB          [防护页,未来用于 vmmemap]
 
-fffffdfffbc00000       fffffdfffbdfffff           2MB          earlyprintk 设备
+AArch64 Linux 在页大小为 64KB,并使用 3 级转换表时的内存布局:
 
-fffffdfffbe00000       fffffdfffbe0ffff          64KB          PCI I/O 空间
-
-fffffdfffbe10000       fffffdfffbffffff          ~2MB          [防护页]
+起始地址                   结束地址                    大小          用途
+-----------------------------------------------------------------------
+0000000000000000       0000ffffffffffff         256TB          用户空间
+ffff000000000000       ffffffffffffffff         256TB          内核空间
 
-fffffdfffc000000       fffffdffffffffff          64MB          模块
 
-fffffe0000000000       ffffffffffffffff           2TB          内核逻辑内存映射
+更详细的内核虚拟内存布局,请参阅内核启动信息。
 
 
 4KB 页大小的转换表查找:
@@ -102,7 +88,7 @@ fffffe0000000000     ffffffffffffffff           2TB          内核逻辑内存映射
  |                 |         |         |         +-> [20:12] L3 索引
  |                 |         |         +-----------> [29:21] L2 索引
  |                 |         +---------------------> [38:30] L1 索引
- |                 +-------------------------------> [47:39] L0 索引 (未使用)
+ |                 +-------------------------------> [47:39] L0 索引
  +-------------------------------------------------> [63] TTBR0/1
 
 
@@ -115,10 +101,11 @@ fffffe0000000000  ffffffffffffffff           2TB          内核逻辑内存映射
  |                 |    |               |              v
  |                 |    |               |            [15:0]  页内偏移
  |                 |    |               +----------> [28:16] L3 索引
- |                 |    +--------------------------> [41:29] L2 索引 (仅使用 38:29 )
- |                 +-------------------------------> [47:42] L1 索引 (未使用)
+ |                 |    +--------------------------> [41:29] L2 索引
+ |                 +-------------------------------> [47:42] L1 索引
  +-------------------------------------------------> [63] TTBR0/1
 
+
 当使用 KVM 时, 管理程序(hypervisor)在 EL2 中通过相对内核虚拟地址的
 一个固定偏移来映射内核页(内核虚拟地址的高 24 位设为零):
 
index f7bbaece56498fcc75b722c042c9a1678a629b81..db335f98cad02316e89c29b72ec89aa0860a8947 100644 (file)
@@ -569,6 +569,12 @@ L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/mailbox/mailbox-altera.c
 
+ALTERA PIO DRIVER
+M:     Tien Hock Loh <thloh@altera.com>
+L:     linux-gpio@vger.kernel.org
+S:     Maintained
+F:     drivers/gpio/gpio-altera.c
+
 ALTERA TRIPLE SPEED ETHERNET DRIVER
 M:     Vince Bridgers <vbridger@opensource.altera.com>
 L:     netdev@vger.kernel.org
@@ -3285,7 +3291,9 @@ S:        Maintained
 F:     Documentation/
 X:     Documentation/ABI/
 X:     Documentation/devicetree/
-X:     Documentation/[a-z][a-z]_[A-Z][A-Z]/
+X:     Documentation/acpi
+X:     Documentation/power
+X:     Documentation/spi
 T:     git git://git.lwn.net/linux-2.6.git docs-next
 
 DOUBLETALK DRIVER
@@ -7237,6 +7245,15 @@ F:       Documentation/devicetree/
 F:     arch/*/boot/dts/
 F:     include/dt-bindings/
 
+OPEN FIRMWARE AND DEVICE TREE OVERLAYS
+M:     Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+L:     devicetree@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/dynamic-resolution-notes.txt
+F:     Documentation/devicetree/overlay-notes.txt
+F:     drivers/of/overlay.c
+F:     drivers/of/resolver.c
+
 OPENRISC ARCHITECTURE
 M:     Jonas Bonn <jonas@southpole.se>
 W:     http://openrisc.net
@@ -8113,6 +8130,12 @@ S:       Maintained
 F:     Documentation/blockdev/ramdisk.txt
 F:     drivers/block/brd.c
 
+PERSISTENT MEMORY DRIVER
+M:     Ross Zwisler <ross.zwisler@linux.intel.com>
+L:     linux-nvdimm@lists.01.org
+S:     Supported
+F:     drivers/block/pmem.c
+
 RANDOM NUMBER DRIVER
 M:     "Theodore Ts'o" <tytso@mit.edu>
 S:     Maintained
diff --git a/README b/README
index a24ec89ba4420ad38e8848422ca5f69baa5ffe1c..69c68fb4a10908a4f8d1986c6fdad2ec7b3c397b 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
-        Linux kernel release 3.x <http://kernel.org/>
+        Linux kernel release 4.x <http://kernel.org/>
 
-These are the release notes for Linux version 3.  Read them carefully,
+These are the release notes for Linux version 4.  Read them carefully,
 as they tell you what this is all about, explain how to install the
 kernel, and what to do if something goes wrong. 
 
@@ -62,11 +62,7 @@ INSTALLING the kernel source:
    directory where you have permissions (eg. your home directory) and
    unpack it:
 
-     gzip -cd linux-3.X.tar.gz | tar xvf -
-
-   or
-
-     bzip2 -dc linux-3.X.tar.bz2 | tar xvf -
+     xz -cd linux-4.X.tar.xz | tar xvf -
 
    Replace "X" with the version number of the latest kernel.
 
@@ -75,16 +71,12 @@ INSTALLING the kernel source:
    files.  They should match the library, and not get messed up by
    whatever the kernel-du-jour happens to be.
 
- - You can also upgrade between 3.x releases by patching.  Patches are
-   distributed in the traditional gzip and the newer bzip2 format.  To
-   install by patching, get all the newer patch files, enter the
-   top level directory of the kernel source (linux-3.X) and execute:
-
-     gzip -cd ../patch-3.x.gz | patch -p1
-
-   or
+ - You can also upgrade between 4.x releases by patching.  Patches are
+   distributed in the xz format.  To install by patching, get all the
+   newer patch files, enter the top level directory of the kernel source
+   (linux-4.X) and execute:
 
-     bzip2 -dc ../patch-3.x.bz2 | patch -p1
+     xz -cd ../patch-4.x.xz | patch -p1
 
    Replace "x" for all versions bigger than the version "X" of your current
    source tree, _in_order_, and you should be ok.  You may want to remove
@@ -92,13 +84,13 @@ INSTALLING the kernel source:
    that there are no failed patches (some-file-name# or some-file-name.rej).
    If there are, either you or I have made a mistake.
 
-   Unlike patches for the 3.x kernels, patches for the 3.x.y kernels
+   Unlike patches for the 4.x kernels, patches for the 4.x.y kernels
    (also known as the -stable kernels) are not incremental but instead apply
-   directly to the base 3.x kernel.  For example, if your base kernel is 3.0
-   and you want to apply the 3.0.3 patch, you must not first apply the 3.0.1
-   and 3.0.2 patches. Similarly, if you are running kernel version 3.0.2 and
-   want to jump to 3.0.3, you must first reverse the 3.0.2 patch (that is,
-   patch -R) _before_ applying the 3.0.3 patch. You can read more on this in
+   directly to the base 4.x kernel.  For example, if your base kernel is 4.0
+   and you want to apply the 4.0.3 patch, you must not first apply the 4.0.1
+   and 4.0.2 patches. Similarly, if you are running kernel version 4.0.2 and
+   want to jump to 4.0.3, you must first reverse the 4.0.2 patch (that is,
+   patch -R) _before_ applying the 4.0.3 patch. You can read more on this in
    Documentation/applying-patches.txt
 
    Alternatively, the script patch-kernel can be used to automate this
@@ -120,7 +112,7 @@ INSTALLING the kernel source:
 
 SOFTWARE REQUIREMENTS
 
-   Compiling and running the 3.x kernels requires up-to-date
+   Compiling and running the 4.x kernels requires up-to-date
    versions of various software packages.  Consult
    Documentation/Changes for the minimum version numbers required
    and how to get updates for these packages.  Beware that using
@@ -137,12 +129,12 @@ BUILD directory for the kernel:
    place for the output files (including .config).
    Example:
 
-     kernel source code: /usr/src/linux-3.X
+     kernel source code: /usr/src/linux-4.X
      build directory:    /home/name/build/kernel
 
    To configure and build the kernel, use:
 
-     cd /usr/src/linux-3.X
+     cd /usr/src/linux-4.X
      make O=/home/name/build/kernel menuconfig
      make O=/home/name/build/kernel
      sudo make O=/home/name/build/kernel modules_install install
index 2198837c256fd63b33d33817fba12e762c88abfa..f5016656494f62b321c881e54f4235d0eeac7bf9 100644 (file)
@@ -1288,6 +1288,7 @@ config CPU_LOONGSON3
        select CPU_SUPPORTS_HUGEPAGES
        select WEAK_ORDERING
        select WEAK_REORDERING_BEYOND_LLSC
+       select ARCH_REQUIRE_GPIOLIB
        help
                The Loongson 3 processor implements the MIPS64R2 instruction
                set with many extensions.
index e51aad9a94b15e9199607df4a988bf236573f9a3..0cbc9863c7c8a75690eba7c827cf69e3094bb581 100644 (file)
@@ -171,6 +171,7 @@ CONFIG_SERIAL_8250_FOURPORT=y
 CONFIG_LEGACY_PTY_COUNT=16
 CONFIG_HW_RANDOM=y
 CONFIG_RTC=y
+CONFIG_GPIO_LOONGSON=y
 CONFIG_THERMAL=y
 CONFIG_MEDIA_SUPPORT=m
 CONFIG_VIDEO_DEV=m
index 7eabcd2031ea884bba3de32fc9cf944d61326ccc..c8442997477b909c5cbc2a3bb229a4469565c8aa 100644 (file)
@@ -243,6 +243,7 @@ CONFIG_HW_RANDOM=y
 CONFIG_RAW_DRIVER=m
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_PIIX4=y
+CONFIG_GPIO_LOONGSON=y
 CONFIG_SENSORS_LM75=m
 CONFIG_SENSORS_LM93=m
 CONFIG_SENSORS_W83627HF=m
index 211a7b7138fe320c2cebde9a1cca9656a6b112f4..b3b216904a9a8a66b9870dbeb97a826f86f0fed8 100644 (file)
@@ -1,8 +1,9 @@
 /*
- * STLS2F GPIO Support
+ * Loongson GPIO Support
  *
  * Copyright (c) 2008  Richard Liu, STMicroelectronics <richard.liu@st.com>
  * Copyright (c) 2008-2010  Arnaud Patard <apatard@mandriva.com>
+ * Copyright (c) 2014  Huacai Chen <chenhc@lemote.com>
  *
  * 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
  * (at your option) any later version.
  */
 
-#ifndef __STLS2F_GPIO_H
-#define __STLS2F_GPIO_H
+#ifndef __LOONGSON_GPIO_H
+#define __LOONGSON_GPIO_H
 
 #include <asm-generic/gpio.h>
 
-extern void gpio_set_value(unsigned gpio, int value);
-extern int gpio_get_value(unsigned gpio);
-extern int gpio_cansleep(unsigned gpio);
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
 
 /* The chip can do interrupt
  * but it has not been tested and doc not clear
@@ -32,4 +33,4 @@ static inline int irq_to_gpio(int gpio)
        return -EINVAL;
 }
 
-#endif                         /* __STLS2F_GPIO_H */
+#endif /* __LOONGSON_GPIO_H */
index d87e03330b29ae0dd5e27e9e84536dcc96af6f5c..e70c33fdb88153ac6bfdf12a4f632d3b3a26ccb9 100644 (file)
@@ -4,7 +4,6 @@
 
 obj-y += setup.o init.o cmdline.o env.o time.o reset.o irq.o \
     bonito-irq.o mem.o machtype.o platform.o
-obj-$(CONFIG_GPIOLIB) += gpio.o
 obj-$(CONFIG_PCI) += pci.o
 
 #
diff --git a/arch/mips/loongson/common/gpio.c b/arch/mips/loongson/common/gpio.c
deleted file mode 100644 (file)
index 29dbaa2..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- *  STLS2F GPIO Support
- *
- *  Copyright (c) 2008 Richard Liu,  STMicroelectronics         <richard.liu@st.com>
- *  Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com>
- *
- *  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/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/err.h>
-#include <asm/types.h>
-#include <loongson.h>
-#include <linux/gpio.h>
-
-#define STLS2F_N_GPIO          4
-#define STLS2F_GPIO_IN_OFFSET  16
-
-static DEFINE_SPINLOCK(gpio_lock);
-
-int gpio_get_value(unsigned gpio)
-{
-       u32 val;
-       u32 mask;
-
-       if (gpio >= STLS2F_N_GPIO)
-               return __gpio_get_value(gpio);
-
-       mask = 1 << (gpio + STLS2F_GPIO_IN_OFFSET);
-       spin_lock(&gpio_lock);
-       val = LOONGSON_GPIODATA;
-       spin_unlock(&gpio_lock);
-
-       return (val & mask) != 0;
-}
-EXPORT_SYMBOL(gpio_get_value);
-
-void gpio_set_value(unsigned gpio, int state)
-{
-       u32 val;
-       u32 mask;
-
-       if (gpio >= STLS2F_N_GPIO) {
-               __gpio_set_value(gpio, state);
-               return ;
-       }
-
-       mask = 1 << gpio;
-
-       spin_lock(&gpio_lock);
-       val = LOONGSON_GPIODATA;
-       if (state)
-               val |= mask;
-       else
-               val &= (~mask);
-       LOONGSON_GPIODATA = val;
-       spin_unlock(&gpio_lock);
-}
-EXPORT_SYMBOL(gpio_set_value);
-
-int gpio_cansleep(unsigned gpio)
-{
-       if (gpio < STLS2F_N_GPIO)
-               return 0;
-       else
-               return __gpio_cansleep(gpio);
-}
-EXPORT_SYMBOL(gpio_cansleep);
-
-static int ls2f_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
-{
-       u32 temp;
-       u32 mask;
-
-       if (gpio >= STLS2F_N_GPIO)
-               return -EINVAL;
-
-       spin_lock(&gpio_lock);
-       mask = 1 << gpio;
-       temp = LOONGSON_GPIOIE;
-       temp |= mask;
-       LOONGSON_GPIOIE = temp;
-       spin_unlock(&gpio_lock);
-
-       return 0;
-}
-
-static int ls2f_gpio_direction_output(struct gpio_chip *chip,
-               unsigned gpio, int level)
-{
-       u32 temp;
-       u32 mask;
-
-       if (gpio >= STLS2F_N_GPIO)
-               return -EINVAL;
-
-       gpio_set_value(gpio, level);
-       spin_lock(&gpio_lock);
-       mask = 1 << gpio;
-       temp = LOONGSON_GPIOIE;
-       temp &= (~mask);
-       LOONGSON_GPIOIE = temp;
-       spin_unlock(&gpio_lock);
-
-       return 0;
-}
-
-static int ls2f_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
-{
-       return gpio_get_value(gpio);
-}
-
-static void ls2f_gpio_set_value(struct gpio_chip *chip,
-               unsigned gpio, int value)
-{
-       gpio_set_value(gpio, value);
-}
-
-static struct gpio_chip ls2f_chip = {
-       .label                  = "ls2f",
-       .direction_input        = ls2f_gpio_direction_input,
-       .get                    = ls2f_gpio_get_value,
-       .direction_output       = ls2f_gpio_direction_output,
-       .set                    = ls2f_gpio_set_value,
-       .base                   = 0,
-       .ngpio                  = STLS2F_N_GPIO,
-};
-
-static int __init ls2f_gpio_setup(void)
-{
-       return gpiochip_add(&ls2f_chip);
-}
-arch_initcall(ls2f_gpio_setup);
index d43e7e1c784b308ee168a988536aa00e514e949c..6049d587599ed5b39d3e5fb9b32c27b8cc859a37 100644 (file)
@@ -178,7 +178,7 @@ config SBUS
 
 config NEED_DMA_MAP_STATE
        def_bool y
-       depends on X86_64 || INTEL_IOMMU || DMA_API_DEBUG
+       depends on X86_64 || INTEL_IOMMU || DMA_API_DEBUG || SWIOTLB
 
 config NEED_SG_DMA_LENGTH
        def_bool y
@@ -1421,6 +1421,16 @@ config ILLEGAL_POINTER_VALUE
 
 source "mm/Kconfig"
 
+config X86_PMEM_LEGACY
+       bool "Support non-standard NVDIMMs and ADR protected memory"
+       help
+         Treat memory marked using the non-standard e820 type of 12 as used
+         by the Intel Sandy Bridge-EP reference BIOS as protected memory.
+         The kernel will offer these regions to the 'pmem' driver so
+         they can be used for persistent storage.
+
+         Say Y if unsure.
+
 config HIGHPTE
        bool "Allocate 3rd-level pagetables from highmem"
        depends on HIGHMEM
index d993e33f523654cf5a985481d62e60810830f66b..960a8a9dc4abafa0326f3822aee80c3c9764af93 100644 (file)
 #define E820_NVS       4
 #define E820_UNUSABLE  5
 
+/*
+ * This is a non-standardized way to represent ADR or NVDIMM regions that
+ * persist over a reboot.  The kernel will ignore their special capabilities
+ * unless the CONFIG_X86_PMEM_LEGACY=y option is set.
+ *
+ * ( Note that older platforms also used 6 for the same type of memory,
+ *   but newer versions switched to 12 as 6 was assigned differently.  Some
+ *   time they will learn... )
+ */
+#define E820_PRAM      12
 
 /*
  * reserved RAM used by kernel itself
index c887cd944f0c18e849fda278ec50dbee6b731919..9bcd0b56ca1775aa82a9dee3a47614461bb7a881 100644 (file)
@@ -95,6 +95,7 @@ obj-$(CONFIG_KVM_GUEST)               += kvm.o kvmclock.o
 obj-$(CONFIG_PARAVIRT)         += paravirt.o paravirt_patch_$(BITS).o
 obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= paravirt-spinlocks.o
 obj-$(CONFIG_PARAVIRT_CLOCK)   += pvclock.o
+obj-$(CONFIG_X86_PMEM_LEGACY)  += pmem.o
 
 obj-$(CONFIG_PCSPKR_PLATFORM)  += pcspeaker.o
 
index 329f0356ad4a0a8b969ae4d61d9b29a10d3b9522..6ac5cb7a9e14839dcd0b622a91f0f0939133c81e 100644 (file)
@@ -65,15 +65,15 @@ struct event_constraint {
 /*
  * struct hw_perf_event.flags flags
  */
-#define PERF_X86_EVENT_PEBS_LDLAT      0x1 /* ld+ldlat data address sampling */
-#define PERF_X86_EVENT_PEBS_ST         0x2 /* st data address sampling */
-#define PERF_X86_EVENT_PEBS_ST_HSW     0x4 /* haswell style datala, store */
-#define PERF_X86_EVENT_COMMITTED       0x8 /* event passed commit_txn */
-#define PERF_X86_EVENT_PEBS_LD_HSW     0x10 /* haswell style datala, load */
-#define PERF_X86_EVENT_PEBS_NA_HSW     0x20 /* haswell style datala, unknown */
-#define PERF_X86_EVENT_EXCL            0x40 /* HT exclusivity on counter */
-#define PERF_X86_EVENT_DYNAMIC         0x80 /* dynamic alloc'd constraint */
-#define PERF_X86_EVENT_RDPMC_ALLOWED   0x40 /* grant rdpmc permission */
+#define PERF_X86_EVENT_PEBS_LDLAT      0x0001 /* ld+ldlat data address sampling */
+#define PERF_X86_EVENT_PEBS_ST         0x0002 /* st data address sampling */
+#define PERF_X86_EVENT_PEBS_ST_HSW     0x0004 /* haswell style datala, store */
+#define PERF_X86_EVENT_COMMITTED       0x0008 /* event passed commit_txn */
+#define PERF_X86_EVENT_PEBS_LD_HSW     0x0010 /* haswell style datala, load */
+#define PERF_X86_EVENT_PEBS_NA_HSW     0x0020 /* haswell style datala, unknown */
+#define PERF_X86_EVENT_EXCL            0x0040 /* HT exclusivity on counter */
+#define PERF_X86_EVENT_DYNAMIC         0x0080 /* dynamic alloc'd constraint */
+#define PERF_X86_EVENT_RDPMC_ALLOWED   0x0100 /* grant rdpmc permission */
 
 
 struct amd_nb {
index 9da2400c2ec37b7ea164e7a17f3bea68b172cbe6..219d3fb423a17a1bb30e99d565ab01de552f49e7 100644 (file)
@@ -3275,7 +3275,7 @@ __init int intel_pmu_init(void)
                hw_cache_extra_regs[C(NODE)][C(OP_WRITE)][C(RESULT_ACCESS)] = HSW_DEMAND_WRITE|
                                                                              BDW_L3_MISS_LOCAL|HSW_SNOOP_DRAM;
 
-               intel_pmu_lbr_init_snb();
+               intel_pmu_lbr_init_hsw();
 
                x86_pmu.event_constraints = intel_bdw_event_constraints;
                x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints;
index ca69ea56c712b590876ff9c5289a839dc91158b7..813f75d71175e3a117f13ec53efe6856a0508bec 100644 (file)
@@ -558,6 +558,8 @@ struct event_constraint intel_core2_pebs_event_constraints[] = {
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1),    /* MEM_LOAD_RETIRED.* */
+       /* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */
+       INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x01),
        EVENT_CONSTRAINT_END
 };
 
@@ -565,6 +567,8 @@ struct event_constraint intel_atom_pebs_event_constraints[] = {
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1),    /* MEM_LOAD_RETIRED.* */
+       /* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */
+       INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x01),
        EVENT_CONSTRAINT_END
 };
 
@@ -588,6 +592,8 @@ struct event_constraint intel_nehalem_pebs_event_constraints[] = {
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf),    /* MEM_LOAD_RETIRED.* */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf),    /* FP_ASSIST.* */
+       /* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */
+       INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x0f),
        EVENT_CONSTRAINT_END
 };
 
@@ -603,6 +609,8 @@ struct event_constraint intel_westmere_pebs_event_constraints[] = {
        INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf),    /* MEM_LOAD_RETIRED.* */
        INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf),    /* FP_ASSIST.* */
+       /* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */
+       INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x0f),
        EVENT_CONSTRAINT_END
 };
 
index f2770641c0fd212c5e585532d51fc1859ab94540..ffe666c2c6b58657b5895948a2e7d69f95223521 100644 (file)
@@ -988,39 +988,36 @@ static int pt_event_add(struct perf_event *event, int mode)
        int ret = -EBUSY;
 
        if (pt->handle.event)
-               goto out;
+               goto fail;
 
        buf = perf_aux_output_begin(&pt->handle, event);
-       if (!buf) {
-               ret = -EINVAL;
-               goto out;
-       }
+       ret = -EINVAL;
+       if (!buf)
+               goto fail_stop;
 
        pt_buffer_reset_offsets(buf, pt->handle.head);
        if (!buf->snapshot) {
                ret = pt_buffer_reset_markers(buf, &pt->handle);
-               if (ret) {
-                       perf_aux_output_end(&pt->handle, 0, true);
-                       goto out;
-               }
+               if (ret)
+                       goto fail_end_stop;
        }
 
        if (mode & PERF_EF_START) {
                pt_event_start(event, 0);
-               if (hwc->state == PERF_HES_STOPPED) {
-                       pt_event_del(event, 0);
-                       ret = -EBUSY;
-               }
+               ret = -EBUSY;
+               if (hwc->state == PERF_HES_STOPPED)
+                       goto fail_end_stop;
        } else {
                hwc->state = PERF_HES_STOPPED;
        }
 
-       ret = 0;
-out:
-
-       if (ret)
-               hwc->state = PERF_HES_STOPPED;
+       return 0;
 
+fail_end_stop:
+       perf_aux_output_end(&pt->handle, 0, true);
+fail_stop:
+       hwc->state = PERF_HES_STOPPED;
+fail:
        return ret;
 }
 
index c4bb8b8e5017403b25847a97ccce42c96bba3837..999289b94025623415693df205054e754e9a7d4b 100644 (file)
 #define RAPL_IDX_PP1_NRG_STAT  3       /* gpu */
 #define INTEL_RAPL_PP1         0x4     /* pseudo-encoding */
 
+#define NR_RAPL_DOMAINS         0x4
+static const char *rapl_domain_names[NR_RAPL_DOMAINS] __initconst = {
+       "pp0-core",
+       "package",
+       "dram",
+       "pp1-gpu",
+};
+
 /* Clients have PP0, PKG */
 #define RAPL_IDX_CLN   (1<<RAPL_IDX_PP0_NRG_STAT|\
                         1<<RAPL_IDX_PKG_NRG_STAT|\
@@ -112,7 +120,6 @@ static struct perf_pmu_events_attr event_attr_##v = {                       \
 
 struct rapl_pmu {
        spinlock_t       lock;
-       int              hw_unit;  /* 1/2^hw_unit Joule */
        int              n_active; /* number of active events */
        struct list_head active_list;
        struct pmu       *pmu; /* pointer to rapl_pmu_class */
@@ -120,6 +127,7 @@ struct rapl_pmu {
        struct hrtimer   hrtimer;
 };
 
+static int rapl_hw_unit[NR_RAPL_DOMAINS] __read_mostly;  /* 1/2^hw_unit Joule */
 static struct pmu rapl_pmu_class;
 static cpumask_t rapl_cpu_mask;
 static int rapl_cntr_mask;
@@ -127,6 +135,7 @@ static int rapl_cntr_mask;
 static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu);
 static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu_to_free);
 
+static struct x86_pmu_quirk *rapl_quirks;
 static inline u64 rapl_read_counter(struct perf_event *event)
 {
        u64 raw;
@@ -134,15 +143,28 @@ static inline u64 rapl_read_counter(struct perf_event *event)
        return raw;
 }
 
-static inline u64 rapl_scale(u64 v)
+#define rapl_add_quirk(func_)                                          \
+do {                                                                   \
+       static struct x86_pmu_quirk __quirk __initdata = {              \
+               .func = func_,                                          \
+       };                                                              \
+       __quirk.next = rapl_quirks;                                     \
+       rapl_quirks = &__quirk;                                         \
+} while (0)
+
+static inline u64 rapl_scale(u64 v, int cfg)
 {
+       if (cfg > NR_RAPL_DOMAINS) {
+               pr_warn("invalid domain %d, failed to scale data\n", cfg);
+               return v;
+       }
        /*
         * scale delta to smallest unit (1/2^32)
         * users must then scale back: count * 1/(1e9*2^32) to get Joules
         * or use ldexp(count, -32).
         * Watts = Joules/Time delta
         */
-       return v << (32 - __this_cpu_read(rapl_pmu)->hw_unit);
+       return v << (32 - rapl_hw_unit[cfg - 1]);
 }
 
 static u64 rapl_event_update(struct perf_event *event)
@@ -173,7 +195,7 @@ again:
        delta = (new_raw_count << shift) - (prev_raw_count << shift);
        delta >>= shift;
 
-       sdelta = rapl_scale(delta);
+       sdelta = rapl_scale(delta, event->hw.config);
 
        local64_add(sdelta, &event->count);
 
@@ -546,12 +568,22 @@ static void rapl_cpu_init(int cpu)
        cpumask_set_cpu(cpu, &rapl_cpu_mask);
 }
 
+static __init void rapl_hsw_server_quirk(void)
+{
+       /*
+        * DRAM domain on HSW server has fixed energy unit which can be
+        * different than the unit from power unit MSR.
+        * "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, V2
+        * of 2. Datasheet, September 2014, Reference Number: 330784-001 "
+        */
+       rapl_hw_unit[RAPL_IDX_RAM_NRG_STAT] = 16;
+}
+
 static int rapl_cpu_prepare(int cpu)
 {
        struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
        int phys_id = topology_physical_package_id(cpu);
        u64 ms;
-       u64 msr_rapl_power_unit_bits;
 
        if (pmu)
                return 0;
@@ -559,24 +591,13 @@ static int rapl_cpu_prepare(int cpu)
        if (phys_id < 0)
                return -1;
 
-       /* protect rdmsrl() to handle virtualization */
-       if (rdmsrl_safe(MSR_RAPL_POWER_UNIT, &msr_rapl_power_unit_bits))
-               return -1;
-
        pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
        if (!pmu)
                return -1;
-
        spin_lock_init(&pmu->lock);
 
        INIT_LIST_HEAD(&pmu->active_list);
 
-       /*
-        * grab power unit as: 1/2^unit Joules
-        *
-        * we cache in local PMU instance
-        */
-       pmu->hw_unit = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
        pmu->pmu = &rapl_pmu_class;
 
        /*
@@ -586,8 +607,8 @@ static int rapl_cpu_prepare(int cpu)
         * divide interval by 2 to avoid lockstep (2 * 100)
         * if hw unit is 32, then we use 2 ms 1/200/2
         */
-       if (pmu->hw_unit < 32)
-               ms = (1000 / (2 * 100)) * (1ULL << (32 - pmu->hw_unit - 1));
+       if (rapl_hw_unit[0] < 32)
+               ms = (1000 / (2 * 100)) * (1ULL << (32 - rapl_hw_unit[0] - 1));
        else
                ms = 2;
 
@@ -655,6 +676,20 @@ static int rapl_cpu_notifier(struct notifier_block *self,
        return NOTIFY_OK;
 }
 
+static int rapl_check_hw_unit(void)
+{
+       u64 msr_rapl_power_unit_bits;
+       int i;
+
+       /* protect rdmsrl() to handle virtualization */
+       if (rdmsrl_safe(MSR_RAPL_POWER_UNIT, &msr_rapl_power_unit_bits))
+               return -1;
+       for (i = 0; i < NR_RAPL_DOMAINS; i++)
+               rapl_hw_unit[i] = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
+
+       return 0;
+}
+
 static const struct x86_cpu_id rapl_cpu_match[] = {
        [0] = { .vendor = X86_VENDOR_INTEL, .family = 6 },
        [1] = {},
@@ -664,6 +699,8 @@ static int __init rapl_pmu_init(void)
 {
        struct rapl_pmu *pmu;
        int cpu, ret;
+       struct x86_pmu_quirk *quirk;
+       int i;
 
        /*
         * check for Intel processor family 6
@@ -678,6 +715,11 @@ static int __init rapl_pmu_init(void)
                rapl_cntr_mask = RAPL_IDX_CLN;
                rapl_pmu_events_group.attrs = rapl_events_cln_attr;
                break;
+       case 63: /* Haswell-Server */
+               rapl_add_quirk(rapl_hsw_server_quirk);
+               rapl_cntr_mask = RAPL_IDX_SRV;
+               rapl_pmu_events_group.attrs = rapl_events_srv_attr;
+               break;
        case 60: /* Haswell */
        case 69: /* Haswell-Celeron */
                rapl_cntr_mask = RAPL_IDX_HSW;
@@ -693,7 +735,13 @@ static int __init rapl_pmu_init(void)
                /* unsupported */
                return 0;
        }
+       ret = rapl_check_hw_unit();
+       if (ret)
+               return ret;
 
+       /* run cpu model quirks */
+       for (quirk = rapl_quirks; quirk; quirk = quirk->next)
+               quirk->func();
        cpu_notifier_register_begin();
 
        for_each_online_cpu(cpu) {
@@ -714,14 +762,18 @@ static int __init rapl_pmu_init(void)
 
        pmu = __this_cpu_read(rapl_pmu);
 
-       pr_info("RAPL PMU detected, hw unit 2^-%d Joules,"
+       pr_info("RAPL PMU detected,"
                " API unit is 2^-32 Joules,"
                " %d fixed counters"
                " %llu ms ovfl timer\n",
-               pmu->hw_unit,
                hweight32(rapl_cntr_mask),
                ktime_to_ms(pmu->timer_interval));
-
+       for (i = 0; i < NR_RAPL_DOMAINS; i++) {
+               if (rapl_cntr_mask & (1 << i)) {
+                       pr_info("hw unit of domain %s 2^-%d Joules\n",
+                               rapl_domain_names[i], rapl_hw_unit[i]);
+               }
+       }
 out:
        cpu_notifier_register_done();
 
index 7d46bb2603346b41dfb924eee28975cd76e704f3..e2ce85db228303b61d1afd80e9c0656e2c343423 100644 (file)
@@ -149,6 +149,9 @@ static void __init e820_print_type(u32 type)
        case E820_UNUSABLE:
                printk(KERN_CONT "unusable");
                break;
+       case E820_PRAM:
+               printk(KERN_CONT "persistent (type %u)", type);
+               break;
        default:
                printk(KERN_CONT "type %u", type);
                break;
@@ -343,7 +346,7 @@ int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
                 * continue building up new bios map based on this
                 * information
                 */
-               if (current_type != last_type {
+               if (current_type != last_type || current_type == E820_PRAM) {
                        if (last_type != 0)      {
                                new_bios[new_bios_entry].size =
                                        change_point[chgidx]->addr - last_addr;
@@ -688,6 +691,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn)
                        register_nosave_region(pfn, PFN_UP(ei->addr));
 
                pfn = PFN_DOWN(ei->addr + ei->size);
+
                if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
                        register_nosave_region(PFN_UP(ei->addr), pfn);
 
@@ -748,7 +752,7 @@ u64 __init early_reserve_e820(u64 size, u64 align)
 /*
  * Find the highest page frame number we have available
  */
-static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
+static unsigned long __init e820_end_pfn(unsigned long limit_pfn)
 {
        int i;
        unsigned long last_pfn = 0;
@@ -759,7 +763,11 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
                unsigned long start_pfn;
                unsigned long end_pfn;
 
-               if (ei->type != type)
+               /*
+                * Persistent memory is accounted as ram for purposes of
+                * establishing max_pfn and mem_map.
+                */
+               if (ei->type != E820_RAM && ei->type != E820_PRAM)
                        continue;
 
                start_pfn = ei->addr >> PAGE_SHIFT;
@@ -784,12 +792,12 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
 }
 unsigned long __init e820_end_of_ram_pfn(void)
 {
-       return e820_end_pfn(MAX_ARCH_PFN, E820_RAM);
+       return e820_end_pfn(MAX_ARCH_PFN);
 }
 
 unsigned long __init e820_end_of_low_ram_pfn(void)
 {
-       return e820_end_pfn(1UL<<(32 - PAGE_SHIFT), E820_RAM);
+       return e820_end_pfn(1UL << (32-PAGE_SHIFT));
 }
 
 static void early_panic(char *msg)
@@ -866,6 +874,9 @@ static int __init parse_memmap_one(char *p)
        } else if (*p == '$') {
                start_at = memparse(p+1, &p);
                e820_add_region(start_at, mem_size, E820_RESERVED);
+       } else if (*p == '!') {
+               start_at = memparse(p+1, &p);
+               e820_add_region(start_at, mem_size, E820_PRAM);
        } else
                e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);
 
@@ -907,6 +918,7 @@ static inline const char *e820_type_to_string(int e820_type)
        case E820_ACPI: return "ACPI Tables";
        case E820_NVS:  return "ACPI Non-volatile Storage";
        case E820_UNUSABLE:     return "Unusable memory";
+       case E820_PRAM: return "Persistent RAM";
        default:        return "reserved";
        }
 }
@@ -940,7 +952,9 @@ void __init e820_reserve_resources(void)
                 * pci device BAR resource and insert them later in
                 * pcibios_resource_survey()
                 */
-               if (e820.map[i].type != E820_RESERVED || res->start < (1ULL<<20)) {
+               if (((e820.map[i].type != E820_RESERVED) &&
+                    (e820.map[i].type != E820_PRAM)) ||
+                    res->start < (1ULL<<20)) {
                        res->flags |= IORESOURCE_BUSY;
                        insert_resource(&iomem_resource, res);
                }
index 367f39d35e9cb98300fa368d3689d2ce143e71a6..009183276bb738fbd28805256ccfaa04e063c364 100644 (file)
@@ -341,7 +341,7 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
                unsigned int pos, unsigned int count,
                void *kbuf, void __user *ubuf)
 {
-       struct xsave_struct *xsave = &target->thread.fpu.state->xsave;
+       struct xsave_struct *xsave;
        int ret;
 
        if (!cpu_has_xsave)
@@ -351,6 +351,8 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
        if (ret)
                return ret;
 
+       xsave = &target->thread.fpu.state->xsave;
+
        /*
         * Copy the 48bytes defined by the software first into the xstate
         * memory layout in the thread struct, so that we can copy the entire
@@ -369,7 +371,7 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
                  unsigned int pos, unsigned int count,
                  const void *kbuf, const void __user *ubuf)
 {
-       struct xsave_struct *xsave = &target->thread.fpu.state->xsave;
+       struct xsave_struct *xsave;
        int ret;
 
        if (!cpu_has_xsave)
@@ -379,6 +381,8 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
        if (ret)
                return ret;
 
+       xsave = &target->thread.fpu.state->xsave;
+
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
        /*
         * mxcsr reserved bits must be masked to zero for security reasons.
diff --git a/arch/x86/kernel/pmem.c b/arch/x86/kernel/pmem.c
new file mode 100644 (file)
index 0000000..3420c87
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, Christoph Hellwig.
+ */
+#include <linux/memblock.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/e820.h>
+#include <asm/page_types.h>
+#include <asm/setup.h>
+
+static __init void register_pmem_device(struct resource *res)
+{
+       struct platform_device *pdev;
+       int error;
+
+       pdev = platform_device_alloc("pmem", PLATFORM_DEVID_AUTO);
+       if (!pdev)
+               return;
+
+       error = platform_device_add_resources(pdev, res, 1);
+       if (error)
+               goto out_put_pdev;
+
+       error = platform_device_add(pdev);
+       if (error)
+               goto out_put_pdev;
+       return;
+
+out_put_pdev:
+       dev_warn(&pdev->dev, "failed to add 'pmem' (persistent memory) device!\n");
+       platform_device_put(pdev);
+}
+
+static __init int register_pmem_devices(void)
+{
+       int i;
+
+       for (i = 0; i < e820.nr_map; i++) {
+               struct e820entry *ei = &e820.map[i];
+
+               if (ei->type == E820_PRAM) {
+                       struct resource res = {
+                               .flags  = IORESOURCE_MEM,
+                               .start  = ei->addr,
+                               .end    = ei->addr + ei->size - 1,
+                       };
+                       register_pmem_device(&res);
+               }
+       }
+
+       return 0;
+}
+device_initcall(register_pmem_devices);
index f9804080ccb36c3b95345a0c53cfe2cd392cfcd1..1ea14fd53933bae96dc5f8e3fec99575a3c3c454 100644 (file)
@@ -616,7 +616,8 @@ setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
 static void
 handle_signal(struct ksignal *ksig, struct pt_regs *regs)
 {
-       bool failed;
+       bool stepping, failed;
+
        /* Are we from a system call? */
        if (syscall_get_nr(current, regs) >= 0) {
                /* If so, check system call restarting.. */
@@ -640,12 +641,13 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
        }
 
        /*
-        * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
-        * flag so that register information in the sigcontext is correct.
+        * If TF is set due to a debugger (TIF_FORCED_TF), clear TF now
+        * so that register information in the sigcontext is correct and
+        * then notify the tracer before entering the signal handler.
         */
-       if (unlikely(regs->flags & X86_EFLAGS_TF) &&
-           likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
-               regs->flags &= ~X86_EFLAGS_TF;
+       stepping = test_thread_flag(TIF_SINGLESTEP);
+       if (stepping)
+               user_disable_single_step(current);
 
        failed = (setup_rt_frame(ksig, regs) < 0);
        if (!failed) {
@@ -656,10 +658,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
                 * it might disable possible debug exception from the
                 * signal handler.
                 *
-                * Clear TF when entering the signal handler, but
-                * notify any tracer that was single-stepping it.
-                * The tracer may want to single-step inside the
-                * handler too.
+                * Clear TF for the case when it wasn't set by debugger to
+                * avoid the recursive send_sigtrap() in SIGTRAP handler.
                 */
                regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF);
                /*
@@ -668,7 +668,7 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
                if (used_math())
                        fpu_reset_state(current);
        }
-       signal_setup_done(failed, ksig, test_thread_flag(TIF_SINGLESTEP));
+       signal_setup_done(failed, ksig, stepping);
 }
 
 #ifdef CONFIG_X86_32
index 1b8094d4d7af70f38e38bd9d3f5736a55f31dd82..eb1fed5bd516ffac33c850eed47fad402250c686 100644 (file)
@@ -404,6 +404,17 @@ config BLK_DEV_RAM_DAX
          and will prevent RAM block device backing store memory from being
          allocated from highmem (only a problem for highmem systems).
 
+config BLK_DEV_PMEM
+       tristate "Persistent memory block device support"
+       help
+         Saying Y here will allow you to use a contiguous range of reserved
+         memory as one or more persistent block devices.
+
+         To compile this driver as a module, choose M here: the module will be
+         called 'pmem'.
+
+         If unsure, say N.
+
 config CDROM_PKTCDVD
        tristate "Packet writing on CD/DVD media"
        depends on !UML
index 02b688d1438d95b6ad4f64cac713dacf77023c40..9cc6c18a1c7e2af444620b2d3257b100e9bc6f0f 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_PS3_VRAM)                += ps3vram.o
 obj-$(CONFIG_ATARI_FLOPPY)     += ataflop.o
 obj-$(CONFIG_AMIGA_Z2RAM)      += z2ram.o
 obj-$(CONFIG_BLK_DEV_RAM)      += brd.o
+obj-$(CONFIG_BLK_DEV_PMEM)     += pmem.o
 obj-$(CONFIG_BLK_DEV_LOOP)     += loop.o
 obj-$(CONFIG_BLK_CPQ_DA)       += cpqarray.o
 obj-$(CONFIG_BLK_CPQ_CISS_DA)  += cciss.o
diff --git a/drivers/block/pmem.c b/drivers/block/pmem.c
new file mode 100644 (file)
index 0000000..eabf4a8
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Persistent Memory Driver
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ * Copyright (c) 2015, Christoph Hellwig <hch@lst.de>.
+ * Copyright (c) 2015, Boaz Harrosh <boaz@plexistor.com>.
+ *
+ * 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 <asm/cacheflush.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+#define PMEM_MINORS            16
+
+struct pmem_device {
+       struct request_queue    *pmem_queue;
+       struct gendisk          *pmem_disk;
+
+       /* One contiguous memory region per device */
+       phys_addr_t             phys_addr;
+       void                    *virt_addr;
+       size_t                  size;
+};
+
+static int pmem_major;
+static atomic_t pmem_index;
+
+static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
+                       unsigned int len, unsigned int off, int rw,
+                       sector_t sector)
+{
+       void *mem = kmap_atomic(page);
+       size_t pmem_off = sector << 9;
+
+       if (rw == READ) {
+               memcpy(mem + off, pmem->virt_addr + pmem_off, len);
+               flush_dcache_page(page);
+       } else {
+               flush_dcache_page(page);
+               memcpy(pmem->virt_addr + pmem_off, mem + off, len);
+       }
+
+       kunmap_atomic(mem);
+}
+
+static void pmem_make_request(struct request_queue *q, struct bio *bio)
+{
+       struct block_device *bdev = bio->bi_bdev;
+       struct pmem_device *pmem = bdev->bd_disk->private_data;
+       int rw;
+       struct bio_vec bvec;
+       sector_t sector;
+       struct bvec_iter iter;
+       int err = 0;
+
+       if (bio_end_sector(bio) > get_capacity(bdev->bd_disk)) {
+               err = -EIO;
+               goto out;
+       }
+
+       BUG_ON(bio->bi_rw & REQ_DISCARD);
+
+       rw = bio_data_dir(bio);
+       sector = bio->bi_iter.bi_sector;
+       bio_for_each_segment(bvec, bio, iter) {
+               pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len, bvec.bv_offset,
+                            rw, sector);
+               sector += bvec.bv_len >> 9;
+       }
+
+out:
+       bio_endio(bio, err);
+}
+
+static int pmem_rw_page(struct block_device *bdev, sector_t sector,
+                      struct page *page, int rw)
+{
+       struct pmem_device *pmem = bdev->bd_disk->private_data;
+
+       pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
+       page_endio(page, rw & WRITE, 0);
+
+       return 0;
+}
+
+static long pmem_direct_access(struct block_device *bdev, sector_t sector,
+                             void **kaddr, unsigned long *pfn, long size)
+{
+       struct pmem_device *pmem = bdev->bd_disk->private_data;
+       size_t offset = sector << 9;
+
+       if (!pmem)
+               return -ENODEV;
+
+       *kaddr = pmem->virt_addr + offset;
+       *pfn = (pmem->phys_addr + offset) >> PAGE_SHIFT;
+
+       return pmem->size - offset;
+}
+
+static const struct block_device_operations pmem_fops = {
+       .owner =                THIS_MODULE,
+       .rw_page =              pmem_rw_page,
+       .direct_access =        pmem_direct_access,
+};
+
+static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res)
+{
+       struct pmem_device *pmem;
+       struct gendisk *disk;
+       int idx, err;
+
+       err = -ENOMEM;
+       pmem = kzalloc(sizeof(*pmem), GFP_KERNEL);
+       if (!pmem)
+               goto out;
+
+       pmem->phys_addr = res->start;
+       pmem->size = resource_size(res);
+
+       err = -EINVAL;
+       if (!request_mem_region(pmem->phys_addr, pmem->size, "pmem")) {
+               dev_warn(dev, "could not reserve region [0x%pa:0x%zx]\n", &pmem->phys_addr, pmem->size);
+               goto out_free_dev;
+       }
+
+       /*
+        * Map the memory as non-cachable, as we can't write back the contents
+        * of the CPU caches in case of a crash.
+        */
+       err = -ENOMEM;
+       pmem->virt_addr = ioremap_nocache(pmem->phys_addr, pmem->size);
+       if (!pmem->virt_addr)
+               goto out_release_region;
+
+       pmem->pmem_queue = blk_alloc_queue(GFP_KERNEL);
+       if (!pmem->pmem_queue)
+               goto out_unmap;
+
+       blk_queue_make_request(pmem->pmem_queue, pmem_make_request);
+       blk_queue_max_hw_sectors(pmem->pmem_queue, 1024);
+       blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY);
+
+       disk = alloc_disk(PMEM_MINORS);
+       if (!disk)
+               goto out_free_queue;
+
+       idx = atomic_inc_return(&pmem_index) - 1;
+
+       disk->major             = pmem_major;
+       disk->first_minor       = PMEM_MINORS * idx;
+       disk->fops              = &pmem_fops;
+       disk->private_data      = pmem;
+       disk->queue             = pmem->pmem_queue;
+       disk->flags             = GENHD_FL_EXT_DEVT;
+       sprintf(disk->disk_name, "pmem%d", idx);
+       disk->driverfs_dev = dev;
+       set_capacity(disk, pmem->size >> 9);
+       pmem->pmem_disk = disk;
+
+       add_disk(disk);
+
+       return pmem;
+
+out_free_queue:
+       blk_cleanup_queue(pmem->pmem_queue);
+out_unmap:
+       iounmap(pmem->virt_addr);
+out_release_region:
+       release_mem_region(pmem->phys_addr, pmem->size);
+out_free_dev:
+       kfree(pmem);
+out:
+       return ERR_PTR(err);
+}
+
+static void pmem_free(struct pmem_device *pmem)
+{
+       del_gendisk(pmem->pmem_disk);
+       put_disk(pmem->pmem_disk);
+       blk_cleanup_queue(pmem->pmem_queue);
+       iounmap(pmem->virt_addr);
+       release_mem_region(pmem->phys_addr, pmem->size);
+       kfree(pmem);
+}
+
+static int pmem_probe(struct platform_device *pdev)
+{
+       struct pmem_device *pmem;
+       struct resource *res;
+
+       if (WARN_ON(pdev->num_resources > 1))
+               return -ENXIO;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENXIO;
+
+       pmem = pmem_alloc(&pdev->dev, res);
+       if (IS_ERR(pmem))
+               return PTR_ERR(pmem);
+
+       platform_set_drvdata(pdev, pmem);
+
+       return 0;
+}
+
+static int pmem_remove(struct platform_device *pdev)
+{
+       struct pmem_device *pmem = platform_get_drvdata(pdev);
+
+       pmem_free(pmem);
+       return 0;
+}
+
+static struct platform_driver pmem_driver = {
+       .probe          = pmem_probe,
+       .remove         = pmem_remove,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "pmem",
+       },
+};
+
+static int __init pmem_init(void)
+{
+       int error;
+
+       pmem_major = register_blkdev(0, "pmem");
+       if (pmem_major < 0)
+               return pmem_major;
+
+       error = platform_driver_register(&pmem_driver);
+       if (error)
+               unregister_blkdev(pmem_major, "pmem");
+       return error;
+}
+module_init(pmem_init);
+
+static void pmem_exit(void)
+{
+       platform_driver_unregister(&pmem_driver);
+       unregister_blkdev(pmem_major, "pmem");
+}
+module_exit(pmem_exit);
+
+MODULE_AUTHOR("Ross Zwisler <ross.zwisler@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
index c3efa418a86d8eda8f715cecd97382deb965a8e8..6f75e9d5b6fb0755d41b3e252daea83179efb4b6 100644 (file)
@@ -52,15 +52,6 @@ of_coresight_get_endpoint_device(struct device_node *endpoint)
                               endpoint, of_dev_node_match);
 }
 
-static struct device_node *of_get_coresight_endpoint(
-               const struct device_node *parent, struct device_node *prev)
-{
-       struct device_node *node = of_graph_get_next_endpoint(parent, prev);
-
-       of_node_put(prev);
-       return node;
-}
-
 static void of_coresight_get_ports(struct device_node *node,
                                   int *nr_inport, int *nr_outport)
 {
@@ -68,7 +59,7 @@ static void of_coresight_get_ports(struct device_node *node,
        int in = 0, out = 0;
 
        do {
-               ep = of_get_coresight_endpoint(node, ep);
+               ep = of_graph_get_next_endpoint(node, ep);
                if (!ep)
                        break;
 
@@ -140,7 +131,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
                /* Iterate through each port to discover topology */
                do {
                        /* Get a handle on a port */
-                       ep = of_get_coresight_endpoint(node, ep);
+                       ep = of_graph_get_next_endpoint(node, ep);
                        if (!ep)
                                break;
 
index dc1aaa83a3474c3299895487ef7f82fca36cf250..38d875d0e4c866cd22f541ab1e3d74ec60699c7b 100644 (file)
@@ -90,27 +90,11 @@ config GPIO_GENERIC
 
 # put drivers in the right section, in alphabetical order
 
-config GPIO_DA9052
-       tristate "Dialog DA9052 GPIO"
-       depends on PMIC_DA9052
-       help
-         Say yes here to enable the GPIO driver for the DA9052 chip.
-
-config GPIO_DA9055
-       tristate "Dialog Semiconductor DA9055 GPIO"
-       depends on MFD_DA9055
-       help
-         Say yes here to enable the GPIO driver for the DA9055 chip.
-
-         The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
-         be controller by this driver.
-
-         If driver is built as a module it will be called gpio-da9055.
-
+# This symbol is selected by both I2C and SPI expanders
 config GPIO_MAX730X
        tristate
 
-comment "Memory mapped GPIO drivers:"
+menu "Memory mapped GPIO drivers"
 
 config GPIO_74XX_MMIO
        tristate "GPIO driver for 74xx-ICs with MMIO access"
@@ -126,6 +110,22 @@ config GPIO_74XX_MMIO
            8 bits:     74244 (Input), 74273 (Output)
            16 bits:    741624 (Input), 7416374 (Output)
 
+config GPIO_ALTERA
+       tristate "Altera GPIO"
+       depends on OF_GPIO
+       select GPIO_GENERIC
+       select GPIOLIB_IRQCHIP
+       help
+         Say Y or M here to build support for the Altera PIO device.
+
+         If driver is built as a module it will be called gpio-altera.
+
+config GPIO_BCM_KONA
+       bool "Broadcom Kona GPIO"
+       depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
+       help
+         Turn on GPIO support for Broadcom "Kona" chips.
+
 config GPIO_CLPS711X
        tristate "CLPS711X GPIO support"
        depends on ARCH_CLPS711X || COMPILE_TEST
@@ -140,28 +140,14 @@ config GPIO_DAVINCI
        help
          Say yes here to enable GPIO support for TI Davinci/Keystone SoCs.
 
-config GPIO_GENERIC_PLATFORM
-       tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
-       select GPIO_GENERIC
-       help
-         Say yes here to support basic platform_device memory-mapped GPIO controllers.
-
 config GPIO_DWAPB
        tristate "Synopsys DesignWare APB GPIO driver"
-       depends on ARM
-       depends on OF_GPIO
        select GPIO_GENERIC
        select GENERIC_IRQ_CHIP
        help
          Say Y or M here to build support for the Synopsys DesignWare APB
          GPIO block.
 
-config GPIO_IT8761E
-       tristate "IT8761E GPIO support"
-       depends on X86  # unconditional access to IO space.
-       help
-         Say yes here to support GPIO functionality of IT8761E super I/O chip.
-
 config GPIO_EM
        tristate "Emma Mobile GPIO"
        depends on ARM && OF_GPIO
@@ -173,36 +159,99 @@ config GPIO_EP93XX
        depends on ARCH_EP93XX
        select GPIO_GENERIC
 
-config GPIO_ZEVIO
-       bool "LSI ZEVIO SoC memory mapped GPIOs"
-       depends on ARM && OF_GPIO
-       help
-         Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
-
-config GPIO_MM_LANTIQ
-       bool "Lantiq Memory mapped GPIOs"
-       depends on LANTIQ && SOC_XWAY
-       help
-         This enables support for memory mapped GPIOs on the External Bus Unit
-         (EBU) found on Lantiq SoCs. The gpios are output only as they are
-         created by attaching a 16bit latch to the bus.
-
 config GPIO_F7188X
-       tristate "F71882FG and F71889F GPIO support"
+       tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
        depends on X86
        help
          This option enables support for GPIOs found on Fintek Super-I/O
-         chips F71882FG and F71889F.
+         chips F71869, F71869A, F71882FG and F71889F.
 
          To compile this driver as a module, choose M here: the module will
          be called f7188x-gpio.
 
+config GPIO_GE_FPGA
+       bool "GE FPGA based GPIO"
+       depends on GE_FPGA
+       select GPIO_GENERIC
+       help
+         Support for common GPIO functionality provided on some GE Single Board
+         Computers.
+
+         This driver provides basic support (configure as input or output, read
+         and write pin state) for GPIO implemented in a number of GE single
+         board computers.
+
+config GPIO_GENERIC_PLATFORM
+       tristate "Generic memory-mapped GPIO controller support (MMIO platform device)"
+       select GPIO_GENERIC
+       help
+         Say yes here to support basic platform_device memory-mapped GPIO controllers.
+
+config GPIO_GRGPIO
+       tristate "Aeroflex Gaisler GRGPIO support"
+       depends on OF
+       select GPIO_GENERIC
+       select IRQ_DOMAIN
+       help
+         Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
+         VHDL IP core library.
+
+config GPIO_ICH
+       tristate "Intel ICH GPIO"
+       depends on PCI && X86
+       select MFD_CORE
+       select LPC_ICH
+       help
+         Say yes here to support the GPIO functionality of a number of Intel
+         ICH-based chipsets.  Currently supported devices: ICH6, ICH7, ICH8
+         ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg
+         Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
+
+         If unsure, say N.
+
+config GPIO_IOP
+       tristate "Intel IOP GPIO"
+       depends on ARM && (ARCH_IOP32X || ARCH_IOP33X)
+       help
+         Say yes here to support the GPIO functionality of a number of Intel
+         IOP32X or IOP33X.
+
+         If unsure, say N.
+
+config GPIO_IT8761E
+       tristate "IT8761E GPIO support"
+       depends on X86  # unconditional access to IO space.
+       help
+         Say yes here to support GPIO functionality of IT8761E super I/O chip.
+
+config GPIO_LOONGSON
+       bool "Loongson-2/3 GPIO support"
+       depends on CPU_LOONGSON2 || CPU_LOONGSON3
+       help
+         driver for GPIO functionality on Loongson-2F/3A/3B processors.
+
+config GPIO_LYNXPOINT
+       tristate "Intel Lynxpoint GPIO support"
+       depends on ACPI && X86
+       select GPIOLIB_IRQCHIP
+       help
+         driver for GPIO functionality on Intel Lynxpoint PCH chipset
+         Requires ACPI device enumeration code to set up a platform device.
+
 config GPIO_MB86S7X
        bool "GPIO support for Fujitsu MB86S7x Platforms"
        depends on ARCH_MB86S7X
        help
          Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs.
 
+config GPIO_MM_LANTIQ
+       bool "Lantiq Memory mapped GPIOs"
+       depends on LANTIQ && SOC_XWAY
+       help
+         This enables support for memory mapped GPIOs on the External Bus Unit
+         (EBU) found on Lantiq SoCs. The gpios are output only as they are
+         created by attaching a 16bit latch to the bus.
+
 config GPIO_MOXART
        bool "MOXART GPIO support"
        depends on ARCH_MOXART
@@ -303,6 +352,33 @@ config GPIO_SAMSUNG
          Legacy GPIO support. Use only for platforms without support for
          pinctrl.
 
+config GPIO_SCH
+       tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
+       depends on PCI && X86
+       select MFD_CORE
+       select LPC_SCH
+       help
+         Say yes here to support GPIO interface on Intel Poulsbo SCH,
+         Intel Tunnel Creek processor, Intel Centerton processor or
+         Intel Quark X1000 SoC.
+
+         The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+         powered by the core power rail and are turned off during sleep
+         modes (S3 and higher). The remaining four GPIOs are powered by
+         the Intel SCH suspend power supply. These GPIOs remain
+         active during S3. The suspend powered GPIOs can be used to wake the
+         system from the Suspend-to-RAM state.
+
+         The Intel Tunnel Creek processor has 5 GPIOs powered by the
+         core power rail and 9 from suspend power supply.
+
+         The Intel Centerton processor has a total of 30 GPIO pins.
+         Twenty-one are powered by the core power rail and 9 from the
+         suspend power supply.
+
+         The Intel Quark X1000 SoC has 2 GPIOs powered by the core
+         power well and 6 from the suspend power well.
+
 config GPIO_SCH311X
        tristate "SMSC SCH311x SuperI/O GPIO"
        help
@@ -327,12 +403,27 @@ config GPIO_STA2X11
          Say yes here to support the STA2x11/ConneXt GPIO device.
          The GPIO module has 128 GPIO pins with alternate functions.
 
+config GPIO_STP_XWAY
+       bool "XWAY STP GPIOs"
+       depends on SOC_XWAY
+       help
+         This enables support for the Serial To Parallel (STP) unit found on
+         XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
+         that can be up to 24 bit. This peripheral is aimed at driving leds.
+         Some of the gpios/leds can be auto updated by the soc with dsl and
+         phy status.
+
 config GPIO_SYSCON
        tristate "GPIO based on SYSCON"
        depends on MFD_SYSCON && OF
        help
          Say yes here to support GPIO functionality though SYSCON driver.
 
+config GPIO_TB10X
+       bool
+       select GENERIC_IRQ_CHIP
+       select OF_GPIO
+
 config GPIO_TS5500
        tristate "TS-5500 DIO blocks and compatibles"
        depends on TS5500 || COMPILE_TEST
@@ -364,6 +455,24 @@ config GPIO_VF610
        help
          Say yes here to support Vybrid vf610 GPIOs.
 
+config GPIO_VR41XX
+       tristate "NEC VR4100 series General-purpose I/O Uint support"
+       depends on CPU_VR41XX
+       help
+         Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+
+config GPIO_VX855
+       tristate "VIA VX855/VX875 GPIO"
+       depends on PCI
+       select MFD_CORE
+       select MFD_VX855
+       help
+         Support access to the VX855/VX875 GPIO lines through the gpio library.
+
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the
+         functionality of the device.
+
 config GPIO_XGENE
        bool "APM X-Gene GPIO controller support"
        depends on ARM64 && OF_GPIO
@@ -387,13 +496,6 @@ config GPIO_XILINX
        help
          Say yes here to support the Xilinx FPGA GPIO device
 
-config GPIO_ZYNQ
-       tristate "Xilinx Zynq GPIO support"
-       depends on ARCH_ZYNQ
-       select GPIOLIB_IRQCHIP
-       help
-         Say yes here to support Xilinx Zynq GPIO controller.
-
 config GPIO_XTENSA
        bool "Xtensa GPIO32 support"
        depends on XTENSA
@@ -403,135 +505,49 @@ config GPIO_XTENSA
          Say yes here to support the Xtensa internal GPIO32 IMPWIRE (input)
          and EXPSTATE (output) ports
 
-config GPIO_VR41XX
-       tristate "NEC VR4100 series General-purpose I/O Uint support"
-       depends on CPU_VR41XX
+config GPIO_ZEVIO
+       bool "LSI ZEVIO SoC memory mapped GPIOs"
+       depends on ARM && OF_GPIO
        help
-         Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+         Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
 
-config GPIO_SCH
-       tristate "Intel SCH/TunnelCreek/Centerton/Quark X1000 GPIO"
-       depends on PCI && X86
-       select MFD_CORE
-       select LPC_SCH
+config GPIO_ZYNQ
+       tristate "Xilinx Zynq GPIO support"
+       depends on ARCH_ZYNQ
+       select GPIOLIB_IRQCHIP
        help
-         Say yes here to support GPIO interface on Intel Poulsbo SCH,
-         Intel Tunnel Creek processor, Intel Centerton processor or
-         Intel Quark X1000 SoC.
-
-         The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
-         powered by the core power rail and are turned off during sleep
-         modes (S3 and higher). The remaining four GPIOs are powered by
-         the Intel SCH suspend power supply. These GPIOs remain
-         active during S3. The suspend powered GPIOs can be used to wake the
-         system from the Suspend-to-RAM state.
-
-         The Intel Tunnel Creek processor has 5 GPIOs powered by the
-         core power rail and 9 from suspend power supply.
+         Say yes here to support Xilinx Zynq GPIO controller.
 
-         The Intel Centerton processor has a total of 30 GPIO pins.
-         Twenty-one are powered by the core power rail and 9 from the
-         suspend power supply.
+endmenu
 
-         The Intel Quark X1000 SoC has 2 GPIOs powered by the core
-         power well and 6 from the suspend power well.
+menu "I2C GPIO expanders"
+       depends on I2C
 
-config GPIO_ICH
-       tristate "Intel ICH GPIO"
-       depends on PCI && X86
-       select MFD_CORE
-       select LPC_ICH
+config GPIO_ADP5588
+       tristate "ADP5588 I2C GPIO expander"
+       depends on I2C
        help
-         Say yes here to support the GPIO functionality of a number of Intel
-         ICH-based chipsets.  Currently supported devices: ICH6, ICH7, ICH8
-         ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg
-         Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
-
-         If unsure, say N.
+         This option enables support for 18 GPIOs found
+         on Analog Devices ADP5588 GPIO Expanders.
 
-config GPIO_IOP
-       tristate "Intel IOP GPIO"
-       depends on ARM && (ARCH_IOP32X || ARCH_IOP33X)
+config GPIO_ADP5588_IRQ
+       bool "Interrupt controller support for ADP5588"
+       depends on GPIO_ADP5588=y
        help
-         Say yes here to support the GPIO functionality of a number of Intel
-         IOP32X or IOP33X.
-
-         If unsure, say N.
-
-config GPIO_VX855
-       tristate "VIA VX855/VX875 GPIO"
-       depends on PCI
-       select MFD_CORE
-       select MFD_VX855
-       help
-         Support access to the VX855/VX875 GPIO lines through the gpio library.
-
-         This driver provides common support for accessing the device,
-         additional drivers must be enabled in order to use the
-         functionality of the device.
-
-config GPIO_GE_FPGA
-       bool "GE FPGA based GPIO"
-       depends on GE_FPGA
-       select GPIO_GENERIC
-       help
-         Support for common GPIO functionality provided on some GE Single Board
-         Computers.
-
-         This driver provides basic support (configure as input or output, read
-         and write pin state) for GPIO implemented in a number of GE single
-         board computers.
-
-config GPIO_LYNXPOINT
-       tristate "Intel Lynxpoint GPIO support"
-       depends on ACPI && X86
-       select GPIOLIB_IRQCHIP
-       help
-         driver for GPIO functionality on Intel Lynxpoint PCH chipset
-         Requires ACPI device enumeration code to set up a platform device.
-
-config GPIO_GRGPIO
-       tristate "Aeroflex Gaisler GRGPIO support"
-       depends on OF
-       select GPIO_GENERIC
-       select IRQ_DOMAIN
-       help
-         Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
-         VHDL IP core library.
-
-config GPIO_TB10X
-       bool
-       select GENERIC_IRQ_CHIP
-       select OF_GPIO
-
-comment "I2C GPIO expanders:"
-
-config GPIO_ARIZONA
-       tristate "Wolfson Microelectronics Arizona class devices"
-       depends on MFD_ARIZONA
-       help
-         Support for GPIOs on Wolfson Arizona class devices.
+         Say yes here to enable the adp5588 to be used as an interrupt
+         controller. It requires the driver to be built in the kernel.
 
-config GPIO_CRYSTAL_COVE
-       tristate "GPIO support for Crystal Cove PMIC"
-       depends on INTEL_SOC_PMIC
+config GPIO_ADNP
+       tristate "Avionic Design N-bit GPIO expander"
+       depends on I2C && OF_GPIO
        select GPIOLIB_IRQCHIP
        help
-         Support for GPIO pins on Crystal Cove PMIC.
-
-         Say Yes if you have a Intel SoC based tablet with Crystal Cove PMIC
-         inside.
-
-         This driver can also be built as a module. If so, the module will be
-         called gpio-crystalcove.
-
-config GPIO_LP3943
-       tristate "TI/National Semiconductor LP3943 GPIO expander"
-       depends on MFD_LP3943
-       help
-         GPIO driver for LP3943 MFD.
-         LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
-         Open drain outputs are required for this usage.
+         This option enables support for N GPIOs found on Avionic Design
+         I2C GPIO expanders. The register space will be extended by powers
+         of two, so the controller will need to accommodate for that. For
+         example: if a controller provides 48 pins, 6 registers will be
+         enough to represent all pins, but the driver will assume a
+         register layout for 64 pins (8 registers).
 
 config GPIO_MAX7300
        tristate "Maxim MAX7300 GPIO expander"
@@ -543,7 +559,6 @@ config GPIO_MAX7300
 config GPIO_MAX732X
        tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
        depends on I2C
-       select IRQ_DOMAIN
        help
          Say yes here to support the MAX7319, MAX7320-7327 series of I2C
          Port Expanders. Each IO port on these chips has a fixed role of
@@ -563,6 +578,7 @@ config GPIO_MAX732X
 config GPIO_MAX732X_IRQ
        bool "Interrupt controller support for MAX732x"
        depends on GPIO_MAX732X=y
+       select GPIOLIB_IRQCHIP
        help
          Say yes here to enable the max732x to be used as an interrupt
          controller. It requires the driver to be built in the kernel.
@@ -604,6 +620,7 @@ config GPIO_PCA953X_IRQ
 config GPIO_PCF857X
        tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
        depends on I2C
+       select GPIOLIB_IRQCHIP
        select IRQ_DOMAIN
        help
          Say yes here to provide access to most "quasi-bidirectional" I2C
@@ -626,15 +643,6 @@ config GPIO_PCF857X
          This driver provides an in-kernel interface to those GPIOs using
          platform-neutral GPIO calls.
 
-config GPIO_RC5T583
-       bool "RICOH RC5T583 GPIO"
-       depends on MFD_RC5T583
-       help
-         Select this option to enable GPIO driver for the Ricoh RC5T583
-         chip family.
-         This driver provides the support for driving/reading the gpio pins
-         of RC5T583 device through standard gpio library.
-
 config GPIO_SX150X
        bool "Semtech SX150x I2C GPIO expander"
        depends on I2C=y
@@ -647,6 +655,124 @@ config GPIO_SX150X
          8 bits:  sx1508q
          16 bits: sx1509q
 
+endmenu
+
+menu "MFD GPIO expanders"
+
+config GPIO_ADP5520
+       tristate "GPIO Support for ADP5520 PMIC"
+       depends on PMIC_ADP5520
+       help
+         This option enables support for on-chip GPIO found
+         on Analog Devices ADP5520 PMICs.
+
+config GPIO_ARIZONA
+       tristate "Wolfson Microelectronics Arizona class devices"
+       depends on MFD_ARIZONA
+       help
+         Support for GPIOs on Wolfson Arizona class devices.
+
+config GPIO_CRYSTAL_COVE
+       tristate "GPIO support for Crystal Cove PMIC"
+       depends on INTEL_SOC_PMIC
+       select GPIOLIB_IRQCHIP
+       help
+         Support for GPIO pins on Crystal Cove PMIC.
+
+         Say Yes if you have a Intel SoC based tablet with Crystal Cove PMIC
+         inside.
+
+         This driver can also be built as a module. If so, the module will be
+         called gpio-crystalcove.
+
+config GPIO_CS5535
+       tristate "AMD CS5535/CS5536 GPIO support"
+       depends on MFD_CS5535
+       help
+         The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
+         can be used for quite a number of things.  The CS5535/6 is found on
+         AMD Geode and Lemote Yeeloong devices.
+
+         If unsure, say N.
+
+config GPIO_DA9052
+       tristate "Dialog DA9052 GPIO"
+       depends on PMIC_DA9052
+       help
+         Say yes here to enable the GPIO driver for the DA9052 chip.
+
+config GPIO_DA9055
+       tristate "Dialog Semiconductor DA9055 GPIO"
+       depends on MFD_DA9055
+       help
+         Say yes here to enable the GPIO driver for the DA9055 chip.
+
+         The Dialog DA9055 PMIC chip has 3 GPIO pins that can be
+         be controller by this driver.
+
+         If driver is built as a module it will be called gpio-da9055.
+
+config GPIO_DLN2
+       tristate "Diolan DLN2 GPIO support"
+       depends on MFD_DLN2
+       select GPIOLIB_IRQCHIP
+
+       help
+         Select this option to enable GPIO driver for the Diolan DLN2
+         board.
+
+         This driver can also be built as a module. If so, the module
+         will be called gpio-dln2.
+
+config GPIO_JANZ_TTL
+       tristate "Janz VMOD-TTL Digital IO Module"
+       depends on MFD_JANZ_CMODIO
+       help
+         This enables support for the Janz VMOD-TTL Digital IO module.
+         This driver provides support for driving the pins in output
+         mode only. Input mode is not supported.
+
+config GPIO_KEMPLD
+       tristate "Kontron ETX / COMexpress GPIO"
+       depends on MFD_KEMPLD
+       help
+         This enables support for the PLD GPIO interface on some Kontron ETX
+         and COMexpress (ETXexpress) modules.
+
+         This driver can also be built as a module. If so, the module will be
+         called gpio-kempld.
+
+config GPIO_LP3943
+       tristate "TI/National Semiconductor LP3943 GPIO expander"
+       depends on MFD_LP3943
+       help
+         GPIO driver for LP3943 MFD.
+         LP3943 can be used as a GPIO expander which provides up to 16 GPIOs.
+         Open drain outputs are required for this usage.
+
+config GPIO_MSIC
+       bool "Intel MSIC mixed signal gpio support"
+       depends on MFD_INTEL_MSIC
+       help
+         Enable support for GPIO on intel MSIC controllers found in
+         intel MID devices
+
+config GPIO_PALMAS
+       bool "TI PALMAS series PMICs GPIO"
+       depends on MFD_PALMAS
+       help
+         Select this option to enable GPIO driver for the TI PALMAS
+         series chip family.
+
+config GPIO_RC5T583
+       bool "RICOH RC5T583 GPIO"
+       depends on MFD_RC5T583
+       help
+         Select this option to enable GPIO driver for the Ricoh RC5T583
+         chip family.
+         This driver provides the support for driving/reading the gpio pins
+         of RC5T583 device through standard gpio library.
+
 config GPIO_STMPE
        bool "STMPE GPIOs"
        depends on MFD_STMPE
@@ -656,16 +782,6 @@ config GPIO_STMPE
          This enables support for the GPIOs found on the STMPE I/O
          Expanders.
 
-config GPIO_STP_XWAY
-       bool "XWAY STP GPIOs"
-       depends on SOC_XWAY
-       help
-         This enables support for the Serial To Parallel (STP) unit found on
-         XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
-         that can be up to 24 bit. This peripheral is aimed at driving leds.
-         Some of the gpios/leds can be auto updated by the soc with dsl and
-         phy status.
-
 config GPIO_TC3589X
        bool "TC3589X GPIOs"
        depends on MFD_TC3589X
@@ -675,6 +791,26 @@ config GPIO_TC3589X
          This enables support for the GPIOs found on the TC3589X
          I/O Expander.
 
+config GPIO_TIMBERDALE
+       bool "Support for timberdale GPIO IP"
+       depends on MFD_TIMBERDALE
+       ---help---
+       Add support for the GPIO IP in the timberdale FPGA.
+
+config GPIO_TPS6586X
+       bool "TPS6586X GPIO"
+       depends on MFD_TPS6586X
+       help
+         Select this option to enable GPIO driver for the TPS6586X
+         chip family.
+
+config GPIO_TPS65910
+       bool "TPS65910 GPIO"
+       depends on MFD_TPS65910
+       help
+         Select this option to enable GPIO driver for the TPS65910
+         chip family.
+
 config GPIO_TPS65912
        tristate "TI TPS65912 GPIO"
        depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI)
@@ -695,6 +831,13 @@ config GPIO_TWL6040
          Say yes here to access the GPO signals of twl6040
          audio chip from Texas Instruments.
 
+config GPIO_UCB1400
+       tristate "Philips UCB1400 GPIO"
+       depends on UCB1400_CORE
+       help
+         This enables support for the Philips UCB1400 GPIO pins.
+         The UCB1400 is an AC97 audio codec.
+
 config GPIO_WM831X
        tristate "WM831x GPIOs"
        depends on MFD_WM831X
@@ -716,50 +859,22 @@ config GPIO_WM8994
          Say yes here to access the GPIO signals of WM8994 audio hub
          CODECs from Wolfson Microelectronics.
 
-config GPIO_ADP5520
-       tristate "GPIO Support for ADP5520 PMIC"
-       depends on PMIC_ADP5520
-       help
-         This option enables support for on-chip GPIO found
-         on Analog Devices ADP5520 PMICs.
+endmenu
 
-config GPIO_ADP5588
-       tristate "ADP5588 I2C GPIO expander"
-       depends on I2C
-       help
-         This option enables support for 18 GPIOs found
-         on Analog Devices ADP5588 GPIO Expanders.
-
-config GPIO_ADP5588_IRQ
-       bool "Interrupt controller support for ADP5588"
-       depends on GPIO_ADP5588=y
-       help
-         Say yes here to enable the adp5588 to be used as an interrupt
-         controller. It requires the driver to be built in the kernel.
+menu "PCI GPIO expanders"
+       depends on PCI
 
-config GPIO_ADNP
-       tristate "Avionic Design N-bit GPIO expander"
-       depends on I2C && OF_GPIO
-       select GPIOLIB_IRQCHIP
+config GPIO_AMD8111
+       tristate "AMD 8111 GPIO driver"
+       depends on PCI
        help
-         This option enables support for N GPIOs found on Avionic Design
-         I2C GPIO expanders. The register space will be extended by powers
-         of two, so the controller will need to accommodate for that. For
-         example: if a controller provides 48 pins, 6 registers will be
-         enough to represent all pins, but the driver will assume a
-         register layout for 64 pins (8 registers).
-
-comment "PCI GPIO expanders:"
+         The AMD 8111 south bridge contains 32 GPIO pins which can be used.
 
-config GPIO_CS5535
-       tristate "AMD CS5535/CS5536 GPIO support"
-       depends on MFD_CS5535
-       help
-         The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that
-         can be used for quite a number of things.  The CS5535/6 is found on
-         AMD Geode and Lemote Yeeloong devices.
+         Note, that usually system firmware/ACPI handles GPIO pins on their
+         own and users might easily break their systems with uncarefull usage
+         of this driver!
 
-         If unsure, say N.
+         If unsure, say N
 
 config GPIO_BT8XX
        tristate "BT8XX GPIO abuser"
@@ -777,18 +892,6 @@ config GPIO_BT8XX
 
          If unsure, say N.
 
-config GPIO_AMD8111
-       tristate "AMD 8111 GPIO driver"
-       depends on PCI
-       help
-         The AMD 8111 south bridge contains 32 GPIO pins which can be used.
-
-         Note, that usually system firmware/ACPI handles GPIO pins on their
-         own and users might easily break their systems with uncarefull usage
-         of this driver!
-
-         If unsure, say N
-
 config GPIO_INTEL_MID
        bool "Intel Mid GPIO support"
        depends on PCI && X86
@@ -796,6 +899,16 @@ config GPIO_INTEL_MID
        help
          Say Y here to support Intel Mid GPIO.
 
+config GPIO_ML_IOH
+       tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
+       depends on PCI
+       select GENERIC_IRQ_CHIP
+       help
+         ML7213 is companion chip for Intel Atom E6xx series.
+         This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
+         Hub) which is for IVI(In-Vehicle Infotainment) use.
+         This driver can access the IOH's GPIO device.
+
 config GPIO_PCH
        tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7223/ML7831) GPIO"
        depends on PCI && (X86_32 || COMPILE_TEST)
@@ -812,15 +925,14 @@ config GPIO_PCH
          ML7223/ML7831 is companion chip for Intel Atom E6xx series.
          ML7223/ML7831 is completely compatible for Intel EG20T PCH.
 
-config GPIO_ML_IOH
-       tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
+config GPIO_RDC321X
+       tristate "RDC R-321x GPIO support"
        depends on PCI
-       select GENERIC_IRQ_CHIP
+       select MFD_CORE
+       select MFD_RDC321X
        help
-         ML7213 is companion chip for Intel Atom E6xx series.
-         This driver can be used for OKI SEMICONDUCTOR ML7213 IOH(Input/Output
-         Hub) which is for IVI(In-Vehicle Infotainment) use.
-         This driver can access the IOH's GPIO device.
+         Support for the RDC R321x SoC GPIOs over southbridge
+         PCI configuration space.
 
 config GPIO_SODAVILLE
        bool "Intel Sodaville GPIO support"
@@ -830,22 +942,18 @@ config GPIO_SODAVILLE
        help
          Say Y here to support Intel Sodaville GPIO.
 
-config GPIO_TIMBERDALE
-       bool "Support for timberdale GPIO IP"
-       depends on MFD_TIMBERDALE
-       ---help---
-       Add support for the GPIO IP in the timberdale FPGA.
+endmenu
 
-config GPIO_RDC321X
-       tristate "RDC R-321x GPIO support"
-       depends on PCI
-       select MFD_CORE
-       select MFD_RDC321X
-       help
-         Support for the RDC R321x SoC GPIOs over southbridge
-         PCI configuration space.
+menu "SPI GPIO expanders"
+       depends on SPI_MASTER
 
-comment "SPI GPIO expanders:"
+config GPIO_74X164
+       tristate "74x164 serial-in/parallel-out 8-bits shift register"
+       depends on SPI_MASTER && OF
+       help
+         Driver for 74x164 compatible serial-in/parallel-out 8-outputs
+         shift registers. This driver can be used to provide access
+         to more gpio outputs.
 
 config GPIO_MAX7301
        tristate "Maxim MAX7301 GPIO expander"
@@ -870,80 +978,10 @@ config GPIO_MC33880
          SPI driver for Freescale MC33880 high-side/low-side switch.
          This provides GPIO interface supporting inputs and outputs.
 
-config GPIO_74X164
-       tristate "74x164 serial-in/parallel-out 8-bits shift register"
-       depends on SPI_MASTER && OF
-       help
-         Driver for 74x164 compatible serial-in/parallel-out 8-outputs
-         shift registers. This driver can be used to provide access
-         to more gpio outputs.
-
-comment "AC97 GPIO expanders:"
-
-config GPIO_UCB1400
-       tristate "Philips UCB1400 GPIO"
-       depends on UCB1400_CORE
-       help
-         This enables support for the Philips UCB1400 GPIO pins.
-         The UCB1400 is an AC97 audio codec.
-
-comment "LPC GPIO expanders:"
-
-config GPIO_KEMPLD
-       tristate "Kontron ETX / COMexpress GPIO"
-       depends on MFD_KEMPLD
-       help
-         This enables support for the PLD GPIO interface on some Kontron ETX
-         and COMexpress (ETXexpress) modules.
-
-         This driver can also be built as a module. If so, the module will be
-         called gpio-kempld.
-
-comment "MODULbus GPIO expanders:"
-
-config GPIO_JANZ_TTL
-       tristate "Janz VMOD-TTL Digital IO Module"
-       depends on MFD_JANZ_CMODIO
-       help
-         This enables support for the Janz VMOD-TTL Digital IO module.
-         This driver provides support for driving the pins in output
-         mode only. Input mode is not supported.
-
-config GPIO_PALMAS
-       bool "TI PALMAS series PMICs GPIO"
-       depends on MFD_PALMAS
-       help
-         Select this option to enable GPIO driver for the TI PALMAS
-         series chip family.
-
-config GPIO_TPS6586X
-       bool "TPS6586X GPIO"
-       depends on MFD_TPS6586X
-       help
-         Select this option to enable GPIO driver for the TPS6586X
-         chip family.
-
-config GPIO_TPS65910
-       bool "TPS65910 GPIO"
-       depends on MFD_TPS65910
-       help
-         Select this option to enable GPIO driver for the TPS65910
-         chip family.
-
-config GPIO_MSIC
-       bool "Intel MSIC mixed signal gpio support"
-       depends on MFD_INTEL_MSIC
-       help
-         Enable support for GPIO on intel MSIC controllers found in
-         intel MID devices
+endmenu
 
-config GPIO_BCM_KONA
-       bool "Broadcom Kona GPIO"
-       depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
-       help
-         Turn on GPIO support for Broadcom "Kona" chips.
-
-comment "USB GPIO expanders:"
+menu "USB GPIO expanders"
+       depends on USB
 
 config GPIO_VIPERBOARD
        tristate "Viperboard GPIO a & b support"
@@ -956,16 +994,6 @@ config GPIO_VIPERBOARD
           River Tech's viperboard.h for detailed meaning
           of the module parameters.
 
-config GPIO_DLN2
-       tristate "Diolan DLN2 GPIO support"
-       depends on MFD_DLN2
-       select GPIOLIB_IRQCHIP
-
-       help
-         Select this option to enable GPIO driver for the Diolan DLN2
-         board.
-
-         This driver can also be built as a module. If so, the module
-         will be called gpio-dln2.
+endmenu
 
 endif
index bdda6a94d2cd6055d9d1b3afade51c2d7b5f8b67..07b816b9b630dbbb8d46b6442eab3d4614918182 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO)  += gpio-74xx-mmio.o
 obj-$(CONFIG_GPIO_ADNP)                += gpio-adnp.o
 obj-$(CONFIG_GPIO_ADP5520)     += gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)     += gpio-adp5588.o
+obj-$(CONFIG_GPIO_ALTERA)      += gpio-altera.o
 obj-$(CONFIG_GPIO_AMD8111)     += gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)     += gpio-arizona.o
 obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
@@ -41,6 +42,7 @@ obj-$(CONFIG_GPIO_JANZ_TTL)   += gpio-janz-ttl.o
 obj-$(CONFIG_GPIO_KEMPLD)      += gpio-kempld.o
 obj-$(CONFIG_ARCH_KS8695)      += gpio-ks8695.o
 obj-$(CONFIG_GPIO_INTEL_MID)   += gpio-intel-mid.o
+obj-$(CONFIG_GPIO_LOONGSON)    += gpio-loongson.o
 obj-$(CONFIG_GPIO_LP3943)      += gpio-lp3943.o
 obj-$(CONFIG_ARCH_LPC32XX)     += gpio-lpc32xx.o
 obj-$(CONFIG_GPIO_LYNXPOINT)   += gpio-lynxpoint.o
index 13dbd3dfc33a03f833db9e3ad1a5a57d64781cdc..07ba82317eceb04493d9fa56e46cacfa98f1c59e 100644 (file)
@@ -35,6 +35,20 @@ static int devm_gpiod_match(struct device *dev, void *res, void *data)
        return *this == *gpio;
 }
 
+static void devm_gpiod_release_array(struct device *dev, void *res)
+{
+       struct gpio_descs **descs = res;
+
+       gpiod_put_array(*descs);
+}
+
+static int devm_gpiod_match_array(struct device *dev, void *res, void *data)
+{
+       struct gpio_descs **this = res, **gpios = data;
+
+       return *this == *gpios;
+}
+
 /**
  * devm_gpiod_get - Resource-managed gpiod_get()
  * @dev:       GPIO consumer
@@ -111,23 +125,39 @@ EXPORT_SYMBOL(__devm_gpiod_get_index);
 /**
  * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node
  * @dev:       GPIO consumer
+ * @con_id:    function within the GPIO consumer
  * @child:     firmware node (child of @dev)
  *
  * GPIO descriptors returned from this function are automatically disposed on
  * driver detach.
  */
 struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
+                                           const char *con_id,
                                            struct fwnode_handle *child)
 {
+       static const char * const suffixes[] = { "gpios", "gpio" };
+       char prop_name[32]; /* 32 is max size of property name */
        struct gpio_desc **dr;
        struct gpio_desc *desc;
+       unsigned int i;
 
        dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
                          GFP_KERNEL);
        if (!dr)
                return ERR_PTR(-ENOMEM);
 
-       desc = fwnode_get_named_gpiod(child, "gpios");
+       for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+               if (con_id)
+                       snprintf(prop_name, sizeof(prop_name), "%s-%s",
+                                           con_id, suffixes[i]);
+               else
+                       snprintf(prop_name, sizeof(prop_name), "%s",
+                                                              suffixes[i]);
+
+               desc = fwnode_get_named_gpiod(child, prop_name);
+               if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
+                       break;
+       }
        if (IS_ERR(desc)) {
                devres_free(dr);
                return desc;
@@ -169,6 +199,66 @@ struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *de
 }
 EXPORT_SYMBOL(__devm_gpiod_get_index_optional);
 
+/**
+ * devm_gpiod_get_array - Resource-managed gpiod_get_array()
+ * @dev:       GPIO consumer
+ * @con_id:    function within the GPIO consumer
+ * @flags:     optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array(). GPIO descriptors returned from this function are
+ * automatically disposed on driver detach. See gpiod_get_array() for detailed
+ * information about behavior and return values.
+ */
+struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
+                                                    const char *con_id,
+                                                    enum gpiod_flags flags)
+{
+       struct gpio_descs **dr;
+       struct gpio_descs *descs;
+
+       dr = devres_alloc(devm_gpiod_release_array,
+                         sizeof(struct gpio_descs *), GFP_KERNEL);
+       if (!dr)
+               return ERR_PTR(-ENOMEM);
+
+       descs = gpiod_get_array(dev, con_id, flags);
+       if (IS_ERR(descs)) {
+               devres_free(dr);
+               return descs;
+       }
+
+       *dr = descs;
+       devres_add(dev, dr);
+
+       return descs;
+}
+EXPORT_SYMBOL(devm_gpiod_get_array);
+
+/**
+ * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional()
+ * @dev:       GPIO consumer
+ * @con_id:    function within the GPIO consumer
+ * @flags:     optional GPIO initialization flags
+ *
+ * Managed gpiod_get_array_optional(). GPIO descriptors returned from this
+ * function are automatically disposed on driver detach.
+ * See gpiod_get_array_optional() for detailed information about behavior and
+ * return values.
+ */
+struct gpio_descs *__must_check
+devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
+                             enum gpiod_flags flags)
+{
+       struct gpio_descs *descs;
+
+       descs = devm_gpiod_get_array(dev, con_id, flags);
+       if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+               return NULL;
+
+       return descs;
+}
+EXPORT_SYMBOL(devm_gpiod_get_array_optional);
+
 /**
  * devm_gpiod_put - Resource-managed gpiod_put()
  * @desc:      GPIO descriptor to dispose of
@@ -184,6 +274,21 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
 }
 EXPORT_SYMBOL(devm_gpiod_put);
 
+/**
+ * devm_gpiod_put_array - Resource-managed gpiod_put_array()
+ * @descs:     GPIO descriptor array to dispose of
+ *
+ * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array().
+ * Normally this function will not be called as the GPIOs will be disposed of
+ * by the resource management code.
+ */
+void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
+{
+       WARN_ON(devres_release(dev, devm_gpiod_release_array,
+                              devm_gpiod_match_array, &descs));
+}
+EXPORT_SYMBOL(devm_gpiod_put_array);
+
 
 
 
index 3beed6ea8c6568fbd4d560e5dd2da8f6102ba167..d3fe6a6776daaa8c43023daba09f3008187c471d 100644 (file)
@@ -367,7 +367,7 @@ static int adp5588_gpio_probe(struct i2c_client *client,
        struct gpio_chip *gc;
        int ret, i, revid;
 
-       if (pdata == NULL) {
+       if (!pdata) {
                dev_err(&client->dev, "missing platform data\n");
                return -ENODEV;
        }
@@ -378,8 +378,8 @@ static int adp5588_gpio_probe(struct i2c_client *client,
                return -EIO;
        }
 
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (dev == NULL)
+       dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
                return -ENOMEM;
 
        dev->client = client;
@@ -446,7 +446,6 @@ static int adp5588_gpio_probe(struct i2c_client *client,
 err_irq:
        adp5588_irq_teardown(dev);
 err:
-       kfree(dev);
        return ret;
 }
 
@@ -472,7 +471,6 @@ static int adp5588_gpio_remove(struct i2c_client *client)
 
        gpiochip_remove(&dev->gpio_chip);
 
-       kfree(dev);
        return 0;
 }
 
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
new file mode 100644 (file)
index 0000000..449fb46
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Based on gpio-mpc8xxx.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#define ALTERA_GPIO_MAX_NGPIO          32
+#define ALTERA_GPIO_DATA               0x0
+#define ALTERA_GPIO_DIR                        0x4
+#define ALTERA_GPIO_IRQ_MASK           0x8
+#define ALTERA_GPIO_EDGE_CAP           0xc
+
+/**
+* struct altera_gpio_chip
+* @mmchip              : memory mapped chip structure.
+* @gpio_lock           : synchronization lock so that new irq/set/get requests
+                         will be blocked until the current one completes.
+* @interrupt_trigger   : specifies the hardware configured IRQ trigger type
+                         (rising, falling, both, high)
+* @mapped_irq          : kernel mapped irq number.
+*/
+struct altera_gpio_chip {
+       struct of_mm_gpio_chip mmchip;
+       spinlock_t gpio_lock;
+       int interrupt_trigger;
+       int mapped_irq;
+};
+
+static void altera_gpio_irq_unmask(struct irq_data *d)
+{
+       struct altera_gpio_chip *altera_gc;
+       struct of_mm_gpio_chip *mm_gc;
+       unsigned long flags;
+       u32 intmask;
+
+       altera_gc = irq_data_get_irq_chip_data(d);
+       mm_gc = &altera_gc->mmchip;
+
+       spin_lock_irqsave(&altera_gc->gpio_lock, flags);
+       intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+       /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */
+       intmask |= BIT(irqd_to_hwirq(d));
+       writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+       spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
+}
+
+static void altera_gpio_irq_mask(struct irq_data *d)
+{
+       struct altera_gpio_chip *altera_gc;
+       struct of_mm_gpio_chip *mm_gc;
+       unsigned long flags;
+       u32 intmask;
+
+       altera_gc = irq_data_get_irq_chip_data(d);
+       mm_gc = &altera_gc->mmchip;
+
+       spin_lock_irqsave(&altera_gc->gpio_lock, flags);
+       intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+       /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */
+       intmask &= ~BIT(irqd_to_hwirq(d));
+       writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+       spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
+}
+
+/**
+ * This controller's IRQ type is synthesized in hardware, so this function
+ * just checks if the requested set_type matches the synthesized IRQ type
+ */
+static int altera_gpio_irq_set_type(struct irq_data *d,
+                                  unsigned int type)
+{
+       struct altera_gpio_chip *altera_gc;
+
+       altera_gc = irq_data_get_irq_chip_data(d);
+
+       if (type == IRQ_TYPE_NONE)
+               return 0;
+       if (type == IRQ_TYPE_LEVEL_HIGH &&
+               altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH)
+               return 0;
+       if (type == IRQ_TYPE_EDGE_RISING &&
+               altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_RISING)
+               return 0;
+       if (type == IRQ_TYPE_EDGE_FALLING &&
+               altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_FALLING)
+               return 0;
+       if (type == IRQ_TYPE_EDGE_BOTH &&
+               altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_BOTH)
+               return 0;
+
+       return -EINVAL;
+}
+
+static unsigned int altera_gpio_irq_startup(struct irq_data *d) {
+       altera_gpio_irq_unmask(d);
+
+       return 0;
+}
+
+static struct irq_chip altera_irq_chip = {
+       .name           = "altera-gpio",
+       .irq_mask       = altera_gpio_irq_mask,
+       .irq_unmask     = altera_gpio_irq_unmask,
+       .irq_set_type   = altera_gpio_irq_set_type,
+       .irq_startup    = altera_gpio_irq_startup,
+       .irq_shutdown   = altera_gpio_irq_mask,
+};
+
+static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+       struct of_mm_gpio_chip *mm_gc;
+
+       mm_gc = to_of_mm_gpio_chip(gc);
+
+       return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset));
+}
+
+static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+       struct of_mm_gpio_chip *mm_gc;
+       struct altera_gpio_chip *chip;
+       unsigned long flags;
+       unsigned int data_reg;
+
+       mm_gc = to_of_mm_gpio_chip(gc);
+       chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+       spin_lock_irqsave(&chip->gpio_lock, flags);
+       data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);
+       if (value)
+               data_reg |= BIT(offset);
+       else
+               data_reg &= ~BIT(offset);
+       writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);
+       spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct of_mm_gpio_chip *mm_gc;
+       struct altera_gpio_chip *chip;
+       unsigned long flags;
+       unsigned int gpio_ddr;
+
+       mm_gc = to_of_mm_gpio_chip(gc);
+       chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+       spin_lock_irqsave(&chip->gpio_lock, flags);
+       /* Set pin as input, assumes software controlled IP */
+       gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);
+       gpio_ddr &= ~BIT(offset);
+       writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);
+       spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+       return 0;
+}
+
+static int altera_gpio_direction_output(struct gpio_chip *gc,
+               unsigned offset, int value)
+{
+       struct of_mm_gpio_chip *mm_gc;
+       struct altera_gpio_chip *chip;
+       unsigned long flags;
+       unsigned int data_reg, gpio_ddr;
+
+       mm_gc = to_of_mm_gpio_chip(gc);
+       chip = container_of(mm_gc, struct altera_gpio_chip, mmchip);
+
+       spin_lock_irqsave(&chip->gpio_lock, flags);
+       /* Sets the GPIO value */
+       data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);
+       if (value)
+               data_reg |= BIT(offset);
+       else
+               data_reg &= ~BIT(offset);
+       writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);
+
+       /* Set pin as output, assumes software controlled IP */
+       gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);
+       gpio_ddr |= BIT(offset);
+       writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);
+       spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+       return 0;
+}
+
+static void altera_gpio_irq_edge_handler(unsigned int irq,
+                                       struct irq_desc *desc)
+{
+       struct altera_gpio_chip *altera_gc;
+       struct irq_chip *chip;
+       struct of_mm_gpio_chip *mm_gc;
+       struct irq_domain *irqdomain;
+       unsigned long status;
+       int i;
+
+       altera_gc = irq_desc_get_handler_data(desc);
+       chip = irq_desc_get_chip(desc);
+       mm_gc = &altera_gc->mmchip;
+       irqdomain = altera_gc->mmchip.gc.irqdomain;
+
+       chained_irq_enter(chip, desc);
+
+       while ((status =
+             (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) &
+             readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) {
+               writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP);
+               for_each_set_bit(i, &status, mm_gc->gc.ngpio) {
+                       generic_handle_irq(irq_find_mapping(irqdomain, i));
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+
+static void altera_gpio_irq_leveL_high_handler(unsigned int irq,
+                                             struct irq_desc *desc)
+{
+       struct altera_gpio_chip *altera_gc;
+       struct irq_chip *chip;
+       struct of_mm_gpio_chip *mm_gc;
+       struct irq_domain *irqdomain;
+       unsigned long status;
+       int i;
+
+       altera_gc = irq_desc_get_handler_data(desc);
+       chip = irq_desc_get_chip(desc);
+       mm_gc = &altera_gc->mmchip;
+       irqdomain = altera_gc->mmchip.gc.irqdomain;
+
+       chained_irq_enter(chip, desc);
+
+       status = readl(mm_gc->regs + ALTERA_GPIO_DATA);
+       status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);
+
+       for_each_set_bit(i, &status, mm_gc->gc.ngpio) {
+               generic_handle_irq(irq_find_mapping(irqdomain, i));
+       }
+       chained_irq_exit(chip, desc);
+}
+
+static int altera_gpio_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       int reg, ret;
+       struct altera_gpio_chip *altera_gc;
+
+       altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL);
+       if (!altera_gc)
+               return -ENOMEM;
+
+       spin_lock_init(&altera_gc->gpio_lock);
+
+       if (of_property_read_u32(node, "altr,ngpio", &reg))
+               /* By default assume maximum ngpio */
+               altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
+       else
+               altera_gc->mmchip.gc.ngpio = reg;
+
+       if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) {
+               dev_warn(&pdev->dev,
+                       "ngpio is greater than %d, defaulting to %d\n",
+                       ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO);
+               altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
+       }
+
+       altera_gc->mmchip.gc.direction_input    = altera_gpio_direction_input;
+       altera_gc->mmchip.gc.direction_output   = altera_gpio_direction_output;
+       altera_gc->mmchip.gc.get                = altera_gpio_get;
+       altera_gc->mmchip.gc.set                = altera_gpio_set;
+       altera_gc->mmchip.gc.owner              = THIS_MODULE;
+       altera_gc->mmchip.gc.dev                = &pdev->dev;
+
+       ret = of_mm_gpiochip_add(node, &altera_gc->mmchip);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, altera_gc);
+
+       altera_gc->mapped_irq = platform_get_irq(pdev, 0);
+
+       if (altera_gc->mapped_irq < 0)
+               goto skip_irq;
+
+       if (of_property_read_u32(node, "altr,interrupt-type", &reg)) {
+               ret = -EINVAL;
+               dev_err(&pdev->dev,
+                       "altr,interrupt-type value not set in device tree\n");
+               goto teardown;
+       }
+       altera_gc->interrupt_trigger = reg;
+
+       ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0,
+               handle_simple_irq, IRQ_TYPE_NONE);
+
+       if (ret) {
+               dev_info(&pdev->dev, "could not add irqchip\n");
+               return ret;
+       }
+
+       gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc,
+               &altera_irq_chip,
+               altera_gc->mapped_irq,
+               altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH ?
+               altera_gpio_irq_leveL_high_handler :
+               altera_gpio_irq_edge_handler);
+
+skip_irq:
+       return 0;
+teardown:
+       pr_err("%s: registration failed with status %d\n",
+               node->full_name, ret);
+
+       return ret;
+}
+
+static int altera_gpio_remove(struct platform_device *pdev)
+{
+       struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev);
+
+       gpiochip_remove(&altera_gc->mmchip.gc);
+
+       return -EIO;
+}
+
+static const struct of_device_id altera_gpio_of_match[] = {
+       { .compatible = "altr,pio-1.0", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, altera_gpio_of_match);
+
+static struct platform_driver altera_gpio_driver = {
+       .driver = {
+               .name   = "altera_gpio",
+               .of_match_table = of_match_ptr(altera_gpio_of_match),
+       },
+       .probe          = altera_gpio_probe,
+       .remove         = altera_gpio_remove,
+};
+
+static int __init altera_gpio_init(void)
+{
+       return platform_driver_register(&altera_gpio_driver);
+}
+subsys_initcall(altera_gpio_init);
+
+static void __exit altera_gpio_exit(void)
+{
+       platform_driver_unregister(&altera_gpio_driver);
+}
+module_exit(altera_gpio_exit);
+
+MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>");
+MODULE_DESCRIPTION("Altera GPIO driver");
+MODULE_LICENSE("GPL");
index 9665d0aa4ebbf5352a3389fff9cb4bf75df49cce..052fbc8fdaaa69da322297e6497a13778f3dabe3 100644 (file)
@@ -103,7 +103,7 @@ static int arizona_gpio_probe(struct platform_device *pdev)
 
        arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
                                    GFP_KERNEL);
-       if (arizona_gpio == NULL)
+       if (!arizona_gpio)
                return -ENOMEM;
 
        arizona_gpio->arizona = arizona;
@@ -156,7 +156,6 @@ static int arizona_gpio_remove(struct platform_device *pdev)
 
 static struct platform_driver arizona_gpio_driver = {
        .driver.name    = "arizona-gpio",
-       .driver.owner   = THIS_MODULE,
        .probe          = arizona_gpio_probe,
        .remove         = arizona_gpio_remove,
 };
index 3d9e08f7e823edf13aa719460ecc13f8327c8b31..91a7ffe831350adc2afe0a45ab472b937113ce57 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/mfd/intel_soc_pmic.h>
 
 #define CRYSTALCOVE_GPIO_NUM   16
-#define CRYSTALCOVE_VGPIO_NUM  94
+#define CRYSTALCOVE_VGPIO_NUM  95
 
 #define UPDATE_IRQ_TYPE                BIT(0)
 #define UPDATE_IRQ_MASK                BIT(1)
@@ -39,6 +39,7 @@
 #define GPIO0P0CTLI            0x33
 #define GPIO1P0CTLO            0x3b
 #define GPIO1P0CTLI            0x43
+#define GPIOPANELCTL           0x52
 
 #define CTLI_INTCNT_DIS                (0)
 #define CTLI_INTCNT_NE         (1 << 1)
@@ -93,6 +94,10 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
 {
        int reg;
 
+       if (gpio == 94) {
+               return GPIOPANELCTL;
+       }
+
        if (reg_type == CTRL_IN) {
                if (gpio < 8)
                        reg = GPIO0P0CTLI;
index 389a4d2a4926d26fa3f53e1acd9bcc4218e498f2..2e9578ec0ca1316381e86f12ea3adae7cc12cbf9 100644 (file)
@@ -212,7 +212,7 @@ static int da9052_gpio_probe(struct platform_device *pdev)
        int ret;
 
        gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
-       if (gpio == NULL)
+       if (!gpio)
                return -ENOMEM;
 
        gpio->da9052 = dev_get_drvdata(pdev->dev.parent);
index b8d7570368871f0b6a94129253e567b98893208f..7227e6ed3cb9291a1142a044622b36cf1ccf123b 100644 (file)
@@ -146,7 +146,7 @@ static int da9055_gpio_probe(struct platform_device *pdev)
        int ret;
 
        gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
-       if (gpio == NULL)
+       if (!gpio)
                return -ENOMEM;
 
        gpio->da9055 = dev_get_drvdata(pdev->dev.parent);
index 1be291ac6319e315c1627cc95029c741e56b787e..dbda8433c4f7ef9af3855add277b6d5ed74e95fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * GPIO driver for Fintek Super-I/O F71882 and F71889
+ * GPIO driver for Fintek Super-I/O F71869, F71869A, F71882 and F71889
  *
  * Copyright (C) 2010-2013 LaCie
  *
 #define SIO_LOCK_KEY           0xAA    /* Key to disable Super-I/O */
 
 #define SIO_FINTEK_ID          0x1934  /* Manufacturer ID */
+#define SIO_F71869_ID          0x0814  /* F71869 chipset ID */
+#define SIO_F71869A_ID         0x1007  /* F71869A chipset ID */
 #define SIO_F71882_ID          0x0541  /* F71882 chipset ID */
 #define SIO_F71889_ID          0x0909  /* F71889 chipset ID */
 
-enum chips { f71882fg, f71889f };
+enum chips { f71869, f71869a, f71882fg, f71889f };
 
 static const char * const f7188x_names[] = {
+       "f71869",
+       "f71869a",
        "f71882fg",
        "f71889f",
 };
@@ -146,6 +150,27 @@ static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
 /* Output mode register (0:open drain 1:push-pull). */
 #define gpio_out_mode(base) (base + 3)
 
+static struct f7188x_gpio_bank f71869_gpio_bank[] = {
+       F7188X_GPIO_BANK(0, 6, 0xF0),
+       F7188X_GPIO_BANK(10, 8, 0xE0),
+       F7188X_GPIO_BANK(20, 8, 0xD0),
+       F7188X_GPIO_BANK(30, 8, 0xC0),
+       F7188X_GPIO_BANK(40, 8, 0xB0),
+       F7188X_GPIO_BANK(50, 5, 0xA0),
+       F7188X_GPIO_BANK(60, 6, 0x90),
+};
+
+static struct f7188x_gpio_bank f71869a_gpio_bank[] = {
+       F7188X_GPIO_BANK(0, 6, 0xF0),
+       F7188X_GPIO_BANK(10, 8, 0xE0),
+       F7188X_GPIO_BANK(20, 8, 0xD0),
+       F7188X_GPIO_BANK(30, 8, 0xC0),
+       F7188X_GPIO_BANK(40, 8, 0xB0),
+       F7188X_GPIO_BANK(50, 5, 0xA0),
+       F7188X_GPIO_BANK(60, 8, 0x90),
+       F7188X_GPIO_BANK(70, 8, 0x80),
+};
+
 static struct f7188x_gpio_bank f71882_gpio_bank[] = {
        F7188X_GPIO_BANK(0 , 8, 0xF0),
        F7188X_GPIO_BANK(10, 8, 0xE0),
@@ -281,6 +306,14 @@ static int f7188x_gpio_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        switch (sio->type) {
+       case f71869:
+               data->nr_bank = ARRAY_SIZE(f71869_gpio_bank);
+               data->bank = f71869_gpio_bank;
+               break;
+       case f71869a:
+               data->nr_bank = ARRAY_SIZE(f71869a_gpio_bank);
+               data->bank = f71869a_gpio_bank;
+               break;
        case f71882fg:
                data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
                data->bank = f71882_gpio_bank;
@@ -354,6 +387,12 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
 
        devid = superio_inw(addr, SIO_DEVID);
        switch (devid) {
+       case SIO_F71869_ID:
+               sio->type = f71869;
+               break;
+       case SIO_F71869A_ID:
+               sio->type = f71869a;
+               break;
        case SIO_F71882_ID:
                sio->type = f71882fg;
                break;
@@ -410,7 +449,7 @@ err:
 }
 
 /*
- * Try to match a supported Fintech device by reading the (hard-wired)
+ * Try to match a supported Fintek device by reading the (hard-wired)
  * configuration I/O ports. If available, then register both the platform
  * device and driver to support the GPIOs.
  */
@@ -450,6 +489,6 @@ static void __exit f7188x_gpio_exit(void)
 }
 module_exit(f7188x_gpio_exit);
 
-MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71882FG and F71889F");
+MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71869, F71869A, F71882FG and F71889F");
 MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
 MODULE_LICENSE("GPL");
index 7818cd1453ae88cd7d806a1664479836b20fb9e4..4ba7ed502131ad1565aa76ac863a07cd53c151b1 100644 (file)
@@ -173,6 +173,11 @@ static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
        return !!(ichx_priv.use_gpio & (1 << (nr / 32)));
 }
 
+static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr)
+{
+       return ichx_read_bit(GPIO_IO_SEL, nr) ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+}
+
 static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
 {
        /*
@@ -286,6 +291,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip)
                ichx_priv.desc->get : ichx_gpio_get;
 
        chip->set = ichx_gpio_set;
+       chip->get_direction = ichx_gpio_get_direction;
        chip->direction_input = ichx_gpio_direction_input;
        chip->direction_output = ichx_gpio_direction_output;
        chip->base = modparam_gpiobase;
index 443518f63f157ab47d69346c972b744ef718acfa..6b8115f342085bb3b25f78ad8c13ed6ce10a9d7b 100644 (file)
@@ -156,7 +156,7 @@ static int kempld_gpio_probe(struct platform_device *pdev)
        }
 
        gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
-       if (gpio == NULL)
+       if (!gpio)
                return -ENOMEM;
 
        gpio->pld = pld;
diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c
new file mode 100644 (file)
index 0000000..ccc65a1
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *  Loongson-2F/3A/3B GPIO Support
+ *
+ *  Copyright (c) 2008 Richard Liu,  STMicroelectronics         <richard.liu@st.com>
+ *  Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com>
+ *  Copyright (c) 2013 Hongbing Hu <huhb@lemote.com>
+ *  Copyright (c) 2014 Huacai Chen <chenhc@lemote.com>
+ *
+ *  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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <asm/types.h>
+#include <loongson.h>
+#include <linux/gpio.h>
+
+#define STLS2F_N_GPIO          4
+#define STLS3A_N_GPIO          16
+
+#ifdef CONFIG_CPU_LOONGSON3
+#define LOONGSON_N_GPIO        STLS3A_N_GPIO
+#else
+#define LOONGSON_N_GPIO        STLS2F_N_GPIO
+#endif
+
+#define LOONGSON_GPIO_IN_OFFSET        16
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+       u32 temp;
+       u32 mask;
+
+       spin_lock(&gpio_lock);
+       mask = 1 << gpio;
+       temp = LOONGSON_GPIOIE;
+       temp |= mask;
+       LOONGSON_GPIOIE = temp;
+       spin_unlock(&gpio_lock);
+
+       return 0;
+}
+
+static int loongson_gpio_direction_output(struct gpio_chip *chip,
+               unsigned gpio, int level)
+{
+       u32 temp;
+       u32 mask;
+
+       gpio_set_value(gpio, level);
+       spin_lock(&gpio_lock);
+       mask = 1 << gpio;
+       temp = LOONGSON_GPIOIE;
+       temp &= (~mask);
+       LOONGSON_GPIOIE = temp;
+       spin_unlock(&gpio_lock);
+
+       return 0;
+}
+
+static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+       u32 val;
+       u32 mask;
+
+       mask = 1 << (gpio + LOONGSON_GPIO_IN_OFFSET);
+       spin_lock(&gpio_lock);
+       val = LOONGSON_GPIODATA;
+       spin_unlock(&gpio_lock);
+
+       return (val & mask) != 0;
+}
+
+static void loongson_gpio_set_value(struct gpio_chip *chip,
+               unsigned gpio, int value)
+{
+       u32 val;
+       u32 mask;
+
+       mask = 1 << gpio;
+
+       spin_lock(&gpio_lock);
+       val = LOONGSON_GPIODATA;
+       if (value)
+               val |= mask;
+       else
+               val &= (~mask);
+       LOONGSON_GPIODATA = val;
+       spin_unlock(&gpio_lock);
+}
+
+static struct gpio_chip loongson_chip = {
+       .label                  = "Loongson-gpio-chip",
+       .direction_input        = loongson_gpio_direction_input,
+       .get                    = loongson_gpio_get_value,
+       .direction_output       = loongson_gpio_direction_output,
+       .set                    = loongson_gpio_set_value,
+       .base                   = 0,
+       .ngpio                  = LOONGSON_N_GPIO,
+       .can_sleep              = false,
+};
+
+static int __init loongson_gpio_setup(void)
+{
+       return gpiochip_add(&loongson_chip);
+}
+postcore_initcall(loongson_gpio_setup);
index 40ab6dfb6021be9f82f12e34b5cf048ef6ee72a1..0cc2c279ab5cab21770ef9c863db030f74c06fb9 100644 (file)
@@ -35,7 +35,6 @@ static int max7300_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct max7301 *ts;
-       int ret;
 
        if (!i2c_check_functionality(client->adapter,
                        I2C_FUNC_SMBUS_BYTE_DATA))
@@ -49,8 +48,7 @@ static int max7300_probe(struct i2c_client *client,
        ts->write = max7300_i2c_write;
        ts->dev = &client->dev;
 
-       ret = __max730x_probe(ts);
-       return ret;
+       return __max730x_probe(ts);
 }
 
 static int max7300_remove(struct i2c_client *client)
index a095b2393fe91910072530996f91eba02fe2f806..0fa4543c5e02505871a2f1e3bfef6cb840d94e47 100644 (file)
@@ -4,6 +4,7 @@
  *  Copyright (C) 2007 Marvell International Ltd.
  *  Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
  *  Copyright (C) 2008 Eric Miao <eric.miao@marvell.com>
+ *  Copyright (C) 2015 Linus Walleij <linus.walleij@linaro.org>
  *
  *  Derived from drivers/gpio/pca953x.c
  *
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/string.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
 #include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
 #include <linux/i2c.h>
 #include <linux/i2c/max732x.h>
 #include <linux/of.h>
@@ -150,9 +149,7 @@ struct max732x_chip {
        uint8_t         reg_out[2];
 
 #ifdef CONFIG_GPIO_MAX732X_IRQ
-       struct irq_domain       *irq_domain;
        struct mutex            irq_lock;
-       int                     irq_base;
        uint8_t                 irq_mask;
        uint8_t                 irq_mask_cur;
        uint8_t                 irq_trig_raise;
@@ -356,35 +353,26 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
        mutex_unlock(&chip->lock);
 }
 
-static int max732x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
-{
-       struct max732x_chip *chip = to_max732x(gc);
-
-       if (chip->irq_domain) {
-               return irq_create_mapping(chip->irq_domain,
-                               chip->irq_base + off);
-       } else {
-               return -ENXIO;
-       }
-}
-
 static void max732x_irq_mask(struct irq_data *d)
 {
-       struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct max732x_chip *chip = to_max732x(gc);
 
        chip->irq_mask_cur &= ~(1 << d->hwirq);
 }
 
 static void max732x_irq_unmask(struct irq_data *d)
 {
-       struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct max732x_chip *chip = to_max732x(gc);
 
        chip->irq_mask_cur |= 1 << d->hwirq;
 }
 
 static void max732x_irq_bus_lock(struct irq_data *d)
 {
-       struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct max732x_chip *chip = to_max732x(gc);
 
        mutex_lock(&chip->irq_lock);
        chip->irq_mask_cur = chip->irq_mask;
@@ -392,7 +380,8 @@ static void max732x_irq_bus_lock(struct irq_data *d)
 
 static void max732x_irq_bus_sync_unlock(struct irq_data *d)
 {
-       struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct max732x_chip *chip = to_max732x(gc);
        uint16_t new_irqs;
        uint16_t level;
 
@@ -410,7 +399,8 @@ static void max732x_irq_bus_sync_unlock(struct irq_data *d)
 
 static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
 {
-       struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct max732x_chip *chip = to_max732x(gc);
        uint16_t off = d->hwirq;
        uint16_t mask = 1 << off;
 
@@ -492,7 +482,8 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)
 
        do {
                level = __ffs(pending);
-               handle_nested_irq(irq_find_mapping(chip->irq_domain, level));
+               handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain,
+                                                  level));
 
                pending &= ~(1 << level);
        } while (pending);
@@ -500,86 +491,50 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)
        return IRQ_HANDLED;
 }
 
-static int max732x_irq_map(struct irq_domain *h, unsigned int virq,
-               irq_hw_number_t hw)
-{
-       struct max732x_chip *chip = h->host_data;
-
-       if (!(chip->dir_input & (1 << hw))) {
-               dev_err(&chip->client->dev,
-                               "Attempt to map output line as IRQ line: %lu\n",
-                               hw);
-               return -EPERM;
-       }
-
-       irq_set_chip_data(virq, chip);
-       irq_set_chip_and_handler(virq, &max732x_irq_chip,
-                       handle_edge_irq);
-       irq_set_nested_thread(virq, 1);
-#ifdef CONFIG_ARM
-       /* ARM needs us to explicitly flag the IRQ as valid
-        * and will set them noprobe when we do so. */
-       set_irq_flags(virq, IRQF_VALID);
-#else
-       irq_set_noprobe(virq);
-#endif
-
-       return 0;
-}
-
-static struct irq_domain_ops max732x_irq_domain_ops = {
-       .map    = max732x_irq_map,
-       .xlate  = irq_domain_xlate_twocell,
-};
-
-static void max732x_irq_teardown(struct max732x_chip *chip)
-{
-       if (chip->client->irq && chip->irq_domain)
-               irq_domain_remove(chip->irq_domain);
-}
-
 static int max732x_irq_setup(struct max732x_chip *chip,
                             const struct i2c_device_id *id)
 {
        struct i2c_client *client = chip->client;
        struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
        int has_irq = max732x_features[id->driver_data] >> 32;
+       int irq_base = 0;
        int ret;
 
        if (((pdata && pdata->irq_base) || client->irq)
                        && has_irq != INT_NONE) {
                if (pdata)
-                       chip->irq_base = pdata->irq_base;
+                       irq_base = pdata->irq_base;
                chip->irq_features = has_irq;
                mutex_init(&chip->irq_lock);
 
-               chip->irq_domain = irq_domain_add_simple(client->dev.of_node,
-                               chip->gpio_chip.ngpio, chip->irq_base,
-                               &max732x_irq_domain_ops, chip);
-               if (!chip->irq_domain) {
-                       dev_err(&client->dev, "Failed to create IRQ domain\n");
-                       return -ENOMEM;
-               }
-
-               ret = request_threaded_irq(client->irq,
-                                          NULL,
-                                          max732x_irq_handler,
-                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                          dev_name(&client->dev), chip);
+               ret = devm_request_threaded_irq(&client->dev,
+                                       client->irq,
+                                       NULL,
+                                       max732x_irq_handler,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       dev_name(&client->dev), chip);
                if (ret) {
                        dev_err(&client->dev, "failed to request irq %d\n",
                                client->irq);
-                       goto out_failed;
+                       return ret;
                }
-
-               chip->gpio_chip.to_irq = max732x_gpio_to_irq;
+               ret =  gpiochip_irqchip_add(&chip->gpio_chip,
+                                           &max732x_irq_chip,
+                                           irq_base,
+                                           handle_edge_irq,
+                                           IRQ_TYPE_NONE);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "could not connect irqchip to gpiochip\n");
+                       return ret;
+               }
+               gpiochip_set_chained_irqchip(&chip->gpio_chip,
+                                            &max732x_irq_chip,
+                                            client->irq,
+                                            NULL);
        }
 
        return 0;
-
-out_failed:
-       max732x_irq_teardown(chip);
-       return ret;
 }
 
 #else /* CONFIG_GPIO_MAX732X_IRQ */
@@ -595,10 +550,6 @@ static int max732x_irq_setup(struct max732x_chip *chip,
 
        return 0;
 }
-
-static void max732x_irq_teardown(struct max732x_chip *chip)
-{
-}
 #endif
 
 static int max732x_setup_gpio(struct max732x_chip *chip,
@@ -730,13 +681,15 @@ static int max732x_probe(struct i2c_client *client,
        if (nr_port > 8)
                max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]);
 
-       ret = max732x_irq_setup(chip, id);
+       ret = gpiochip_add(&chip->gpio_chip);
        if (ret)
                goto out_failed;
 
-       ret = gpiochip_add(&chip->gpio_chip);
-       if (ret)
+       ret = max732x_irq_setup(chip, id);
+       if (ret) {
+               gpiochip_remove(&chip->gpio_chip);
                goto out_failed;
+       }
 
        if (pdata && pdata->setup) {
                ret = pdata->setup(client, chip->gpio_chip.base,
@@ -751,7 +704,6 @@ static int max732x_probe(struct i2c_client *client,
 out_failed:
        if (chip->client_dummy)
                i2c_unregister_device(chip->client_dummy);
-       max732x_irq_teardown(chip);
        return ret;
 }
 
@@ -774,8 +726,6 @@ static int max732x_remove(struct i2c_client *client)
 
        gpiochip_remove(&chip->gpio_chip);
 
-       max732x_irq_teardown(chip);
-
        /* unregister any dummy i2c_client */
        if (chip->client_dummy)
                i2c_unregister_device(chip->client_dummy);
index 21b1ce5abdfe1306c4b0a71e07a07ccca7faee51..ee93c0ab0a597d8d41a0d86c26d2b1ac34ac0e6b 100644 (file)
@@ -58,6 +58,11 @@ static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned gpio)
        spin_lock_irqsave(&gchip->lock, flags);
 
        val = readl(gchip->base + PFR(gpio));
+       if (!(val & OFFSET(gpio))) {
+               spin_unlock_irqrestore(&gchip->lock, flags);
+               return -EINVAL;
+       }
+
        val &= ~OFFSET(gpio);
        writel(val, gchip->base + PFR(gpio));
 
index 4e3e160e5db2cbd72ca9755ab9eedff2fc04c99c..a431604c9e677beb30c69be99c221e44ea70f2e5 100644 (file)
@@ -151,7 +151,7 @@ static int mc33880_remove(struct spi_device *spi)
        struct mc33880 *mc;
 
        mc = spi_get_drvdata(spi);
-       if (mc == NULL)
+       if (!mc)
                return -ENODEV;
 
        gpiochip_remove(&mc->chip);
index eea5d7e578c994bd28b04271837d06fe3fee3d69..2fc7ff852d1676d728e150d9a27e37e02403f72f 100644 (file)
@@ -949,10 +949,12 @@ static int mcp23s08_probe(struct spi_device *spi)
        if (!chips)
                return -ENODEV;
 
-       data = kzalloc(sizeof(*data) + chips * sizeof(struct mcp23s08),
-                       GFP_KERNEL);
+       data = devm_kzalloc(&spi->dev,
+                           sizeof(*data) + chips * sizeof(struct mcp23s08),
+                           GFP_KERNEL);
        if (!data)
                return -ENOMEM;
+
        spi_set_drvdata(spi, data);
 
        spi->irq = irq_of_parse_and_map(spi->dev.of_node, 0);
@@ -989,7 +991,6 @@ fail:
                        continue;
                gpiochip_remove(&data->mcp[addr]->chip);
        }
-       kfree(data);
        return status;
 }
 
@@ -1007,7 +1008,7 @@ static int mcp23s08_remove(struct spi_device *spi)
                        mcp23s08_irq_teardown(data->mcp[addr]);
                gpiochip_remove(&data->mcp[addr]->chip);
        }
-       kfree(data);
+
        return 0;
 }
 
index d0bc123c7975200a204a6b2608b5620ed5ec71b8..1a54205860f590c97812da14d331f56a8f5e04af 100644 (file)
@@ -320,11 +320,13 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
 {
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mvebu_gpio_chip *mvchip = gc->private;
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
        u32 mask = 1 << (d->irq - gc->irq_base);
 
        irq_gc_lock(gc);
-       gc->mask_cache &= ~mask;
-       writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip));
+       ct->mask_cache_priv &= ~mask;
+
+       writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
        irq_gc_unlock(gc);
 }
 
@@ -332,11 +334,13 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
 {
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mvebu_gpio_chip *mvchip = gc->private;
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
        u32 mask = 1 << (d->irq - gc->irq_base);
 
        irq_gc_lock(gc);
-       gc->mask_cache |= mask;
-       writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip));
+       ct->mask_cache_priv |= mask;
+       writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
        irq_gc_unlock(gc);
 }
 
@@ -344,11 +348,13 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d)
 {
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mvebu_gpio_chip *mvchip = gc->private;
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
        u32 mask = 1 << (d->irq - gc->irq_base);
 
        irq_gc_lock(gc);
-       gc->mask_cache &= ~mask;
-       writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip));
+       ct->mask_cache_priv &= ~mask;
+       writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
        irq_gc_unlock(gc);
 }
 
@@ -356,11 +362,13 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
 {
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mvebu_gpio_chip *mvchip = gc->private;
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
+
        u32 mask = 1 << (d->irq - gc->irq_base);
 
        irq_gc_lock(gc);
-       gc->mask_cache |= mask;
-       writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip));
+       ct->mask_cache_priv |= mask;
+       writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
        irq_gc_unlock(gc);
 }
 
index f476ae2eb0b3c8610e54377cf7e3010079e916bf..cd1d5bf48f36e8a490e6a99fbafdf98c4b2abadf 100644 (file)
@@ -75,14 +75,12 @@ struct gpio_bank {
        int power_mode;
        bool workaround_enabled;
 
-       void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
+       void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
        int (*get_context_loss_count)(struct device *dev);
 
        struct omap_gpio_reg_offs *regs;
 };
 
-#define GPIO_INDEX(bank, gpio) (gpio % bank->width)
-#define GPIO_BIT(bank, gpio) (BIT(GPIO_INDEX(bank, gpio)))
 #define GPIO_MOD_CTRL_BIT      BIT(0)
 
 #define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
@@ -90,11 +88,6 @@ struct gpio_bank {
 
 static void omap_gpio_unmask_irq(struct irq_data *d);
 
-static int omap_irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
-{
-       return bank->chip.base + gpio_irq;
-}
-
 static inline struct gpio_bank *omap_irq_data_get_bank(struct irq_data *d)
 {
        struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
@@ -119,11 +112,11 @@ static void omap_set_gpio_direction(struct gpio_bank *bank, int gpio,
 
 
 /* set data out value using dedicate set/clear register */
-static void omap_set_gpio_dataout_reg(struct gpio_bank *bank, int gpio,
+static void omap_set_gpio_dataout_reg(struct gpio_bank *bank, unsigned offset,
                                      int enable)
 {
        void __iomem *reg = bank->base;
-       u32 l = GPIO_BIT(bank, gpio);
+       u32 l = BIT(offset);
 
        if (enable) {
                reg += bank->regs->set_dataout;
@@ -137,11 +130,11 @@ static void omap_set_gpio_dataout_reg(struct gpio_bank *bank, int gpio,
 }
 
 /* set data out value using mask register */
-static void omap_set_gpio_dataout_mask(struct gpio_bank *bank, int gpio,
+static void omap_set_gpio_dataout_mask(struct gpio_bank *bank, unsigned offset,
                                       int enable)
 {
        void __iomem *reg = bank->base + bank->regs->dataout;
-       u32 gpio_bit = GPIO_BIT(bank, gpio);
+       u32 gpio_bit = BIT(offset);
        u32 l;
 
        l = readl_relaxed(reg);
@@ -208,13 +201,13 @@ static inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
 /**
  * omap2_set_gpio_debounce - low level gpio debounce time
  * @bank: the gpio bank we're acting upon
- * @gpio: the gpio number on this @gpio
+ * @offset: the gpio number on this @bank
  * @debounce: debounce time to use
  *
  * OMAP's debounce time is in 31us steps so we need
  * to convert and round up to the closest unit.
  */
-static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
+static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
                                    unsigned debounce)
 {
        void __iomem            *reg;
@@ -231,7 +224,7 @@ static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
        else
                debounce = (debounce / 0x1f) - 1;
 
-       l = GPIO_BIT(bank, gpio);
+       l = BIT(offset);
 
        clk_prepare_enable(bank->dbck);
        reg = bank->base + bank->regs->debounce;
@@ -266,16 +259,16 @@ static void omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
 /**
  * omap_clear_gpio_debounce - clear debounce settings for a gpio
  * @bank: the gpio bank we're acting upon
- * @gpio: the gpio number on this @gpio
+ * @offset: the gpio number on this @bank
  *
  * If a gpio is using debounce, then clear the debounce enable bit and if
  * this is the only gpio in this bank using debounce, then clear the debounce
  * time too. The debounce clock will also be disabled when calling this function
  * if this is the only gpio in the bank using debounce.
  */
-static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned gpio)
+static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset)
 {
-       u32 gpio_bit = GPIO_BIT(bank, gpio);
+       u32 gpio_bit = BIT(offset);
 
        if (!bank->dbck_flag)
                return;
@@ -472,42 +465,32 @@ static void omap_disable_gpio_module(struct gpio_bank *bank, unsigned offset)
        }
 }
 
-static int omap_gpio_is_input(struct gpio_bank *bank, int mask)
+static int omap_gpio_is_input(struct gpio_bank *bank, unsigned offset)
 {
        void __iomem *reg = bank->base + bank->regs->direction;
 
-       return readl_relaxed(reg) & mask;
+       return readl_relaxed(reg) & BIT(offset);
 }
 
-static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned gpio,
-                              unsigned offset)
+static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned offset)
 {
        if (!LINE_USED(bank->mod_usage, offset)) {
                omap_enable_gpio_module(bank, offset);
                omap_set_gpio_direction(bank, offset, 1);
        }
-       bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio));
+       bank->irq_usage |= BIT(offset);
 }
 
 static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned gpio = 0;
        int retval;
        unsigned long flags;
-       unsigned offset;
+       unsigned offset = d->hwirq;
 
        if (!BANK_USED(bank))
                pm_runtime_get_sync(bank->dev);
 
-#ifdef CONFIG_ARCH_OMAP1
-       if (d->irq > IH_MPUIO_BASE)
-               gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
-#endif
-
-       if (!gpio)
-               gpio = omap_irq_to_gpio(bank, d->hwirq);
-
        if (type & ~IRQ_TYPE_SENSE_MASK)
                return -EINVAL;
 
@@ -516,10 +499,9 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
                return -EINVAL;
 
        spin_lock_irqsave(&bank->lock, flags);
-       offset = GPIO_INDEX(bank, gpio);
        retval = omap_set_gpio_triggering(bank, offset, type);
-       omap_gpio_init_irq(bank, gpio, offset);
-       if (!omap_gpio_is_input(bank, BIT(offset))) {
+       omap_gpio_init_irq(bank, offset);
+       if (!omap_gpio_is_input(bank, offset)) {
                spin_unlock_irqrestore(&bank->lock, flags);
                return -EINVAL;
        }
@@ -550,9 +532,10 @@ static void omap_clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
        readl_relaxed(reg);
 }
 
-static inline void omap_clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
+static inline void omap_clear_gpio_irqstatus(struct gpio_bank *bank,
+                                            unsigned offset)
 {
-       omap_clear_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+       omap_clear_gpio_irqbank(bank, BIT(offset));
 }
 
 static u32 omap_get_gpio_irqbank_mask(struct gpio_bank *bank)
@@ -613,13 +596,13 @@ static void omap_disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
        writel_relaxed(l, reg);
 }
 
-static inline void omap_set_gpio_irqenable(struct gpio_bank *bank, int gpio,
-                                          int enable)
+static inline void omap_set_gpio_irqenable(struct gpio_bank *bank,
+                                          unsigned offset, int enable)
 {
        if (enable)
-               omap_enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+               omap_enable_gpio_irqbank(bank, BIT(offset));
        else
-               omap_disable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+               omap_disable_gpio_irqbank(bank, BIT(offset));
 }
 
 /*
@@ -630,14 +613,16 @@ static inline void omap_set_gpio_irqenable(struct gpio_bank *bank, int gpio,
  * enabled. When system is suspended, only selected GPIO interrupts need
  * to have wake-up enabled.
  */
-static int omap_set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
+static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset,
+                               int enable)
 {
-       u32 gpio_bit = GPIO_BIT(bank, gpio);
+       u32 gpio_bit = BIT(offset);
        unsigned long flags;
 
        if (bank->non_wakeup_gpios & gpio_bit) {
                dev_err(bank->dev,
-                       "Unable to modify wakeup on non-wakeup GPIO%d\n", gpio);
+                       "Unable to modify wakeup on non-wakeup GPIO%d\n",
+                       offset);
                return -EINVAL;
        }
 
@@ -653,22 +638,22 @@ static int omap_set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
        return 0;
 }
 
-static void omap_reset_gpio(struct gpio_bank *bank, int gpio)
+static void omap_reset_gpio(struct gpio_bank *bank, unsigned offset)
 {
-       omap_set_gpio_direction(bank, GPIO_INDEX(bank, gpio), 1);
-       omap_set_gpio_irqenable(bank, gpio, 0);
-       omap_clear_gpio_irqstatus(bank, gpio);
-       omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE);
-       omap_clear_gpio_debounce(bank, gpio);
+       omap_set_gpio_direction(bank, offset, 1);
+       omap_set_gpio_irqenable(bank, offset, 0);
+       omap_clear_gpio_irqstatus(bank, offset);
+       omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+       omap_clear_gpio_debounce(bank, offset);
 }
 
 /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
 static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
+       unsigned offset = d->hwirq;
 
-       return omap_set_gpio_wakeup(bank, gpio, enable);
+       return omap_set_gpio_wakeup(bank, offset, enable);
 }
 
 static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -706,7 +691,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
        spin_lock_irqsave(&bank->lock, flags);
        bank->mod_usage &= ~(BIT(offset));
        omap_disable_gpio_module(bank, offset);
-       omap_reset_gpio(bank, bank->chip.base + offset);
+       omap_reset_gpio(bank, offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
        /*
@@ -803,15 +788,14 @@ exit:
 static unsigned int omap_gpio_irq_startup(struct irq_data *d)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
        unsigned long flags;
-       unsigned offset = GPIO_INDEX(bank, gpio);
+       unsigned offset = d->hwirq;
 
        if (!BANK_USED(bank))
                pm_runtime_get_sync(bank->dev);
 
        spin_lock_irqsave(&bank->lock, flags);
-       omap_gpio_init_irq(bank, gpio, offset);
+       omap_gpio_init_irq(bank, offset);
        spin_unlock_irqrestore(&bank->lock, flags);
        omap_gpio_unmask_irq(d);
 
@@ -821,15 +805,13 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d)
 static void omap_gpio_irq_shutdown(struct irq_data *d)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
        unsigned long flags;
-       unsigned offset = GPIO_INDEX(bank, gpio);
+       unsigned offset = d->hwirq;
 
        spin_lock_irqsave(&bank->lock, flags);
-       gpiochip_unlock_as_irq(&bank->chip, offset);
        bank->irq_usage &= ~(BIT(offset));
        omap_disable_gpio_module(bank, offset);
-       omap_reset_gpio(bank, gpio);
+       omap_reset_gpio(bank, offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
        /*
@@ -843,43 +825,42 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
 static void omap_gpio_ack_irq(struct irq_data *d)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
+       unsigned offset = d->hwirq;
 
-       omap_clear_gpio_irqstatus(bank, gpio);
+       omap_clear_gpio_irqstatus(bank, offset);
 }
 
 static void omap_gpio_mask_irq(struct irq_data *d)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
+       unsigned offset = d->hwirq;
        unsigned long flags;
 
        spin_lock_irqsave(&bank->lock, flags);
-       omap_set_gpio_irqenable(bank, gpio, 0);
-       omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE);
+       omap_set_gpio_irqenable(bank, offset, 0);
+       omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
        spin_unlock_irqrestore(&bank->lock, flags);
 }
 
 static void omap_gpio_unmask_irq(struct irq_data *d)
 {
        struct gpio_bank *bank = omap_irq_data_get_bank(d);
-       unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
-       unsigned int irq_mask = GPIO_BIT(bank, gpio);
+       unsigned offset = d->hwirq;
        u32 trigger = irqd_get_trigger_type(d);
        unsigned long flags;
 
        spin_lock_irqsave(&bank->lock, flags);
        if (trigger)
-               omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), trigger);
+               omap_set_gpio_triggering(bank, offset, trigger);
 
        /* For level-triggered GPIOs, the clearing must be done after
         * the HW source is cleared, thus after the handler has run */
-       if (bank->level_mask & irq_mask) {
-               omap_set_gpio_irqenable(bank, gpio, 0);
-               omap_clear_gpio_irqstatus(bank, gpio);
+       if (bank->level_mask & BIT(offset)) {
+               omap_set_gpio_irqenable(bank, offset, 0);
+               omap_clear_gpio_irqstatus(bank, offset);
        }
 
-       omap_set_gpio_irqenable(bank, gpio, 1);
+       omap_set_gpio_irqenable(bank, offset, 1);
        spin_unlock_irqrestore(&bank->lock, flags);
 }
 
@@ -977,12 +958,10 @@ static int omap_gpio_input(struct gpio_chip *chip, unsigned offset)
 static int omap_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct gpio_bank *bank;
-       u32 mask;
 
        bank = container_of(chip, struct gpio_bank, chip);
-       mask = (BIT(offset));
 
-       if (omap_gpio_is_input(bank, mask))
+       if (omap_gpio_is_input(bank, offset))
                return omap_get_gpio_datain(bank, offset);
        else
                return omap_get_gpio_dataout(bank, offset);
index 236708ad0a5ba0ab9c0ee95a8d2db648e930cb29..945f0cda8529d38af0cf747e300d58ef1e71d1f6 100644 (file)
@@ -88,11 +88,9 @@ struct pcf857x {
        struct gpio_chip        chip;
        struct i2c_client       *client;
        struct mutex            lock;           /* protect 'out' */
-       struct irq_domain       *irq_domain;    /* for irq demux  */
        spinlock_t              slock;          /* protect irq demux */
        unsigned                out;            /* software latch */
        unsigned                status;         /* current status */
-       unsigned                irq_mapped;     /* mapped gpio irqs */
 
        int (*write)(struct i2c_client *client, unsigned data);
        int (*read)(struct i2c_client *client);
@@ -182,18 +180,6 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value)
 
 /*-------------------------------------------------------------------------*/
 
-static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
-{
-       struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
-       int ret;
-
-       ret = irq_create_mapping(gpio->irq_domain, offset);
-       if (ret > 0)
-               gpio->irq_mapped |= (1 << offset);
-
-       return ret;
-}
-
 static irqreturn_t pcf857x_irq(int irq, void *data)
 {
        struct pcf857x  *gpio = data;
@@ -208,9 +194,9 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
         * interrupt source, just to avoid bad irqs
         */
 
-       change = ((gpio->status ^ status) & gpio->irq_mapped);
+       change = (gpio->status ^ status);
        for_each_set_bit(i, &change, gpio->chip.ngpio)
-               generic_handle_irq(irq_find_mapping(gpio->irq_domain, i));
+               handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i));
        gpio->status = status;
 
        spin_unlock_irqrestore(&gpio->slock, flags);
@@ -218,66 +204,36 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int irq,
-                                irq_hw_number_t hw)
-{
-       struct pcf857x *gpio = domain->host_data;
-
-       irq_set_chip_and_handler(irq,
-                                &dummy_irq_chip,
-                                handle_level_irq);
-#ifdef CONFIG_ARM
-       set_irq_flags(irq, IRQF_VALID);
-#else
-       irq_set_noprobe(irq);
-#endif
-       gpio->irq_mapped |= (1 << hw);
-
-       return 0;
-}
-
-static struct irq_domain_ops pcf857x_irq_domain_ops = {
-       .map    = pcf857x_irq_domain_map,
-};
+/*
+ * NOP functions
+ */
+static void noop(struct irq_data *data) { }
 
-static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
+static unsigned int noop_ret(struct irq_data *data)
 {
-       if (gpio->irq_domain)
-               irq_domain_remove(gpio->irq_domain);
-
+       return 0;
 }
 
-static int pcf857x_irq_domain_init(struct pcf857x *gpio,
-                                  struct i2c_client *client)
+static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
 {
-       int status;
-
-       gpio->irq_domain = irq_domain_add_linear(client->dev.of_node,
-                                                gpio->chip.ngpio,
-                                                &pcf857x_irq_domain_ops,
-                                                gpio);
-       if (!gpio->irq_domain)
-               goto fail;
-
-       /* enable real irq */
-       status = devm_request_threaded_irq(&client->dev, client->irq,
-                               NULL, pcf857x_irq, IRQF_ONESHOT |
-                               IRQF_TRIGGER_FALLING | IRQF_SHARED,
-                               dev_name(&client->dev), gpio);
-
-       if (status)
-               goto fail;
-
-       /* enable gpio_to_irq() */
-       gpio->chip.to_irq       = pcf857x_to_irq;
+       struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
 
+       irq_set_irq_wake(gpio->client->irq, on);
        return 0;
-
-fail:
-       pcf857x_irq_domain_cleanup(gpio);
-       return -EINVAL;
 }
 
+static struct irq_chip pcf857x_irq_chip = {
+       .name           = "pcf857x",
+       .irq_startup    = noop_ret,
+       .irq_shutdown   = noop,
+       .irq_enable     = noop,
+       .irq_disable    = noop,
+       .irq_ack        = noop,
+       .irq_mask       = noop,
+       .irq_unmask     = noop,
+       .irq_set_wake   = pcf857x_irq_set_wake,
+};
+
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_probe(struct i2c_client *client,
@@ -314,15 +270,6 @@ static int pcf857x_probe(struct i2c_client *client,
        gpio->chip.direction_output     = pcf857x_output;
        gpio->chip.ngpio                = id->driver_data;
 
-       /* enable gpio_to_irq() if platform has settings */
-       if (client->irq) {
-               status = pcf857x_irq_domain_init(gpio, client);
-               if (status < 0) {
-                       dev_err(&client->dev, "irq_domain init failed\n");
-                       goto fail_irq_domain;
-               }
-       }
-
        /* NOTE:  the OnSemi jlc1562b is also largely compatible with
         * these parts, notably for output.  It has a low-resolution
         * DAC instead of pin change IRQs; and its inputs can be the
@@ -398,6 +345,27 @@ static int pcf857x_probe(struct i2c_client *client,
        if (status < 0)
                goto fail;
 
+       /* Enable irqchip if we have an interrupt */
+       if (client->irq) {
+               status = gpiochip_irqchip_add(&gpio->chip, &pcf857x_irq_chip,
+                                             0, handle_level_irq,
+                                             IRQ_TYPE_NONE);
+               if (status) {
+                       dev_err(&client->dev, "cannot add irqchip\n");
+                       goto fail_irq;
+               }
+
+               status = devm_request_threaded_irq(&client->dev, client->irq,
+                                       NULL, pcf857x_irq, IRQF_ONESHOT |
+                                       IRQF_TRIGGER_FALLING | IRQF_SHARED,
+                                       dev_name(&client->dev), gpio);
+               if (status)
+                       goto fail_irq;
+
+               gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
+                                            client->irq, NULL);
+       }
+
        /* Let platform code set up the GPIOs and their users.
         * Now is the first time anyone could use them.
         */
@@ -413,13 +381,12 @@ static int pcf857x_probe(struct i2c_client *client,
 
        return 0;
 
-fail:
-       if (client->irq)
-               pcf857x_irq_domain_cleanup(gpio);
+fail_irq:
+       gpiochip_remove(&gpio->chip);
 
-fail_irq_domain:
-       dev_dbg(&client->dev, "probe error %d for '%s'\n",
-               status, client->name);
+fail:
+       dev_dbg(&client->dev, "probe error %d for '%s'\n", status,
+               client->name);
 
        return status;
 }
@@ -441,9 +408,6 @@ static int pcf857x_remove(struct i2c_client *client)
                }
        }
 
-       if (client->irq)
-               pcf857x_irq_domain_cleanup(gpio);
-
        gpiochip_remove(&gpio->chip);
        return status;
 }
index 2fdb04b6f1012730dda0e5e0defe3820ad4cd47e..cdbbcf0faf9da4a263504f7a7b0cc3d8aabc8cdb 100644 (file)
@@ -59,8 +59,7 @@
 #define GAFR_OFFSET    0x54
 #define ED_MASK_OFFSET 0x9C    /* GPIO edge detection for AP side */
 
-#define BANK_OFF(n)    (((n) < 3) ? (n) << 2 : ((n) > 5 ? 0x200 : 0x100)       \
-                       + (((n) % 3) << 2))
+#define BANK_OFF(n)    (((n) / 3) << 8) + (((n) % 3) << 2)
 
 int pxa_last_gpio;
 static int irq_base;
index c49522efa7b3bce4aa71d4c006acc1ee86a95be2..fd39774659484fa68fb76d229f1ec9834c480b60 100644 (file)
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
@@ -37,20 +38,22 @@ struct gpio_rcar_priv {
        struct platform_device *pdev;
        struct gpio_chip gpio_chip;
        struct irq_chip irq_chip;
+       unsigned int irq_parent;
+       struct clk *clk;
 };
 
-#define IOINTSEL 0x00
-#define INOUTSEL 0x04
-#define OUTDT 0x08
-#define INDT 0x0c
-#define INTDT 0x10
-#define INTCLR 0x14
-#define INTMSK 0x18
-#define MSKCLR 0x1c
-#define POSNEG 0x20
-#define EDGLEVEL 0x24
-#define FILONOFF 0x28
-#define BOTHEDGE 0x4c
+#define IOINTSEL 0x00  /* General IO/Interrupt Switching Register */
+#define INOUTSEL 0x04  /* General Input/Output Switching Register */
+#define OUTDT 0x08     /* General Output Register */
+#define INDT 0x0c      /* General Input Register */
+#define INTDT 0x10     /* Interrupt Display Register */
+#define INTCLR 0x14    /* Interrupt Clear Register */
+#define INTMSK 0x18    /* Interrupt Mask Register */
+#define MSKCLR 0x1c    /* Interrupt Mask Clear Register */
+#define POSNEG 0x20    /* Positive/Negative Logic Select Register */
+#define EDGLEVEL 0x24  /* Edge/level Select Register */
+#define FILONOFF 0x28  /* Chattering Prevention On/Off Register */
+#define BOTHEDGE 0x4c  /* One Edge/Both Edge Select Register */
 
 #define RCAR_MAX_GPIO_PER_BANK         32
 
@@ -169,6 +172,25 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
+static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
+                                               gpio_chip);
+
+       irq_set_irq_wake(p->irq_parent, on);
+
+       if (!p->clk)
+               return 0;
+
+       if (on)
+               clk_enable(p->clk);
+       else
+               clk_disable(p->clk);
+
+       return 0;
+}
+
 static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
 {
        struct gpio_rcar_priv *p = dev_id;
@@ -367,6 +389,12 @@ static int gpio_rcar_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, p);
 
+       p->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(p->clk)) {
+               dev_warn(dev, "unable to get clock\n");
+               p->clk = NULL;
+       }
+
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
@@ -404,8 +432,8 @@ static int gpio_rcar_probe(struct platform_device *pdev)
        irq_chip->irq_mask = gpio_rcar_irq_disable;
        irq_chip->irq_unmask = gpio_rcar_irq_enable;
        irq_chip->irq_set_type = gpio_rcar_irq_set_type;
-       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_SET_TYPE_MASKED
-                        | IRQCHIP_MASK_ON_SUSPEND;
+       irq_chip->irq_set_wake = gpio_rcar_irq_set_wake;
+       irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
 
        ret = gpiochip_add(gpio_chip);
        if (ret) {
@@ -413,13 +441,14 @@ static int gpio_rcar_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       ret = gpiochip_irqchip_add(&p->gpio_chip, irq_chip, p->config.irq_base,
+       ret = gpiochip_irqchip_add(gpio_chip, irq_chip, p->config.irq_base,
                                   handle_level_irq, IRQ_TYPE_NONE);
        if (ret) {
                dev_err(dev, "cannot add irqchip\n");
                goto err1;
        }
 
+       p->irq_parent = irq->start;
        if (devm_request_irq(dev, irq->start, gpio_rcar_irq_handler,
                             IRQF_SHARED, name, p)) {
                dev_err(dev, "failed to request IRQ\n");
@@ -431,7 +460,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
 
        /* warn in case of mismatch if irq base is specified */
        if (p->config.irq_base) {
-               ret = irq_find_mapping(p->gpio_chip.irqdomain, 0);
+               ret = irq_find_mapping(gpio_chip->irqdomain, 0);
                if (p->config.irq_base != ret)
                        dev_warn(dev, "irq base mismatch (%u/%u)\n",
                                 p->config.irq_base, ret);
@@ -447,7 +476,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
        return 0;
 
 err1:
-       gpiochip_remove(&p->gpio_chip);
+       gpiochip_remove(gpio_chip);
 err0:
        pm_runtime_put(dev);
        pm_runtime_disable(dev);
index 62ab9f4b2cd363c7a955eb609e442dba1d3661ac..46b89614aa9121ba7d82a1a162f9a164a82e295c 100644 (file)
@@ -283,7 +283,7 @@ fail_ioremap:
        return ret;
 }
 
-static int __exit tb10x_gpio_remove(struct platform_device *pdev)
+static int tb10x_gpio_remove(struct platform_device *pdev)
 {
        struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
 
index 971c73964ef1af41547c76da8778f8c42ae79e82..7bd9f209ffa81a1b00141d4345f4fe3becc7f317 100644 (file)
@@ -244,16 +244,16 @@ static int vf610_gpio_probe(struct platform_device *pdev)
        gc = &port->gc;
        gc->of_node = np;
        gc->dev = dev;
-       gc->label = "vf610-gpio",
-       gc->ngpio = VF610_GPIO_PER_PORT,
+       gc->label = "vf610-gpio";
+       gc->ngpio = VF610_GPIO_PER_PORT;
        gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;
 
-       gc->request = vf610_gpio_request,
-       gc->free = vf610_gpio_free,
-       gc->direction_input = vf610_gpio_direction_input,
-       gc->get = vf610_gpio_get,
-       gc->direction_output = vf610_gpio_direction_output,
-       gc->set = vf610_gpio_set,
+       gc->request = vf610_gpio_request;
+       gc->free = vf610_gpio_free;
+       gc->direction_input = vf610_gpio_direction_input;
+       gc->get = vf610_gpio_get;
+       gc->direction_output = vf610_gpio_direction_output;
+       gc->set = vf610_gpio_set;
 
        ret = gpiochip_add(gc);
        if (ret < 0)
index b6a15c39293e2ad0e7ff1b2bcb323d4d66407085..fb9d29a5d584c0cf7793ab517f13c3383393405f 100644 (file)
@@ -93,7 +93,7 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(&pdev->dev, res);
-       if (!regs)
+       if (IS_ERR(regs))
                return PTR_ERR(regs);
 
        ret = bgpio_init(&priv->bgc, &pdev->dev, 4,
index df990f29757a7e045fd8a42760944b2d8bd2ba84..d2303d50f56141c527c9d8b82c956c6c8169e239 100644 (file)
@@ -304,7 +304,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
                return;
 
        INIT_LIST_HEAD(&acpi_gpio->events);
-       acpi_walk_resources(ACPI_HANDLE(chip->dev), "_AEI",
+       acpi_walk_resources(handle, "_AEI",
                            acpi_gpiochip_request_interrupt, acpi_gpio);
 }
 
@@ -722,3 +722,87 @@ void acpi_gpiochip_remove(struct gpio_chip *chip)
        acpi_detach_data(handle, acpi_gpio_chip_dh);
        kfree(acpi_gpio);
 }
+
+static unsigned int acpi_gpio_package_count(const union acpi_object *obj)
+{
+       const union acpi_object *element = obj->package.elements;
+       const union acpi_object *end = element + obj->package.count;
+       unsigned int count = 0;
+
+       while (element < end) {
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE)
+                       count++;
+
+               element++;
+       }
+       return count;
+}
+
+static int acpi_find_gpio_count(struct acpi_resource *ares, void *data)
+{
+       unsigned int *count = data;
+
+       if (ares->type == ACPI_RESOURCE_TYPE_GPIO)
+               *count += ares->data.gpio.pin_table_length;
+
+       return 1;
+}
+
+/**
+ * acpi_gpio_count - return the number of GPIOs associated with a
+ *             device / function or -ENOENT if no GPIO has been
+ *             assigned to the requested function.
+ * @dev:       GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id:    function within the GPIO consumer
+ */
+int acpi_gpio_count(struct device *dev, const char *con_id)
+{
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+       const union acpi_object *obj;
+       const struct acpi_gpio_mapping *gm;
+       int count = -ENOENT;
+       int ret;
+       char propname[32];
+       unsigned int i;
+
+       /* Try first from _DSD */
+       for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+               if (con_id && strcmp(con_id, "gpios"))
+                       snprintf(propname, sizeof(propname), "%s-%s",
+                                con_id, gpio_suffixes[i]);
+               else
+                       snprintf(propname, sizeof(propname), "%s",
+                                gpio_suffixes[i]);
+
+               ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
+                                           &obj);
+               if (ret == 0) {
+                       if (obj->type == ACPI_TYPE_LOCAL_REFERENCE)
+                               count = 1;
+                       else if (obj->type == ACPI_TYPE_PACKAGE)
+                               count = acpi_gpio_package_count(obj);
+               } else if (adev->driver_gpios) {
+                       for (gm = adev->driver_gpios; gm->name; gm++)
+                               if (strcmp(propname, gm->name) == 0) {
+                                       count = gm->size;
+                                       break;
+                               }
+               }
+               if (count >= 0)
+                       break;
+       }
+
+       /* Then from plain _CRS GPIOs */
+       if (count < 0) {
+               struct list_head resource_list;
+               unsigned int crs_count = 0;
+
+               INIT_LIST_HEAD(&resource_list);
+               acpi_dev_get_resources(adev, &resource_list,
+                                      acpi_find_gpio_count, &crs_count);
+               acpi_dev_free_resource_list(&resource_list);
+               if (crs_count > 0)
+                       count = crs_count;
+       }
+       return count;
+}
index 4650bf830d6b6306f96e309d4f2782da8a859575..a6c67c6b468045f9325e6249019f0c1080a46398 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/of_gpio.h>
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/slab.h>
+#include <linux/gpio/machine.h>
 
 #include "gpiolib.h"
 
@@ -117,6 +118,114 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
 }
 EXPORT_SYMBOL(of_get_named_gpio_flags);
 
+/**
+ * of_get_gpio_hog() - Get a GPIO hog descriptor, names and flags for GPIO API
+ * @np:                device node to get GPIO from
+ * @name:      GPIO line name
+ * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
+ *             of_get_gpio_hog()
+ * @dflags:    gpiod_flags - optional GPIO initialization flags
+ *
+ * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno
+ * value on the error condition.
+ */
+static struct gpio_desc *of_get_gpio_hog(struct device_node *np,
+                                 const char **name,
+                                 enum gpio_lookup_flags *lflags,
+                                 enum gpiod_flags *dflags)
+{
+       struct device_node *chip_np;
+       enum of_gpio_flags xlate_flags;
+       struct gpio_desc *desc;
+       struct gg_data gg_data = {
+               .flags = &xlate_flags,
+       };
+       u32 tmp;
+       int i, ret;
+
+       chip_np = np->parent;
+       if (!chip_np)
+               return ERR_PTR(-EINVAL);
+
+       xlate_flags = 0;
+       *lflags = 0;
+       *dflags = 0;
+
+       ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (tmp > MAX_PHANDLE_ARGS)
+               return ERR_PTR(-EINVAL);
+
+       gg_data.gpiospec.args_count = tmp;
+       gg_data.gpiospec.np = chip_np;
+       for (i = 0; i < tmp; i++) {
+               ret = of_property_read_u32_index(np, "gpios", i,
+                                          &gg_data.gpiospec.args[i]);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
+       gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+       if (!gg_data.out_gpio) {
+               if (np->parent == np)
+                       return ERR_PTR(-ENXIO);
+               else
+                       return ERR_PTR(-EINVAL);
+       }
+
+       if (xlate_flags & OF_GPIO_ACTIVE_LOW)
+               *lflags |= GPIO_ACTIVE_LOW;
+
+       if (of_property_read_bool(np, "input"))
+               *dflags |= GPIOD_IN;
+       else if (of_property_read_bool(np, "output-low"))
+               *dflags |= GPIOD_OUT_LOW;
+       else if (of_property_read_bool(np, "output-high"))
+               *dflags |= GPIOD_OUT_HIGH;
+       else {
+               pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
+                       desc_to_gpio(gg_data.out_gpio), np->name);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (name && of_property_read_string(np, "line-name", name))
+               *name = np->name;
+
+       desc = gg_data.out_gpio;
+
+       return desc;
+}
+
+/**
+ * of_gpiochip_scan_hogs - Scan gpio-controller and apply GPIO hog as requested
+ * @chip:      gpio chip to act on
+ *
+ * This is only used by of_gpiochip_add to request/set GPIO initial
+ * configuration.
+ */
+static void of_gpiochip_scan_hogs(struct gpio_chip *chip)
+{
+       struct gpio_desc *desc = NULL;
+       struct device_node *np;
+       const char *name;
+       enum gpio_lookup_flags lflags;
+       enum gpiod_flags dflags;
+
+       for_each_child_of_node(chip->of_node, np) {
+               if (!of_property_read_bool(np, "gpio-hog"))
+                       continue;
+
+               desc = of_get_gpio_hog(np, &name, &lflags, &dflags);
+               if (IS_ERR(desc))
+                       continue;
+
+               if (gpiod_hog(desc, name, lflags, dflags))
+                       continue;
+       }
+}
+
 /**
  * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags
  * @gc:                pointer to the gpio_chip structure
@@ -326,6 +435,8 @@ void of_gpiochip_add(struct gpio_chip *chip)
 
        of_gpiochip_add_pin_range(chip);
        of_node_get(chip->of_node);
+
+       of_gpiochip_scan_hogs(chip);
 }
 
 void of_gpiochip_remove(struct gpio_chip *chip)
index 1ca9295b2c1017df34dacad5c0217677b6c2d60d..59eaa23767d8dca5bddf740fa90a24c9699c4a0b 100644 (file)
@@ -315,6 +315,7 @@ EXPORT_SYMBOL_GPL(gpiochip_add);
 
 /* Forward-declaration */
 static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+static void gpiochip_free_hogs(struct gpio_chip *chip);
 
 /**
  * gpiochip_remove() - unregister a gpio_chip
@@ -333,6 +334,7 @@ void gpiochip_remove(struct gpio_chip *chip)
 
        acpi_gpiochip_remove(chip);
        gpiochip_remove_pin_ranges(chip);
+       gpiochip_free_hogs(chip);
        of_gpiochip_remove(chip);
 
        spin_lock_irqsave(&gpio_lock, flags);
@@ -866,6 +868,7 @@ static bool __gpiod_free(struct gpio_desc *desc)
                clear_bit(FLAG_REQUESTED, &desc->flags);
                clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
                clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
+               clear_bit(FLAG_IS_HOGGED, &desc->flags);
                ret = true;
        }
 
@@ -1659,19 +1662,18 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
                                      unsigned int idx,
                                      enum gpio_lookup_flags *flags)
 {
-       static const char * const suffixes[] = { "gpios", "gpio" };
        char prop_name[32]; /* 32 is max size of property name */
        enum of_gpio_flags of_flags;
        struct gpio_desc *desc;
        unsigned int i;
 
-       for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+       for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
                if (con_id)
                        snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
-                                                              suffixes[i]);
+                                gpio_suffixes[i]);
                else
                        snprintf(prop_name, sizeof(prop_name), "%s",
-                                                              suffixes[i]);
+                                gpio_suffixes[i]);
 
                desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
                                                &of_flags);
@@ -1692,7 +1694,6 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
                                        unsigned int idx,
                                        enum gpio_lookup_flags *flags)
 {
-       static const char * const suffixes[] = { "gpios", "gpio" };
        struct acpi_device *adev = ACPI_COMPANION(dev);
        struct acpi_gpio_info info;
        struct gpio_desc *desc;
@@ -1700,13 +1701,13 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
        int i;
 
        /* Try first from _DSD */
-       for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+       for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
                if (con_id && strcmp(con_id, "gpios")) {
                        snprintf(propname, sizeof(propname), "%s-%s",
-                                con_id, suffixes[i]);
+                                con_id, gpio_suffixes[i]);
                } else {
                        snprintf(propname, sizeof(propname), "%s",
-                                suffixes[i]);
+                                gpio_suffixes[i]);
                }
 
                desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
@@ -1805,6 +1806,70 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
        return desc;
 }
 
+static int dt_gpio_count(struct device *dev, const char *con_id)
+{
+       int ret;
+       char propname[32];
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+               if (con_id)
+                       snprintf(propname, sizeof(propname), "%s-%s",
+                                con_id, gpio_suffixes[i]);
+               else
+                       snprintf(propname, sizeof(propname), "%s",
+                                gpio_suffixes[i]);
+
+               ret = of_gpio_named_count(dev->of_node, propname);
+               if (ret >= 0)
+                       break;
+       }
+       return ret;
+}
+
+static int platform_gpio_count(struct device *dev, const char *con_id)
+{
+       struct gpiod_lookup_table *table;
+       struct gpiod_lookup *p;
+       unsigned int count = 0;
+
+       table = gpiod_find_lookup_table(dev);
+       if (!table)
+               return -ENOENT;
+
+       for (p = &table->table[0]; p->chip_label; p++) {
+               if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
+                   (!con_id && !p->con_id))
+                       count++;
+       }
+       if (!count)
+               return -ENOENT;
+
+       return count;
+}
+
+/**
+ * gpiod_count - return the number of GPIOs associated with a device / function
+ *             or -ENOENT if no GPIO has been assigned to the requested function
+ * @dev:       GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id:    function within the GPIO consumer
+ */
+int gpiod_count(struct device *dev, const char *con_id)
+{
+       int count = -ENOENT;
+
+       if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
+               count = dt_gpio_count(dev, con_id);
+       else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev))
+               count = acpi_gpio_count(dev, con_id);
+
+       if (count < 0)
+               count = platform_gpio_count(dev, con_id);
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(gpiod_count);
+
 /**
  * gpiod_get - obtain a GPIO for a given GPIO function
  * @dev:       GPIO consumer, can be NULL for system-global GPIOs
@@ -1840,6 +1905,47 @@ struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(__gpiod_get_optional);
 
+
+/**
+ * gpiod_configure_flags - helper function to configure a given GPIO
+ * @desc:      gpio whose value will be assigned
+ * @con_id:    function within the GPIO consumer
+ * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
+ *             of_get_gpio_hog()
+ * @dflags:    gpiod_flags - optional GPIO initialization flags
+ *
+ * Return 0 on success, -ENOENT if no GPIO has been assigned to the
+ * requested function and/or index, or another IS_ERR() code if an error
+ * occurred while trying to acquire the GPIO.
+ */
+static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
+               unsigned long lflags, enum gpiod_flags dflags)
+{
+       int status;
+
+       if (lflags & GPIO_ACTIVE_LOW)
+               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+       if (lflags & GPIO_OPEN_DRAIN)
+               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+       if (lflags & GPIO_OPEN_SOURCE)
+               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
+       /* No particular flag request, return here... */
+       if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
+               pr_debug("no flags found for %s\n", con_id);
+               return 0;
+       }
+
+       /* Process flags */
+       if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
+               status = gpiod_direction_output(desc,
+                                             dflags & GPIOD_FLAGS_BIT_DIR_VAL);
+       else
+               status = gpiod_direction_input(desc);
+
+       return status;
+}
+
 /**
  * gpiod_get_index - obtain a GPIO from a multi-index GPIO function
  * @dev:       GPIO consumer, can be NULL for system-global GPIOs
@@ -1865,13 +1971,15 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
 
        dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
 
-       /* Using device tree? */
-       if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {
-               dev_dbg(dev, "using device tree for GPIO lookup\n");
-               desc = of_find_gpio(dev, con_id, idx, &lookupflags);
-       } else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) {
-               dev_dbg(dev, "using ACPI for GPIO lookup\n");
-               desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
+       if (dev) {
+               /* Using device tree? */
+               if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+                       dev_dbg(dev, "using device tree for GPIO lookup\n");
+                       desc = of_find_gpio(dev, con_id, idx, &lookupflags);
+               } else if (ACPI_COMPANION(dev)) {
+                       dev_dbg(dev, "using ACPI for GPIO lookup\n");
+                       desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
+               }
        }
 
        /*
@@ -1889,28 +1997,10 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
        }
 
        status = gpiod_request(desc, con_id);
-
        if (status < 0)
                return ERR_PTR(status);
 
-       if (lookupflags & GPIO_ACTIVE_LOW)
-               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
-       if (lookupflags & GPIO_OPEN_DRAIN)
-               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
-       if (lookupflags & GPIO_OPEN_SOURCE)
-               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-
-       /* No particular flag request, return here... */
-       if (!(flags & GPIOD_FLAGS_BIT_DIR_SET))
-               return desc;
-
-       /* Process flags */
-       if (flags & GPIOD_FLAGS_BIT_DIR_OUT)
-               status = gpiod_direction_output(desc,
-                                             flags & GPIOD_FLAGS_BIT_DIR_VAL);
-       else
-               status = gpiod_direction_input(desc);
-
+       status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
        if (status < 0) {
                dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
                gpiod_put(desc);
@@ -2005,6 +2095,132 @@ struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(__gpiod_get_index_optional);
 
+/**
+ * gpiod_hog - Hog the specified GPIO desc given the provided flags
+ * @desc:      gpio whose value will be assigned
+ * @name:      gpio line name
+ * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
+ *             of_get_gpio_hog()
+ * @dflags:    gpiod_flags - optional GPIO initialization flags
+ */
+int gpiod_hog(struct gpio_desc *desc, const char *name,
+             unsigned long lflags, enum gpiod_flags dflags)
+{
+       struct gpio_chip *chip;
+       struct gpio_desc *local_desc;
+       int hwnum;
+       int status;
+
+       chip = gpiod_to_chip(desc);
+       hwnum = gpio_chip_hwgpio(desc);
+
+       local_desc = gpiochip_request_own_desc(chip, hwnum, name);
+       if (IS_ERR(local_desc)) {
+               pr_debug("requesting own GPIO %s failed\n", name);
+               return PTR_ERR(local_desc);
+       }
+
+       status = gpiod_configure_flags(desc, name, lflags, dflags);
+       if (status < 0) {
+               pr_debug("setup of GPIO %s failed\n", name);
+               gpiochip_free_own_desc(desc);
+               return status;
+       }
+
+       /* Mark GPIO as hogged so it can be identified and removed later */
+       set_bit(FLAG_IS_HOGGED, &desc->flags);
+
+       pr_info("GPIO line %d (%s) hogged as %s%s\n",
+               desc_to_gpio(desc), name,
+               (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
+               (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ?
+                 (dflags&GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low":"");
+
+       return 0;
+}
+
+/**
+ * gpiochip_free_hogs - Scan gpio-controller chip and release GPIO hog
+ * @chip:      gpio chip to act on
+ *
+ * This is only used by of_gpiochip_remove to free hogged gpios
+ */
+static void gpiochip_free_hogs(struct gpio_chip *chip)
+{
+       int id;
+
+       for (id = 0; id < chip->ngpio; id++) {
+               if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags))
+                       gpiochip_free_own_desc(&chip->desc[id]);
+       }
+}
+
+/**
+ * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function
+ * @dev:       GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id:    function within the GPIO consumer
+ * @flags:     optional GPIO initialization flags
+ *
+ * This function acquires all the GPIOs defined under a given function.
+ *
+ * Return a struct gpio_descs containing an array of descriptors, -ENOENT if
+ * no GPIO has been assigned to the requested function, or another IS_ERR()
+ * code if an error occurred while trying to acquire the GPIOs.
+ */
+struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
+                                               const char *con_id,
+                                               enum gpiod_flags flags)
+{
+       struct gpio_desc *desc;
+       struct gpio_descs *descs;
+       int count;
+
+       count = gpiod_count(dev, con_id);
+       if (count < 0)
+               return ERR_PTR(count);
+
+       descs = kzalloc(sizeof(*descs) + sizeof(descs->desc[0]) * count,
+                       GFP_KERNEL);
+       if (!descs)
+               return ERR_PTR(-ENOMEM);
+
+       for (descs->ndescs = 0; descs->ndescs < count; ) {
+               desc = gpiod_get_index(dev, con_id, descs->ndescs, flags);
+               if (IS_ERR(desc)) {
+                       gpiod_put_array(descs);
+                       return ERR_CAST(desc);
+               }
+               descs->desc[descs->ndescs] = desc;
+               descs->ndescs++;
+       }
+       return descs;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array);
+
+/**
+ * gpiod_get_array_optional - obtain multiple GPIOs from a multi-index GPIO
+ *                            function
+ * @dev:       GPIO consumer, can be NULL for system-global GPIOs
+ * @con_id:    function within the GPIO consumer
+ * @flags:     optional GPIO initialization flags
+ *
+ * This is equivalent to gpiod_get_array(), except that when no GPIO was
+ * assigned to the requested function it will return NULL.
+ */
+struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
+                                                       const char *con_id,
+                                                       enum gpiod_flags flags)
+{
+       struct gpio_descs *descs;
+
+       descs = gpiod_get_array(dev, con_id, flags);
+       if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT))
+               return NULL;
+
+       return descs;
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array_optional);
+
 /**
  * gpiod_put - dispose of a GPIO descriptor
  * @desc:      GPIO descriptor to dispose of
@@ -2017,6 +2233,21 @@ void gpiod_put(struct gpio_desc *desc)
 }
 EXPORT_SYMBOL_GPL(gpiod_put);
 
+/**
+ * gpiod_put_array - dispose of multiple GPIO descriptors
+ * @descs:     struct gpio_descs containing an array of descriptors
+ */
+void gpiod_put_array(struct gpio_descs *descs)
+{
+       unsigned int i;
+
+       for (i = 0; i < descs->ndescs; i++)
+               gpiod_put(descs->desc[i]);
+
+       kfree(descs);
+}
+EXPORT_SYMBOL_GPL(gpiod_put_array);
+
 #ifdef CONFIG_DEBUG_FS
 
 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
index ab892be26dc21005e22e42f00221f647bb2e2923..594b1798c0e7c69e7841e1b401b03326daa8db07 100644 (file)
@@ -29,6 +29,9 @@ struct acpi_gpio_info {
        bool active_low;
 };
 
+/* gpio suffixes used for ACPI and device tree lookup */
+static const char * const gpio_suffixes[] = { "gpios", "gpio" };
+
 #ifdef CONFIG_ACPI
 void acpi_gpiochip_add(struct gpio_chip *chip);
 void acpi_gpiochip_remove(struct gpio_chip *chip);
@@ -39,6 +42,8 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
 struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
                                          const char *propname, int index,
                                          struct acpi_gpio_info *info);
+
+int acpi_gpio_count(struct device *dev, const char *con_id);
 #else
 static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
 static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
@@ -55,6 +60,11 @@ acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
 {
        return ERR_PTR(-ENOSYS);
 }
+
+static inline int acpi_gpio_count(struct device *dev, const char *con_id)
+{
+       return -ENODEV;
+}
 #endif
 
 struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
@@ -80,6 +90,7 @@ struct gpio_desc {
 #define FLAG_OPEN_SOURCE 8     /* Gpio is open source type */
 #define FLAG_USED_AS_IRQ 9     /* GPIO is connected to an IRQ */
 #define FLAG_SYSFS_DIR 10      /* show sysfs direction attribute */
+#define FLAG_IS_HOGGED 11      /* GPIO is hogged */
 
 #define ID_SHIFT       16      /* add new flags before this one */
 
@@ -91,6 +102,8 @@ struct gpio_desc {
 
 int gpiod_request(struct gpio_desc *desc, const char *label);
 void gpiod_free(struct gpio_desc *desc);
+int gpiod_hog(struct gpio_desc *desc, const char *name,
+               unsigned long lflags, enum gpiod_flags dflags);
 
 /*
  * Return the GPIO number of the passed descriptor relative to its chip
index a002f53aab0e3a3654a722f218be7854ffb70cd4..84cf99f8d957ae1218412066346aece4b77e9dd4 100644 (file)
@@ -431,15 +431,6 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
 }
 EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of);
 
-static struct device_node *imx_drm_of_get_next_endpoint(
-               const struct device_node *parent, struct device_node *prev)
-{
-       struct device_node *node = of_graph_get_next_endpoint(parent, prev);
-
-       of_node_put(prev);
-       return node;
-}
-
 /*
  * @node: device tree node containing encoder input ports
  * @encoder: drm_encoder
@@ -457,7 +448,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
                return -EINVAL;
 
        do {
-               ep = imx_drm_of_get_next_endpoint(node, ep);
+               ep = of_graph_get_next_endpoint(node, ep);
                if (!ep)
                        break;
 
index cc9136e8ee9cd2f73c65850215fad60a584358ee..68dab2601bf54a8e44dda63c87341ff558bec3e1 100644 (file)
@@ -206,7 +206,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
        enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE;
        struct device_node *connector = NULL;
        struct device_node *encoder = NULL;
-       struct device_node *prev = NULL;
+       struct device_node *ep_node = NULL;
        struct device_node *entity_ep_node;
        struct device_node *entity;
        int ret;
@@ -225,11 +225,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
        entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0);
 
        while (1) {
-               struct device_node *ep_node;
-
-               ep_node = of_graph_get_next_endpoint(entity, prev);
-               of_node_put(prev);
-               prev = ep_node;
+               ep_node = of_graph_get_next_endpoint(entity, ep_node);
 
                if (!ep_node)
                        break;
@@ -300,7 +296,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
 static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
 {
        struct device_node *np = rcdu->dev->of_node;
-       struct device_node *prev = NULL;
+       struct device_node *ep_node = NULL;
        unsigned int num_encoders = 0;
 
        /*
@@ -308,15 +304,12 @@ static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
         * pipeline.
         */
        while (1) {
-               struct device_node *ep_node;
                enum rcar_du_output output;
                struct of_endpoint ep;
                unsigned int i;
                int ret;
 
-               ep_node = of_graph_get_next_endpoint(np, prev);
-               of_node_put(prev);
-               prev = ep_node;
+               ep_node = of_graph_get_next_endpoint(np, ep_node);
 
                if (ep_node == NULL)
                        break;
index ce1d6932416990cdd4e67789b2bb51e5c379ad16..19b2d689a5ef72d76f2745bb3cb35dad78e61ad2 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
index 1672e6b127747d5adc266e5a5d13e68ad53b03b7..098f698fe8f4708afc6b2d5785a7b4be8635a428 100644 (file)
@@ -596,6 +596,7 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
        adap->bus_recovery_info->set_scl(adap, 1);
        return i2c_generic_recovery(adap);
 }
+EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
 
 int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
 {
@@ -610,6 +611,7 @@ int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(i2c_generic_gpio_recovery);
 
 int i2c_recover_bus(struct i2c_adapter *adap)
 {
@@ -619,6 +621,7 @@ int i2c_recover_bus(struct i2c_adapter *adap)
        dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
        return adap->bus_recovery_info->recover_bus(adap);
 }
+EXPORT_SYMBOL_GPL(i2c_recover_bus);
 
 static int i2c_device_probe(struct device *dev)
 {
index 90df4df58b076ee3feac7f50db0d3d27760f9cbb..097d7216d98ee4e4d394726e9d4c9743067d14f8 100644 (file)
@@ -125,7 +125,7 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
        device_for_each_child_node(dev, child) {
                struct gpio_desc *desc;
 
-               desc = devm_get_gpiod_from_child(dev, child);
+               desc = devm_get_gpiod_from_child(dev, NULL, child);
                if (IS_ERR(desc)) {
                        error = PTR_ERR(desc);
                        if (error != -EPROBE_DEFER)
index d26af0a79a90bf698fdc411302b465a0f87fd19c..15eb3f86f670ffe43605615b81c994aa58b40b2c 100644 (file)
@@ -184,7 +184,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
                struct gpio_led led = {};
                const char *state = NULL;
 
-               led.gpiod = devm_get_gpiod_from_child(dev, child);
+               led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
                if (IS_ERR(led.gpiod)) {
                        fwnode_handle_put(child);
                        ret = PTR_ERR(led.gpiod);
index 63e05e32b46269e29f8e75e03073d5587fd6d916..6ddc983417d5a8120700fbfa98508d1db3e35546 100644 (file)
@@ -196,6 +196,17 @@ config BLK_DEV_DM
 
          If unsure, say N.
 
+config DM_MQ_DEFAULT
+       bool "request-based DM: use blk-mq I/O path by default"
+       depends on BLK_DEV_DM
+       ---help---
+         This option enables the blk-mq based I/O path for request-based
+         DM devices by default.  With the option the dm_mod.use_blk_mq
+         module/boot option defaults to Y, without it to N, but it can
+         still be overriden either way.
+
+         If unsure say N.
+
 config DM_DEBUG
        bool "Device mapper debugging support"
        depends on BLK_DEV_DM
@@ -432,4 +443,20 @@ config DM_SWITCH
 
          If unsure, say N.
 
+config DM_LOG_WRITES
+       tristate "Log writes target support"
+       depends on BLK_DEV_DM
+       ---help---
+         This device-mapper target takes two devices, one device to use
+         normally, one to log all write operations done to the first device.
+         This is for use by file system developers wishing to verify that
+         their fs is writing a consitent file system at all times by allowing
+         them to replay the log in a variety of ways and to check the
+         contents.
+
+         To compile this code as a module, choose M here: the module will
+         be called dm-log-writes.
+
+         If unsure, say N.
+
 endif # MD
index a2da532b1c2bda38d18a6324c2dfdcfcc7108df5..1863feaa584612eb862ff20d7806c8c12608b17c 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_DM_CACHE)                += dm-cache.o
 obj-$(CONFIG_DM_CACHE_MQ)      += dm-cache-mq.o
 obj-$(CONFIG_DM_CACHE_CLEANER) += dm-cache-cleaner.o
 obj-$(CONFIG_DM_ERA)           += dm-era.o
+obj-$(CONFIG_DM_LOG_WRITES)    += dm-log-writes.o
 
 ifeq ($(CONFIG_DM_UEVENT),y)
 dm-mod-objs                    += dm-uevent.o
index 13f547a4eeb61f2715845090b8e2e5ce311c73d1..3ddd1162334df3bcefe551ca86a5287b9f3ea4ca 100644 (file)
@@ -8,6 +8,7 @@
 #include "dm.h"
 
 #include <linux/hash.h>
+#include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -124,32 +125,41 @@ static void iot_examine_bio(struct io_tracker *t, struct bio *bio)
  * sorted queue.
  */
 #define NR_QUEUE_LEVELS 16u
+#define NR_SENTINELS NR_QUEUE_LEVELS * 3
+
+#define WRITEBACK_PERIOD HZ
 
 struct queue {
+       unsigned nr_elts;
+       bool current_writeback_sentinels;
+       unsigned long next_writeback;
        struct list_head qs[NR_QUEUE_LEVELS];
+       struct list_head sentinels[NR_SENTINELS];
 };
 
 static void queue_init(struct queue *q)
 {
        unsigned i;
 
-       for (i = 0; i < NR_QUEUE_LEVELS; i++)
+       q->nr_elts = 0;
+       q->current_writeback_sentinels = false;
+       q->next_writeback = 0;
+       for (i = 0; i < NR_QUEUE_LEVELS; i++) {
                INIT_LIST_HEAD(q->qs + i);
+               INIT_LIST_HEAD(q->sentinels + i);
+               INIT_LIST_HEAD(q->sentinels + NR_QUEUE_LEVELS + i);
+               INIT_LIST_HEAD(q->sentinels + (2 * NR_QUEUE_LEVELS) + i);
+       }
 }
 
-/*
- * Checks to see if the queue is empty.
- * FIXME: reduce cpu usage.
- */
-static bool queue_empty(struct queue *q)
+static unsigned queue_size(struct queue *q)
 {
-       unsigned i;
-
-       for (i = 0; i < NR_QUEUE_LEVELS; i++)
-               if (!list_empty(q->qs + i))
-                       return false;
+       return q->nr_elts;
+}
 
-       return true;
+static bool queue_empty(struct queue *q)
+{
+       return q->nr_elts == 0;
 }
 
 /*
@@ -157,24 +167,19 @@ static bool queue_empty(struct queue *q)
  */
 static void queue_push(struct queue *q, unsigned level, struct list_head *elt)
 {
+       q->nr_elts++;
        list_add_tail(elt, q->qs + level);
 }
 
-static void queue_remove(struct list_head *elt)
+static void queue_remove(struct queue *q, struct list_head *elt)
 {
+       q->nr_elts--;
        list_del(elt);
 }
 
-/*
- * Shifts all regions down one level.  This has no effect on the order of
- * the queue.
- */
-static void queue_shift_down(struct queue *q)
+static bool is_sentinel(struct queue *q, struct list_head *h)
 {
-       unsigned level;
-
-       for (level = 1; level < NR_QUEUE_LEVELS; level++)
-               list_splice_init(q->qs + level, q->qs + level - 1);
+       return (h >= q->sentinels) && (h < (q->sentinels + NR_SENTINELS));
 }
 
 /*
@@ -184,10 +189,12 @@ static void queue_shift_down(struct queue *q)
 static struct list_head *queue_peek(struct queue *q)
 {
        unsigned level;
+       struct list_head *h;
 
        for (level = 0; level < NR_QUEUE_LEVELS; level++)
-               if (!list_empty(q->qs + level))
-                       return q->qs[level].next;
+               list_for_each(h, q->qs + level)
+                       if (!is_sentinel(q, h))
+                               return h;
 
        return NULL;
 }
@@ -197,16 +204,34 @@ static struct list_head *queue_pop(struct queue *q)
        struct list_head *r = queue_peek(q);
 
        if (r) {
+               q->nr_elts--;
                list_del(r);
-
-               /* have we just emptied the bottom level? */
-               if (list_empty(q->qs))
-                       queue_shift_down(q);
        }
 
        return r;
 }
 
+/*
+ * Pops an entry from a level that is not past a sentinel.
+ */
+static struct list_head *queue_pop_old(struct queue *q)
+{
+       unsigned level;
+       struct list_head *h;
+
+       for (level = 0; level < NR_QUEUE_LEVELS; level++)
+               list_for_each(h, q->qs + level) {
+                       if (is_sentinel(q, h))
+                               break;
+
+                       q->nr_elts--;
+                       list_del(h);
+                       return h;
+               }
+
+       return NULL;
+}
+
 static struct list_head *list_pop(struct list_head *lh)
 {
        struct list_head *r = lh->next;
@@ -217,6 +242,62 @@ static struct list_head *list_pop(struct list_head *lh)
        return r;
 }
 
+static struct list_head *writeback_sentinel(struct queue *q, unsigned level)
+{
+       if (q->current_writeback_sentinels)
+               return q->sentinels + NR_QUEUE_LEVELS + level;
+       else
+               return q->sentinels + 2 * NR_QUEUE_LEVELS + level;
+}
+
+static void queue_update_writeback_sentinels(struct queue *q)
+{
+       unsigned i;
+       struct list_head *h;
+
+       if (time_after(jiffies, q->next_writeback)) {
+               for (i = 0; i < NR_QUEUE_LEVELS; i++) {
+                       h = writeback_sentinel(q, i);
+                       list_del(h);
+                       list_add_tail(h, q->qs + i);
+               }
+
+               q->next_writeback = jiffies + WRITEBACK_PERIOD;
+               q->current_writeback_sentinels = !q->current_writeback_sentinels;
+       }
+}
+
+/*
+ * Sometimes we want to iterate through entries that have been pushed since
+ * a certain event.  We use sentinel entries on the queues to delimit these
+ * 'tick' events.
+ */
+static void queue_tick(struct queue *q)
+{
+       unsigned i;
+
+       for (i = 0; i < NR_QUEUE_LEVELS; i++) {
+               list_del(q->sentinels + i);
+               list_add_tail(q->sentinels + i, q->qs + i);
+       }
+}
+
+typedef void (*iter_fn)(struct list_head *, void *);
+static void queue_iterate_tick(struct queue *q, iter_fn fn, void *context)
+{
+       unsigned i;
+       struct list_head *h;
+
+       for (i = 0; i < NR_QUEUE_LEVELS; i++) {
+               list_for_each_prev(h, q->qs + i) {
+                       if (is_sentinel(q, h))
+                               break;
+
+                       fn(h, context);
+               }
+       }
+}
+
 /*----------------------------------------------------------------*/
 
 /*
@@ -232,8 +313,6 @@ struct entry {
         */
        bool dirty:1;
        unsigned hit_count;
-       unsigned generation;
-       unsigned tick;
 };
 
 /*
@@ -481,7 +560,6 @@ static bool in_cache(struct mq_policy *mq, struct entry *e)
  */
 static void push(struct mq_policy *mq, struct entry *e)
 {
-       e->tick = mq->tick;
        hash_insert(mq, e);
 
        if (in_cache(mq, e))
@@ -496,7 +574,11 @@ static void push(struct mq_policy *mq, struct entry *e)
  */
 static void del(struct mq_policy *mq, struct entry *e)
 {
-       queue_remove(&e->list);
+       if (in_cache(mq, e))
+               queue_remove(e->dirty ? &mq->cache_dirty : &mq->cache_clean, &e->list);
+       else
+               queue_remove(&mq->pre_cache, &e->list);
+
        hash_remove(e);
 }
 
@@ -518,18 +600,24 @@ static struct entry *pop(struct mq_policy *mq, struct queue *q)
        return e;
 }
 
-static struct entry *peek(struct queue *q)
+static struct entry *pop_old(struct mq_policy *mq, struct queue *q)
 {
-       struct list_head *h = queue_peek(q);
-       return h ? container_of(h, struct entry, list) : NULL;
+       struct entry *e;
+       struct list_head *h = queue_pop_old(q);
+
+       if (!h)
+               return NULL;
+
+       e = container_of(h, struct entry, list);
+       hash_remove(e);
+
+       return e;
 }
 
-/*
- * Has this entry already been updated?
- */
-static bool updated_this_tick(struct mq_policy *mq, struct entry *e)
+static struct entry *peek(struct queue *q)
 {
-       return mq->tick == e->tick;
+       struct list_head *h = queue_peek(q);
+       return h ? container_of(h, struct entry, list) : NULL;
 }
 
 /*
@@ -583,20 +671,9 @@ static void check_generation(struct mq_policy *mq)
  * Whenever we use an entry we bump up it's hit counter, and push it to the
  * back to it's current level.
  */
-static void requeue_and_update_tick(struct mq_policy *mq, struct entry *e)
+static void requeue(struct mq_policy *mq, struct entry *e)
 {
-       if (updated_this_tick(mq, e))
-               return;
-
-       e->hit_count++;
-       mq->hit_count++;
        check_generation(mq);
-
-       /* generation adjustment, to stop the counts increasing forever. */
-       /* FIXME: divide? */
-       /* e->hit_count -= min(e->hit_count - 1, mq->generation - e->generation); */
-       e->generation = mq->generation;
-
        del(mq, e);
        push(mq, e);
 }
@@ -703,7 +780,7 @@ static int cache_entry_found(struct mq_policy *mq,
                             struct entry *e,
                             struct policy_result *result)
 {
-       requeue_and_update_tick(mq, e);
+       requeue(mq, e);
 
        if (in_cache(mq, e)) {
                result->op = POLICY_HIT;
@@ -740,8 +817,6 @@ static int pre_cache_to_cache(struct mq_policy *mq, struct entry *e,
        new_e->oblock = e->oblock;
        new_e->dirty = false;
        new_e->hit_count = e->hit_count;
-       new_e->generation = e->generation;
-       new_e->tick = e->tick;
 
        del(mq, e);
        free_entry(&mq->pre_cache_pool, e);
@@ -757,18 +832,16 @@ static int pre_cache_entry_found(struct mq_policy *mq, struct entry *e,
                                 int data_dir, struct policy_result *result)
 {
        int r = 0;
-       bool updated = updated_this_tick(mq, e);
 
-       if ((!discarded_oblock && updated) ||
-           !should_promote(mq, e, discarded_oblock, data_dir)) {
-               requeue_and_update_tick(mq, e);
+       if (!should_promote(mq, e, discarded_oblock, data_dir)) {
+               requeue(mq, e);
                result->op = POLICY_MISS;
 
        } else if (!can_migrate)
                r = -EWOULDBLOCK;
 
        else {
-               requeue_and_update_tick(mq, e);
+               requeue(mq, e);
                r = pre_cache_to_cache(mq, e, result);
        }
 
@@ -795,7 +868,6 @@ static void insert_in_pre_cache(struct mq_policy *mq,
        e->dirty = false;
        e->oblock = oblock;
        e->hit_count = 1;
-       e->generation = mq->generation;
        push(mq, e);
 }
 
@@ -828,7 +900,6 @@ static void insert_in_cache(struct mq_policy *mq, dm_oblock_t oblock,
        e->oblock = oblock;
        e->dirty = false;
        e->hit_count = 1;
-       e->generation = mq->generation;
        push(mq, e);
 
        result->cblock = infer_cblock(&mq->cache_pool, e);
@@ -905,12 +976,37 @@ static void mq_destroy(struct dm_cache_policy *p)
        kfree(mq);
 }
 
+static void update_pre_cache_hits(struct list_head *h, void *context)
+{
+       struct entry *e = container_of(h, struct entry, list);
+       e->hit_count++;
+}
+
+static void update_cache_hits(struct list_head *h, void *context)
+{
+       struct mq_policy *mq = context;
+       struct entry *e = container_of(h, struct entry, list);
+       e->hit_count++;
+       mq->hit_count++;
+}
+
 static void copy_tick(struct mq_policy *mq)
 {
-       unsigned long flags;
+       unsigned long flags, tick;
 
        spin_lock_irqsave(&mq->tick_lock, flags);
-       mq->tick = mq->tick_protected;
+       tick = mq->tick_protected;
+       if (tick != mq->tick) {
+               queue_iterate_tick(&mq->pre_cache, update_pre_cache_hits, mq);
+               queue_iterate_tick(&mq->cache_dirty, update_cache_hits, mq);
+               queue_iterate_tick(&mq->cache_clean, update_cache_hits, mq);
+               mq->tick = tick;
+       }
+
+       queue_tick(&mq->pre_cache);
+       queue_tick(&mq->cache_dirty);
+       queue_tick(&mq->cache_clean);
+       queue_update_writeback_sentinels(&mq->cache_dirty);
        spin_unlock_irqrestore(&mq->tick_lock, flags);
 }
 
@@ -1001,7 +1097,6 @@ static int mq_load_mapping(struct dm_cache_policy *p,
        e->oblock = oblock;
        e->dirty = false;       /* this gets corrected in a minute */
        e->hit_count = hint_valid ? hint : 1;
-       e->generation = mq->generation;
        push(mq, e);
 
        return 0;
@@ -1012,10 +1107,15 @@ static int mq_save_hints(struct mq_policy *mq, struct queue *q,
 {
        int r;
        unsigned level;
+       struct list_head *h;
        struct entry *e;
 
        for (level = 0; level < NR_QUEUE_LEVELS; level++)
-               list_for_each_entry(e, q->qs + level, list) {
+               list_for_each(h, q->qs + level) {
+                       if (is_sentinel(q, h))
+                               continue;
+
+                       e = container_of(h, struct entry, list);
                        r = fn(context, infer_cblock(&mq->cache_pool, e),
                               e->oblock, e->hit_count);
                        if (r)
@@ -1087,10 +1187,27 @@ static int mq_remove_cblock(struct dm_cache_policy *p, dm_cblock_t cblock)
        return r;
 }
 
+#define CLEAN_TARGET_PERCENTAGE 25
+
+static bool clean_target_met(struct mq_policy *mq)
+{
+       /*
+        * Cache entries may not be populated.  So we're cannot rely on the
+        * size of the clean queue.
+        */
+       unsigned nr_clean = from_cblock(mq->cache_size) - queue_size(&mq->cache_dirty);
+       unsigned target = from_cblock(mq->cache_size) * CLEAN_TARGET_PERCENTAGE / 100;
+
+       return nr_clean >= target;
+}
+
 static int __mq_writeback_work(struct mq_policy *mq, dm_oblock_t *oblock,
                              dm_cblock_t *cblock)
 {
-       struct entry *e = pop(mq, &mq->cache_dirty);
+       struct entry *e = pop_old(mq, &mq->cache_dirty);
+
+       if (!e && !clean_target_met(mq))
+               e = pop(mq, &mq->cache_dirty);
 
        if (!e)
                return -ENODATA;
index 713a96237a80c34951302dfa4a5ea6db9f44c39b..9eeea196328acc63c3220c309399abf014dfbb4b 100644 (file)
@@ -228,7 +228,7 @@ static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc)
  *
  * tcw:  Compatible implementation of the block chaining mode used
  *       by the TrueCrypt device encryption system (prior to version 4.1).
- *       For more info see: http://www.truecrypt.org
+ *       For more info see: https://gitlab.com/cryptsetup/cryptsetup/wikis/TrueCryptOnDiskFormat
  *       It operates on full 512 byte sectors and uses CBC
  *       with an IV derived from initial key and the sector number.
  *       In addition, whitening value is applied on every sector, whitening
@@ -925,11 +925,10 @@ static int crypt_convert(struct crypt_config *cc,
 
                switch (r) {
                /* async */
+               case -EINPROGRESS:
                case -EBUSY:
                        wait_for_completion(&ctx->restart);
                        reinit_completion(&ctx->restart);
-                       /* fall through*/
-               case -EINPROGRESS:
                        ctx->req = NULL;
                        ctx->cc_sector++;
                        continue;
@@ -1124,15 +1123,15 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone)
 static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp)
 {
        struct crypt_config *cc = io->cc;
-       struct bio *base_bio = io->base_bio;
        struct bio *clone;
 
        /*
-        * The block layer might modify the bvec array, so always
-        * copy the required bvecs because we need the original
-        * one in order to decrypt the whole bio data *afterwards*.
+        * We need the original biovec array in order to decrypt
+        * the whole bio data *afterwards* -- thanks to immutable
+        * biovecs we don't need to worry about the block layer
+        * modifying the biovec array; so leverage bio_clone_fast().
         */
-       clone = bio_clone_bioset(base_bio, gfp, cc->bs);
+       clone = bio_clone_fast(io->base_bio, gfp, cc->bs);
        if (!clone)
                return 1;
 
@@ -1346,10 +1345,8 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
        struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
        struct crypt_config *cc = io->cc;
 
-       if (error == -EINPROGRESS) {
-               complete(&ctx->restart);
+       if (error == -EINPROGRESS)
                return;
-       }
 
        if (!error && cc->iv_gen_ops && cc->iv_gen_ops->post)
                error = cc->iv_gen_ops->post(cc, iv_of_dmreq(cc, dmreq), dmreq);
@@ -1360,12 +1357,15 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
        crypt_free_req(cc, req_of_dmreq(cc, dmreq), io->base_bio);
 
        if (!atomic_dec_and_test(&ctx->cc_pending))
-               return;
+               goto done;
 
        if (bio_data_dir(io->base_bio) == READ)
                kcryptd_crypt_read_done(io);
        else
                kcryptd_crypt_write_io_submit(io, 1);
+done:
+       if (!completion_done(&ctx->restart))
+               complete(&ctx->restart);
 }
 
 static void kcryptd_crypt(struct work_struct *work)
@@ -1816,6 +1816,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                if (ret)
                        goto bad;
 
+               ret = -EINVAL;
                while (opt_params--) {
                        opt_string = dm_shift_arg(&as);
                        if (!opt_string) {
index 42c3a27a14cc3a906b5f892a6206de348b6b58ee..57b6a1901c917127eccb8247cd1015a9b588159e 100644 (file)
@@ -236,7 +236,7 @@ static int delay_bio(struct delay_c *dc, int delay, struct bio *bio)
        delayed = dm_per_bio_data(bio, sizeof(struct dm_delay_info));
 
        delayed->context = dc;
-       delayed->expires = expires = jiffies + (delay * HZ / 1000);
+       delayed->expires = expires = jiffies + msecs_to_jiffies(delay);
 
        mutex_lock(&delayed_bios_lock);
 
index 03177ca0b0090435256510e58c80b7dbdce3b417..058256d2eeea8b6094f50190b9a0116137b238b4 100644 (file)
@@ -17,7 +17,9 @@
 
 #define DM_LOG_USERSPACE_VSN "1.3.0"
 
-struct flush_entry {
+#define FLUSH_ENTRY_POOL_SIZE 16
+
+struct dm_dirty_log_flush_entry {
        int type;
        region_t region;
        struct list_head list;
@@ -34,22 +36,14 @@ struct flush_entry {
 struct log_c {
        struct dm_target *ti;
        struct dm_dev *log_dev;
-       uint32_t region_size;
-       region_t region_count;
-       uint64_t luid;
-       char uuid[DM_UUID_LEN];
 
        char *usr_argv_str;
        uint32_t usr_argc;
 
-       /*
-        * in_sync_hint gets set when doing is_remote_recovering.  It
-        * represents the first region that needs recovery.  IOW, the
-        * first zero bit of sync_bits.  This can be useful for to limit
-        * traffic for calls like is_remote_recovering and get_resync_work,
-        * but be take care in its use for anything else.
-        */
-       uint64_t in_sync_hint;
+       uint32_t region_size;
+       region_t region_count;
+       uint64_t luid;
+       char uuid[DM_UUID_LEN];
 
        /*
         * Mark and clear requests are held until a flush is issued
@@ -61,6 +55,15 @@ struct log_c {
        struct list_head mark_list;
        struct list_head clear_list;
 
+       /*
+        * in_sync_hint gets set when doing is_remote_recovering.  It
+        * represents the first region that needs recovery.  IOW, the
+        * first zero bit of sync_bits.  This can be useful for to limit
+        * traffic for calls like is_remote_recovering and get_resync_work,
+        * but be take care in its use for anything else.
+        */
+       uint64_t in_sync_hint;
+
        /*
         * Workqueue for flush of clear region requests.
         */
@@ -72,19 +75,11 @@ struct log_c {
         * Combine userspace flush and mark requests for efficiency.
         */
        uint32_t integrated_flush;
-};
-
-static mempool_t *flush_entry_pool;
 
-static void *flush_entry_alloc(gfp_t gfp_mask, void *pool_data)
-{
-       return kmalloc(sizeof(struct flush_entry), gfp_mask);
-}
+       mempool_t *flush_entry_pool;
+};
 
-static void flush_entry_free(void *element, void *pool_data)
-{
-       kfree(element);
-}
+static struct kmem_cache *_flush_entry_cache;
 
 static int userspace_do_request(struct log_c *lc, const char *uuid,
                                int request_type, char *data, size_t data_size,
@@ -254,6 +249,14 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
                goto out;
        }
 
+       lc->flush_entry_pool = mempool_create_slab_pool(FLUSH_ENTRY_POOL_SIZE,
+                                                       _flush_entry_cache);
+       if (!lc->flush_entry_pool) {
+               DMERR("Failed to create flush_entry_pool");
+               r = -ENOMEM;
+               goto out;
+       }
+
        /*
         * Send table string and get back any opened device.
         */
@@ -310,6 +313,8 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
 out:
        kfree(devices_rdata);
        if (r) {
+               if (lc->flush_entry_pool)
+                       mempool_destroy(lc->flush_entry_pool);
                kfree(lc);
                kfree(ctr_str);
        } else {
@@ -338,6 +343,8 @@ static void userspace_dtr(struct dm_dirty_log *log)
        if (lc->log_dev)
                dm_put_device(lc->ti, lc->log_dev);
 
+       mempool_destroy(lc->flush_entry_pool);
+
        kfree(lc->usr_argv_str);
        kfree(lc);
 
@@ -461,7 +468,7 @@ static int userspace_in_sync(struct dm_dirty_log *log, region_t region,
 static int flush_one_by_one(struct log_c *lc, struct list_head *flush_list)
 {
        int r = 0;
-       struct flush_entry *fe;
+       struct dm_dirty_log_flush_entry *fe;
 
        list_for_each_entry(fe, flush_list, list) {
                r = userspace_do_request(lc, lc->uuid, fe->type,
@@ -481,7 +488,7 @@ static int flush_by_group(struct log_c *lc, struct list_head *flush_list,
        int r = 0;
        int count;
        uint32_t type = 0;
-       struct flush_entry *fe, *tmp_fe;
+       struct dm_dirty_log_flush_entry *fe, *tmp_fe;
        LIST_HEAD(tmp_list);
        uint64_t group[MAX_FLUSH_GROUP_COUNT];
 
@@ -563,7 +570,8 @@ static int userspace_flush(struct dm_dirty_log *log)
        LIST_HEAD(clear_list);
        int mark_list_is_empty;
        int clear_list_is_empty;
-       struct flush_entry *fe, *tmp_fe;
+       struct dm_dirty_log_flush_entry *fe, *tmp_fe;
+       mempool_t *flush_entry_pool = lc->flush_entry_pool;
 
        spin_lock_irqsave(&lc->flush_lock, flags);
        list_splice_init(&lc->mark_list, &mark_list);
@@ -643,10 +651,10 @@ static void userspace_mark_region(struct dm_dirty_log *log, region_t region)
 {
        unsigned long flags;
        struct log_c *lc = log->context;
-       struct flush_entry *fe;
+       struct dm_dirty_log_flush_entry *fe;
 
        /* Wait for an allocation, but _never_ fail */
-       fe = mempool_alloc(flush_entry_pool, GFP_NOIO);
+       fe = mempool_alloc(lc->flush_entry_pool, GFP_NOIO);
        BUG_ON(!fe);
 
        spin_lock_irqsave(&lc->flush_lock, flags);
@@ -672,7 +680,7 @@ static void userspace_clear_region(struct dm_dirty_log *log, region_t region)
 {
        unsigned long flags;
        struct log_c *lc = log->context;
-       struct flush_entry *fe;
+       struct dm_dirty_log_flush_entry *fe;
 
        /*
         * If we fail to allocate, we skip the clearing of
@@ -680,7 +688,7 @@ static void userspace_clear_region(struct dm_dirty_log *log, region_t region)
         * to cause the region to be resync'ed when the
         * device is activated next time.
         */
-       fe = mempool_alloc(flush_entry_pool, GFP_ATOMIC);
+       fe = mempool_alloc(lc->flush_entry_pool, GFP_ATOMIC);
        if (!fe) {
                DMERR("Failed to allocate memory to clear region.");
                return;
@@ -733,7 +741,6 @@ static int userspace_get_resync_work(struct dm_dirty_log *log, region_t *region)
 static void userspace_set_region_sync(struct dm_dirty_log *log,
                                      region_t region, int in_sync)
 {
-       int r;
        struct log_c *lc = log->context;
        struct {
                region_t r;
@@ -743,12 +750,12 @@ static void userspace_set_region_sync(struct dm_dirty_log *log,
        pkg.r = region;
        pkg.i = (int64_t)in_sync;
 
-       r = userspace_do_request(lc, lc->uuid, DM_ULOG_SET_REGION_SYNC,
-                                (char *)&pkg, sizeof(pkg), NULL, NULL);
+       (void) userspace_do_request(lc, lc->uuid, DM_ULOG_SET_REGION_SYNC,
+                                   (char *)&pkg, sizeof(pkg), NULL, NULL);
 
        /*
         * It would be nice to be able to report failures.
-        * However, it is easy emough to detect and resolve.
+        * However, it is easy enough to detect and resolve.
         */
        return;
 }
@@ -886,18 +893,16 @@ static int __init userspace_dirty_log_init(void)
 {
        int r = 0;
 
-       flush_entry_pool = mempool_create(100, flush_entry_alloc,
-                                         flush_entry_free, NULL);
-
-       if (!flush_entry_pool) {
-               DMWARN("Unable to create flush_entry_pool:  No memory.");
+       _flush_entry_cache = KMEM_CACHE(dm_dirty_log_flush_entry, 0);
+       if (!_flush_entry_cache) {
+               DMWARN("Unable to create flush_entry_cache: No memory.");
                return -ENOMEM;
        }
 
        r = dm_ulog_tfr_init();
        if (r) {
                DMWARN("Unable to initialize userspace log communications");
-               mempool_destroy(flush_entry_pool);
+               kmem_cache_destroy(_flush_entry_cache);
                return r;
        }
 
@@ -905,7 +910,7 @@ static int __init userspace_dirty_log_init(void)
        if (r) {
                DMWARN("Couldn't register userspace dirty log type");
                dm_ulog_tfr_exit();
-               mempool_destroy(flush_entry_pool);
+               kmem_cache_destroy(_flush_entry_cache);
                return r;
        }
 
@@ -917,7 +922,7 @@ static void __exit userspace_dirty_log_exit(void)
 {
        dm_dirty_log_type_unregister(&_userspace_type);
        dm_ulog_tfr_exit();
-       mempool_destroy(flush_entry_pool);
+       kmem_cache_destroy(_flush_entry_cache);
 
        DMINFO("version " DM_LOG_USERSPACE_VSN " unloaded");
        return;
index 39ad9664d39731022796430b0a3a30b7a8464898..fdf8ec304f8d26ae0cd45600e985ab3d845b47df 100644 (file)
@@ -172,6 +172,7 @@ int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
                         char *rdata, size_t *rdata_size)
 {
        int r = 0;
+       unsigned long tmo;
        size_t dummy = 0;
        int overhead_size = sizeof(struct dm_ulog_request) + sizeof(struct cn_msg);
        struct dm_ulog_request *tfr = prealloced_ulog_tfr;
@@ -236,11 +237,11 @@ resend:
                goto out;
        }
 
-       r = wait_for_completion_timeout(&(pkg.complete), DM_ULOG_RETRY_TIMEOUT);
+       tmo = wait_for_completion_timeout(&(pkg.complete), DM_ULOG_RETRY_TIMEOUT);
        spin_lock(&receiving_list_lock);
        list_del_init(&(pkg.list));
        spin_unlock(&receiving_list_lock);
-       if (!r) {
+       if (!tmo) {
                DMWARN("[%s] Request timed out: [%u/%u] - retrying",
                       (strlen(uuid) > 8) ?
                       (uuid + (strlen(uuid) - 8)) : (uuid),
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
new file mode 100644 (file)
index 0000000..93e0844
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2014 Facebook. All rights reserved.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/device-mapper.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#define DM_MSG_PREFIX "log-writes"
+
+/*
+ * This target will sequentially log all writes to the target device onto the
+ * log device.  This is helpful for replaying writes to check for fs consistency
+ * at all times.  This target provides a mechanism to mark specific events to
+ * check data at a later time.  So for example you would:
+ *
+ * write data
+ * fsync
+ * dmsetup message /dev/whatever mark mymark
+ * unmount /mnt/test
+ *
+ * Then replay the log up to mymark and check the contents of the replay to
+ * verify it matches what was written.
+ *
+ * We log writes only after they have been flushed, this makes the log describe
+ * close to the order in which the data hits the actual disk, not its cache.  So
+ * for example the following sequence (W means write, C means complete)
+ *
+ * Wa,Wb,Wc,Cc,Ca,FLUSH,FUAd,Cb,CFLUSH,CFUAd
+ *
+ * Would result in the log looking like this:
+ *
+ * c,a,flush,fuad,b,<other writes>,<next flush>
+ *
+ * This is meant to help expose problems where file systems do not properly wait
+ * on data being written before invoking a FLUSH.  FUA bypasses cache so once it
+ * completes it is added to the log as it should be on disk.
+ *
+ * We treat DISCARDs as if they don't bypass cache so that they are logged in
+ * order of completion along with the normal writes.  If we didn't do it this
+ * way we would process all the discards first and then write all the data, when
+ * in fact we want to do the data and the discard in the order that they
+ * completed.
+ */
+#define LOG_FLUSH_FLAG (1 << 0)
+#define LOG_FUA_FLAG (1 << 1)
+#define LOG_DISCARD_FLAG (1 << 2)
+#define LOG_MARK_FLAG (1 << 3)
+
+#define WRITE_LOG_VERSION 1
+#define WRITE_LOG_MAGIC 0x6a736677736872
+
+/*
+ * The disk format for this is braindead simple.
+ *
+ * At byte 0 we have our super, followed by the following sequence for
+ * nr_entries:
+ *
+ * [   1 sector    ][  entry->nr_sectors ]
+ * [log_write_entry][    data written    ]
+ *
+ * The log_write_entry takes up a full sector so we can have arbitrary length
+ * marks and it leaves us room for extra content in the future.
+ */
+
+/*
+ * Basic info about the log for userspace.
+ */
+struct log_write_super {
+       __le64 magic;
+       __le64 version;
+       __le64 nr_entries;
+       __le32 sectorsize;
+};
+
+/*
+ * sector - the sector we wrote.
+ * nr_sectors - the number of sectors we wrote.
+ * flags - flags for this log entry.
+ * data_len - the size of the data in this log entry, this is for private log
+ * entry stuff, the MARK data provided by userspace for example.
+ */
+struct log_write_entry {
+       __le64 sector;
+       __le64 nr_sectors;
+       __le64 flags;
+       __le64 data_len;
+};
+
+struct log_writes_c {
+       struct dm_dev *dev;
+       struct dm_dev *logdev;
+       u64 logged_entries;
+       u32 sectorsize;
+       atomic_t io_blocks;
+       atomic_t pending_blocks;
+       sector_t next_sector;
+       sector_t end_sector;
+       bool logging_enabled;
+       bool device_supports_discard;
+       spinlock_t blocks_lock;
+       struct list_head unflushed_blocks;
+       struct list_head logging_blocks;
+       wait_queue_head_t wait;
+       struct task_struct *log_kthread;
+};
+
+struct pending_block {
+       int vec_cnt;
+       u64 flags;
+       sector_t sector;
+       sector_t nr_sectors;
+       char *data;
+       u32 datalen;
+       struct list_head list;
+       struct bio_vec vecs[0];
+};
+
+struct per_bio_data {
+       struct pending_block *block;
+};
+
+static void put_pending_block(struct log_writes_c *lc)
+{
+       if (atomic_dec_and_test(&lc->pending_blocks)) {
+               smp_mb__after_atomic();
+               if (waitqueue_active(&lc->wait))
+                       wake_up(&lc->wait);
+       }
+}
+
+static void put_io_block(struct log_writes_c *lc)
+{
+       if (atomic_dec_and_test(&lc->io_blocks)) {
+               smp_mb__after_atomic();
+               if (waitqueue_active(&lc->wait))
+                       wake_up(&lc->wait);
+       }
+}
+
+static void log_end_io(struct bio *bio, int err)
+{
+       struct log_writes_c *lc = bio->bi_private;
+       struct bio_vec *bvec;
+       int i;
+
+       if (err) {
+               unsigned long flags;
+
+               DMERR("Error writing log block, error=%d", err);
+               spin_lock_irqsave(&lc->blocks_lock, flags);
+               lc->logging_enabled = false;
+               spin_unlock_irqrestore(&lc->blocks_lock, flags);
+       }
+
+       bio_for_each_segment_all(bvec, bio, i)
+               __free_page(bvec->bv_page);
+
+       put_io_block(lc);
+       bio_put(bio);
+}
+
+/*
+ * Meant to be called if there is an error, it will free all the pages
+ * associated with the block.
+ */
+static void free_pending_block(struct log_writes_c *lc,
+                              struct pending_block *block)
+{
+       int i;
+
+       for (i = 0; i < block->vec_cnt; i++) {
+               if (block->vecs[i].bv_page)
+                       __free_page(block->vecs[i].bv_page);
+       }
+       kfree(block->data);
+       kfree(block);
+       put_pending_block(lc);
+}
+
+static int write_metadata(struct log_writes_c *lc, void *entry,
+                         size_t entrylen, void *data, size_t datalen,
+                         sector_t sector)
+{
+       struct bio *bio;
+       struct page *page;
+       void *ptr;
+       size_t ret;
+
+       bio = bio_alloc(GFP_KERNEL, 1);
+       if (!bio) {
+               DMERR("Couldn't alloc log bio");
+               goto error;
+       }
+       bio->bi_iter.bi_size = 0;
+       bio->bi_iter.bi_sector = sector;
+       bio->bi_bdev = lc->logdev->bdev;
+       bio->bi_end_io = log_end_io;
+       bio->bi_private = lc;
+       set_bit(BIO_UPTODATE, &bio->bi_flags);
+
+       page = alloc_page(GFP_KERNEL);
+       if (!page) {
+               DMERR("Couldn't alloc log page");
+               bio_put(bio);
+               goto error;
+       }
+
+       ptr = kmap_atomic(page);
+       memcpy(ptr, entry, entrylen);
+       if (datalen)
+               memcpy(ptr + entrylen, data, datalen);
+       memset(ptr + entrylen + datalen, 0,
+              lc->sectorsize - entrylen - datalen);
+       kunmap_atomic(ptr);
+
+       ret = bio_add_page(bio, page, lc->sectorsize, 0);
+       if (ret != lc->sectorsize) {
+               DMERR("Couldn't add page to the log block");
+               goto error_bio;
+       }
+       submit_bio(WRITE, bio);
+       return 0;
+error_bio:
+       bio_put(bio);
+       __free_page(page);
+error:
+       put_io_block(lc);
+       return -1;
+}
+
+static int log_one_block(struct log_writes_c *lc,
+                        struct pending_block *block, sector_t sector)
+{
+       struct bio *bio;
+       struct log_write_entry entry;
+       size_t ret;
+       int i;
+
+       entry.sector = cpu_to_le64(block->sector);
+       entry.nr_sectors = cpu_to_le64(block->nr_sectors);
+       entry.flags = cpu_to_le64(block->flags);
+       entry.data_len = cpu_to_le64(block->datalen);
+       if (write_metadata(lc, &entry, sizeof(entry), block->data,
+                          block->datalen, sector)) {
+               free_pending_block(lc, block);
+               return -1;
+       }
+
+       if (!block->vec_cnt)
+               goto out;
+       sector++;
+
+       bio = bio_alloc(GFP_KERNEL, block->vec_cnt);
+       if (!bio) {
+               DMERR("Couldn't alloc log bio");
+               goto error;
+       }
+       atomic_inc(&lc->io_blocks);
+       bio->bi_iter.bi_size = 0;
+       bio->bi_iter.bi_sector = sector;
+       bio->bi_bdev = lc->logdev->bdev;
+       bio->bi_end_io = log_end_io;
+       bio->bi_private = lc;
+       set_bit(BIO_UPTODATE, &bio->bi_flags);
+
+       for (i = 0; i < block->vec_cnt; i++) {
+               /*
+                * The page offset is always 0 because we allocate a new page
+                * for every bvec in the original bio for simplicity sake.
+                */
+               ret = bio_add_page(bio, block->vecs[i].bv_page,
+                                  block->vecs[i].bv_len, 0);
+               if (ret != block->vecs[i].bv_len) {
+                       atomic_inc(&lc->io_blocks);
+                       submit_bio(WRITE, bio);
+                       bio = bio_alloc(GFP_KERNEL, block->vec_cnt - i);
+                       if (!bio) {
+                               DMERR("Couldn't alloc log bio");
+                               goto error;
+                       }
+                       bio->bi_iter.bi_size = 0;
+                       bio->bi_iter.bi_sector = sector;
+                       bio->bi_bdev = lc->logdev->bdev;
+                       bio->bi_end_io = log_end_io;
+                       bio->bi_private = lc;
+                       set_bit(BIO_UPTODATE, &bio->bi_flags);
+
+                       ret = bio_add_page(bio, block->vecs[i].bv_page,
+                                          block->vecs[i].bv_len, 0);
+                       if (ret != block->vecs[i].bv_len) {
+                               DMERR("Couldn't add page on new bio?");
+                               bio_put(bio);
+                               goto error;
+                       }
+               }
+               sector += block->vecs[i].bv_len >> SECTOR_SHIFT;
+       }
+       submit_bio(WRITE, bio);
+out:
+       kfree(block->data);
+       kfree(block);
+       put_pending_block(lc);
+       return 0;
+error:
+       free_pending_block(lc, block);
+       put_io_block(lc);
+       return -1;
+}
+
+static int log_super(struct log_writes_c *lc)
+{
+       struct log_write_super super;
+
+       super.magic = cpu_to_le64(WRITE_LOG_MAGIC);
+       super.version = cpu_to_le64(WRITE_LOG_VERSION);
+       super.nr_entries = cpu_to_le64(lc->logged_entries);
+       super.sectorsize = cpu_to_le32(lc->sectorsize);
+
+       if (write_metadata(lc, &super, sizeof(super), NULL, 0, 0)) {
+               DMERR("Couldn't write super");
+               return -1;
+       }
+
+       return 0;
+}
+
+static inline sector_t logdev_last_sector(struct log_writes_c *lc)
+{
+       return i_size_read(lc->logdev->bdev->bd_inode) >> SECTOR_SHIFT;
+}
+
+static int log_writes_kthread(void *arg)
+{
+       struct log_writes_c *lc = (struct log_writes_c *)arg;
+       sector_t sector = 0;
+
+       while (!kthread_should_stop()) {
+               bool super = false;
+               bool logging_enabled;
+               struct pending_block *block = NULL;
+               int ret;
+
+               spin_lock_irq(&lc->blocks_lock);
+               if (!list_empty(&lc->logging_blocks)) {
+                       block = list_first_entry(&lc->logging_blocks,
+                                                struct pending_block, list);
+                       list_del_init(&block->list);
+                       if (!lc->logging_enabled)
+                               goto next;
+
+                       sector = lc->next_sector;
+                       if (block->flags & LOG_DISCARD_FLAG)
+                               lc->next_sector++;
+                       else
+                               lc->next_sector += block->nr_sectors + 1;
+
+                       /*
+                        * Apparently the size of the device may not be known
+                        * right away, so handle this properly.
+                        */
+                       if (!lc->end_sector)
+                               lc->end_sector = logdev_last_sector(lc);
+                       if (lc->end_sector &&
+                           lc->next_sector >= lc->end_sector) {
+                               DMERR("Ran out of space on the logdev");
+                               lc->logging_enabled = false;
+                               goto next;
+                       }
+                       lc->logged_entries++;
+                       atomic_inc(&lc->io_blocks);
+
+                       super = (block->flags & (LOG_FUA_FLAG | LOG_MARK_FLAG));
+                       if (super)
+                               atomic_inc(&lc->io_blocks);
+               }
+next:
+               logging_enabled = lc->logging_enabled;
+               spin_unlock_irq(&lc->blocks_lock);
+               if (block) {
+                       if (logging_enabled) {
+                               ret = log_one_block(lc, block, sector);
+                               if (!ret && super)
+                                       ret = log_super(lc);
+                               if (ret) {
+                                       spin_lock_irq(&lc->blocks_lock);
+                                       lc->logging_enabled = false;
+                                       spin_unlock_irq(&lc->blocks_lock);
+                               }
+                       } else
+                               free_pending_block(lc, block);
+                       continue;
+               }
+
+               if (!try_to_freeze()) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       if (!kthread_should_stop() &&
+                           !atomic_read(&lc->pending_blocks))
+                               schedule();
+                       __set_current_state(TASK_RUNNING);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Construct a log-writes mapping:
+ * log-writes <dev_path> <log_dev_path>
+ */
+static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+       struct log_writes_c *lc;
+       struct dm_arg_set as;
+       const char *devname, *logdevname;
+
+       as.argc = argc;
+       as.argv = argv;
+
+       if (argc < 2) {
+               ti->error = "Invalid argument count";
+               return -EINVAL;
+       }
+
+       lc = kzalloc(sizeof(struct log_writes_c), GFP_KERNEL);
+       if (!lc) {
+               ti->error = "Cannot allocate context";
+               return -ENOMEM;
+       }
+       spin_lock_init(&lc->blocks_lock);
+       INIT_LIST_HEAD(&lc->unflushed_blocks);
+       INIT_LIST_HEAD(&lc->logging_blocks);
+       init_waitqueue_head(&lc->wait);
+       lc->sectorsize = 1 << SECTOR_SHIFT;
+       atomic_set(&lc->io_blocks, 0);
+       atomic_set(&lc->pending_blocks, 0);
+
+       devname = dm_shift_arg(&as);
+       if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &lc->dev)) {
+               ti->error = "Device lookup failed";
+               goto bad;
+       }
+
+       logdevname = dm_shift_arg(&as);
+       if (dm_get_device(ti, logdevname, dm_table_get_mode(ti->table), &lc->logdev)) {
+               ti->error = "Log device lookup failed";
+               dm_put_device(ti, lc->dev);
+               goto bad;
+       }
+
+       lc->log_kthread = kthread_run(log_writes_kthread, lc, "log-write");
+       if (!lc->log_kthread) {
+               ti->error = "Couldn't alloc kthread";
+               dm_put_device(ti, lc->dev);
+               dm_put_device(ti, lc->logdev);
+               goto bad;
+       }
+
+       /* We put the super at sector 0, start logging at sector 1 */
+       lc->next_sector = 1;
+       lc->logging_enabled = true;
+       lc->end_sector = logdev_last_sector(lc);
+       lc->device_supports_discard = true;
+
+       ti->num_flush_bios = 1;
+       ti->flush_supported = true;
+       ti->num_discard_bios = 1;
+       ti->discards_supported = true;
+       ti->per_bio_data_size = sizeof(struct per_bio_data);
+       ti->private = lc;
+       return 0;
+
+bad:
+       kfree(lc);
+       return -EINVAL;
+}
+
+static int log_mark(struct log_writes_c *lc, char *data)
+{
+       struct pending_block *block;
+       size_t maxsize = lc->sectorsize - sizeof(struct log_write_entry);
+
+       block = kzalloc(sizeof(struct pending_block), GFP_KERNEL);
+       if (!block) {
+               DMERR("Error allocating pending block");
+               return -ENOMEM;
+       }
+
+       block->data = kstrndup(data, maxsize, GFP_KERNEL);
+       if (!block->data) {
+               DMERR("Error copying mark data");
+               kfree(block);
+               return -ENOMEM;
+       }
+       atomic_inc(&lc->pending_blocks);
+       block->datalen = strlen(block->data);
+       block->flags |= LOG_MARK_FLAG;
+       spin_lock_irq(&lc->blocks_lock);
+       list_add_tail(&block->list, &lc->logging_blocks);
+       spin_unlock_irq(&lc->blocks_lock);
+       wake_up_process(lc->log_kthread);
+       return 0;
+}
+
+static void log_writes_dtr(struct dm_target *ti)
+{
+       struct log_writes_c *lc = ti->private;
+
+       spin_lock_irq(&lc->blocks_lock);
+       list_splice_init(&lc->unflushed_blocks, &lc->logging_blocks);
+       spin_unlock_irq(&lc->blocks_lock);
+
+       /*
+        * This is just nice to have since it'll update the super to include the
+        * unflushed blocks, if it fails we don't really care.
+        */
+       log_mark(lc, "dm-log-writes-end");
+       wake_up_process(lc->log_kthread);
+       wait_event(lc->wait, !atomic_read(&lc->io_blocks) &&
+                  !atomic_read(&lc->pending_blocks));
+       kthread_stop(lc->log_kthread);
+
+       WARN_ON(!list_empty(&lc->logging_blocks));
+       WARN_ON(!list_empty(&lc->unflushed_blocks));
+       dm_put_device(ti, lc->dev);
+       dm_put_device(ti, lc->logdev);
+       kfree(lc);
+}
+
+static void normal_map_bio(struct dm_target *ti, struct bio *bio)
+{
+       struct log_writes_c *lc = ti->private;
+
+       bio->bi_bdev = lc->dev->bdev;
+}
+
+static int log_writes_map(struct dm_target *ti, struct bio *bio)
+{
+       struct log_writes_c *lc = ti->private;
+       struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
+       struct pending_block *block;
+       struct bvec_iter iter;
+       struct bio_vec bv;
+       size_t alloc_size;
+       int i = 0;
+       bool flush_bio = (bio->bi_rw & REQ_FLUSH);
+       bool fua_bio = (bio->bi_rw & REQ_FUA);
+       bool discard_bio = (bio->bi_rw & REQ_DISCARD);
+
+       pb->block = NULL;
+
+       /* Don't bother doing anything if logging has been disabled */
+       if (!lc->logging_enabled)
+               goto map_bio;
+
+       /*
+        * Map reads as normal.
+        */
+       if (bio_data_dir(bio) == READ)
+               goto map_bio;
+
+       /* No sectors and not a flush?  Don't care */
+       if (!bio_sectors(bio) && !flush_bio)
+               goto map_bio;
+
+       /*
+        * Discards will have bi_size set but there's no actual data, so just
+        * allocate the size of the pending block.
+        */
+       if (discard_bio)
+               alloc_size = sizeof(struct pending_block);
+       else
+               alloc_size = sizeof(struct pending_block) + sizeof(struct bio_vec) * bio_segments(bio);
+
+       block = kzalloc(alloc_size, GFP_NOIO);
+       if (!block) {
+               DMERR("Error allocating pending block");
+               spin_lock_irq(&lc->blocks_lock);
+               lc->logging_enabled = false;
+               spin_unlock_irq(&lc->blocks_lock);
+               return -ENOMEM;
+       }
+       INIT_LIST_HEAD(&block->list);
+       pb->block = block;
+       atomic_inc(&lc->pending_blocks);
+
+       if (flush_bio)
+               block->flags |= LOG_FLUSH_FLAG;
+       if (fua_bio)
+               block->flags |= LOG_FUA_FLAG;
+       if (discard_bio)
+               block->flags |= LOG_DISCARD_FLAG;
+
+       block->sector = bio->bi_iter.bi_sector;
+       block->nr_sectors = bio_sectors(bio);
+
+       /* We don't need the data, just submit */
+       if (discard_bio) {
+               WARN_ON(flush_bio || fua_bio);
+               if (lc->device_supports_discard)
+                       goto map_bio;
+               bio_endio(bio, 0);
+               return DM_MAPIO_SUBMITTED;
+       }
+
+       /* Flush bio, splice the unflushed blocks onto this list and submit */
+       if (flush_bio && !bio_sectors(bio)) {
+               spin_lock_irq(&lc->blocks_lock);
+               list_splice_init(&lc->unflushed_blocks, &block->list);
+               spin_unlock_irq(&lc->blocks_lock);
+               goto map_bio;
+       }
+
+       /*
+        * We will write this bio somewhere else way later so we need to copy
+        * the actual contents into new pages so we know the data will always be
+        * there.
+        *
+        * We do this because this could be a bio from O_DIRECT in which case we
+        * can't just hold onto the page until some later point, we have to
+        * manually copy the contents.
+        */
+       bio_for_each_segment(bv, bio, iter) {
+               struct page *page;
+               void *src, *dst;
+
+               page = alloc_page(GFP_NOIO);
+               if (!page) {
+                       DMERR("Error allocing page");
+                       free_pending_block(lc, block);
+                       spin_lock_irq(&lc->blocks_lock);
+                       lc->logging_enabled = false;
+                       spin_unlock_irq(&lc->blocks_lock);
+                       return -ENOMEM;
+               }
+
+               src = kmap_atomic(bv.bv_page);
+               dst = kmap_atomic(page);
+               memcpy(dst, src + bv.bv_offset, bv.bv_len);
+               kunmap_atomic(dst);
+               kunmap_atomic(src);
+               block->vecs[i].bv_page = page;
+               block->vecs[i].bv_len = bv.bv_len;
+               block->vec_cnt++;
+               i++;
+       }
+
+       /* Had a flush with data in it, weird */
+       if (flush_bio) {
+               spin_lock_irq(&lc->blocks_lock);
+               list_splice_init(&lc->unflushed_blocks, &block->list);
+               spin_unlock_irq(&lc->blocks_lock);
+       }
+map_bio:
+       normal_map_bio(ti, bio);
+       return DM_MAPIO_REMAPPED;
+}
+
+static int normal_end_io(struct dm_target *ti, struct bio *bio, int error)
+{
+       struct log_writes_c *lc = ti->private;
+       struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
+
+       if (bio_data_dir(bio) == WRITE && pb->block) {
+               struct pending_block *block = pb->block;
+               unsigned long flags;
+
+               spin_lock_irqsave(&lc->blocks_lock, flags);
+               if (block->flags & LOG_FLUSH_FLAG) {
+                       list_splice_tail_init(&block->list, &lc->logging_blocks);
+                       list_add_tail(&block->list, &lc->logging_blocks);
+                       wake_up_process(lc->log_kthread);
+               } else if (block->flags & LOG_FUA_FLAG) {
+                       list_add_tail(&block->list, &lc->logging_blocks);
+                       wake_up_process(lc->log_kthread);
+               } else
+                       list_add_tail(&block->list, &lc->unflushed_blocks);
+               spin_unlock_irqrestore(&lc->blocks_lock, flags);
+       }
+
+       return error;
+}
+
+/*
+ * INFO format: <logged entries> <highest allocated sector>
+ */
+static void log_writes_status(struct dm_target *ti, status_type_t type,
+                             unsigned status_flags, char *result,
+                             unsigned maxlen)
+{
+       unsigned sz = 0;
+       struct log_writes_c *lc = ti->private;
+
+       switch (type) {
+       case STATUSTYPE_INFO:
+               DMEMIT("%llu %llu", lc->logged_entries,
+                      (unsigned long long)lc->next_sector - 1);
+               if (!lc->logging_enabled)
+                       DMEMIT(" logging_disabled");
+               break;
+
+       case STATUSTYPE_TABLE:
+               DMEMIT("%s %s", lc->dev->name, lc->logdev->name);
+               break;
+       }
+}
+
+static int log_writes_ioctl(struct dm_target *ti, unsigned int cmd,
+                           unsigned long arg)
+{
+       struct log_writes_c *lc = ti->private;
+       struct dm_dev *dev = lc->dev;
+       int r = 0;
+
+       /*
+        * Only pass ioctls through if the device sizes match exactly.
+        */
+       if (ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
+               r = scsi_verify_blk_ioctl(NULL, cmd);
+
+       return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg);
+}
+
+static int log_writes_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+                           struct bio_vec *biovec, int max_size)
+{
+       struct log_writes_c *lc = ti->private;
+       struct request_queue *q = bdev_get_queue(lc->dev->bdev);
+
+       if (!q->merge_bvec_fn)
+               return max_size;
+
+       bvm->bi_bdev = lc->dev->bdev;
+       bvm->bi_sector = dm_target_offset(ti, bvm->bi_sector);
+
+       return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static int log_writes_iterate_devices(struct dm_target *ti,
+                                     iterate_devices_callout_fn fn,
+                                     void *data)
+{
+       struct log_writes_c *lc = ti->private;
+
+       return fn(ti, lc->dev, 0, ti->len, data);
+}
+
+/*
+ * Messages supported:
+ *   mark <mark data> - specify the marked data.
+ */
+static int log_writes_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+       int r = -EINVAL;
+       struct log_writes_c *lc = ti->private;
+
+       if (argc != 2) {
+               DMWARN("Invalid log-writes message arguments, expect 2 arguments, got %d", argc);
+               return r;
+       }
+
+       if (!strcasecmp(argv[0], "mark"))
+               r = log_mark(lc, argv[1]);
+       else
+               DMWARN("Unrecognised log writes target message received: %s", argv[0]);
+
+       return r;
+}
+
+static void log_writes_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+       struct log_writes_c *lc = ti->private;
+       struct request_queue *q = bdev_get_queue(lc->dev->bdev);
+
+       if (!q || !blk_queue_discard(q)) {
+               lc->device_supports_discard = false;
+               limits->discard_granularity = 1 << SECTOR_SHIFT;
+               limits->max_discard_sectors = (UINT_MAX >> SECTOR_SHIFT);
+       }
+}
+
+static struct target_type log_writes_target = {
+       .name   = "log-writes",
+       .version = {1, 0, 0},
+       .module = THIS_MODULE,
+       .ctr    = log_writes_ctr,
+       .dtr    = log_writes_dtr,
+       .map    = log_writes_map,
+       .end_io = normal_end_io,
+       .status = log_writes_status,
+       .ioctl  = log_writes_ioctl,
+       .merge  = log_writes_merge,
+       .message = log_writes_message,
+       .iterate_devices = log_writes_iterate_devices,
+       .io_hints = log_writes_io_hints,
+};
+
+static int __init dm_log_writes_init(void)
+{
+       int r = dm_register_target(&log_writes_target);
+
+       if (r < 0)
+               DMERR("register failed %d", r);
+
+       return r;
+}
+
+static void __exit dm_log_writes_exit(void)
+{
+       dm_unregister_target(&log_writes_target);
+}
+
+module_init(dm_log_writes_init);
+module_exit(dm_log_writes_exit);
+
+MODULE_DESCRIPTION(DM_NAME " log writes target");
+MODULE_AUTHOR("Josef Bacik <jbacik@fb.com>");
+MODULE_LICENSE("GPL");
index d376dc87716ebbd64666e10d7ab54805b0e28f76..63953477a07c36e771a32d5bde686bd0f05890f1 100644 (file)
@@ -428,7 +428,7 @@ static int __multipath_map(struct dm_target *ti, struct request *clone,
        } else {
                /* blk-mq request-based interface */
                *__clone = blk_get_request(bdev_get_queue(bdev),
-                                          rq_data_dir(rq), GFP_KERNEL);
+                                          rq_data_dir(rq), GFP_ATOMIC);
                if (IS_ERR(*__clone))
                        /* ENOMEM, requeue */
                        return r;
@@ -1627,7 +1627,7 @@ static int __pgpath_busy(struct pgpath *pgpath)
 {
        struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
 
-       return dm_underlying_device_busy(q);
+       return blk_lld_busy(q);
 }
 
 /*
@@ -1703,7 +1703,7 @@ out:
  *---------------------------------------------------------------*/
 static struct target_type multipath_target = {
        .name = "multipath",
-       .version = {1, 8, 0},
+       .version = {1, 9, 0},
        .module = THIS_MODULE,
        .ctr = multipath_ctr,
        .dtr = multipath_dtr,
index c62c5ab6aed52018f6018f84f633124f6356e821..7e818f5f1dc4e048344b08a7b1ddf8e32304b2ac 100644 (file)
@@ -11,7 +11,7 @@
 struct dm_sysfs_attr {
        struct attribute attr;
        ssize_t (*show)(struct mapped_device *, char *);
-       ssize_t (*store)(struct mapped_device *, char *);
+       ssize_t (*store)(struct mapped_device *, const char *, size_t count);
 };
 
 #define DM_ATTR_RO(_name) \
@@ -39,6 +39,31 @@ static ssize_t dm_attr_show(struct kobject *kobj, struct attribute *attr,
        return ret;
 }
 
+#define DM_ATTR_RW(_name) \
+struct dm_sysfs_attr dm_attr_##_name = \
+       __ATTR(_name, S_IRUGO | S_IWUSR, dm_attr_##_name##_show, dm_attr_##_name##_store)
+
+static ssize_t dm_attr_store(struct kobject *kobj, struct attribute *attr,
+                            const char *page, size_t count)
+{
+       struct dm_sysfs_attr *dm_attr;
+       struct mapped_device *md;
+       ssize_t ret;
+
+       dm_attr = container_of(attr, struct dm_sysfs_attr, attr);
+       if (!dm_attr->store)
+               return -EIO;
+
+       md = dm_get_from_kobject(kobj);
+       if (!md)
+               return -EINVAL;
+
+       ret = dm_attr->store(md, page, count);
+       dm_put(md);
+
+       return ret;
+}
+
 static ssize_t dm_attr_name_show(struct mapped_device *md, char *buf)
 {
        if (dm_copy_name_and_uuid(md, buf, NULL))
@@ -64,25 +89,33 @@ static ssize_t dm_attr_suspended_show(struct mapped_device *md, char *buf)
        return strlen(buf);
 }
 
+static ssize_t dm_attr_use_blk_mq_show(struct mapped_device *md, char *buf)
+{
+       sprintf(buf, "%d\n", dm_use_blk_mq(md));
+
+       return strlen(buf);
+}
+
 static DM_ATTR_RO(name);
 static DM_ATTR_RO(uuid);
 static DM_ATTR_RO(suspended);
+static DM_ATTR_RO(use_blk_mq);
+static DM_ATTR_RW(rq_based_seq_io_merge_deadline);
 
 static struct attribute *dm_attrs[] = {
        &dm_attr_name.attr,
        &dm_attr_uuid.attr,
        &dm_attr_suspended.attr,
+       &dm_attr_use_blk_mq.attr,
+       &dm_attr_rq_based_seq_io_merge_deadline.attr,
        NULL,
 };
 
 static const struct sysfs_ops dm_sysfs_ops = {
        .show   = dm_attr_show,
+       .store  = dm_attr_store,
 };
 
-/*
- * dm kobject is embedded in mapped_device structure
- * no need to define release function here
- */
 static struct kobj_type dm_ktype = {
        .sysfs_ops      = &dm_sysfs_ops,
        .default_attrs  = dm_attrs,
index 6554d9148927771296e52cc2882f9e6f7fa68145..d9b00b8565c6dc1a36f5a3d863baa370126da593 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/atomic.h>
+#include <linux/blk-mq.h>
+#include <linux/mount.h>
 
 #define DM_MSG_PREFIX "table"
 
@@ -372,23 +374,18 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
        int r;
        dev_t uninitialized_var(dev);
        struct dm_dev_internal *dd;
-       unsigned int major, minor;
        struct dm_table *t = ti->table;
-       char dummy;
+       struct block_device *bdev;
 
        BUG_ON(!t);
 
-       if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) {
-               /* Extract the major/minor numbers */
-               dev = MKDEV(major, minor);
-               if (MAJOR(dev) != major || MINOR(dev) != minor)
-                       return -EOVERFLOW;
+       /* convert the path to a device */
+       bdev = lookup_bdev(path);
+       if (IS_ERR(bdev)) {
+               dev = name_to_dev_t(path);
+               if (!dev)
+                       return -ENODEV;
        } else {
-               /* convert the path to a device */
-               struct block_device *bdev = lookup_bdev(path);
-
-               if (IS_ERR(bdev))
-                       return PTR_ERR(bdev);
                dev = bdev->bd_dev;
                bdput(bdev);
        }
@@ -939,7 +936,7 @@ bool dm_table_mq_request_based(struct dm_table *t)
        return dm_table_get_type(t) == DM_TYPE_MQ_REQUEST_BASED;
 }
 
-static int dm_table_alloc_md_mempools(struct dm_table *t)
+static int dm_table_alloc_md_mempools(struct dm_table *t, struct mapped_device *md)
 {
        unsigned type = dm_table_get_type(t);
        unsigned per_bio_data_size = 0;
@@ -957,7 +954,7 @@ static int dm_table_alloc_md_mempools(struct dm_table *t)
                        per_bio_data_size = max(per_bio_data_size, tgt->per_bio_data_size);
                }
 
-       t->mempools = dm_alloc_md_mempools(type, t->integrity_supported, per_bio_data_size);
+       t->mempools = dm_alloc_md_mempools(md, type, t->integrity_supported, per_bio_data_size);
        if (!t->mempools)
                return -ENOMEM;
 
@@ -1127,7 +1124,7 @@ int dm_table_complete(struct dm_table *t)
                return r;
        }
 
-       r = dm_table_alloc_md_mempools(t);
+       r = dm_table_alloc_md_mempools(t, t->md);
        if (r)
                DMERR("unable to allocate mempools");
 
@@ -1339,14 +1336,14 @@ static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
                        continue;
 
                if (ti->flush_supported)
-                       return 1;
+                       return true;
 
                if (ti->type->iterate_devices &&
                    ti->type->iterate_devices(ti, device_flush_capable, &flush))
-                       return 1;
+                       return true;
        }
 
-       return 0;
+       return false;
 }
 
 static bool dm_table_discard_zeroes_data(struct dm_table *t)
@@ -1359,10 +1356,10 @@ static bool dm_table_discard_zeroes_data(struct dm_table *t)
                ti = dm_table_get_target(t, i++);
 
                if (ti->discard_zeroes_data_unsupported)
-                       return 0;
+                       return false;
        }
 
-       return 1;
+       return true;
 }
 
 static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
@@ -1408,10 +1405,10 @@ static bool dm_table_all_devices_attribute(struct dm_table *t,
 
                if (!ti->type->iterate_devices ||
                    !ti->type->iterate_devices(ti, func, NULL))
-                       return 0;
+                       return false;
        }
 
-       return 1;
+       return true;
 }
 
 static int device_not_write_same_capable(struct dm_target *ti, struct dm_dev *dev,
@@ -1468,14 +1465,14 @@ static bool dm_table_supports_discards(struct dm_table *t)
                        continue;
 
                if (ti->discards_supported)
-                       return 1;
+                       return true;
 
                if (ti->type->iterate_devices &&
                    ti->type->iterate_devices(ti, device_discard_capable, NULL))
-                       return 1;
+                       return true;
        }
 
-       return 0;
+       return false;
 }
 
 void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
@@ -1677,20 +1674,6 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits)
        return r;
 }
 
-int dm_table_any_busy_target(struct dm_table *t)
-{
-       unsigned i;
-       struct dm_target *ti;
-
-       for (i = 0; i < t->num_targets; i++) {
-               ti = t->targets + i;
-               if (ti->type->busy && ti->type->busy(ti))
-                       return 1;
-       }
-
-       return 0;
-}
-
 struct mapped_device *dm_table_get_md(struct dm_table *t)
 {
        return t->md;
@@ -1709,9 +1692,13 @@ void dm_table_run_md_queue_async(struct dm_table *t)
        md = dm_table_get_md(t);
        queue = dm_get_md_queue(md);
        if (queue) {
-               spin_lock_irqsave(queue->queue_lock, flags);
-               blk_run_queue_async(queue);
-               spin_unlock_irqrestore(queue->queue_lock, flags);
+               if (queue->mq_ops)
+                       blk_mq_run_hw_queues(queue, true);
+               else {
+                       spin_lock_irqsave(queue->queue_lock, flags);
+                       blk_run_queue_async(queue);
+                       spin_unlock_irqrestore(queue->queue_lock, flags);
+               }
        }
 }
 EXPORT_SYMBOL(dm_table_run_md_queue_async);
index 7a7bab8947ae3485d31c132cb3398251c7d507cf..66616db33e6fdbc14896704fe50483930ffb1cfd 100644 (file)
 
 #include <linux/module.h>
 #include <linux/device-mapper.h>
+#include <linux/reboot.h>
 #include <crypto/hash.h>
 
 #define DM_MSG_PREFIX                  "verity"
 
+#define DM_VERITY_ENV_LENGTH           42
+#define DM_VERITY_ENV_VAR_NAME         "DM_VERITY_ERR_BLOCK_NR"
+
 #define DM_VERITY_IO_VEC_INLINE                16
 #define DM_VERITY_MEMPOOL_SIZE         4
 #define DM_VERITY_DEFAULT_PREFETCH_SIZE        262144
 
 #define DM_VERITY_MAX_LEVELS           63
+#define DM_VERITY_MAX_CORRUPTED_ERRS   100
+
+#define DM_VERITY_OPT_LOGGING          "ignore_corruption"
+#define DM_VERITY_OPT_RESTART          "restart_on_corruption"
 
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 
 module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
 
+enum verity_mode {
+       DM_VERITY_MODE_EIO,
+       DM_VERITY_MODE_LOGGING,
+       DM_VERITY_MODE_RESTART
+};
+
+enum verity_block_type {
+       DM_VERITY_BLOCK_TYPE_DATA,
+       DM_VERITY_BLOCK_TYPE_METADATA
+};
+
 struct dm_verity {
        struct dm_dev *data_dev;
        struct dm_dev *hash_dev;
@@ -54,6 +73,8 @@ struct dm_verity {
        unsigned digest_size;   /* digest size for the current hash algorithm */
        unsigned shash_descsize;/* the size of temporary space for crypto */
        int hash_failed;        /* set to 1 if hash of any block failed */
+       enum verity_mode mode;  /* mode for handling verification errors */
+       unsigned corrupted_errs;/* Number of errors for corrupted blocks */
 
        mempool_t *vec_mempool; /* mempool of bio vector */
 
@@ -174,6 +195,57 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
                *offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
 }
 
+/*
+ * Handle verification errors.
+ */
+static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
+                            unsigned long long block)
+{
+       char verity_env[DM_VERITY_ENV_LENGTH];
+       char *envp[] = { verity_env, NULL };
+       const char *type_str = "";
+       struct mapped_device *md = dm_table_get_md(v->ti->table);
+
+       /* Corruption should be visible in device status in all modes */
+       v->hash_failed = 1;
+
+       if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
+               goto out;
+
+       v->corrupted_errs++;
+
+       switch (type) {
+       case DM_VERITY_BLOCK_TYPE_DATA:
+               type_str = "data";
+               break;
+       case DM_VERITY_BLOCK_TYPE_METADATA:
+               type_str = "metadata";
+               break;
+       default:
+               BUG();
+       }
+
+       DMERR("%s: %s block %llu is corrupted", v->data_dev->name, type_str,
+               block);
+
+       if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
+               DMERR("%s: reached maximum errors", v->data_dev->name);
+
+       snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
+               DM_VERITY_ENV_VAR_NAME, type, block);
+
+       kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);
+
+out:
+       if (v->mode == DM_VERITY_MODE_LOGGING)
+               return 0;
+
+       if (v->mode == DM_VERITY_MODE_RESTART)
+               kernel_restart("dm-verity device corrupted");
+
+       return 1;
+}
+
 /*
  * Verify hash of a metadata block pertaining to the specified data block
  * ("block" argument) at a specified level ("level" argument).
@@ -251,11 +323,11 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
                        goto release_ret_r;
                }
                if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
-                       DMERR_LIMIT("metadata block %llu is corrupted",
-                               (unsigned long long)hash_block);
-                       v->hash_failed = 1;
-                       r = -EIO;
-                       goto release_ret_r;
+                       if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
+                                             hash_block)) {
+                               r = -EIO;
+                               goto release_ret_r;
+                       }
                } else
                        aux->hash_verified = 1;
        }
@@ -367,10 +439,9 @@ test_block_hash:
                        return r;
                }
                if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
-                       DMERR_LIMIT("data block %llu is corrupted",
-                               (unsigned long long)(io->block + b));
-                       v->hash_failed = 1;
-                       return -EIO;
+                       if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
+                                             io->block + b))
+                               return -EIO;
                }
        }
 
@@ -546,6 +617,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                else
                        for (x = 0; x < v->salt_size; x++)
                                DMEMIT("%02x", v->salt[x]);
+               if (v->mode != DM_VERITY_MODE_EIO) {
+                       DMEMIT(" 1 ");
+                       switch (v->mode) {
+                       case DM_VERITY_MODE_LOGGING:
+                               DMEMIT(DM_VERITY_OPT_LOGGING);
+                               break;
+                       case DM_VERITY_MODE_RESTART:
+                               DMEMIT(DM_VERITY_OPT_RESTART);
+                               break;
+                       default:
+                               BUG();
+                       }
+               }
                break;
        }
 }
@@ -647,13 +731,19 @@ static void verity_dtr(struct dm_target *ti)
 static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 {
        struct dm_verity *v;
-       unsigned num;
+       struct dm_arg_set as;
+       const char *opt_string;
+       unsigned int num, opt_params;
        unsigned long long num_ll;
        int r;
        int i;
        sector_t hash_position;
        char dummy;
 
+       static struct dm_arg _args[] = {
+               {0, 1, "Invalid number of feature args"},
+       };
+
        v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
        if (!v) {
                ti->error = "Cannot allocate verity structure";
@@ -668,8 +758,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
-       if (argc != 10) {
-               ti->error = "Invalid argument count: exactly 10 arguments required";
+       if (argc < 10) {
+               ti->error = "Not enough arguments";
                r = -EINVAL;
                goto bad;
        }
@@ -790,6 +880,39 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                }
        }
 
+       argv += 10;
+       argc -= 10;
+
+       /* Optional parameters */
+       if (argc) {
+               as.argc = argc;
+               as.argv = argv;
+
+               r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+               if (r)
+                       goto bad;
+
+               while (opt_params) {
+                       opt_params--;
+                       opt_string = dm_shift_arg(&as);
+                       if (!opt_string) {
+                               ti->error = "Not enough feature arguments";
+                               r = -EINVAL;
+                               goto bad;
+                       }
+
+                       if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
+                               v->mode = DM_VERITY_MODE_LOGGING;
+                       else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
+                               v->mode = DM_VERITY_MODE_RESTART;
+                       else {
+                               ti->error = "Invalid feature arguments";
+                               r = -EINVAL;
+                               goto bad;
+                       }
+               }
+       }
+
        v->hash_per_block_bits =
                __fls((1 << v->hash_dev_block_bits) / v->digest_size);
 
index 8001fe9e3434734ad92c8109ef8fa860906238ce..f8c7ca3e8947378484a6d3f9745c363c78ab2879 100644 (file)
@@ -21,6 +21,9 @@
 #include <linux/delay.h>
 #include <linux/wait.h>
 #include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/elevator.h> /* for rq_end_sector() */
+#include <linux/blk-mq.h>
 
 #include <trace/events/block.h>
 
@@ -216,8 +219,29 @@ struct mapped_device {
 
        struct kthread_worker kworker;
        struct task_struct *kworker_task;
+
+       /* for request-based merge heuristic in dm_request_fn() */
+       unsigned seq_rq_merge_deadline_usecs;
+       int last_rq_rw;
+       sector_t last_rq_pos;
+       ktime_t last_rq_start_time;
+
+       /* for blk-mq request-based DM support */
+       struct blk_mq_tag_set tag_set;
+       bool use_blk_mq;
 };
 
+#ifdef CONFIG_DM_MQ_DEFAULT
+static bool use_blk_mq = true;
+#else
+static bool use_blk_mq = false;
+#endif
+
+bool dm_use_blk_mq(struct mapped_device *md)
+{
+       return md->use_blk_mq;
+}
+
 /*
  * For mempools pre-allocation at the table loading time.
  */
@@ -250,35 +274,35 @@ static unsigned reserved_bio_based_ios = RESERVED_BIO_BASED_IOS;
  */
 static unsigned reserved_rq_based_ios = RESERVED_REQUEST_BASED_IOS;
 
-static unsigned __dm_get_reserved_ios(unsigned *reserved_ios,
+static unsigned __dm_get_module_param(unsigned *module_param,
                                      unsigned def, unsigned max)
 {
-       unsigned ios = ACCESS_ONCE(*reserved_ios);
-       unsigned modified_ios = 0;
+       unsigned param = ACCESS_ONCE(*module_param);
+       unsigned modified_param = 0;
 
-       if (!ios)
-               modified_ios = def;
-       else if (ios > max)
-               modified_ios = max;
+       if (!param)
+               modified_param = def;
+       else if (param > max)
+               modified_param = max;
 
-       if (modified_ios) {
-               (void)cmpxchg(reserved_ios, ios, modified_ios);
-               ios = modified_ios;
+       if (modified_param) {
+               (void)cmpxchg(module_param, param, modified_param);
+               param = modified_param;
        }
 
-       return ios;
+       return param;
 }
 
 unsigned dm_get_reserved_bio_based_ios(void)
 {
-       return __dm_get_reserved_ios(&reserved_bio_based_ios,
+       return __dm_get_module_param(&reserved_bio_based_ios,
                                     RESERVED_BIO_BASED_IOS, RESERVED_MAX_IOS);
 }
 EXPORT_SYMBOL_GPL(dm_get_reserved_bio_based_ios);
 
 unsigned dm_get_reserved_rq_based_ios(void)
 {
-       return __dm_get_reserved_ios(&reserved_rq_based_ios,
+       return __dm_get_module_param(&reserved_rq_based_ios,
                                     RESERVED_REQUEST_BASED_IOS, RESERVED_MAX_IOS);
 }
 EXPORT_SYMBOL_GPL(dm_get_reserved_rq_based_ios);
@@ -1017,6 +1041,11 @@ static void end_clone_bio(struct bio *clone, int error)
        blk_update_request(tio->orig, 0, nr_bytes);
 }
 
+static struct dm_rq_target_io *tio_from_request(struct request *rq)
+{
+       return (rq->q->mq_ops ? blk_mq_rq_to_pdu(rq) : rq->special);
+}
+
 /*
  * Don't touch any member of the md after calling this function because
  * the md may be freed in dm_put() at the end of this function.
@@ -1024,10 +1053,13 @@ static void end_clone_bio(struct bio *clone, int error)
  */
 static void rq_completed(struct mapped_device *md, int rw, bool run_queue)
 {
+       int nr_requests_pending;
+
        atomic_dec(&md->pending[rw]);
 
        /* nudge anyone waiting on suspend queue */
-       if (!md_in_flight(md))
+       nr_requests_pending = md_in_flight(md);
+       if (!nr_requests_pending)
                wake_up(&md->wait);
 
        /*
@@ -1036,8 +1068,13 @@ static void rq_completed(struct mapped_device *md, int rw, bool run_queue)
         * back into ->request_fn() could deadlock attempting to grab the
         * queue lock again.
         */
-       if (run_queue)
-               blk_run_queue_async(md->queue);
+       if (run_queue) {
+               if (md->queue->mq_ops)
+                       blk_mq_run_hw_queues(md->queue, true);
+               else if (!nr_requests_pending ||
+                        (nr_requests_pending >= md->queue->nr_congestion_on))
+                       blk_run_queue_async(md->queue);
+       }
 
        /*
         * dm_put() must be at the end of this function. See the comment above
@@ -1048,13 +1085,18 @@ static void rq_completed(struct mapped_device *md, int rw, bool run_queue)
 static void free_rq_clone(struct request *clone)
 {
        struct dm_rq_target_io *tio = clone->end_io_data;
+       struct mapped_device *md = tio->md;
 
        blk_rq_unprep_clone(clone);
-       if (clone->q && clone->q->mq_ops)
+
+       if (clone->q->mq_ops)
                tio->ti->type->release_clone_rq(clone);
-       else
-               free_clone_request(tio->md, clone);
-       free_rq_tio(tio);
+       else if (!md->queue->mq_ops)
+               /* request_fn queue stacked on request_fn queue(s) */
+               free_clone_request(md, clone);
+
+       if (!md->queue->mq_ops)
+               free_rq_tio(tio);
 }
 
 /*
@@ -1083,17 +1125,22 @@ static void dm_end_request(struct request *clone, int error)
        }
 
        free_rq_clone(clone);
-       blk_end_request_all(rq, error);
+       if (!rq->q->mq_ops)
+               blk_end_request_all(rq, error);
+       else
+               blk_mq_end_request(rq, error);
        rq_completed(md, rw, true);
 }
 
 static void dm_unprep_request(struct request *rq)
 {
-       struct dm_rq_target_io *tio = rq->special;
+       struct dm_rq_target_io *tio = tio_from_request(rq);
        struct request *clone = tio->clone;
 
-       rq->special = NULL;
-       rq->cmd_flags &= ~REQ_DONTPREP;
+       if (!rq->q->mq_ops) {
+               rq->special = NULL;
+               rq->cmd_flags &= ~REQ_DONTPREP;
+       }
 
        if (clone)
                free_rq_clone(clone);
@@ -1102,18 +1149,29 @@ static void dm_unprep_request(struct request *rq)
 /*
  * Requeue the original request of a clone.
  */
-static void dm_requeue_unmapped_original_request(struct mapped_device *md,
-                                                struct request *rq)
+static void old_requeue_request(struct request *rq)
 {
-       int rw = rq_data_dir(rq);
        struct request_queue *q = rq->q;
        unsigned long flags;
 
-       dm_unprep_request(rq);
-
        spin_lock_irqsave(q->queue_lock, flags);
        blk_requeue_request(q, rq);
        spin_unlock_irqrestore(q->queue_lock, flags);
+}
+
+static void dm_requeue_unmapped_original_request(struct mapped_device *md,
+                                                struct request *rq)
+{
+       int rw = rq_data_dir(rq);
+
+       dm_unprep_request(rq);
+
+       if (!rq->q->mq_ops)
+               old_requeue_request(rq);
+       else {
+               blk_mq_requeue_request(rq);
+               blk_mq_kick_requeue_list(rq->q);
+       }
 
        rq_completed(md, rw, false);
 }
@@ -1125,35 +1183,44 @@ static void dm_requeue_unmapped_request(struct request *clone)
        dm_requeue_unmapped_original_request(tio->md, tio->orig);
 }
 
-static void __stop_queue(struct request_queue *q)
-{
-       blk_stop_queue(q);
-}
-
-static void stop_queue(struct request_queue *q)
+static void old_stop_queue(struct request_queue *q)
 {
        unsigned long flags;
 
+       if (blk_queue_stopped(q))
+               return;
+
        spin_lock_irqsave(q->queue_lock, flags);
-       __stop_queue(q);
+       blk_stop_queue(q);
        spin_unlock_irqrestore(q->queue_lock, flags);
 }
 
-static void __start_queue(struct request_queue *q)
+static void stop_queue(struct request_queue *q)
 {
-       if (blk_queue_stopped(q))
-               blk_start_queue(q);
+       if (!q->mq_ops)
+               old_stop_queue(q);
+       else
+               blk_mq_stop_hw_queues(q);
 }
 
-static void start_queue(struct request_queue *q)
+static void old_start_queue(struct request_queue *q)
 {
        unsigned long flags;
 
        spin_lock_irqsave(q->queue_lock, flags);
-       __start_queue(q);
+       if (blk_queue_stopped(q))
+               blk_start_queue(q);
        spin_unlock_irqrestore(q->queue_lock, flags);
 }
 
+static void start_queue(struct request_queue *q)
+{
+       if (!q->mq_ops)
+               old_start_queue(q);
+       else
+               blk_mq_start_stopped_hw_queues(q, true);
+}
+
 static void dm_done(struct request *clone, int error, bool mapped)
 {
        int r = error;
@@ -1192,13 +1259,20 @@ static void dm_done(struct request *clone, int error, bool mapped)
 static void dm_softirq_done(struct request *rq)
 {
        bool mapped = true;
-       struct dm_rq_target_io *tio = rq->special;
+       struct dm_rq_target_io *tio = tio_from_request(rq);
        struct request *clone = tio->clone;
+       int rw;
 
        if (!clone) {
-               blk_end_request_all(rq, tio->error);
-               rq_completed(tio->md, rq_data_dir(rq), false);
-               free_rq_tio(tio);
+               rw = rq_data_dir(rq);
+               if (!rq->q->mq_ops) {
+                       blk_end_request_all(rq, tio->error);
+                       rq_completed(tio->md, rw, false);
+                       free_rq_tio(tio);
+               } else {
+                       blk_mq_end_request(rq, tio->error);
+                       rq_completed(tio->md, rw, false);
+               }
                return;
        }
 
@@ -1214,7 +1288,7 @@ static void dm_softirq_done(struct request *rq)
  */
 static void dm_complete_request(struct request *rq, int error)
 {
-       struct dm_rq_target_io *tio = rq->special;
+       struct dm_rq_target_io *tio = tio_from_request(rq);
 
        tio->error = error;
        blk_complete_request(rq);
@@ -1233,7 +1307,7 @@ static void dm_kill_unmapped_request(struct request *rq, int error)
 }
 
 /*
- * Called with the clone's queue lock held
+ * Called with the clone's queue lock held (for non-blk-mq)
  */
 static void end_clone_request(struct request *clone, int error)
 {
@@ -1693,7 +1767,7 @@ out:
  * The request function that just remaps the bio built up by
  * dm_merge_bvec.
  */
-static void _dm_request(struct request_queue *q, struct bio *bio)
+static void dm_make_request(struct request_queue *q, struct bio *bio)
 {
        int rw = bio_data_dir(bio);
        struct mapped_device *md = q->queuedata;
@@ -1725,16 +1799,6 @@ int dm_request_based(struct mapped_device *md)
        return blk_queue_stackable(md->queue);
 }
 
-static void dm_request(struct request_queue *q, struct bio *bio)
-{
-       struct mapped_device *md = q->queuedata;
-
-       if (dm_request_based(md))
-               blk_queue_bio(q, bio);
-       else
-               _dm_request(q, bio);
-}
-
 static void dm_dispatch_clone_request(struct request *clone, struct request *rq)
 {
        int r;
@@ -1787,15 +1851,25 @@ static int setup_clone(struct request *clone, struct request *rq,
 static struct request *clone_rq(struct request *rq, struct mapped_device *md,
                                struct dm_rq_target_io *tio, gfp_t gfp_mask)
 {
-       struct request *clone = alloc_clone_request(md, gfp_mask);
+       /*
+        * Do not allocate a clone if tio->clone was already set
+        * (see: dm_mq_queue_rq).
+        */
+       bool alloc_clone = !tio->clone;
+       struct request *clone;
 
-       if (!clone)
-               return NULL;
+       if (alloc_clone) {
+               clone = alloc_clone_request(md, gfp_mask);
+               if (!clone)
+                       return NULL;
+       } else
+               clone = tio->clone;
 
        blk_rq_init(NULL, clone);
        if (setup_clone(clone, rq, tio, gfp_mask)) {
                /* -ENOMEM */
-               free_clone_request(md, clone);
+               if (alloc_clone)
+                       free_clone_request(md, clone);
                return NULL;
        }
 
@@ -1804,6 +1878,19 @@ static struct request *clone_rq(struct request *rq, struct mapped_device *md,
 
 static void map_tio_request(struct kthread_work *work);
 
+static void init_tio(struct dm_rq_target_io *tio, struct request *rq,
+                    struct mapped_device *md)
+{
+       tio->md = md;
+       tio->ti = NULL;
+       tio->clone = NULL;
+       tio->orig = rq;
+       tio->error = 0;
+       memset(&tio->info, 0, sizeof(tio->info));
+       if (md->kworker_task)
+               init_kthread_work(&tio->work, map_tio_request);
+}
+
 static struct dm_rq_target_io *prep_tio(struct request *rq,
                                        struct mapped_device *md, gfp_t gfp_mask)
 {
@@ -1815,13 +1902,7 @@ static struct dm_rq_target_io *prep_tio(struct request *rq,
        if (!tio)
                return NULL;
 
-       tio->md = md;
-       tio->ti = NULL;
-       tio->clone = NULL;
-       tio->orig = rq;
-       tio->error = 0;
-       memset(&tio->info, 0, sizeof(tio->info));
-       init_kthread_work(&tio->work, map_tio_request);
+       init_tio(tio, rq, md);
 
        table = dm_get_live_table(md, &srcu_idx);
        if (!dm_table_mq_request_based(table)) {
@@ -1865,11 +1946,11 @@ static int dm_prep_fn(struct request_queue *q, struct request *rq)
  * DM_MAPIO_REQUEUE : the original request needs to be requeued
  * < 0              : the request was completed due to failure
  */
-static int map_request(struct dm_target *ti, struct request *rq,
+static int map_request(struct dm_rq_target_io *tio, struct request *rq,
                       struct mapped_device *md)
 {
        int r;
-       struct dm_rq_target_io *tio = rq->special;
+       struct dm_target *ti = tio->ti;
        struct request *clone = NULL;
 
        if (tio->clone) {
@@ -1884,7 +1965,7 @@ static int map_request(struct dm_target *ti, struct request *rq,
                }
                if (IS_ERR(clone))
                        return DM_MAPIO_REQUEUE;
-               if (setup_clone(clone, rq, tio, GFP_KERNEL)) {
+               if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
                        /* -ENOMEM */
                        ti->type->release_clone_rq(clone);
                        return DM_MAPIO_REQUEUE;
@@ -1925,15 +2006,24 @@ static void map_tio_request(struct kthread_work *work)
        struct request *rq = tio->orig;
        struct mapped_device *md = tio->md;
 
-       if (map_request(tio->ti, rq, md) == DM_MAPIO_REQUEUE)
+       if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE)
                dm_requeue_unmapped_original_request(md, rq);
 }
 
 static void dm_start_request(struct mapped_device *md, struct request *orig)
 {
-       blk_start_request(orig);
+       if (!orig->q->mq_ops)
+               blk_start_request(orig);
+       else
+               blk_mq_start_request(orig);
        atomic_inc(&md->pending[rq_data_dir(orig)]);
 
+       if (md->seq_rq_merge_deadline_usecs) {
+               md->last_rq_pos = rq_end_sector(orig);
+               md->last_rq_rw = rq_data_dir(orig);
+               md->last_rq_start_time = ktime_get();
+       }
+
        /*
         * Hold the md reference here for the in-flight I/O.
         * We can't rely on the reference count by device opener,
@@ -1944,6 +2034,45 @@ static void dm_start_request(struct mapped_device *md, struct request *orig)
        dm_get(md);
 }
 
+#define MAX_SEQ_RQ_MERGE_DEADLINE_USECS 100000
+
+ssize_t dm_attr_rq_based_seq_io_merge_deadline_show(struct mapped_device *md, char *buf)
+{
+       return sprintf(buf, "%u\n", md->seq_rq_merge_deadline_usecs);
+}
+
+ssize_t dm_attr_rq_based_seq_io_merge_deadline_store(struct mapped_device *md,
+                                                    const char *buf, size_t count)
+{
+       unsigned deadline;
+
+       if (!dm_request_based(md) || md->use_blk_mq)
+               return count;
+
+       if (kstrtouint(buf, 10, &deadline))
+               return -EINVAL;
+
+       if (deadline > MAX_SEQ_RQ_MERGE_DEADLINE_USECS)
+               deadline = MAX_SEQ_RQ_MERGE_DEADLINE_USECS;
+
+       md->seq_rq_merge_deadline_usecs = deadline;
+
+       return count;
+}
+
+static bool dm_request_peeked_before_merge_deadline(struct mapped_device *md)
+{
+       ktime_t kt_deadline;
+
+       if (!md->seq_rq_merge_deadline_usecs)
+               return false;
+
+       kt_deadline = ns_to_ktime((u64)md->seq_rq_merge_deadline_usecs * NSEC_PER_USEC);
+       kt_deadline = ktime_add_safe(md->last_rq_start_time, kt_deadline);
+
+       return !ktime_after(ktime_get(), kt_deadline);
+}
+
 /*
  * q->request_fn for request-based dm.
  * Called with the queue lock held.
@@ -1967,7 +2096,7 @@ static void dm_request_fn(struct request_queue *q)
        while (!blk_queue_stopped(q)) {
                rq = blk_peek_request(q);
                if (!rq)
-                       goto delay_and_out;
+                       goto out;
 
                /* always use block 0 to find the target for flushes for now */
                pos = 0;
@@ -1986,12 +2115,17 @@ static void dm_request_fn(struct request_queue *q)
                        continue;
                }
 
+               if (dm_request_peeked_before_merge_deadline(md) &&
+                   md_in_flight(md) && rq->bio && rq->bio->bi_vcnt == 1 &&
+                   md->last_rq_pos == pos && md->last_rq_rw == rq_data_dir(rq))
+                       goto delay_and_out;
+
                if (ti->type->busy && ti->type->busy(ti))
                        goto delay_and_out;
 
                dm_start_request(md, rq);
 
-               tio = rq->special;
+               tio = tio_from_request(rq);
                /* Establish tio->ti before queuing work (map_tio_request) */
                tio->ti = ti;
                queue_kthread_work(&md->kworker, &tio->work);
@@ -2001,33 +2135,11 @@ static void dm_request_fn(struct request_queue *q)
        goto out;
 
 delay_and_out:
-       blk_delay_queue(q, HZ / 10);
+       blk_delay_queue(q, HZ / 100);
 out:
        dm_put_live_table(md, srcu_idx);
 }
 
-int dm_underlying_device_busy(struct request_queue *q)
-{
-       return blk_lld_busy(q);
-}
-EXPORT_SYMBOL_GPL(dm_underlying_device_busy);
-
-static int dm_lld_busy(struct request_queue *q)
-{
-       int r;
-       struct mapped_device *md = q->queuedata;
-       struct dm_table *map = dm_get_live_table_fast(md);
-
-       if (!map || test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))
-               r = 1;
-       else
-               r = dm_table_any_busy_target(map);
-
-       dm_put_live_table_fast(md);
-
-       return r;
-}
-
 static int dm_any_congested(void *congested_data, int bdi_bits)
 {
        int r = bdi_bits;
@@ -2110,7 +2222,7 @@ static void dm_init_md_queue(struct mapped_device *md)
 {
        /*
         * Request-based dm devices cannot be stacked on top of bio-based dm
-        * devices.  The type of this dm device has not been decided yet.
+        * devices.  The type of this dm device may not have been decided yet.
         * The type is decided at the first table loading time.
         * To prevent problematic device stacking, clear the queue flag
         * for request stacking support until then.
@@ -2118,13 +2230,21 @@ static void dm_init_md_queue(struct mapped_device *md)
         * This queue is new, so no concurrency on the queue_flags.
         */
        queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue);
+}
+
+static void dm_init_old_md_queue(struct mapped_device *md)
+{
+       md->use_blk_mq = false;
+       dm_init_md_queue(md);
 
+       /*
+        * Initialize aspects of queue that aren't relevant for blk-mq
+        */
        md->queue->queuedata = md;
        md->queue->backing_dev_info.congested_fn = dm_any_congested;
        md->queue->backing_dev_info.congested_data = md;
-       blk_queue_make_request(md->queue, dm_request);
+
        blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
-       blk_queue_merge_bvec(md->queue, dm_merge_bvec);
 }
 
 /*
@@ -2156,6 +2276,7 @@ static struct mapped_device *alloc_dev(int minor)
        if (r < 0)
                goto bad_io_barrier;
 
+       md->use_blk_mq = use_blk_mq;
        md->type = DM_TYPE_NONE;
        mutex_init(&md->suspend_lock);
        mutex_init(&md->type_lock);
@@ -2267,6 +2388,8 @@ static void free_dev(struct mapped_device *md)
        del_gendisk(md->disk);
        put_disk(md->disk);
        blk_cleanup_queue(md->queue);
+       if (md->use_blk_mq)
+               blk_mq_free_tag_set(&md->tag_set);
        bdput(md->bdev);
        free_minor(minor);
 
@@ -2278,7 +2401,7 @@ static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
 {
        struct dm_md_mempools *p = dm_table_get_md_mempools(t);
 
-       if (md->io_pool && md->bs) {
+       if (md->bs) {
                /* The md already has necessary mempools. */
                if (dm_table_get_type(t) == DM_TYPE_BIO_BASED) {
                        /*
@@ -2310,7 +2433,7 @@ static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
        p->bs = NULL;
 
 out:
-       /* mempool bind completed, now no need any mempools in the table */
+       /* mempool bind completed, no longer need any mempools in the table */
        dm_table_free_md_mempools(t);
 }
 
@@ -2357,7 +2480,7 @@ int dm_queue_merge_is_compulsory(struct request_queue *q)
        if (!q->merge_bvec_fn)
                return 0;
 
-       if (q->make_request_fn == dm_request) {
+       if (q->make_request_fn == dm_make_request) {
                dev_md = q->queuedata;
                if (test_bit(DMF_MERGE_IS_OPTIONAL, &dev_md->flags))
                        return 0;
@@ -2426,7 +2549,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
         * This must be done before setting the queue restrictions,
         * because request-based dm may be run just after the setting.
         */
-       if (dm_table_request_based(t) && !blk_queue_stopped(q))
+       if (dm_table_request_based(t))
                stop_queue(q);
 
        __bind_mempools(md, t);
@@ -2508,14 +2631,6 @@ unsigned dm_get_md_type(struct mapped_device *md)
        return md->type;
 }
 
-static bool dm_md_type_request_based(struct mapped_device *md)
-{
-       unsigned table_type = dm_get_md_type(md);
-
-       return (table_type == DM_TYPE_REQUEST_BASED ||
-               table_type == DM_TYPE_MQ_REQUEST_BASED);
-}
-
 struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
 {
        return md->immutable_target_type;
@@ -2532,6 +2647,14 @@ struct queue_limits *dm_get_queue_limits(struct mapped_device *md)
 }
 EXPORT_SYMBOL_GPL(dm_get_queue_limits);
 
+static void init_rq_based_worker_thread(struct mapped_device *md)
+{
+       /* Initialize the request-based DM worker thread */
+       init_kthread_worker(&md->kworker);
+       md->kworker_task = kthread_run(kthread_worker_fn, &md->kworker,
+                                      "kdmwork-%s", dm_device_name(md));
+}
+
 /*
  * Fully initialize a request-based queue (->elevator, ->request_fn, etc).
  */
@@ -2540,27 +2663,160 @@ static int dm_init_request_based_queue(struct mapped_device *md)
        struct request_queue *q = NULL;
 
        if (md->queue->elevator)
-               return 1;
+               return 0;
 
        /* Fully initialize the queue */
        q = blk_init_allocated_queue(md->queue, dm_request_fn, NULL);
        if (!q)
-               return 0;
+               return -EINVAL;
+
+       /* disable dm_request_fn's merge heuristic by default */
+       md->seq_rq_merge_deadline_usecs = 0;
 
        md->queue = q;
-       dm_init_md_queue(md);
+       dm_init_old_md_queue(md);
        blk_queue_softirq_done(md->queue, dm_softirq_done);
        blk_queue_prep_rq(md->queue, dm_prep_fn);
-       blk_queue_lld_busy(md->queue, dm_lld_busy);
 
-       /* Also initialize the request-based DM worker thread */
-       init_kthread_worker(&md->kworker);
-       md->kworker_task = kthread_run(kthread_worker_fn, &md->kworker,
-                                      "kdmwork-%s", dm_device_name(md));
+       init_rq_based_worker_thread(md);
 
        elv_register_queue(md->queue);
 
-       return 1;
+       return 0;
+}
+
+static int dm_mq_init_request(void *data, struct request *rq,
+                             unsigned int hctx_idx, unsigned int request_idx,
+                             unsigned int numa_node)
+{
+       struct mapped_device *md = data;
+       struct dm_rq_target_io *tio = blk_mq_rq_to_pdu(rq);
+
+       /*
+        * Must initialize md member of tio, otherwise it won't
+        * be available in dm_mq_queue_rq.
+        */
+       tio->md = md;
+
+       return 0;
+}
+
+static int dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
+                         const struct blk_mq_queue_data *bd)
+{
+       struct request *rq = bd->rq;
+       struct dm_rq_target_io *tio = blk_mq_rq_to_pdu(rq);
+       struct mapped_device *md = tio->md;
+       int srcu_idx;
+       struct dm_table *map = dm_get_live_table(md, &srcu_idx);
+       struct dm_target *ti;
+       sector_t pos;
+
+       /* always use block 0 to find the target for flushes for now */
+       pos = 0;
+       if (!(rq->cmd_flags & REQ_FLUSH))
+               pos = blk_rq_pos(rq);
+
+       ti = dm_table_find_target(map, pos);
+       if (!dm_target_is_valid(ti)) {
+               dm_put_live_table(md, srcu_idx);
+               DMERR_LIMIT("request attempted access beyond the end of device");
+               /*
+                * Must perform setup, that rq_completed() requires,
+                * before returning BLK_MQ_RQ_QUEUE_ERROR
+                */
+               dm_start_request(md, rq);
+               return BLK_MQ_RQ_QUEUE_ERROR;
+       }
+       dm_put_live_table(md, srcu_idx);
+
+       if (ti->type->busy && ti->type->busy(ti))
+               return BLK_MQ_RQ_QUEUE_BUSY;
+
+       dm_start_request(md, rq);
+
+       /* Init tio using md established in .init_request */
+       init_tio(tio, rq, md);
+
+       /*
+        * Establish tio->ti before queuing work (map_tio_request)
+        * or making direct call to map_request().
+        */
+       tio->ti = ti;
+
+       /* Clone the request if underlying devices aren't blk-mq */
+       if (dm_table_get_type(map) == DM_TYPE_REQUEST_BASED) {
+               /* clone request is allocated at the end of the pdu */
+               tio->clone = (void *)blk_mq_rq_to_pdu(rq) + sizeof(struct dm_rq_target_io);
+               if (!clone_rq(rq, md, tio, GFP_ATOMIC))
+                       return BLK_MQ_RQ_QUEUE_BUSY;
+               queue_kthread_work(&md->kworker, &tio->work);
+       } else {
+               /* Direct call is fine since .queue_rq allows allocations */
+               if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE)
+                       dm_requeue_unmapped_original_request(md, rq);
+       }
+
+       return BLK_MQ_RQ_QUEUE_OK;
+}
+
+static struct blk_mq_ops dm_mq_ops = {
+       .queue_rq = dm_mq_queue_rq,
+       .map_queue = blk_mq_map_queue,
+       .complete = dm_softirq_done,
+       .init_request = dm_mq_init_request,
+};
+
+static int dm_init_request_based_blk_mq_queue(struct mapped_device *md)
+{
+       unsigned md_type = dm_get_md_type(md);
+       struct request_queue *q;
+       int err;
+
+       memset(&md->tag_set, 0, sizeof(md->tag_set));
+       md->tag_set.ops = &dm_mq_ops;
+       md->tag_set.queue_depth = BLKDEV_MAX_RQ;
+       md->tag_set.numa_node = NUMA_NO_NODE;
+       md->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+       md->tag_set.nr_hw_queues = 1;
+       if (md_type == DM_TYPE_REQUEST_BASED) {
+               /* make the memory for non-blk-mq clone part of the pdu */
+               md->tag_set.cmd_size = sizeof(struct dm_rq_target_io) + sizeof(struct request);
+       } else
+               md->tag_set.cmd_size = sizeof(struct dm_rq_target_io);
+       md->tag_set.driver_data = md;
+
+       err = blk_mq_alloc_tag_set(&md->tag_set);
+       if (err)
+               return err;
+
+       q = blk_mq_init_allocated_queue(&md->tag_set, md->queue);
+       if (IS_ERR(q)) {
+               err = PTR_ERR(q);
+               goto out_tag_set;
+       }
+       md->queue = q;
+       dm_init_md_queue(md);
+
+       /* backfill 'mq' sysfs registration normally done in blk_register_queue */
+       blk_mq_register_disk(md->disk);
+
+       if (md_type == DM_TYPE_REQUEST_BASED)
+               init_rq_based_worker_thread(md);
+
+       return 0;
+
+out_tag_set:
+       blk_mq_free_tag_set(&md->tag_set);
+       return err;
+}
+
+static unsigned filter_md_type(unsigned type, struct mapped_device *md)
+{
+       if (type == DM_TYPE_BIO_BASED)
+               return type;
+
+       return !md->use_blk_mq ? DM_TYPE_REQUEST_BASED : DM_TYPE_MQ_REQUEST_BASED;
 }
 
 /*
@@ -2568,9 +2824,29 @@ static int dm_init_request_based_queue(struct mapped_device *md)
  */
 int dm_setup_md_queue(struct mapped_device *md)
 {
-       if (dm_md_type_request_based(md) && !dm_init_request_based_queue(md)) {
-               DMWARN("Cannot initialize queue for request-based mapped device");
-               return -EINVAL;
+       int r;
+       unsigned md_type = filter_md_type(dm_get_md_type(md), md);
+
+       switch (md_type) {
+       case DM_TYPE_REQUEST_BASED:
+               r = dm_init_request_based_queue(md);
+               if (r) {
+                       DMWARN("Cannot initialize queue for request-based mapped device");
+                       return r;
+               }
+               break;
+       case DM_TYPE_MQ_REQUEST_BASED:
+               r = dm_init_request_based_blk_mq_queue(md);
+               if (r) {
+                       DMWARN("Cannot initialize queue for request-based blk-mq mapped device");
+                       return r;
+               }
+               break;
+       case DM_TYPE_BIO_BASED:
+               dm_init_old_md_queue(md);
+               blk_queue_make_request(md->queue, dm_make_request);
+               blk_queue_merge_bvec(md->queue, dm_merge_bvec);
+               break;
        }
 
        return 0;
@@ -2654,7 +2930,7 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
        set_bit(DMF_FREEING, &md->flags);
        spin_unlock(&_minor_lock);
 
-       if (dm_request_based(md))
+       if (dm_request_based(md) && md->kworker_task)
                flush_kthread_worker(&md->kworker);
 
        /*
@@ -2908,7 +3184,8 @@ static int __dm_suspend(struct mapped_device *md, struct dm_table *map,
         */
        if (dm_request_based(md)) {
                stop_queue(md->queue);
-               flush_kthread_worker(&md->kworker);
+               if (md->kworker_task)
+                       flush_kthread_worker(&md->kworker);
        }
 
        flush_workqueue(md->wq);
@@ -3206,6 +3483,7 @@ struct gendisk *dm_disk(struct mapped_device *md)
 {
        return md->disk;
 }
+EXPORT_SYMBOL_GPL(dm_disk);
 
 struct kobject *dm_kobject(struct mapped_device *md)
 {
@@ -3253,16 +3531,19 @@ int dm_noflush_suspending(struct dm_target *ti)
 }
 EXPORT_SYMBOL_GPL(dm_noflush_suspending);
 
-struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, unsigned per_bio_data_size)
+struct dm_md_mempools *dm_alloc_md_mempools(struct mapped_device *md, unsigned type,
+                                           unsigned integrity, unsigned per_bio_data_size)
 {
        struct dm_md_mempools *pools = kzalloc(sizeof(*pools), GFP_KERNEL);
-       struct kmem_cache *cachep;
+       struct kmem_cache *cachep = NULL;
        unsigned int pool_size = 0;
        unsigned int front_pad;
 
        if (!pools)
                return NULL;
 
+       type = filter_md_type(type, md);
+
        switch (type) {
        case DM_TYPE_BIO_BASED:
                cachep = _io_cache;
@@ -3270,13 +3551,13 @@ struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, u
                front_pad = roundup(per_bio_data_size, __alignof__(struct dm_target_io)) + offsetof(struct dm_target_io, clone);
                break;
        case DM_TYPE_REQUEST_BASED:
+               cachep = _rq_tio_cache;
                pool_size = dm_get_reserved_rq_based_ios();
                pools->rq_pool = mempool_create_slab_pool(pool_size, _rq_cache);
                if (!pools->rq_pool)
                        goto out;
                /* fall through to setup remaining rq-based pools */
        case DM_TYPE_MQ_REQUEST_BASED:
-               cachep = _rq_tio_cache;
                if (!pool_size)
                        pool_size = dm_get_reserved_rq_based_ios();
                front_pad = offsetof(struct dm_rq_clone_bio_info, clone);
@@ -3284,12 +3565,14 @@ struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, u
                WARN_ON(per_bio_data_size != 0);
                break;
        default:
-               goto out;
+               BUG();
        }
 
-       pools->io_pool = mempool_create_slab_pool(pool_size, cachep);
-       if (!pools->io_pool)
-               goto out;
+       if (cachep) {
+               pools->io_pool = mempool_create_slab_pool(pool_size, cachep);
+               if (!pools->io_pool)
+                       goto out;
+       }
 
        pools->bs = bioset_create_nobvec(pool_size, front_pad);
        if (!pools->bs)
@@ -3346,6 +3629,9 @@ MODULE_PARM_DESC(reserved_bio_based_ios, "Reserved IOs in bio-based mempools");
 module_param(reserved_rq_based_ios, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(reserved_rq_based_ios, "Reserved IOs in request-based mempools");
 
+module_param(use_blk_mq, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(use_blk_mq, "Use block multiqueue for request-based DM devices");
+
 MODULE_DESCRIPTION(DM_NAME " driver");
 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
 MODULE_LICENSE("GPL");
index 59f53e79db8264521dc00d2563c7b58182b7e0da..6123c2bf9150cb836c1ecd80ebfe51c9f9aa82fd 100644 (file)
@@ -70,7 +70,6 @@ void dm_table_presuspend_undo_targets(struct dm_table *t);
 void dm_table_postsuspend_targets(struct dm_table *t);
 int dm_table_resume_targets(struct dm_table *t);
 int dm_table_any_congested(struct dm_table *t, int bdi_bits);
-int dm_table_any_busy_target(struct dm_table *t);
 unsigned dm_table_get_type(struct dm_table *t);
 struct target_type *dm_table_get_immutable_target_type(struct dm_table *t);
 bool dm_table_request_based(struct dm_table *t);
@@ -212,6 +211,8 @@ int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
 void dm_internal_suspend(struct mapped_device *md);
 void dm_internal_resume(struct mapped_device *md);
 
+bool dm_use_blk_mq(struct mapped_device *md);
+
 int dm_io_init(void);
 void dm_io_exit(void);
 
@@ -221,7 +222,8 @@ void dm_kcopyd_exit(void);
 /*
  * Mempool operations
  */
-struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity, unsigned per_bio_data_size);
+struct dm_md_mempools *dm_alloc_md_mempools(struct mapped_device *md, unsigned type,
+                                           unsigned integrity, unsigned per_bio_data_size);
 void dm_free_md_mempools(struct dm_md_mempools *pools);
 
 /*
@@ -235,4 +237,8 @@ static inline bool dm_message_test_buffer_overflow(char *result, unsigned maxlen
        return !maxlen || strlen(result) + 1 >= maxlen;
 }
 
+ssize_t dm_attr_rq_based_seq_io_merge_deadline_show(struct mapped_device *md, char *buf);
+ssize_t dm_attr_rq_based_seq_io_merge_deadline_store(struct mapped_device *md,
+                                                    const char *buf, size_t count);
+
 #endif
index 56a5cb0d215203f242b55e7609c4ddf6e7b4bc8f..0d07fca756fee2e8b5b8680b945ff3c2bed188ef 100644 (file)
@@ -2504,7 +2504,6 @@ vpfe_get_pdata(struct platform_device *pdev)
                                             GFP_KERNEL);
                pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF;
                pdata->asd[i]->match.of.node = rem;
-               of_node_put(endpoint);
                of_node_put(rem);
        }
 
index 66634b469c9899f043f79a69c4c232599ec8ee0f..9039d989c01ccc5d0928be7be5db3297f96fa164 100644 (file)
@@ -1694,7 +1694,6 @@ static void scan_of_host(struct soc_camera_host *ici)
                if (!i)
                        soc_of_bind(ici, epn, ren->parent);
 
-               of_node_put(epn);
                of_node_put(ren);
 
                if (i) {
@@ -1702,6 +1701,8 @@ static void scan_of_host(struct soc_camera_host *ici)
                        break;
                }
        }
+
+       of_node_put(epn);
 }
 
 #else
index 7bcaeec876c0c3a5ea80a01d13b13b186bb2227f..1470b52278341a79ab1e0304cdd915888045cfb6 100644 (file)
@@ -34,7 +34,11 @@ config OF_PROMTREE
 # Hardly any platforms need this.  It is safe to select, but only do so if you
 # need it.
 config OF_DYNAMIC
-       bool
+       bool "Support for dynamic device trees" if OF_UNITTEST
+       help
+         On some platforms, the device tree can be manipulated at runtime.
+         While this option is selected automatically on such platforms, you
+         can enable it manually to improve device tree unit test coverage.
 
 config OF_ADDRESS
        def_bool y
@@ -87,5 +91,10 @@ config OF_OVERLAY
        bool "Device Tree overlays"
        select OF_DYNAMIC
        select OF_RESOLVE
+       help
+         Overlays are a method to dynamically modify part of the kernel's
+         device tree with dynamically loaded data.
+         While this option is selected automatically when needed, you can
+         enable it manually to improve device tree unit test coverage.
 
 endmenu # OF
index 7563f36c71db344740c3d9821435a1285533b6ed..fcacb186a67beee65742542f86fc45feff8e4352 100644 (file)
@@ -6,8 +6,7 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
 obj-$(CONFIG_OF_ADDRESS)  += address.o
 obj-$(CONFIG_OF_IRQ)    += irq.o
 obj-$(CONFIG_OF_NET)   += of_net.o
-obj-$(CONFIG_OF_UNITTEST) += of_unittest.o
-of_unittest-objs := unittest.o unittest-data/testcases.dtb.o
+obj-$(CONFIG_OF_UNITTEST) += unittest.o
 obj-$(CONFIG_OF_MDIO)  += of_mdio.o
 obj-$(CONFIG_OF_PCI)   += of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
@@ -16,5 +15,7 @@ obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
 obj-$(CONFIG_OF_RESOLVE)  += resolver.o
 obj-$(CONFIG_OF_OVERLAY) += overlay.o
 
+obj-$(CONFIG_OF_UNITTEST) += unittest-data/
+
 CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
 CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
index 4cc06c702c41a46653c74d4ab863d708804e4e17..a1aa0c7dee50c59d51bcf6a672b34f10107be27d 100644 (file)
@@ -2108,14 +2108,45 @@ int of_graph_parse_endpoint(const struct device_node *node,
 }
 EXPORT_SYMBOL(of_graph_parse_endpoint);
 
+/**
+ * of_graph_get_port_by_id() - get the port matching a given id
+ * @parent: pointer to the parent device node
+ * @id: id of the port
+ *
+ * Return: A 'port' node pointer with refcount incremented. The caller
+ * has to use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
+{
+       struct device_node *node, *port;
+
+       node = of_get_child_by_name(parent, "ports");
+       if (node)
+               parent = node;
+
+       for_each_child_of_node(parent, port) {
+               u32 port_id = 0;
+
+               if (of_node_cmp(port->name, "port") != 0)
+                       continue;
+               of_property_read_u32(port, "reg", &port_id);
+               if (id == port_id)
+                       break;
+       }
+
+       of_node_put(node);
+
+       return port;
+}
+EXPORT_SYMBOL(of_graph_get_port_by_id);
+
 /**
  * of_graph_get_next_endpoint() - get next endpoint node
  * @parent: pointer to the parent device node
  * @prev: previous endpoint node, or NULL to get first
  *
  * Return: An 'endpoint' node pointer with refcount incremented. Refcount
- * of the passed @prev node is not decremented, the caller have to use
- * of_node_put() on it when done.
+ * of the passed @prev node is decremented.
  */
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
                                        struct device_node *prev)
@@ -2151,12 +2182,6 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
                if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
                              __func__, prev->full_name))
                        return NULL;
-
-               /*
-                * Avoid dropping prev node refcount to 0 when getting the next
-                * child below.
-                */
-               of_node_get(prev);
        }
 
        while (1) {
index 73e14184aafebbefa9f9eccc203bd43a8c519975..d820f3edd4311821696e6045d843024166b83dfc 100644 (file)
@@ -38,6 +38,15 @@ int of_get_phy_mode(struct device_node *np)
 }
 EXPORT_SYMBOL_GPL(of_get_phy_mode);
 
+static const void *of_get_mac_addr(struct device_node *np, const char *name)
+{
+       struct property *pp = of_find_property(np, name, NULL);
+
+       if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value))
+               return pp->value;
+       return NULL;
+}
+
 /**
  * Search the device tree for the best MAC address to use.  'mac-address' is
  * checked first, because that is supposed to contain to "most recent" MAC
@@ -58,20 +67,16 @@ EXPORT_SYMBOL_GPL(of_get_phy_mode);
 */
 const void *of_get_mac_address(struct device_node *np)
 {
-       struct property *pp;
+       const void *addr;
 
-       pp = of_find_property(np, "mac-address", NULL);
-       if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
-               return pp->value;
+       addr = of_get_mac_addr(np, "mac-address");
+       if (addr)
+               return addr;
 
-       pp = of_find_property(np, "local-mac-address", NULL);
-       if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
-               return pp->value;
+       addr = of_get_mac_addr(np, "local-mac-address");
+       if (addr)
+               return addr;
 
-       pp = of_find_property(np, "address", NULL);
-       if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
-               return pp->value;
-
-       return NULL;
+       return of_get_mac_addr(np, "address");
 }
 EXPORT_SYMBOL(of_get_mac_address);
diff --git a/drivers/of/unittest-data/.gitignore b/drivers/of/unittest-data/.gitignore
new file mode 100644 (file)
index 0000000..4b3cf8b
--- /dev/null
@@ -0,0 +1,2 @@
+testcases.dtb
+testcases.dtb.S
diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile
new file mode 100644 (file)
index 0000000..1ac5cc0
--- /dev/null
@@ -0,0 +1,7 @@
+obj-y += testcases.dtb.o
+
+targets += testcases.dtb testcases.dtb.S
+
+.SECONDARY: \
+       $(obj)/testcases.dtb.S \
+       $(obj)/testcases.dtb
index 244226cbb5a3b7fb8929fd75b77171cb172c1aa9..02ba56c20fe1247b7ec435eb34d7a6a2b84eca38 100644 (file)
@@ -4,94 +4,94 @@
                overlay-node {
 
                        /* test bus */
-                       selftestbus: test-bus {
+                       unittestbus: test-bus {
                                compatible = "simple-bus";
                                #address-cells = <1>;
                                #size-cells = <0>;
 
-                               selftest100: test-selftest100 {
-                                       compatible = "selftest";
+                               unittest100: test-unittest100 {
+                                       compatible = "unittest";
                                        status = "okay";
                                        reg = <100>;
                                };
 
-                               selftest101: test-selftest101 {
-                                       compatible = "selftest";
+                               unittest101: test-unittest101 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <101>;
                                };
 
-                               selftest0: test-selftest0 {
-                                       compatible = "selftest";
+                               unittest0: test-unittest0 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <0>;
                                };
 
-                               selftest1: test-selftest1 {
-                                       compatible = "selftest";
+                               unittest1: test-unittest1 {
+                                       compatible = "unittest";
                                        status = "okay";
                                        reg = <1>;
                                };
 
-                               selftest2: test-selftest2 {
-                                       compatible = "selftest";
+                               unittest2: test-unittest2 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <2>;
                                };
 
-                               selftest3: test-selftest3 {
-                                       compatible = "selftest";
+                               unittest3: test-unittest3 {
+                                       compatible = "unittest";
                                        status = "okay";
                                        reg = <3>;
                                };
 
-                               selftest5: test-selftest5 {
-                                       compatible = "selftest";
+                               unittest5: test-unittest5 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <5>;
                                };
 
-                               selftest6: test-selftest6 {
-                                       compatible = "selftest";
+                               unittest6: test-unittest6 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <6>;
                                };
 
-                               selftest7: test-selftest7 {
-                                       compatible = "selftest";
+                               unittest7: test-unittest7 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <7>;
                                };
 
-                               selftest8: test-selftest8 {
-                                       compatible = "selftest";
+                               unittest8: test-unittest8 {
+                                       compatible = "unittest";
                                        status = "disabled";
                                        reg = <8>;
                                };
 
                                i2c-test-bus {
-                                       compatible = "selftest-i2c-bus";
+                                       compatible = "unittest-i2c-bus";
                                        status = "okay";
                                        reg = <50>;
 
                                        #address-cells = <1>;
                                        #size-cells = <0>;
 
-                                       test-selftest12 {
+                                       test-unittest12 {
                                                reg = <8>;
-                                               compatible = "selftest-i2c-dev";
+                                               compatible = "unittest-i2c-dev";
                                                status = "disabled";
                                        };
 
-                                       test-selftest13 {
+                                       test-unittest13 {
                                                reg = <9>;
-                                               compatible = "selftest-i2c-dev";
+                                               compatible = "unittest-i2c-dev";
                                                status = "okay";
                                        };
 
-                                       test-selftest14 {
+                                       test-unittest14 {
                                                reg = <10>;
-                                               compatible = "selftest-i2c-mux";
+                                               compatible = "unittest-i2c-mux";
                                                status = "okay";
 
                                                #address-cells = <1>;
 
                                                        test-mux-dev {
                                                                reg = <32>;
-                                                               compatible = "selftest-i2c-dev";
+                                                               compatible = "unittest-i2c-dev";
                                                                status = "okay";
                                                        };
                                                };
                /* test enable using absolute target path */
                overlay0 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
                                __overlay__ {
                                        status = "okay";
                                };
                /* test disable using absolute target path */
                overlay1 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
                                __overlay__ {
                                        status = "disabled";
                                };
                /* test enable using label */
                overlay2 {
                        fragment@0 {
-                               target = <&selftest2>;
+                               target = <&unittest2>;
                                __overlay__ {
                                        status = "okay";
                                };
                /* test disable using label */
                overlay3 {
                        fragment@0 {
-                               target = <&selftest3>;
+                               target = <&unittest3>;
                                __overlay__ {
                                        status = "disabled";
                                };
                /* test insertion of a full node */
                overlay4 {
                        fragment@0 {
-                               target = <&selftestbus>;
+                               target = <&unittestbus>;
                                __overlay__ {
 
                                        /* suppress DTC warning */
                                        #address-cells = <1>;
                                        #size-cells = <0>;
 
-                                       test-selftest4 {
-                                               compatible = "selftest";
+                                       test-unittest4 {
+                                               compatible = "unittest";
                                                status = "okay";
                                                reg = <4>;
                                        };
                /* test overlay apply revert */
                overlay5 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest5";
                                __overlay__ {
                                        status = "okay";
                                };
                /* test overlays application and removal in sequence */
                overlay6 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest6";
                                __overlay__ {
                                        status = "okay";
                                };
                };
                overlay7 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest7";
                                __overlay__ {
                                        status = "okay";
                                };
                /* test overlays application and removal in bad sequence */
                overlay8 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
                                __overlay__ {
                                        status = "okay";
                                };
                };
                overlay9 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+                               target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
                                __overlay__ {
                                        property-foo = "bar";
                                };
                                        #address-cells = <1>;
                                        #size-cells = <0>;
 
-                                       test-selftest10 {
-                                               compatible = "selftest";
+                                       test-unittest10 {
+                                               compatible = "unittest";
                                                status = "okay";
                                                reg = <10>;
 
                                                #address-cells = <1>;
                                                #size-cells = <0>;
 
-                                               test-selftest101 {
-                                                       compatible = "selftest";
+                                               test-unittest101 {
+                                                       compatible = "unittest";
                                                        status = "okay";
                                                        reg = <1>;
                                                };
                                        #address-cells = <1>;
                                        #size-cells = <0>;
 
-                                       test-selftest11 {
-                                               compatible = "selftest";
+                                       test-unittest11 {
+                                               compatible = "unittest";
                                                status = "okay";
                                                reg = <11>;
 
                                                #address-cells = <1>;
                                                #size-cells = <0>;
 
-                                               test-selftest111 {
-                                                       compatible = "selftest";
+                                               test-unittest111 {
+                                                       compatible = "unittest";
                                                        status = "okay";
                                                        reg = <1>;
                                                };
                /* test enable using absolute target path (i2c) */
                overlay12 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-selftest12";
+                               target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
                                __overlay__ {
                                        status = "okay";
                                };
                /* test disable using absolute target path (i2c) */
                overlay13 {
                        fragment@0 {
-                               target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-selftest13";
+                               target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
                                __overlay__ {
                                        status = "disabled";
                                };
                                __overlay__ {
                                        #address-cells = <1>;
                                        #size-cells = <0>;
-                                       test-selftest15 {
+                                       test-unittest15 {
                                                reg = <11>;
-                                               compatible = "selftest-i2c-mux";
+                                               compatible = "unittest-i2c-mux";
                                                status = "okay";
 
                                                #address-cells = <1>;
 
                                                        test-mux-dev {
                                                                reg = <32>;
-                                                               compatible = "selftest-i2c-dev";
+                                                               compatible = "unittest-i2c-dev";
                                                                status = "okay";
                                                        };
                                                };
index 52c45c7df07ff6623c7fb7656961f8bdcf804da3..e844907c9efa5edeb9f9a98babace1c70a9e1833 100644 (file)
 
 #include "of_private.h"
 
-static struct selftest_results {
+static struct unittest_results {
        int passed;
        int failed;
-} selftest_results;
+} unittest_results;
 
-#define selftest(result, fmt, ...) ({ \
+#define unittest(result, fmt, ...) ({ \
        bool failed = !(result); \
        if (failed) { \
-               selftest_results.failed++; \
+               unittest_results.failed++; \
                pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \
        } else { \
-               selftest_results.passed++; \
+               unittest_results.passed++; \
                pr_debug("pass %s():%i\n", __func__, __LINE__); \
        } \
        failed; \
 })
 
-static void __init of_selftest_find_node_by_name(void)
+static void __init of_unittest_find_node_by_name(void)
 {
        struct device_node *np;
        const char *options;
 
        np = of_find_node_by_path("/testcase-data");
-       selftest(np && !strcmp("/testcase-data", np->full_name),
+       unittest(np && !strcmp("/testcase-data", np->full_name),
                "find /testcase-data failed\n");
        of_node_put(np);
 
        /* Test if trailing '/' works */
        np = of_find_node_by_path("/testcase-data/");
-       selftest(!np, "trailing '/' on /testcase-data/ should fail\n");
+       unittest(!np, "trailing '/' on /testcase-data/ should fail\n");
 
        np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
-       selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+       unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
                "find /testcase-data/phandle-tests/consumer-a failed\n");
        of_node_put(np);
 
        np = of_find_node_by_path("testcase-alias");
-       selftest(np && !strcmp("/testcase-data", np->full_name),
+       unittest(np && !strcmp("/testcase-data", np->full_name),
                "find testcase-alias failed\n");
        of_node_put(np);
 
        /* Test if trailing '/' works on aliases */
        np = of_find_node_by_path("testcase-alias/");
-       selftest(!np, "trailing '/' on testcase-alias/ should fail\n");
+       unittest(!np, "trailing '/' on testcase-alias/ should fail\n");
 
        np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
-       selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+       unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
                "find testcase-alias/phandle-tests/consumer-a failed\n");
        of_node_put(np);
 
        np = of_find_node_by_path("/testcase-data/missing-path");
-       selftest(!np, "non-existent path returned node %s\n", np->full_name);
+       unittest(!np, "non-existent path returned node %s\n", np->full_name);
        of_node_put(np);
 
        np = of_find_node_by_path("missing-alias");
-       selftest(!np, "non-existent alias returned node %s\n", np->full_name);
+       unittest(!np, "non-existent alias returned node %s\n", np->full_name);
        of_node_put(np);
 
        np = of_find_node_by_path("testcase-alias/missing-path");
-       selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
+       unittest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
        of_node_put(np);
 
        np = of_find_node_opts_by_path("/testcase-data:testoption", &options);
-       selftest(np && !strcmp("testoption", options),
+       unittest(np && !strcmp("testoption", options),
                 "option path test failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("/testcase-data:test/option", &options);
-       selftest(np && !strcmp("test/option", options),
+       unittest(np && !strcmp("test/option", options),
                 "option path test, subcase #1 failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options);
-       selftest(np && !strcmp("test/option", options),
+       unittest(np && !strcmp("test/option", options),
                 "option path test, subcase #2 failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("/testcase-data:testoption", NULL);
-       selftest(np, "NULL option path test failed\n");
+       unittest(np, "NULL option path test failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("testcase-alias:testaliasoption",
                                       &options);
-       selftest(np && !strcmp("testaliasoption", options),
+       unittest(np && !strcmp("testaliasoption", options),
                 "option alias path test failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("testcase-alias:test/alias/option",
                                       &options);
-       selftest(np && !strcmp("test/alias/option", options),
+       unittest(np && !strcmp("test/alias/option", options),
                 "option alias path test, subcase #1 failed\n");
        of_node_put(np);
 
        np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL);
-       selftest(np, "NULL option alias path test failed\n");
+       unittest(np, "NULL option alias path test failed\n");
        of_node_put(np);
 
        options = "testoption";
        np = of_find_node_opts_by_path("testcase-alias", &options);
-       selftest(np && !options, "option clearing test failed\n");
+       unittest(np && !options, "option clearing test failed\n");
        of_node_put(np);
 
        options = "testoption";
        np = of_find_node_opts_by_path("/", &options);
-       selftest(np && !options, "option clearing root node test failed\n");
+       unittest(np && !options, "option clearing root node test failed\n");
        of_node_put(np);
 }
 
-static void __init of_selftest_dynamic(void)
+static void __init of_unittest_dynamic(void)
 {
        struct device_node *np;
        struct property *prop;
@@ -147,7 +147,7 @@ static void __init of_selftest_dynamic(void)
        /* Array of 4 properties for the purpose of testing */
        prop = kzalloc(sizeof(*prop) * 4, GFP_KERNEL);
        if (!prop) {
-               selftest(0, "kzalloc() failed\n");
+               unittest(0, "kzalloc() failed\n");
                return;
        }
 
@@ -155,20 +155,20 @@ static void __init of_selftest_dynamic(void)
        prop->name = "new-property";
        prop->value = "new-property-data";
        prop->length = strlen(prop->value);
-       selftest(of_add_property(np, prop) == 0, "Adding a new property failed\n");
+       unittest(of_add_property(np, prop) == 0, "Adding a new property failed\n");
 
        /* Try to add an existing property - should fail */
        prop++;
        prop->name = "new-property";
        prop->value = "new-property-data-should-fail";
        prop->length = strlen(prop->value);
-       selftest(of_add_property(np, prop) != 0,
+       unittest(of_add_property(np, prop) != 0,
                 "Adding an existing property should have failed\n");
 
        /* Try to modify an existing property - should pass */
        prop->value = "modify-property-data-should-pass";
        prop->length = strlen(prop->value);
-       selftest(of_update_property(np, prop) == 0,
+       unittest(of_update_property(np, prop) == 0,
                 "Updating an existing property should have passed\n");
 
        /* Try to modify non-existent property - should pass*/
@@ -176,11 +176,11 @@ static void __init of_selftest_dynamic(void)
        prop->name = "modify-property";
        prop->value = "modify-missing-property-data-should-pass";
        prop->length = strlen(prop->value);
-       selftest(of_update_property(np, prop) == 0,
+       unittest(of_update_property(np, prop) == 0,
                 "Updating a missing property should have passed\n");
 
        /* Remove property - should pass */
-       selftest(of_remove_property(np, prop) == 0,
+       unittest(of_remove_property(np, prop) == 0,
                 "Removing a property should have passed\n");
 
        /* Adding very large property - should pass */
@@ -188,13 +188,13 @@ static void __init of_selftest_dynamic(void)
        prop->name = "large-property-PAGE_SIZEx8";
        prop->length = PAGE_SIZE * 8;
        prop->value = kzalloc(prop->length, GFP_KERNEL);
-       selftest(prop->value != NULL, "Unable to allocate large buffer\n");
+       unittest(prop->value != NULL, "Unable to allocate large buffer\n");
        if (prop->value)
-               selftest(of_add_property(np, prop) == 0,
+               unittest(of_add_property(np, prop) == 0,
                         "Adding a large property should have passed\n");
 }
 
-static int __init of_selftest_check_node_linkage(struct device_node *np)
+static int __init of_unittest_check_node_linkage(struct device_node *np)
 {
        struct device_node *child;
        int count = 0, rc;
@@ -206,7 +206,7 @@ static int __init of_selftest_check_node_linkage(struct device_node *np)
                        return -EINVAL;
                }
 
-               rc = of_selftest_check_node_linkage(child);
+               rc = of_unittest_check_node_linkage(child);
                if (rc < 0)
                        return rc;
                count += rc;
@@ -215,7 +215,7 @@ static int __init of_selftest_check_node_linkage(struct device_node *np)
        return count + 1;
 }
 
-static void __init of_selftest_check_tree_linkage(void)
+static void __init of_unittest_check_tree_linkage(void)
 {
        struct device_node *np;
        int allnode_count = 0, child_count;
@@ -225,11 +225,12 @@ static void __init of_selftest_check_tree_linkage(void)
 
        for_each_of_allnodes(np)
                allnode_count++;
-       child_count = of_selftest_check_node_linkage(of_root);
+       child_count = of_unittest_check_node_linkage(of_root);
 
-       selftest(child_count > 0, "Device node data structure is corrupted\n");
-       selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match"
-                "sibling lists size (%i)\n", allnode_count, child_count);
+       unittest(child_count > 0, "Device node data structure is corrupted\n");
+       unittest(child_count == allnode_count,
+                "allnodes list size (%i) doesn't match sibling lists size (%i)\n",
+                allnode_count, child_count);
        pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count);
 }
 
@@ -239,7 +240,7 @@ struct node_hash {
 };
 
 static DEFINE_HASHTABLE(phandle_ht, 8);
-static void __init of_selftest_check_phandles(void)
+static void __init of_unittest_check_phandles(void)
 {
        struct device_node *np;
        struct node_hash *nh;
@@ -267,7 +268,7 @@ static void __init of_selftest_check_phandles(void)
                hash_add(phandle_ht, &nh->node, np->phandle);
                phandle_count++;
        }
-       selftest(dup_count == 0, "Found %i duplicates in %i phandles\n",
+       unittest(dup_count == 0, "Found %i duplicates in %i phandles\n",
                 dup_count, phandle_count);
 
        /* Clean up */
@@ -277,7 +278,7 @@ static void __init of_selftest_check_phandles(void)
        }
 }
 
-static void __init of_selftest_parse_phandle_with_args(void)
+static void __init of_unittest_parse_phandle_with_args(void)
 {
        struct device_node *np;
        struct of_phandle_args args;
@@ -290,10 +291,11 @@ static void __init of_selftest_parse_phandle_with_args(void)
        }
 
        rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells");
-       selftest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
+       unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
 
        for (i = 0; i < 8; i++) {
                bool passed = true;
+
                rc = of_parse_phandle_with_args(np, "phandle-list",
                                                "#phandle-cells", i, &args);
 
@@ -342,44 +344,44 @@ static void __init of_selftest_parse_phandle_with_args(void)
                        passed = false;
                }
 
-               selftest(passed, "index %i - data error on node %s rc=%i\n",
+               unittest(passed, "index %i - data error on node %s rc=%i\n",
                         i, args.np->full_name, rc);
        }
 
        /* Check for missing list property */
        rc = of_parse_phandle_with_args(np, "phandle-list-missing",
                                        "#phandle-cells", 0, &args);
-       selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
+       unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
        rc = of_count_phandle_with_args(np, "phandle-list-missing",
                                        "#phandle-cells");
-       selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
+       unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
 
        /* Check for missing cells property */
        rc = of_parse_phandle_with_args(np, "phandle-list",
                                        "#phandle-cells-missing", 0, &args);
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
        rc = of_count_phandle_with_args(np, "phandle-list",
                                        "#phandle-cells-missing");
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
 
        /* Check for bad phandle in list */
        rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle",
                                        "#phandle-cells", 0, &args);
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
        rc = of_count_phandle_with_args(np, "phandle-list-bad-phandle",
                                        "#phandle-cells");
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
 
        /* Check for incorrectly formed argument list */
        rc = of_parse_phandle_with_args(np, "phandle-list-bad-args",
                                        "#phandle-cells", 1, &args);
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
        rc = of_count_phandle_with_args(np, "phandle-list-bad-args",
                                        "#phandle-cells");
-       selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+       unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
 }
 
-static void __init of_selftest_property_string(void)
+static void __init of_unittest_property_string(void)
 {
        const char *strings[4];
        struct device_node *np;
@@ -392,79 +394,79 @@ static void __init of_selftest_property_string(void)
        }
 
        rc = of_property_match_string(np, "phandle-list-names", "first");
-       selftest(rc == 0, "first expected:0 got:%i\n", rc);
+       unittest(rc == 0, "first expected:0 got:%i\n", rc);
        rc = of_property_match_string(np, "phandle-list-names", "second");
-       selftest(rc == 1, "second expected:1 got:%i\n", rc);
+       unittest(rc == 1, "second expected:1 got:%i\n", rc);
        rc = of_property_match_string(np, "phandle-list-names", "third");
-       selftest(rc == 2, "third expected:2 got:%i\n", rc);
+       unittest(rc == 2, "third expected:2 got:%i\n", rc);
        rc = of_property_match_string(np, "phandle-list-names", "fourth");
-       selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
+       unittest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
        rc = of_property_match_string(np, "missing-property", "blah");
-       selftest(rc == -EINVAL, "missing property; rc=%i\n", rc);
+       unittest(rc == -EINVAL, "missing property; rc=%i\n", rc);
        rc = of_property_match_string(np, "empty-property", "blah");
-       selftest(rc == -ENODATA, "empty property; rc=%i\n", rc);
+       unittest(rc == -ENODATA, "empty property; rc=%i\n", rc);
        rc = of_property_match_string(np, "unterminated-string", "blah");
-       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+       unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
 
        /* of_property_count_strings() tests */
        rc = of_property_count_strings(np, "string-property");
-       selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+       unittest(rc == 1, "Incorrect string count; rc=%i\n", rc);
        rc = of_property_count_strings(np, "phandle-list-names");
-       selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+       unittest(rc == 3, "Incorrect string count; rc=%i\n", rc);
        rc = of_property_count_strings(np, "unterminated-string");
-       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+       unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
        rc = of_property_count_strings(np, "unterminated-string-list");
-       selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+       unittest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
 
        /* of_property_read_string_index() tests */
        rc = of_property_read_string_index(np, "string-property", 0, strings);
-       selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
        strings[0] = NULL;
        rc = of_property_read_string_index(np, "string-property", 1, strings);
-       selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
        rc = of_property_read_string_index(np, "phandle-list-names", 0, strings);
-       selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
        rc = of_property_read_string_index(np, "phandle-list-names", 1, strings);
-       selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
        rc = of_property_read_string_index(np, "phandle-list-names", 2, strings);
-       selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
        strings[0] = NULL;
        rc = of_property_read_string_index(np, "phandle-list-names", 3, strings);
-       selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
        strings[0] = NULL;
        rc = of_property_read_string_index(np, "unterminated-string", 0, strings);
-       selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
        rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings);
-       selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
        strings[0] = NULL;
        rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */
-       selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       unittest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
        strings[1] = NULL;
 
        /* of_property_read_string_array() tests */
        rc = of_property_read_string_array(np, "string-property", strings, 4);
-       selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+       unittest(rc == 1, "Incorrect string count; rc=%i\n", rc);
        rc = of_property_read_string_array(np, "phandle-list-names", strings, 4);
-       selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+       unittest(rc == 3, "Incorrect string count; rc=%i\n", rc);
        rc = of_property_read_string_array(np, "unterminated-string", strings, 4);
-       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+       unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
        /* -- An incorrectly formed string should cause a failure */
        rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4);
-       selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+       unittest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
        /* -- parsing the correctly formed strings should still work: */
        strings[2] = NULL;
        rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2);
-       selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
+       unittest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
        strings[1] = NULL;
        rc = of_property_read_string_array(np, "phandle-list-names", strings, 1);
-       selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
+       unittest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
 }
 
 #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
                        (p1)->value && (p2)->value && \
                        !memcmp((p1)->value, (p2)->value, (p1)->length) && \
                        !strcmp((p1)->name, (p2)->name))
-static void __init of_selftest_property_copy(void)
+static void __init of_unittest_property_copy(void)
 {
 #ifdef CONFIG_OF_DYNAMIC
        struct property p1 = { .name = "p1", .length = 0, .value = "" };
@@ -472,20 +474,20 @@ static void __init of_selftest_property_copy(void)
        struct property *new;
 
        new = __of_prop_dup(&p1, GFP_KERNEL);
-       selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
+       unittest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
        kfree(new->value);
        kfree(new->name);
        kfree(new);
 
        new = __of_prop_dup(&p2, GFP_KERNEL);
-       selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
+       unittest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
        kfree(new->value);
        kfree(new->name);
        kfree(new);
 #endif
 }
 
-static void __init of_selftest_changeset(void)
+static void __init of_unittest_changeset(void)
 {
 #ifdef CONFIG_OF_DYNAMIC
        struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
@@ -495,51 +497,51 @@ static void __init of_selftest_changeset(void)
        struct of_changeset chgset;
 
        n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1");
-       selftest(n1, "testcase setup failure\n");
+       unittest(n1, "testcase setup failure\n");
        n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2");
-       selftest(n2, "testcase setup failure\n");
+       unittest(n2, "testcase setup failure\n");
        n21 = __of_node_dup(NULL, "%s/%s", "/testcase-data/changeset/n2", "n21");
-       selftest(n21, "testcase setup failure %p\n", n21);
+       unittest(n21, "testcase setup failure %p\n", n21);
        nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
-       selftest(nremove, "testcase setup failure\n");
+       unittest(nremove, "testcase setup failure\n");
        ppadd = __of_prop_dup(&padd, GFP_KERNEL);
-       selftest(ppadd, "testcase setup failure\n");
+       unittest(ppadd, "testcase setup failure\n");
        ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL);
-       selftest(ppupdate, "testcase setup failure\n");
+       unittest(ppupdate, "testcase setup failure\n");
        parent = nremove->parent;
        n1->parent = parent;
        n2->parent = parent;
        n21->parent = n2;
        n2->child = n21;
        ppremove = of_find_property(parent, "prop-remove", NULL);
-       selftest(ppremove, "failed to find removal prop");
+       unittest(ppremove, "failed to find removal prop");
 
        of_changeset_init(&chgset);
-       selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
-       selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
-       selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
-       selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
-       selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
-       selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
-       selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
+       unittest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
+       unittest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
+       unittest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
+       unittest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
+       unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
+       unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
+       unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
        mutex_lock(&of_mutex);
-       selftest(!of_changeset_apply(&chgset), "apply failed\n");
+       unittest(!of_changeset_apply(&chgset), "apply failed\n");
        mutex_unlock(&of_mutex);
 
        /* Make sure node names are constructed correctly */
-       selftest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
+       unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
                 "'%s' not added\n", n21->full_name);
        of_node_put(np);
 
        mutex_lock(&of_mutex);
-       selftest(!of_changeset_revert(&chgset), "revert failed\n");
+       unittest(!of_changeset_revert(&chgset), "revert failed\n");
        mutex_unlock(&of_mutex);
 
        of_changeset_destroy(&chgset);
 #endif
 }
 
-static void __init of_selftest_parse_interrupts(void)
+static void __init of_unittest_parse_interrupts(void)
 {
        struct device_node *np;
        struct of_phandle_args args;
@@ -553,6 +555,7 @@ static void __init of_selftest_parse_interrupts(void)
 
        for (i = 0; i < 4; i++) {
                bool passed = true;
+
                args.args_count = 0;
                rc = of_irq_parse_one(np, i, &args);
 
@@ -560,7 +563,7 @@ static void __init of_selftest_parse_interrupts(void)
                passed &= (args.args_count == 1);
                passed &= (args.args[0] == (i + 1));
 
-               selftest(passed, "index %i - data error on node %s rc=%i\n",
+               unittest(passed, "index %i - data error on node %s rc=%i\n",
                         i, args.np->full_name, rc);
        }
        of_node_put(np);
@@ -573,6 +576,7 @@ static void __init of_selftest_parse_interrupts(void)
 
        for (i = 0; i < 4; i++) {
                bool passed = true;
+
                args.args_count = 0;
                rc = of_irq_parse_one(np, i, &args);
 
@@ -605,13 +609,13 @@ static void __init of_selftest_parse_interrupts(void)
                default:
                        passed = false;
                }
-               selftest(passed, "index %i - data error on node %s rc=%i\n",
+               unittest(passed, "index %i - data error on node %s rc=%i\n",
                         i, args.np->full_name, rc);
        }
        of_node_put(np);
 }
 
-static void __init of_selftest_parse_interrupts_extended(void)
+static void __init of_unittest_parse_interrupts_extended(void)
 {
        struct device_node *np;
        struct of_phandle_args args;
@@ -625,6 +629,7 @@ static void __init of_selftest_parse_interrupts_extended(void)
 
        for (i = 0; i < 7; i++) {
                bool passed = true;
+
                rc = of_irq_parse_one(np, i, &args);
 
                /* Test the values from tests-phandle.dtsi */
@@ -674,13 +679,13 @@ static void __init of_selftest_parse_interrupts_extended(void)
                        passed = false;
                }
 
-               selftest(passed, "index %i - data error on node %s rc=%i\n",
+               unittest(passed, "index %i - data error on node %s rc=%i\n",
                         i, args.np->full_name, rc);
        }
        of_node_put(np);
 }
 
-static struct of_device_id match_node_table[] = {
+static const struct of_device_id match_node_table[] = {
        { .data = "A", .name = "name0", }, /* Name alone is lowest priority */
        { .data = "B", .type = "type1", }, /* followed by type alone */
 
@@ -715,7 +720,7 @@ static struct {
        { .path = "/testcase-data/match-node/name9", .data = "K", },
 };
 
-static void __init of_selftest_match_node(void)
+static void __init of_unittest_match_node(void)
 {
        struct device_node *np;
        const struct of_device_id *match;
@@ -724,37 +729,37 @@ static void __init of_selftest_match_node(void)
        for (i = 0; i < ARRAY_SIZE(match_node_tests); i++) {
                np = of_find_node_by_path(match_node_tests[i].path);
                if (!np) {
-                       selftest(0, "missing testcase node %s\n",
+                       unittest(0, "missing testcase node %s\n",
                                match_node_tests[i].path);
                        continue;
                }
 
                match = of_match_node(match_node_table, np);
                if (!match) {
-                       selftest(0, "%s didn't match anything\n",
+                       unittest(0, "%s didn't match anything\n",
                                match_node_tests[i].path);
                        continue;
                }
 
                if (strcmp(match->data, match_node_tests[i].data) != 0) {
-                       selftest(0, "%s got wrong match. expected %s, got %s\n",
+                       unittest(0, "%s got wrong match. expected %s, got %s\n",
                                match_node_tests[i].path, match_node_tests[i].data,
                                (const char *)match->data);
                        continue;
                }
-               selftest(1, "passed");
+               unittest(1, "passed");
        }
 }
 
-struct device test_bus = {
-       .init_name = "unittest-bus",
+static const struct platform_device_info test_bus_info = {
+       .name = "unittest-bus",
 };
-static void __init of_selftest_platform_populate(void)
+static void __init of_unittest_platform_populate(void)
 {
        int irq, rc;
        struct device_node *np, *child, *grandchild;
-       struct platform_device *pdev;
-       struct of_device_id match[] = {
+       struct platform_device *pdev, *test_bus;
+       const struct of_device_id match[] = {
                { .compatible = "test-device", },
                {}
        };
@@ -765,43 +770,47 @@ static void __init of_selftest_platform_populate(void)
        /* Test that a missing irq domain returns -EPROBE_DEFER */
        np = of_find_node_by_path("/testcase-data/testcase-device1");
        pdev = of_find_device_by_node(np);
-       selftest(pdev, "device 1 creation failed\n");
+       unittest(pdev, "device 1 creation failed\n");
 
        irq = platform_get_irq(pdev, 0);
-       selftest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq);
+       unittest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq);
 
        /* Test that a parsing failure does not return -EPROBE_DEFER */
        np = of_find_node_by_path("/testcase-data/testcase-device2");
        pdev = of_find_device_by_node(np);
-       selftest(pdev, "device 2 creation failed\n");
+       unittest(pdev, "device 2 creation failed\n");
        irq = platform_get_irq(pdev, 0);
-       selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
+       unittest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
 
-       if (selftest(np = of_find_node_by_path("/testcase-data/platform-tests"),
-                    "No testcase data in device tree\n"));
+       np = of_find_node_by_path("/testcase-data/platform-tests");
+       unittest(np, "No testcase data in device tree\n");
+       if (!np)
                return;
 
-       if (selftest(!(rc = device_register(&test_bus)),
-                    "testbus registration failed; rc=%i\n", rc));
+       test_bus = platform_device_register_full(&test_bus_info);
+       rc = PTR_ERR_OR_ZERO(test_bus);
+       unittest(!rc, "testbus registration failed; rc=%i\n", rc);
+       if (rc)
                return;
+       test_bus->dev.of_node = np;
 
+       of_platform_populate(np, match, NULL, &test_bus->dev);
        for_each_child_of_node(np, child) {
-               of_platform_populate(child, match, NULL, &test_bus);
                for_each_child_of_node(child, grandchild)
-                       selftest(of_find_device_by_node(grandchild),
+                       unittest(of_find_device_by_node(grandchild),
                                 "Could not create device for node '%s'\n",
                                 grandchild->name);
        }
 
-       of_platform_depopulate(&test_bus);
+       of_platform_depopulate(&test_bus->dev);
        for_each_child_of_node(np, child) {
                for_each_child_of_node(child, grandchild)
-                       selftest(!of_find_device_by_node(grandchild),
+                       unittest(!of_find_device_by_node(grandchild),
                                 "device didn't get destroyed '%s'\n",
                                 grandchild->name);
        }
 
-       device_unregister(&test_bus);
+       platform_device_unregister(test_bus);
        of_node_put(np);
 }
 
@@ -866,13 +875,17 @@ static int attach_node_and_children(struct device_node *np)
 }
 
 /**
- *     selftest_data_add - Reads, copies data from
+ *     unittest_data_add - Reads, copies data from
  *     linked tree and attaches it to the live tree
  */
-static int __init selftest_data_add(void)
+static int __init unittest_data_add(void)
 {
-       void *selftest_data;
-       struct device_node *selftest_data_node, *np;
+       void *unittest_data;
+       struct device_node *unittest_data_node, *np;
+       /*
+        * __dtb_testcases_begin[] and __dtb_testcases_end[] are magically
+        * created by cmd_dt_S_dtb in scripts/Makefile.lib
+        */
        extern uint8_t __dtb_testcases_begin[];
        extern uint8_t __dtb_testcases_end[];
        const int size = __dtb_testcases_end - __dtb_testcases_begin;
@@ -885,27 +898,27 @@ static int __init selftest_data_add(void)
        }
 
        /* creating copy */
-       selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
+       unittest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
 
-       if (!selftest_data) {
-               pr_warn("%s: Failed to allocate memory for selftest_data; "
+       if (!unittest_data) {
+               pr_warn("%s: Failed to allocate memory for unittest_data; "
                        "not running tests\n", __func__);
                return -ENOMEM;
        }
-       of_fdt_unflatten_tree(selftest_data, &selftest_data_node);
-       if (!selftest_data_node) {
+       of_fdt_unflatten_tree(unittest_data, &unittest_data_node);
+       if (!unittest_data_node) {
                pr_warn("%s: No tree to attach; not running tests\n", __func__);
                return -ENODATA;
        }
-       of_node_set_flag(selftest_data_node, OF_DETACHED);
-       rc = of_resolve_phandles(selftest_data_node);
+       of_node_set_flag(unittest_data_node, OF_DETACHED);
+       rc = of_resolve_phandles(unittest_data_node);
        if (rc) {
                pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
                return -EINVAL;
        }
 
        if (!of_root) {
-               of_root = selftest_data_node;
+               of_root = unittest_data_node;
                for_each_of_allnodes(np)
                        __of_attach_node_sysfs(np);
                of_aliases = of_find_node_by_path("/aliases");
@@ -914,9 +927,10 @@ static int __init selftest_data_add(void)
        }
 
        /* attach the sub-tree to live tree */
-       np = selftest_data_node->child;
+       np = unittest_data_node->child;
        while (np) {
                struct device_node *next = np->sibling;
+
                np->parent = of_root;
                attach_node_and_children(np);
                np = next;
@@ -926,7 +940,7 @@ static int __init selftest_data_add(void)
 
 #ifdef CONFIG_OF_OVERLAY
 
-static int selftest_probe(struct platform_device *pdev)
+static int unittest_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
@@ -944,7 +958,7 @@ static int selftest_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int selftest_remove(struct platform_device *pdev)
+static int unittest_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
@@ -953,18 +967,18 @@ static int selftest_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id selftest_match[] = {
-       { .compatible = "selftest", },
+static const struct of_device_id unittest_match[] = {
+       { .compatible = "unittest", },
        {},
 };
 
-static struct platform_driver selftest_driver = {
-       .probe                  = selftest_probe,
-       .remove                 = selftest_remove,
+static struct platform_driver unittest_driver = {
+       .probe                  = unittest_probe,
+       .remove                 = unittest_remove,
        .driver = {
-               .name           = "selftest",
+               .name           = "unittest",
                .owner          = THIS_MODULE,
-               .of_match_table = of_match_ptr(selftest_match),
+               .of_match_table = of_match_ptr(unittest_match),
        },
 };
 
@@ -1046,7 +1060,7 @@ static int of_path_device_type_exists(const char *path,
        return 0;
 }
 
-static const char *selftest_path(int nr, enum overlay_type ovtype)
+static const char *unittest_path(int nr, enum overlay_type ovtype)
 {
        const char *base;
        static char buf[256];
@@ -1062,16 +1076,16 @@ static const char *selftest_path(int nr, enum overlay_type ovtype)
                buf[0] = '\0';
                return buf;
        }
-       snprintf(buf, sizeof(buf) - 1, "%s/test-selftest%d", base, nr);
+       snprintf(buf, sizeof(buf) - 1, "%s/test-unittest%d", base, nr);
        buf[sizeof(buf) - 1] = '\0';
        return buf;
 }
 
-static int of_selftest_device_exists(int selftest_nr, enum overlay_type ovtype)
+static int of_unittest_device_exists(int unittest_nr, enum overlay_type ovtype)
 {
        const char *path;
 
-       path = selftest_path(selftest_nr, ovtype);
+       path = unittest_path(unittest_nr, ovtype);
 
        switch (ovtype) {
        case PDEV_OVERLAY:
@@ -1095,7 +1109,7 @@ static const char *overlay_path(int nr)
 
 static const char *bus_path = "/testcase-data/overlay-node/test-bus";
 
-static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
+static int of_unittest_apply_overlay(int unittest_nr, int overlay_nr,
                int *overlay_id)
 {
        struct device_node *np = NULL;
@@ -1103,7 +1117,7 @@ static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
 
        np = of_find_node_by_path(overlay_path(overlay_nr));
        if (np == NULL) {
-               selftest(0, "could not find overlay node @\"%s\"\n",
+               unittest(0, "could not find overlay node @\"%s\"\n",
                                overlay_path(overlay_nr));
                ret = -EINVAL;
                goto out;
@@ -1111,7 +1125,7 @@ static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
 
        ret = of_overlay_create(np);
        if (ret < 0) {
-               selftest(0, "could not create overlay from \"%s\"\n",
+               unittest(0, "could not create overlay from \"%s\"\n",
                                overlay_path(overlay_nr));
                goto out;
        }
@@ -1129,31 +1143,31 @@ out:
 }
 
 /* apply an overlay while checking before and after states */
-static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
+static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
                int before, int after, enum overlay_type ovtype)
 {
        int ret;
 
-       /* selftest device must not be in before state */
-       if (of_selftest_device_exists(selftest_nr, ovtype) != before) {
-               selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+       /* unittest device must not be in before state */
+       if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
+               unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype),
+                               unittest_path(unittest_nr, ovtype),
                                !before ? "enabled" : "disabled");
                return -EINVAL;
        }
 
-       ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, NULL);
+       ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, NULL);
        if (ret != 0) {
-               /* of_selftest_apply_overlay already called selftest() */
+               /* of_unittest_apply_overlay already called unittest() */
                return ret;
        }
 
-       /* selftest device must be to set to after state */
-       if (of_selftest_device_exists(selftest_nr, ovtype) != after) {
-               selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+       /* unittest device must be to set to after state */
+       if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
+               unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype),
+                               unittest_path(unittest_nr, ovtype),
                                !after ? "enabled" : "disabled");
                return -EINVAL;
        }
@@ -1162,50 +1176,50 @@ static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
 }
 
 /* apply an overlay and then revert it while checking before, after states */
-static int of_selftest_apply_revert_overlay_check(int overlay_nr,
-               int selftest_nr, int before, int after,
+static int of_unittest_apply_revert_overlay_check(int overlay_nr,
+               int unittest_nr, int before, int after,
                enum overlay_type ovtype)
 {
        int ret, ov_id;
 
-       /* selftest device must be in before state */
-       if (of_selftest_device_exists(selftest_nr, ovtype) != before) {
-               selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+       /* unittest device must be in before state */
+       if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
+               unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype),
+                               unittest_path(unittest_nr, ovtype),
                                !before ? "enabled" : "disabled");
                return -EINVAL;
        }
 
        /* apply the overlay */
-       ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, &ov_id);
+       ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ov_id);
        if (ret != 0) {
-               /* of_selftest_apply_overlay already called selftest() */
+               /* of_unittest_apply_overlay already called unittest() */
                return ret;
        }
 
-       /* selftest device must be in after state */
-       if (of_selftest_device_exists(selftest_nr, ovtype) != after) {
-               selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+       /* unittest device must be in after state */
+       if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
+               unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype),
+                               unittest_path(unittest_nr, ovtype),
                                !after ? "enabled" : "disabled");
                return -EINVAL;
        }
 
        ret = of_overlay_destroy(ov_id);
        if (ret != 0) {
-               selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
+               unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype));
+                               unittest_path(unittest_nr, ovtype));
                return ret;
        }
 
-       /* selftest device must be again in before state */
-       if (of_selftest_device_exists(selftest_nr, PDEV_OVERLAY) != before) {
-               selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+       /* unittest device must be again in before state */
+       if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) {
+               unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
                                overlay_path(overlay_nr),
-                               selftest_path(selftest_nr, ovtype),
+                               unittest_path(unittest_nr, ovtype),
                                !before ? "enabled" : "disabled");
                return -EINVAL;
        }
@@ -1214,98 +1228,98 @@ static int of_selftest_apply_revert_overlay_check(int overlay_nr,
 }
 
 /* test activation of device */
-static void of_selftest_overlay_0(void)
+static void of_unittest_overlay_0(void)
 {
        int ret;
 
        /* device should enable */
-       ret = of_selftest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY);
+       ret = of_unittest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 0);
+       unittest(1, "overlay test %d passed\n", 0);
 }
 
 /* test deactivation of device */
-static void of_selftest_overlay_1(void)
+static void of_unittest_overlay_1(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY);
+       ret = of_unittest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 1);
+       unittest(1, "overlay test %d passed\n", 1);
 }
 
 /* test activation of device */
-static void of_selftest_overlay_2(void)
+static void of_unittest_overlay_2(void)
 {
        int ret;
 
        /* device should enable */
-       ret = of_selftest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY);
+       ret = of_unittest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 2);
+       unittest(1, "overlay test %d passed\n", 2);
 }
 
 /* test deactivation of device */
-static void of_selftest_overlay_3(void)
+static void of_unittest_overlay_3(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY);
+       ret = of_unittest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 3);
+       unittest(1, "overlay test %d passed\n", 3);
 }
 
 /* test activation of a full device node */
-static void of_selftest_overlay_4(void)
+static void of_unittest_overlay_4(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY);
+       ret = of_unittest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 4);
+       unittest(1, "overlay test %d passed\n", 4);
 }
 
 /* test overlay apply/revert sequence */
-static void of_selftest_overlay_5(void)
+static void of_unittest_overlay_5(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY);
+       ret = of_unittest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 5);
+       unittest(1, "overlay test %d passed\n", 5);
 }
 
 /* test overlay application in sequence */
-static void of_selftest_overlay_6(void)
+static void of_unittest_overlay_6(void)
 {
        struct device_node *np;
        int ret, i, ov_id[2];
-       int overlay_nr = 6, selftest_nr = 6;
+       int overlay_nr = 6, unittest_nr = 6;
        int before = 0, after = 1;
 
-       /* selftest device must be in before state */
+       /* unittest device must be in before state */
        for (i = 0; i < 2; i++) {
-               if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY)
+               if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
                                != before) {
-                       selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+                       unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
                                        overlay_path(overlay_nr + i),
-                                       selftest_path(selftest_nr + i,
+                                       unittest_path(unittest_nr + i,
                                                PDEV_OVERLAY),
                                        !before ? "enabled" : "disabled");
                        return;
@@ -1317,14 +1331,14 @@ static void of_selftest_overlay_6(void)
 
                np = of_find_node_by_path(overlay_path(overlay_nr + i));
                if (np == NULL) {
-                       selftest(0, "could not find overlay node @\"%s\"\n",
+                       unittest(0, "could not find overlay node @\"%s\"\n",
                                        overlay_path(overlay_nr + i));
                        return;
                }
 
                ret = of_overlay_create(np);
                if (ret < 0)  {
-                       selftest(0, "could not create overlay from \"%s\"\n",
+                       unittest(0, "could not create overlay from \"%s\"\n",
                                        overlay_path(overlay_nr + i));
                        return;
                }
@@ -1332,12 +1346,12 @@ static void of_selftest_overlay_6(void)
        }
 
        for (i = 0; i < 2; i++) {
-               /* selftest device must be in after state */
-               if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY)
+               /* unittest device must be in after state */
+               if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
                                != after) {
-                       selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
+                       unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
                                        overlay_path(overlay_nr + i),
-                                       selftest_path(selftest_nr + i,
+                                       unittest_path(unittest_nr + i,
                                                PDEV_OVERLAY),
                                        !after ? "enabled" : "disabled");
                        return;
@@ -1347,36 +1361,36 @@ static void of_selftest_overlay_6(void)
        for (i = 1; i >= 0; i--) {
                ret = of_overlay_destroy(ov_id[i]);
                if (ret != 0) {
-                       selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
+                       unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
                                        overlay_path(overlay_nr + i),
-                                       selftest_path(selftest_nr + i,
+                                       unittest_path(unittest_nr + i,
                                                PDEV_OVERLAY));
                        return;
                }
        }
 
        for (i = 0; i < 2; i++) {
-               /* selftest device must be again in before state */
-               if (of_selftest_device_exists(selftest_nr + i, PDEV_OVERLAY)
+               /* unittest device must be again in before state */
+               if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
                                != before) {
-                       selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+                       unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
                                        overlay_path(overlay_nr + i),
-                                       selftest_path(selftest_nr + i,
+                                       unittest_path(unittest_nr + i,
                                                PDEV_OVERLAY),
                                        !before ? "enabled" : "disabled");
                        return;
                }
        }
 
-       selftest(1, "overlay test %d passed\n", 6);
+       unittest(1, "overlay test %d passed\n", 6);
 }
 
 /* test overlay application in sequence */
-static void of_selftest_overlay_8(void)
+static void of_unittest_overlay_8(void)
 {
        struct device_node *np;
        int ret, i, ov_id[2];
-       int overlay_nr = 8, selftest_nr = 8;
+       int overlay_nr = 8, unittest_nr = 8;
 
        /* we don't care about device state in this test */
 
@@ -1385,14 +1399,14 @@ static void of_selftest_overlay_8(void)
 
                np = of_find_node_by_path(overlay_path(overlay_nr + i));
                if (np == NULL) {
-                       selftest(0, "could not find overlay node @\"%s\"\n",
+                       unittest(0, "could not find overlay node @\"%s\"\n",
                                        overlay_path(overlay_nr + i));
                        return;
                }
 
                ret = of_overlay_create(np);
                if (ret < 0)  {
-                       selftest(0, "could not create overlay from \"%s\"\n",
+                       unittest(0, "could not create overlay from \"%s\"\n",
                                        overlay_path(overlay_nr + i));
                        return;
                }
@@ -1402,9 +1416,9 @@ static void of_selftest_overlay_8(void)
        /* now try to remove first overlay (it should fail) */
        ret = of_overlay_destroy(ov_id[0]);
        if (ret == 0) {
-               selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
+               unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
                                overlay_path(overlay_nr + 0),
-                               selftest_path(selftest_nr,
+                               unittest_path(unittest_nr,
                                        PDEV_OVERLAY));
                return;
        }
@@ -1413,85 +1427,85 @@ static void of_selftest_overlay_8(void)
        for (i = 1; i >= 0; i--) {
                ret = of_overlay_destroy(ov_id[i]);
                if (ret != 0) {
-                       selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
+                       unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
                                        overlay_path(overlay_nr + i),
-                                       selftest_path(selftest_nr,
+                                       unittest_path(unittest_nr,
                                                PDEV_OVERLAY));
                        return;
                }
        }
 
-       selftest(1, "overlay test %d passed\n", 8);
+       unittest(1, "overlay test %d passed\n", 8);
 }
 
 /* test insertion of a bus with parent devices */
-static void of_selftest_overlay_10(void)
+static void of_unittest_overlay_10(void)
 {
        int ret;
        char *child_path;
 
        /* device should disable */
-       ret = of_selftest_apply_overlay_check(10, 10, 0, 1, PDEV_OVERLAY);
-       if (selftest(ret == 0,
+       ret = of_unittest_apply_overlay_check(10, 10, 0, 1, PDEV_OVERLAY);
+       if (unittest(ret == 0,
                        "overlay test %d failed; overlay application\n", 10))
                return;
 
-       child_path = kasprintf(GFP_KERNEL, "%s/test-selftest101",
-                       selftest_path(10, PDEV_OVERLAY));
-       if (selftest(child_path, "overlay test %d failed; kasprintf\n", 10))
+       child_path = kasprintf(GFP_KERNEL, "%s/test-unittest101",
+                       unittest_path(10, PDEV_OVERLAY));
+       if (unittest(child_path, "overlay test %d failed; kasprintf\n", 10))
                return;
 
        ret = of_path_device_type_exists(child_path, PDEV_OVERLAY);
        kfree(child_path);
-       if (selftest(ret, "overlay test %d failed; no child device\n", 10))
+       if (unittest(ret, "overlay test %d failed; no child device\n", 10))
                return;
 }
 
 /* test insertion of a bus with parent devices (and revert) */
-static void of_selftest_overlay_11(void)
+static void of_unittest_overlay_11(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_revert_overlay_check(11, 11, 0, 1,
+       ret = of_unittest_apply_revert_overlay_check(11, 11, 0, 1,
                        PDEV_OVERLAY);
-       if (selftest(ret == 0,
+       if (unittest(ret == 0,
                        "overlay test %d failed; overlay application\n", 11))
                return;
 }
 
 #if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY)
 
-struct selftest_i2c_bus_data {
+struct unittest_i2c_bus_data {
        struct platform_device  *pdev;
        struct i2c_adapter      adap;
 };
 
-static int selftest_i2c_master_xfer(struct i2c_adapter *adap,
+static int unittest_i2c_master_xfer(struct i2c_adapter *adap,
                struct i2c_msg *msgs, int num)
 {
-       struct selftest_i2c_bus_data *std = i2c_get_adapdata(adap);
+       struct unittest_i2c_bus_data *std = i2c_get_adapdata(adap);
 
        (void)std;
 
        return num;
 }
 
-static u32 selftest_i2c_functionality(struct i2c_adapter *adap)
+static u32 unittest_i2c_functionality(struct i2c_adapter *adap)
 {
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
 
-static const struct i2c_algorithm selftest_i2c_algo = {
-       .master_xfer    = selftest_i2c_master_xfer,
-       .functionality  = selftest_i2c_functionality,
+static const struct i2c_algorithm unittest_i2c_algo = {
+       .master_xfer    = unittest_i2c_master_xfer,
+       .functionality  = unittest_i2c_functionality,
 };
 
-static int selftest_i2c_bus_probe(struct platform_device *pdev)
+static int unittest_i2c_bus_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
-       struct selftest_i2c_bus_data *std;
+       struct unittest_i2c_bus_data *std;
        struct i2c_adapter *adap;
        int ret;
 
@@ -1505,7 +1519,7 @@ static int selftest_i2c_bus_probe(struct platform_device *pdev)
 
        std = devm_kzalloc(dev, sizeof(*std), GFP_KERNEL);
        if (!std) {
-               dev_err(dev, "Failed to allocate selftest i2c data\n");
+               dev_err(dev, "Failed to allocate unittest i2c data\n");
                return -ENOMEM;
        }
 
@@ -1518,7 +1532,7 @@ static int selftest_i2c_bus_probe(struct platform_device *pdev)
        adap->nr = -1;
        strlcpy(adap->name, pdev->name, sizeof(adap->name));
        adap->class = I2C_CLASS_DEPRECATED;
-       adap->algo = &selftest_i2c_algo;
+       adap->algo = &unittest_i2c_algo;
        adap->dev.parent = dev;
        adap->dev.of_node = dev->of_node;
        adap->timeout = 5 * HZ;
@@ -1533,11 +1547,11 @@ static int selftest_i2c_bus_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int selftest_i2c_bus_remove(struct platform_device *pdev)
+static int unittest_i2c_bus_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
-       struct selftest_i2c_bus_data *std = platform_get_drvdata(pdev);
+       struct unittest_i2c_bus_data *std = platform_get_drvdata(pdev);
 
        dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
        i2c_del_adapter(&std->adap);
@@ -1545,21 +1559,21 @@ static int selftest_i2c_bus_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id selftest_i2c_bus_match[] = {
-       { .compatible = "selftest-i2c-bus", },
+static const struct of_device_id unittest_i2c_bus_match[] = {
+       { .compatible = "unittest-i2c-bus", },
        {},
 };
 
-static struct platform_driver selftest_i2c_bus_driver = {
-       .probe                  = selftest_i2c_bus_probe,
-       .remove                 = selftest_i2c_bus_remove,
+static struct platform_driver unittest_i2c_bus_driver = {
+       .probe                  = unittest_i2c_bus_probe,
+       .remove                 = unittest_i2c_bus_remove,
        .driver = {
-               .name           = "selftest-i2c-bus",
-               .of_match_table = of_match_ptr(selftest_i2c_bus_match),
+               .name           = "unittest-i2c-bus",
+               .of_match_table = of_match_ptr(unittest_i2c_bus_match),
        },
 };
 
-static int selftest_i2c_dev_probe(struct i2c_client *client,
+static int unittest_i2c_dev_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
@@ -1575,7 +1589,7 @@ static int selftest_i2c_dev_probe(struct i2c_client *client,
        return 0;
 };
 
-static int selftest_i2c_dev_remove(struct i2c_client *client)
+static int unittest_i2c_dev_remove(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device_node *np = client->dev.of_node;
@@ -1584,42 +1598,42 @@ static int selftest_i2c_dev_remove(struct i2c_client *client)
        return 0;
 }
 
-static const struct i2c_device_id selftest_i2c_dev_id[] = {
-       { .name = "selftest-i2c-dev" },
+static const struct i2c_device_id unittest_i2c_dev_id[] = {
+       { .name = "unittest-i2c-dev" },
        { }
 };
 
-static struct i2c_driver selftest_i2c_dev_driver = {
+static struct i2c_driver unittest_i2c_dev_driver = {
        .driver = {
-               .name = "selftest-i2c-dev",
+               .name = "unittest-i2c-dev",
                .owner = THIS_MODULE,
        },
-       .probe = selftest_i2c_dev_probe,
-       .remove = selftest_i2c_dev_remove,
-       .id_table = selftest_i2c_dev_id,
+       .probe = unittest_i2c_dev_probe,
+       .remove = unittest_i2c_dev_remove,
+       .id_table = unittest_i2c_dev_id,
 };
 
 #if IS_BUILTIN(CONFIG_I2C_MUX)
 
-struct selftest_i2c_mux_data {
+struct unittest_i2c_mux_data {
        int nchans;
        struct i2c_adapter *adap[];
 };
 
-static int selftest_i2c_mux_select_chan(struct i2c_adapter *adap,
+static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap,
                               void *client, u32 chan)
 {
        return 0;
 }
 
-static int selftest_i2c_mux_probe(struct i2c_client *client,
+static int unittest_i2c_mux_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
        int ret, i, nchans, size;
        struct device *dev = &client->dev;
        struct i2c_adapter *adap = to_i2c_adapter(dev->parent);
        struct device_node *np = client->dev.of_node, *child;
-       struct selftest_i2c_mux_data *stm;
+       struct unittest_i2c_mux_data *stm;
        u32 reg, max_reg;
 
        dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
@@ -1643,7 +1657,7 @@ static int selftest_i2c_mux_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       size = offsetof(struct selftest_i2c_mux_data, adap[nchans]);
+       size = offsetof(struct unittest_i2c_mux_data, adap[nchans]);
        stm = devm_kzalloc(dev, size, GFP_KERNEL);
        if (!stm) {
                dev_err(dev, "Out of memory\n");
@@ -1652,7 +1666,7 @@ static int selftest_i2c_mux_probe(struct i2c_client *client,
        stm->nchans = nchans;
        for (i = 0; i < nchans; i++) {
                stm->adap[i] = i2c_add_mux_adapter(adap, dev, client,
-                               0, i, 0, selftest_i2c_mux_select_chan, NULL);
+                               0, i, 0, unittest_i2c_mux_select_chan, NULL);
                if (!stm->adap[i]) {
                        dev_err(dev, "Failed to register mux #%d\n", i);
                        for (i--; i >= 0; i--)
@@ -1666,11 +1680,11 @@ static int selftest_i2c_mux_probe(struct i2c_client *client,
        return 0;
 };
 
-static int selftest_i2c_mux_remove(struct i2c_client *client)
+static int unittest_i2c_mux_remove(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device_node *np = client->dev.of_node;
-       struct selftest_i2c_mux_data *stm = i2c_get_clientdata(client);
+       struct unittest_i2c_mux_data *stm = i2c_get_clientdata(client);
        int i;
 
        dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
@@ -1679,166 +1693,166 @@ static int selftest_i2c_mux_remove(struct i2c_client *client)
        return 0;
 }
 
-static const struct i2c_device_id selftest_i2c_mux_id[] = {
-       { .name = "selftest-i2c-mux" },
+static const struct i2c_device_id unittest_i2c_mux_id[] = {
+       { .name = "unittest-i2c-mux" },
        { }
 };
 
-static struct i2c_driver selftest_i2c_mux_driver = {
+static struct i2c_driver unittest_i2c_mux_driver = {
        .driver = {
-               .name = "selftest-i2c-mux",
+               .name = "unittest-i2c-mux",
                .owner = THIS_MODULE,
        },
-       .probe = selftest_i2c_mux_probe,
-       .remove = selftest_i2c_mux_remove,
-       .id_table = selftest_i2c_mux_id,
+       .probe = unittest_i2c_mux_probe,
+       .remove = unittest_i2c_mux_remove,
+       .id_table = unittest_i2c_mux_id,
 };
 
 #endif
 
-static int of_selftest_overlay_i2c_init(void)
+static int of_unittest_overlay_i2c_init(void)
 {
        int ret;
 
-       ret = i2c_add_driver(&selftest_i2c_dev_driver);
-       if (selftest(ret == 0,
-                       "could not register selftest i2c device driver\n"))
+       ret = i2c_add_driver(&unittest_i2c_dev_driver);
+       if (unittest(ret == 0,
+                       "could not register unittest i2c device driver\n"))
                return ret;
 
-       ret = platform_driver_register(&selftest_i2c_bus_driver);
-       if (selftest(ret == 0,
-                       "could not register selftest i2c bus driver\n"))
+       ret = platform_driver_register(&unittest_i2c_bus_driver);
+       if (unittest(ret == 0,
+                       "could not register unittest i2c bus driver\n"))
                return ret;
 
 #if IS_BUILTIN(CONFIG_I2C_MUX)
-       ret = i2c_add_driver(&selftest_i2c_mux_driver);
-       if (selftest(ret == 0,
-                       "could not register selftest i2c mux driver\n"))
+       ret = i2c_add_driver(&unittest_i2c_mux_driver);
+       if (unittest(ret == 0,
+                       "could not register unittest i2c mux driver\n"))
                return ret;
 #endif
 
        return 0;
 }
 
-static void of_selftest_overlay_i2c_cleanup(void)
+static void of_unittest_overlay_i2c_cleanup(void)
 {
 #if IS_BUILTIN(CONFIG_I2C_MUX)
-       i2c_del_driver(&selftest_i2c_mux_driver);
+       i2c_del_driver(&unittest_i2c_mux_driver);
 #endif
-       platform_driver_unregister(&selftest_i2c_bus_driver);
-       i2c_del_driver(&selftest_i2c_dev_driver);
+       platform_driver_unregister(&unittest_i2c_bus_driver);
+       i2c_del_driver(&unittest_i2c_dev_driver);
 }
 
-static void of_selftest_overlay_i2c_12(void)
+static void of_unittest_overlay_i2c_12(void)
 {
        int ret;
 
        /* device should enable */
-       ret = of_selftest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY);
+       ret = of_unittest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 12);
+       unittest(1, "overlay test %d passed\n", 12);
 }
 
 /* test deactivation of device */
-static void of_selftest_overlay_i2c_13(void)
+static void of_unittest_overlay_i2c_13(void)
 {
        int ret;
 
        /* device should disable */
-       ret = of_selftest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY);
+       ret = of_unittest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 13);
+       unittest(1, "overlay test %d passed\n", 13);
 }
 
 /* just check for i2c mux existence */
-static void of_selftest_overlay_i2c_14(void)
+static void of_unittest_overlay_i2c_14(void)
 {
 }
 
-static void of_selftest_overlay_i2c_15(void)
+static void of_unittest_overlay_i2c_15(void)
 {
        int ret;
 
        /* device should enable */
-       ret = of_selftest_apply_overlay_check(16, 15, 0, 1, I2C_OVERLAY);
+       ret = of_unittest_apply_overlay_check(16, 15, 0, 1, I2C_OVERLAY);
        if (ret != 0)
                return;
 
-       selftest(1, "overlay test %d passed\n", 15);
+       unittest(1, "overlay test %d passed\n", 15);
 }
 
 #else
 
-static inline void of_selftest_overlay_i2c_14(void) { }
-static inline void of_selftest_overlay_i2c_15(void) { }
+static inline void of_unittest_overlay_i2c_14(void) { }
+static inline void of_unittest_overlay_i2c_15(void) { }
 
 #endif
 
-static void __init of_selftest_overlay(void)
+static void __init of_unittest_overlay(void)
 {
        struct device_node *bus_np = NULL;
        int ret;
 
-       ret = platform_driver_register(&selftest_driver);
+       ret = platform_driver_register(&unittest_driver);
        if (ret != 0) {
-               selftest(0, "could not register selftest driver\n");
+               unittest(0, "could not register unittest driver\n");
                goto out;
        }
 
        bus_np = of_find_node_by_path(bus_path);
        if (bus_np == NULL) {
-               selftest(0, "could not find bus_path \"%s\"\n", bus_path);
+               unittest(0, "could not find bus_path \"%s\"\n", bus_path);
                goto out;
        }
 
        ret = of_platform_populate(bus_np, of_default_bus_match_table,
                        NULL, NULL);
        if (ret != 0) {
-               selftest(0, "could not populate bus @ \"%s\"\n", bus_path);
+               unittest(0, "could not populate bus @ \"%s\"\n", bus_path);
                goto out;
        }
 
-       if (!of_selftest_device_exists(100, PDEV_OVERLAY)) {
-               selftest(0, "could not find selftest0 @ \"%s\"\n",
-                               selftest_path(100, PDEV_OVERLAY));
+       if (!of_unittest_device_exists(100, PDEV_OVERLAY)) {
+               unittest(0, "could not find unittest0 @ \"%s\"\n",
+                               unittest_path(100, PDEV_OVERLAY));
                goto out;
        }
 
-       if (of_selftest_device_exists(101, PDEV_OVERLAY)) {
-               selftest(0, "selftest1 @ \"%s\" should not exist\n",
-                               selftest_path(101, PDEV_OVERLAY));
+       if (of_unittest_device_exists(101, PDEV_OVERLAY)) {
+               unittest(0, "unittest1 @ \"%s\" should not exist\n",
+                               unittest_path(101, PDEV_OVERLAY));
                goto out;
        }
 
-       selftest(1, "basic infrastructure of overlays passed");
+       unittest(1, "basic infrastructure of overlays passed");
 
        /* tests in sequence */
-       of_selftest_overlay_0();
-       of_selftest_overlay_1();
-       of_selftest_overlay_2();
-       of_selftest_overlay_3();
-       of_selftest_overlay_4();
-       of_selftest_overlay_5();
-       of_selftest_overlay_6();
-       of_selftest_overlay_8();
-
-       of_selftest_overlay_10();
-       of_selftest_overlay_11();
+       of_unittest_overlay_0();
+       of_unittest_overlay_1();
+       of_unittest_overlay_2();
+       of_unittest_overlay_3();
+       of_unittest_overlay_4();
+       of_unittest_overlay_5();
+       of_unittest_overlay_6();
+       of_unittest_overlay_8();
+
+       of_unittest_overlay_10();
+       of_unittest_overlay_11();
 
 #if IS_BUILTIN(CONFIG_I2C)
-       if (selftest(of_selftest_overlay_i2c_init() == 0, "i2c init failed\n"))
+       if (unittest(of_unittest_overlay_i2c_init() == 0, "i2c init failed\n"))
                goto out;
 
-       of_selftest_overlay_i2c_12();
-       of_selftest_overlay_i2c_13();
-       of_selftest_overlay_i2c_14();
-       of_selftest_overlay_i2c_15();
+       of_unittest_overlay_i2c_12();
+       of_unittest_overlay_i2c_13();
+       of_unittest_overlay_i2c_14();
+       of_unittest_overlay_i2c_15();
 
-       of_selftest_overlay_i2c_cleanup();
+       of_unittest_overlay_i2c_cleanup();
 #endif
 
 out:
@@ -1846,16 +1860,16 @@ out:
 }
 
 #else
-static inline void __init of_selftest_overlay(void) { }
+static inline void __init of_unittest_overlay(void) { }
 #endif
 
-static int __init of_selftest(void)
+static int __init of_unittest(void)
 {
        struct device_node *np;
        int res;
 
-       /* adding data for selftest */
-       res = selftest_data_add();
+       /* adding data for unittest */
+       res = unittest_data_add();
        if (res)
                return res;
        if (!of_aliases)
@@ -1868,27 +1882,27 @@ static int __init of_selftest(void)
        }
        of_node_put(np);
 
-       pr_info("start of selftest - you will see error messages\n");
-       of_selftest_check_tree_linkage();
-       of_selftest_check_phandles();
-       of_selftest_find_node_by_name();
-       of_selftest_dynamic();
-       of_selftest_parse_phandle_with_args();
-       of_selftest_property_string();
-       of_selftest_property_copy();
-       of_selftest_changeset();
-       of_selftest_parse_interrupts();
-       of_selftest_parse_interrupts_extended();
-       of_selftest_match_node();
-       of_selftest_platform_populate();
-       of_selftest_overlay();
+       pr_info("start of unittest - you will see error messages\n");
+       of_unittest_check_tree_linkage();
+       of_unittest_check_phandles();
+       of_unittest_find_node_by_name();
+       of_unittest_dynamic();
+       of_unittest_parse_phandle_with_args();
+       of_unittest_property_string();
+       of_unittest_property_copy();
+       of_unittest_changeset();
+       of_unittest_parse_interrupts();
+       of_unittest_parse_interrupts_extended();
+       of_unittest_match_node();
+       of_unittest_platform_populate();
+       of_unittest_overlay();
 
        /* Double check linkage after removing testcase data */
-       of_selftest_check_tree_linkage();
+       of_unittest_check_tree_linkage();
 
-       pr_info("end of selftest - %i passed, %i failed\n",
-               selftest_results.passed, selftest_results.failed);
+       pr_info("end of unittest - %i passed, %i failed\n",
+               unittest_results.passed, unittest_results.failed);
 
        return 0;
 }
-late_initcall(of_selftest);
+late_initcall(of_unittest);
index 42b87f95267c255f26bce067615607021eca99cd..8b6f6d5fdd68bc442449d4e4d5a5ea1ce9a85f41 100644 (file)
@@ -164,20 +164,15 @@ static void __init omapdss_walk_device(struct device_node *node, bool root)
 
                pn = of_graph_get_remote_port_parent(n);
 
-               if (!pn) {
-                       of_node_put(n);
+               if (!pn)
                        continue;
-               }
 
                if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
                        of_node_put(pn);
-                       of_node_put(n);
                        continue;
                }
 
                omapdss_walk_device(pn, false);
-
-               of_node_put(n);
        }
 }
 
index 099c7712631ca698cf43dd6337d483e7e9ebf126..fb9ffcb432779b55620909090b825ead7ddbcede 100644 (file)
@@ -78,7 +78,6 @@ enum p9_cache_modes {
  * @cache: cache mode of type &p9_cache_modes
  * @cachetag: the tag of the cache associated with this session
  * @fscache: session cookie associated with FS-Cache
- * @options: copy of options string given by user
  * @uname: string user name to mount hierarchy as
  * @aname: mount specifier for remote hierarchy
  * @maxdata: maximum data to be sent/recvd per protocol message
index be35d05a4d0efc00c5955cc047ac20bea293149a..e9e04376c52ce8ef4673d041281c1db1e2c4ac99 100644 (file)
@@ -231,9 +231,7 @@ static int v9fs_launder_page(struct page *page)
 /**
  * v9fs_direct_IO - 9P address space operation for direct I/O
  * @iocb: target I/O control block
- * @iov: array of vectors that define I/O buffer
  * @pos: offset in file to begin the operation
- * @nr_segs: size of iovec array
  *
  * The presence of v9fs_direct_IO() in the address space ops vector
  * allowes open() O_DIRECT flags which would have failed otherwise.
index 2a9dd37dc426d6cf38eeac64727217164c903ae6..1ef16bd8280b595c0d261bcfa4246d5473d7015e 100644 (file)
@@ -151,7 +151,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
        struct p9_flock flock;
        struct p9_fid *fid;
-       uint8_t status;
+       uint8_t status = P9_LOCK_ERROR;
        int res = 0;
        unsigned char fl_type;
 
@@ -196,7 +196,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
        for (;;) {
                res = p9_client_lock_dotl(fid, &flock, &status);
                if (res < 0)
-                       break;
+                       goto out_unlock;
 
                if (status != P9_LOCK_BLOCKED)
                        break;
@@ -214,14 +214,16 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
        case P9_LOCK_BLOCKED:
                res = -EAGAIN;
                break;
+       default:
+               WARN_ONCE(1, "unknown lock status code: %d\n", status);
+               /* fallthough */
        case P9_LOCK_ERROR:
        case P9_LOCK_GRACE:
                res = -ENOLCK;
                break;
-       default:
-               BUG();
        }
 
+out_unlock:
        /*
         * incase server returned error for lock request, revert
         * it locally
index ec35851e5b71c4a19897bf97a70d5ec80fb67128..011f43365d7b1e536586a8a4243262aa3aa60baf 100644 (file)
@@ -32,6 +32,7 @@ source "fs/gfs2/Kconfig"
 source "fs/ocfs2/Kconfig"
 source "fs/btrfs/Kconfig"
 source "fs/nilfs2/Kconfig"
+source "fs/f2fs/Kconfig"
 
 config FS_DAX
        bool "Direct Access (DAX) support"
@@ -217,7 +218,6 @@ source "fs/pstore/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
-source "fs/f2fs/Kconfig"
 
 endif # MISC_FILESYSTEMS
 
index 94e2d2ffabe1a18d747fcccbe360b74389490ad0..05f0f663f14ca460277ecc66d09650ff6bb4be76 100644 (file)
@@ -1,5 +1,5 @@
 config F2FS_FS
-       tristate "F2FS filesystem support (EXPERIMENTAL)"
+       tristate "F2FS filesystem support"
        depends on BLOCK
        help
          F2FS is based on Log-structured File System (LFS), which supports
index 742202779bd517029ff8a910a361f37181c530f6..4320ffab3495bbadb6c9b176e98241904bc51ecb 100644 (file)
@@ -351,13 +351,11 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 
        *acl = f2fs_acl_clone(p, GFP_NOFS);
        if (!*acl)
-               return -ENOMEM;
+               goto no_mem;
 
        ret = f2fs_acl_create_masq(*acl, mode);
-       if (ret < 0) {
-               posix_acl_release(*acl);
-               return -ENOMEM;
-       }
+       if (ret < 0)
+               goto no_mem_clone;
 
        if (ret == 0) {
                posix_acl_release(*acl);
@@ -378,6 +376,12 @@ no_acl:
        *default_acl = NULL;
        *acl = NULL;
        return 0;
+
+no_mem_clone:
+       posix_acl_release(*acl);
+no_mem:
+       posix_acl_release(p);
+       return -ENOMEM;
 }
 
 int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
index 7f794b72b3b7d27f36828fac8eb84fea21bfdc69..a5e17a2a078147bcbabf40e177714abcb1360aba 100644 (file)
@@ -276,7 +276,7 @@ continue_unlock:
                        if (!clear_page_dirty_for_io(page))
                                goto continue_unlock;
 
-                       if (f2fs_write_meta_page(page, &wbc)) {
+                       if (mapping->a_ops->writepage(page, &wbc)) {
                                unlock_page(page);
                                break;
                        }
@@ -464,20 +464,19 @@ static void recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
 
 void recover_orphan_inodes(struct f2fs_sb_info *sbi)
 {
-       block_t start_blk, orphan_blkaddr, i, j;
+       block_t start_blk, orphan_blocks, i, j;
 
        if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG))
                return;
 
        set_sbi_flag(sbi, SBI_POR_DOING);
 
-       start_blk = __start_cp_addr(sbi) + 1 +
-               le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
-       orphan_blkaddr = __start_sum_addr(sbi) - 1;
+       start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
+       orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
 
-       ra_meta_pages(sbi, start_blk, orphan_blkaddr, META_CP);
+       ra_meta_pages(sbi, start_blk, orphan_blocks, META_CP);
 
-       for (i = 0; i < orphan_blkaddr; i++) {
+       for (i = 0; i < orphan_blocks; i++) {
                struct page *page = get_meta_page(sbi, start_blk + i);
                struct f2fs_orphan_block *orphan_blk;
 
@@ -615,7 +614,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
        unsigned long blk_size = sbi->blocksize;
        unsigned long long cp1_version = 0, cp2_version = 0;
        unsigned long long cp_start_blk_no;
-       unsigned int cp_blks = 1 + le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
+       unsigned int cp_blks = 1 + __cp_payload(sbi);
        block_t cp_blk_no;
        int i;
 
@@ -796,6 +795,7 @@ retry:
                 * wribacking dentry pages in the freeing inode.
                 */
                f2fs_submit_merged_bio(sbi, DATA, WRITE);
+               cond_resched();
        }
        goto retry;
 }
@@ -884,7 +884,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        __u32 crc32 = 0;
        void *kaddr;
        int i;
-       int cp_payload_blks = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
+       int cp_payload_blks = __cp_payload(sbi);
 
        /*
         * This avoids to conduct wrong roll-forward operations and uses
@@ -1048,17 +1048,18 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
        unsigned long long ckpt_ver;
 
-       trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops");
-
        mutex_lock(&sbi->cp_mutex);
 
        if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
-                       cpc->reason != CP_DISCARD && cpc->reason != CP_UMOUNT)
+               (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC))
                goto out;
        if (unlikely(f2fs_cp_error(sbi)))
                goto out;
        if (f2fs_readonly(sbi->sb))
                goto out;
+
+       trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops");
+
        if (block_operations(sbi))
                goto out;
 
@@ -1085,6 +1086,10 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 
        unblock_operations(sbi);
        stat_inc_cp_count(sbi->stat_info);
+
+       if (cpc->reason == CP_RECOVERY)
+               f2fs_msg(sbi->sb, KERN_NOTICE,
+                       "checkpoint: version = %llx", ckpt_ver);
 out:
        mutex_unlock(&sbi->cp_mutex);
        trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint");
@@ -1103,14 +1108,9 @@ void init_ino_entry_info(struct f2fs_sb_info *sbi)
                im->ino_num = 0;
        }
 
-       /*
-        * considering 512 blocks in a segment 8 blocks are needed for cp
-        * and log segment summaries. Remaining blocks are used to keep
-        * orphan entries with the limitation one reserved segment
-        * for cp pack we can have max 1020*504 orphan entries
-        */
        sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS -
-                       NR_CURSEG_TYPE) * F2FS_ORPHANS_PER_BLOCK;
+                       NR_CURSEG_TYPE - __cp_payload(sbi)) *
+                               F2FS_ORPHANS_PER_BLOCK;
 }
 
 int __init create_checkpoint_caches(void)
index 319eda511c4ff6e4bd869aaf2e1b711ecdc6470c..b91b0e10678eb1adcb87e45628f7794caa0dcc9b 100644 (file)
@@ -25,6 +25,9 @@
 #include "trace.h"
 #include <trace/events/f2fs.h>
 
+static struct kmem_cache *extent_tree_slab;
+static struct kmem_cache *extent_node_slab;
+
 static void f2fs_read_end_io(struct bio *bio, int err)
 {
        struct bio_vec *bvec;
@@ -197,7 +200,7 @@ alloc_new:
  *  ->node_page
  *    update block addresses in the node page
  */
-static void __set_data_blkaddr(struct dnode_of_data *dn)
+void set_data_blkaddr(struct dnode_of_data *dn)
 {
        struct f2fs_node *rn;
        __le32 *addr_array;
@@ -226,7 +229,7 @@ int reserve_new_block(struct dnode_of_data *dn)
        trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node);
 
        dn->data_blkaddr = NEW_ADDR;
-       __set_data_blkaddr(dn);
+       set_data_blkaddr(dn);
        mark_inode_dirty(dn->inode);
        sync_inode_page(dn);
        return 0;
@@ -248,73 +251,62 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index)
        return err;
 }
 
-static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
-                                       struct buffer_head *bh_result)
+static void f2fs_map_bh(struct super_block *sb, pgoff_t pgofs,
+                       struct extent_info *ei, struct buffer_head *bh_result)
+{
+       unsigned int blkbits = sb->s_blocksize_bits;
+       size_t max_size = bh_result->b_size;
+       size_t mapped_size;
+
+       clear_buffer_new(bh_result);
+       map_bh(bh_result, sb, ei->blk + pgofs - ei->fofs);
+       mapped_size = (ei->fofs + ei->len - pgofs) << blkbits;
+       bh_result->b_size = min(max_size, mapped_size);
+}
+
+static bool lookup_extent_info(struct inode *inode, pgoff_t pgofs,
+                                                       struct extent_info *ei)
 {
        struct f2fs_inode_info *fi = F2FS_I(inode);
        pgoff_t start_fofs, end_fofs;
        block_t start_blkaddr;
 
-       if (is_inode_flag_set(fi, FI_NO_EXTENT))
-               return 0;
-
-       read_lock(&fi->ext.ext_lock);
+       read_lock(&fi->ext_lock);
        if (fi->ext.len == 0) {
-               read_unlock(&fi->ext.ext_lock);
-               return 0;
+               read_unlock(&fi->ext_lock);
+               return false;
        }
 
        stat_inc_total_hit(inode->i_sb);
 
        start_fofs = fi->ext.fofs;
        end_fofs = fi->ext.fofs + fi->ext.len - 1;
-       start_blkaddr = fi->ext.blk_addr;
+       start_blkaddr = fi->ext.blk;
 
        if (pgofs >= start_fofs && pgofs <= end_fofs) {
-               unsigned int blkbits = inode->i_sb->s_blocksize_bits;
-               size_t count;
-
-               set_buffer_new(bh_result);
-               map_bh(bh_result, inode->i_sb,
-                               start_blkaddr + pgofs - start_fofs);
-               count = end_fofs - pgofs + 1;
-               if (count < (UINT_MAX >> blkbits))
-                       bh_result->b_size = (count << blkbits);
-               else
-                       bh_result->b_size = UINT_MAX;
-
+               *ei = fi->ext;
                stat_inc_read_hit(inode->i_sb);
-               read_unlock(&fi->ext.ext_lock);
-               return 1;
+               read_unlock(&fi->ext_lock);
+               return true;
        }
-       read_unlock(&fi->ext.ext_lock);
-       return 0;
+       read_unlock(&fi->ext_lock);
+       return false;
 }
 
-void update_extent_cache(struct dnode_of_data *dn)
+static bool update_extent_info(struct inode *inode, pgoff_t fofs,
+                                                               block_t blkaddr)
 {
-       struct f2fs_inode_info *fi = F2FS_I(dn->inode);
-       pgoff_t fofs, start_fofs, end_fofs;
+       struct f2fs_inode_info *fi = F2FS_I(inode);
+       pgoff_t start_fofs, end_fofs;
        block_t start_blkaddr, end_blkaddr;
        int need_update = true;
 
-       f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
-
-       /* Update the page address in the parent node */
-       __set_data_blkaddr(dn);
-
-       if (is_inode_flag_set(fi, FI_NO_EXTENT))
-               return;
-
-       fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
-                                                       dn->ofs_in_node;
-
-       write_lock(&fi->ext.ext_lock);
+       write_lock(&fi->ext_lock);
 
        start_fofs = fi->ext.fofs;
        end_fofs = fi->ext.fofs + fi->ext.len - 1;
-       start_blkaddr = fi->ext.blk_addr;
-       end_blkaddr = fi->ext.blk_addr + fi->ext.len - 1;
+       start_blkaddr = fi->ext.blk;
+       end_blkaddr = fi->ext.blk + fi->ext.len - 1;
 
        /* Drop and initialize the matched extent */
        if (fi->ext.len == 1 && fofs == start_fofs)
@@ -322,24 +314,24 @@ void update_extent_cache(struct dnode_of_data *dn)
 
        /* Initial extent */
        if (fi->ext.len == 0) {
-               if (dn->data_blkaddr != NULL_ADDR) {
+               if (blkaddr != NULL_ADDR) {
                        fi->ext.fofs = fofs;
-                       fi->ext.blk_addr = dn->data_blkaddr;
+                       fi->ext.blk = blkaddr;
                        fi->ext.len = 1;
                }
                goto end_update;
        }
 
        /* Front merge */
-       if (fofs == start_fofs - 1 && dn->data_blkaddr == start_blkaddr - 1) {
+       if (fofs == start_fofs - 1 && blkaddr == start_blkaddr - 1) {
                fi->ext.fofs--;
-               fi->ext.blk_addr--;
+               fi->ext.blk--;
                fi->ext.len++;
                goto end_update;
        }
 
        /* Back merge */
-       if (fofs == end_fofs + 1 && dn->data_blkaddr == end_blkaddr + 1) {
+       if (fofs == end_fofs + 1 && blkaddr == end_blkaddr + 1) {
                fi->ext.len++;
                goto end_update;
        }
@@ -351,8 +343,7 @@ void update_extent_cache(struct dnode_of_data *dn)
                        fi->ext.len = fofs - start_fofs;
                } else {
                        fi->ext.fofs = fofs + 1;
-                       fi->ext.blk_addr = start_blkaddr +
-                                       fofs - start_fofs + 1;
+                       fi->ext.blk = start_blkaddr + fofs - start_fofs + 1;
                        fi->ext.len -= fofs - start_fofs + 1;
                }
        } else {
@@ -366,27 +357,583 @@ void update_extent_cache(struct dnode_of_data *dn)
                need_update = true;
        }
 end_update:
-       write_unlock(&fi->ext.ext_lock);
-       if (need_update)
-               sync_inode_page(dn);
+       write_unlock(&fi->ext_lock);
+       return need_update;
+}
+
+static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
+                               struct extent_tree *et, struct extent_info *ei,
+                               struct rb_node *parent, struct rb_node **p)
+{
+       struct extent_node *en;
+
+       en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC);
+       if (!en)
+               return NULL;
+
+       en->ei = *ei;
+       INIT_LIST_HEAD(&en->list);
+
+       rb_link_node(&en->rb_node, parent, p);
+       rb_insert_color(&en->rb_node, &et->root);
+       et->count++;
+       atomic_inc(&sbi->total_ext_node);
+       return en;
+}
+
+static void __detach_extent_node(struct f2fs_sb_info *sbi,
+                               struct extent_tree *et, struct extent_node *en)
+{
+       rb_erase(&en->rb_node, &et->root);
+       et->count--;
+       atomic_dec(&sbi->total_ext_node);
+
+       if (et->cached_en == en)
+               et->cached_en = NULL;
+}
+
+static struct extent_tree *__find_extent_tree(struct f2fs_sb_info *sbi,
+                                                       nid_t ino)
+{
+       struct extent_tree *et;
+
+       down_read(&sbi->extent_tree_lock);
+       et = radix_tree_lookup(&sbi->extent_tree_root, ino);
+       if (!et) {
+               up_read(&sbi->extent_tree_lock);
+               return NULL;
+       }
+       atomic_inc(&et->refcount);
+       up_read(&sbi->extent_tree_lock);
+
+       return et;
+}
+
+static struct extent_tree *__grab_extent_tree(struct inode *inode)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct extent_tree *et;
+       nid_t ino = inode->i_ino;
+
+       down_write(&sbi->extent_tree_lock);
+       et = radix_tree_lookup(&sbi->extent_tree_root, ino);
+       if (!et) {
+               et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
+               f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et);
+               memset(et, 0, sizeof(struct extent_tree));
+               et->ino = ino;
+               et->root = RB_ROOT;
+               et->cached_en = NULL;
+               rwlock_init(&et->lock);
+               atomic_set(&et->refcount, 0);
+               et->count = 0;
+               sbi->total_ext_tree++;
+       }
+       atomic_inc(&et->refcount);
+       up_write(&sbi->extent_tree_lock);
+
+       return et;
+}
+
+static struct extent_node *__lookup_extent_tree(struct extent_tree *et,
+                                                       unsigned int fofs)
+{
+       struct rb_node *node = et->root.rb_node;
+       struct extent_node *en;
+
+       if (et->cached_en) {
+               struct extent_info *cei = &et->cached_en->ei;
+
+               if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
+                       return et->cached_en;
+       }
+
+       while (node) {
+               en = rb_entry(node, struct extent_node, rb_node);
+
+               if (fofs < en->ei.fofs) {
+                       node = node->rb_left;
+               } else if (fofs >= en->ei.fofs + en->ei.len) {
+                       node = node->rb_right;
+               } else {
+                       et->cached_en = en;
+                       return en;
+               }
+       }
+       return NULL;
+}
+
+static struct extent_node *__try_back_merge(struct f2fs_sb_info *sbi,
+                               struct extent_tree *et, struct extent_node *en)
+{
+       struct extent_node *prev;
+       struct rb_node *node;
+
+       node = rb_prev(&en->rb_node);
+       if (!node)
+               return NULL;
+
+       prev = rb_entry(node, struct extent_node, rb_node);
+       if (__is_back_mergeable(&en->ei, &prev->ei)) {
+               en->ei.fofs = prev->ei.fofs;
+               en->ei.blk = prev->ei.blk;
+               en->ei.len += prev->ei.len;
+               __detach_extent_node(sbi, et, prev);
+               return prev;
+       }
+       return NULL;
+}
+
+static struct extent_node *__try_front_merge(struct f2fs_sb_info *sbi,
+                               struct extent_tree *et, struct extent_node *en)
+{
+       struct extent_node *next;
+       struct rb_node *node;
+
+       node = rb_next(&en->rb_node);
+       if (!node)
+               return NULL;
+
+       next = rb_entry(node, struct extent_node, rb_node);
+       if (__is_front_mergeable(&en->ei, &next->ei)) {
+               en->ei.len += next->ei.len;
+               __detach_extent_node(sbi, et, next);
+               return next;
+       }
+       return NULL;
+}
+
+static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
+                               struct extent_tree *et, struct extent_info *ei,
+                               struct extent_node **den)
+{
+       struct rb_node **p = &et->root.rb_node;
+       struct rb_node *parent = NULL;
+       struct extent_node *en;
+
+       while (*p) {
+               parent = *p;
+               en = rb_entry(parent, struct extent_node, rb_node);
+
+               if (ei->fofs < en->ei.fofs) {
+                       if (__is_front_mergeable(ei, &en->ei)) {
+                               f2fs_bug_on(sbi, !den);
+                               en->ei.fofs = ei->fofs;
+                               en->ei.blk = ei->blk;
+                               en->ei.len += ei->len;
+                               *den = __try_back_merge(sbi, et, en);
+                               return en;
+                       }
+                       p = &(*p)->rb_left;
+               } else if (ei->fofs >= en->ei.fofs + en->ei.len) {
+                       if (__is_back_mergeable(ei, &en->ei)) {
+                               f2fs_bug_on(sbi, !den);
+                               en->ei.len += ei->len;
+                               *den = __try_front_merge(sbi, et, en);
+                               return en;
+                       }
+                       p = &(*p)->rb_right;
+               } else {
+                       f2fs_bug_on(sbi, 1);
+               }
+       }
+
+       return __attach_extent_node(sbi, et, ei, parent, p);
+}
+
+static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
+                                       struct extent_tree *et, bool free_all)
+{
+       struct rb_node *node, *next;
+       struct extent_node *en;
+       unsigned int count = et->count;
+
+       node = rb_first(&et->root);
+       while (node) {
+               next = rb_next(node);
+               en = rb_entry(node, struct extent_node, rb_node);
+
+               if (free_all) {
+                       spin_lock(&sbi->extent_lock);
+                       if (!list_empty(&en->list))
+                               list_del_init(&en->list);
+                       spin_unlock(&sbi->extent_lock);
+               }
+
+               if (free_all || list_empty(&en->list)) {
+                       __detach_extent_node(sbi, et, en);
+                       kmem_cache_free(extent_node_slab, en);
+               }
+               node = next;
+       }
+
+       return count - et->count;
+}
+
+static void f2fs_init_extent_tree(struct inode *inode,
+                                               struct f2fs_extent *i_ext)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct extent_tree *et;
+       struct extent_node *en;
+       struct extent_info ei;
+
+       if (le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN)
+               return;
+
+       et = __grab_extent_tree(inode);
+
+       write_lock(&et->lock);
+       if (et->count)
+               goto out;
+
+       set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
+               le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
+
+       en = __insert_extent_tree(sbi, et, &ei, NULL);
+       if (en) {
+               et->cached_en = en;
+
+               spin_lock(&sbi->extent_lock);
+               list_add_tail(&en->list, &sbi->extent_list);
+               spin_unlock(&sbi->extent_lock);
+       }
+out:
+       write_unlock(&et->lock);
+       atomic_dec(&et->refcount);
+}
+
+static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
+                                                       struct extent_info *ei)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct extent_tree *et;
+       struct extent_node *en;
+
+       trace_f2fs_lookup_extent_tree_start(inode, pgofs);
+
+       et = __find_extent_tree(sbi, inode->i_ino);
+       if (!et)
+               return false;
+
+       read_lock(&et->lock);
+       en = __lookup_extent_tree(et, pgofs);
+       if (en) {
+               *ei = en->ei;
+               spin_lock(&sbi->extent_lock);
+               if (!list_empty(&en->list))
+                       list_move_tail(&en->list, &sbi->extent_list);
+               spin_unlock(&sbi->extent_lock);
+               stat_inc_read_hit(sbi->sb);
+       }
+       stat_inc_total_hit(sbi->sb);
+       read_unlock(&et->lock);
+
+       trace_f2fs_lookup_extent_tree_end(inode, pgofs, en);
+
+       atomic_dec(&et->refcount);
+       return en ? true : false;
+}
+
+static void f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
+                                                       block_t blkaddr)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct extent_tree *et;
+       struct extent_node *en = NULL, *en1 = NULL, *en2 = NULL, *en3 = NULL;
+       struct extent_node *den = NULL;
+       struct extent_info ei, dei;
+       unsigned int endofs;
+
+       trace_f2fs_update_extent_tree(inode, fofs, blkaddr);
+
+       et = __grab_extent_tree(inode);
+
+       write_lock(&et->lock);
+
+       /* 1. lookup and remove existing extent info in cache */
+       en = __lookup_extent_tree(et, fofs);
+       if (!en)
+               goto update_extent;
+
+       dei = en->ei;
+       __detach_extent_node(sbi, et, en);
+
+       /* 2. if extent can be split more, split and insert the left part */
+       if (dei.len > 1) {
+               /*  insert left part of split extent into cache */
+               if (fofs - dei.fofs >= F2FS_MIN_EXTENT_LEN) {
+                       set_extent_info(&ei, dei.fofs, dei.blk,
+                                                       fofs - dei.fofs);
+                       en1 = __insert_extent_tree(sbi, et, &ei, NULL);
+               }
+
+               /* insert right part of split extent into cache */
+               endofs = dei.fofs + dei.len - 1;
+               if (endofs - fofs >= F2FS_MIN_EXTENT_LEN) {
+                       set_extent_info(&ei, fofs + 1,
+                               fofs - dei.fofs + dei.blk, endofs - fofs);
+                       en2 = __insert_extent_tree(sbi, et, &ei, NULL);
+               }
+       }
+
+update_extent:
+       /* 3. update extent in extent cache */
+       if (blkaddr) {
+               set_extent_info(&ei, fofs, blkaddr, 1);
+               en3 = __insert_extent_tree(sbi, et, &ei, &den);
+       }
+
+       /* 4. update in global extent list */
+       spin_lock(&sbi->extent_lock);
+       if (en && !list_empty(&en->list))
+               list_del(&en->list);
+       /*
+        * en1 and en2 split from en, they will become more and more smaller
+        * fragments after splitting several times. So if the length is smaller
+        * than F2FS_MIN_EXTENT_LEN, we will not add them into extent tree.
+        */
+       if (en1)
+               list_add_tail(&en1->list, &sbi->extent_list);
+       if (en2)
+               list_add_tail(&en2->list, &sbi->extent_list);
+       if (en3) {
+               if (list_empty(&en3->list))
+                       list_add_tail(&en3->list, &sbi->extent_list);
+               else
+                       list_move_tail(&en3->list, &sbi->extent_list);
+       }
+       if (den && !list_empty(&den->list))
+               list_del(&den->list);
+       spin_unlock(&sbi->extent_lock);
+
+       /* 5. release extent node */
+       if (en)
+               kmem_cache_free(extent_node_slab, en);
+       if (den)
+               kmem_cache_free(extent_node_slab, den);
+
+       write_unlock(&et->lock);
+       atomic_dec(&et->refcount);
+}
+
+void f2fs_preserve_extent_tree(struct inode *inode)
+{
+       struct extent_tree *et;
+       struct extent_info *ext = &F2FS_I(inode)->ext;
+       bool sync = false;
+
+       if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE))
+               return;
+
+       et = __find_extent_tree(F2FS_I_SB(inode), inode->i_ino);
+       if (!et) {
+               if (ext->len) {
+                       ext->len = 0;
+                       update_inode_page(inode);
+               }
+               return;
+       }
+
+       read_lock(&et->lock);
+       if (et->count) {
+               struct extent_node *en;
+
+               if (et->cached_en) {
+                       en = et->cached_en;
+               } else {
+                       struct rb_node *node = rb_first(&et->root);
+
+                       if (!node)
+                               node = rb_last(&et->root);
+                       en = rb_entry(node, struct extent_node, rb_node);
+               }
+
+               if (__is_extent_same(ext, &en->ei))
+                       goto out;
+
+               *ext = en->ei;
+               sync = true;
+       } else if (ext->len) {
+               ext->len = 0;
+               sync = true;
+       }
+out:
+       read_unlock(&et->lock);
+       atomic_dec(&et->refcount);
+
+       if (sync)
+               update_inode_page(inode);
+}
+
+void f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
+{
+       struct extent_tree *treevec[EXT_TREE_VEC_SIZE];
+       struct extent_node *en, *tmp;
+       unsigned long ino = F2FS_ROOT_INO(sbi);
+       struct radix_tree_iter iter;
+       void **slot;
+       unsigned int found;
+       unsigned int node_cnt = 0, tree_cnt = 0;
+
+       if (!test_opt(sbi, EXTENT_CACHE))
+               return;
+
+       if (available_free_memory(sbi, EXTENT_CACHE))
+               return;
+
+       spin_lock(&sbi->extent_lock);
+       list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) {
+               if (!nr_shrink--)
+                       break;
+               list_del_init(&en->list);
+       }
+       spin_unlock(&sbi->extent_lock);
+
+       down_read(&sbi->extent_tree_lock);
+       while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root,
+                               (void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
+               unsigned i;
+
+               ino = treevec[found - 1]->ino + 1;
+               for (i = 0; i < found; i++) {
+                       struct extent_tree *et = treevec[i];
+
+                       atomic_inc(&et->refcount);
+                       write_lock(&et->lock);
+                       node_cnt += __free_extent_tree(sbi, et, false);
+                       write_unlock(&et->lock);
+                       atomic_dec(&et->refcount);
+               }
+       }
+       up_read(&sbi->extent_tree_lock);
+
+       down_write(&sbi->extent_tree_lock);
+       radix_tree_for_each_slot(slot, &sbi->extent_tree_root, &iter,
+                                                       F2FS_ROOT_INO(sbi)) {
+               struct extent_tree *et = (struct extent_tree *)*slot;
+
+               if (!atomic_read(&et->refcount) && !et->count) {
+                       radix_tree_delete(&sbi->extent_tree_root, et->ino);
+                       kmem_cache_free(extent_tree_slab, et);
+                       sbi->total_ext_tree--;
+                       tree_cnt++;
+               }
+       }
+       up_write(&sbi->extent_tree_lock);
+
+       trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
+}
+
+void f2fs_destroy_extent_tree(struct inode *inode)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct extent_tree *et;
+       unsigned int node_cnt = 0;
+
+       if (!test_opt(sbi, EXTENT_CACHE))
+               return;
+
+       et = __find_extent_tree(sbi, inode->i_ino);
+       if (!et)
+               goto out;
+
+       /* free all extent info belong to this extent tree */
+       write_lock(&et->lock);
+       node_cnt = __free_extent_tree(sbi, et, true);
+       write_unlock(&et->lock);
+
+       atomic_dec(&et->refcount);
+
+       /* try to find and delete extent tree entry in radix tree */
+       down_write(&sbi->extent_tree_lock);
+       et = radix_tree_lookup(&sbi->extent_tree_root, inode->i_ino);
+       if (!et) {
+               up_write(&sbi->extent_tree_lock);
+               goto out;
+       }
+       f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count);
+       radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
+       kmem_cache_free(extent_tree_slab, et);
+       sbi->total_ext_tree--;
+       up_write(&sbi->extent_tree_lock);
+out:
+       trace_f2fs_destroy_extent_tree(inode, node_cnt);
        return;
 }
 
+void f2fs_init_extent_cache(struct inode *inode, struct f2fs_extent *i_ext)
+{
+       if (test_opt(F2FS_I_SB(inode), EXTENT_CACHE))
+               f2fs_init_extent_tree(inode, i_ext);
+
+       write_lock(&F2FS_I(inode)->ext_lock);
+       get_extent_info(&F2FS_I(inode)->ext, *i_ext);
+       write_unlock(&F2FS_I(inode)->ext_lock);
+}
+
+static bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
+                                                       struct extent_info *ei)
+{
+       if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
+               return false;
+
+       if (test_opt(F2FS_I_SB(inode), EXTENT_CACHE))
+               return f2fs_lookup_extent_tree(inode, pgofs, ei);
+
+       return lookup_extent_info(inode, pgofs, ei);
+}
+
+void f2fs_update_extent_cache(struct dnode_of_data *dn)
+{
+       struct f2fs_inode_info *fi = F2FS_I(dn->inode);
+       pgoff_t fofs;
+
+       f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
+
+       if (is_inode_flag_set(fi, FI_NO_EXTENT))
+               return;
+
+       fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
+                                                       dn->ofs_in_node;
+
+       if (test_opt(F2FS_I_SB(dn->inode), EXTENT_CACHE))
+               return f2fs_update_extent_tree(dn->inode, fofs,
+                                                       dn->data_blkaddr);
+
+       if (update_extent_info(dn->inode, fofs, dn->data_blkaddr))
+               sync_inode_page(dn);
+}
+
 struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
 {
        struct address_space *mapping = inode->i_mapping;
        struct dnode_of_data dn;
        struct page *page;
+       struct extent_info ei;
        int err;
        struct f2fs_io_info fio = {
                .type = DATA,
                .rw = sync ? READ_SYNC : READA,
        };
 
+       /*
+        * If sync is false, it needs to check its block allocation.
+        * This is need and triggered by two flows:
+        *   gc and truncate_partial_data_page.
+        */
+       if (!sync)
+               goto search;
+
        page = find_get_page(mapping, index);
        if (page && PageUptodate(page))
                return page;
        f2fs_put_page(page, 0);
+search:
+       if (f2fs_lookup_extent_cache(inode, index, &ei)) {
+               dn.data_blkaddr = ei.blk + index - ei.fofs;
+               goto got_it;
+       }
 
        set_new_dnode(&dn, inode, NULL, NULL, 0);
        err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
@@ -401,6 +948,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
        if (unlikely(dn.data_blkaddr == NEW_ADDR))
                return ERR_PTR(-EINVAL);
 
+got_it:
        page = grab_cache_page(mapping, index);
        if (!page)
                return ERR_PTR(-ENOMEM);
@@ -435,6 +983,7 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
        struct address_space *mapping = inode->i_mapping;
        struct dnode_of_data dn;
        struct page *page;
+       struct extent_info ei;
        int err;
        struct f2fs_io_info fio = {
                .type = DATA,
@@ -445,6 +994,11 @@ repeat:
        if (!page)
                return ERR_PTR(-ENOMEM);
 
+       if (f2fs_lookup_extent_cache(inode, index, &ei)) {
+               dn.data_blkaddr = ei.blk + index - ei.fofs;
+               goto got_it;
+       }
+
        set_new_dnode(&dn, inode, NULL, NULL, 0);
        err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
        if (err) {
@@ -458,6 +1012,7 @@ repeat:
                return ERR_PTR(-ENOENT);
        }
 
+got_it:
        if (PageUptodate(page))
                return page;
 
@@ -569,19 +1124,26 @@ static int __allocate_data_block(struct dnode_of_data *dn)
 
        if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC)))
                return -EPERM;
+
+       dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node);
+       if (dn->data_blkaddr == NEW_ADDR)
+               goto alloc;
+
        if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1)))
                return -ENOSPC;
 
+alloc:
        get_node_info(sbi, dn->nid, &ni);
        set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
 
        if (dn->ofs_in_node == 0 && dn->inode_page == dn->node_page)
                seg = CURSEG_DIRECT_IO;
 
-       allocate_data_block(sbi, NULL, NULL_ADDR, &dn->data_blkaddr, &sum, seg);
+       allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr,
+                                                               &sum, seg);
 
        /* direct IO doesn't use extent cache to maximize the performance */
-       __set_data_blkaddr(dn);
+       set_data_blkaddr(dn);
 
        /* update i_size */
        fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
@@ -615,7 +1177,10 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset,
                end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
 
                while (dn.ofs_in_node < end_offset && len) {
-                       if (dn.data_blkaddr == NULL_ADDR) {
+                       block_t blkaddr;
+
+                       blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
+                       if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
                                if (__allocate_data_block(&dn))
                                        goto sync_out;
                                allocated = true;
@@ -659,13 +1224,16 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
        int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA;
        pgoff_t pgofs, end_offset;
        int err = 0, ofs = 1;
+       struct extent_info ei;
        bool allocated = false;
 
        /* Get the page offset from the block offset(iblock) */
        pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits));
 
-       if (check_extent_cache(inode, pgofs, bh_result))
+       if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) {
+               f2fs_map_bh(inode->i_sb, pgofs, &ei, bh_result);
                goto out;
+       }
 
        if (create)
                f2fs_lock_op(F2FS_I_SB(inode));
@@ -682,7 +1250,7 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
                goto put_out;
 
        if (dn.data_blkaddr != NULL_ADDR) {
-               set_buffer_new(bh_result);
+               clear_buffer_new(bh_result);
                map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
        } else if (create) {
                err = __allocate_data_block(&dn);
@@ -727,6 +1295,7 @@ get_next:
                        if (err)
                                goto sync_out;
                        allocated = true;
+                       set_buffer_new(bh_result);
                        blkaddr = dn.data_blkaddr;
                }
                /* Give more consecutive addresses for the readahead */
@@ -813,8 +1382,10 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio)
        fio->blk_addr = dn.data_blkaddr;
 
        /* This page is already truncated */
-       if (fio->blk_addr == NULL_ADDR)
+       if (fio->blk_addr == NULL_ADDR) {
+               ClearPageUptodate(page);
                goto out_writepage;
+       }
 
        set_page_writeback(page);
 
@@ -827,10 +1398,15 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio)
                        need_inplace_update(inode))) {
                rewrite_data_page(page, fio);
                set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE);
+               trace_f2fs_do_write_data_page(page, IPU);
        } else {
                write_data_page(page, &dn, fio);
-               update_extent_cache(&dn);
+               set_data_blkaddr(&dn);
+               f2fs_update_extent_cache(&dn);
+               trace_f2fs_do_write_data_page(page, OPU);
                set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
+               if (page->index == 0)
+                       set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
        }
 out_writepage:
        f2fs_put_dnode(&dn);
@@ -909,6 +1485,8 @@ done:
        clear_cold_data(page);
 out:
        inode_dec_dirty_pages(inode);
+       if (err)
+               ClearPageUptodate(page);
        unlock_page(page);
        if (need_balance_fs)
                f2fs_balance_fs(sbi);
@@ -935,7 +1513,6 @@ static int f2fs_write_data_pages(struct address_space *mapping,
 {
        struct inode *inode = mapping->host;
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
-       bool locked = false;
        int ret;
        long diff;
 
@@ -950,15 +1527,13 @@ static int f2fs_write_data_pages(struct address_space *mapping,
                        available_free_memory(sbi, DIRTY_DENTS))
                goto skip_write;
 
+       /* during POR, we don't need to trigger writepage at all. */
+       if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
+               goto skip_write;
+
        diff = nr_pages_to_write(sbi, DATA, wbc);
 
-       if (!S_ISDIR(inode->i_mode)) {
-               mutex_lock(&sbi->writepages);
-               locked = true;
-       }
        ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
-       if (locked)
-               mutex_unlock(&sbi->writepages);
 
        f2fs_submit_merged_bio(sbi, DATA, WRITE);
 
@@ -1236,6 +1811,37 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
        return generic_block_bmap(mapping, block, get_data_block);
 }
 
+void init_extent_cache_info(struct f2fs_sb_info *sbi)
+{
+       INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
+       init_rwsem(&sbi->extent_tree_lock);
+       INIT_LIST_HEAD(&sbi->extent_list);
+       spin_lock_init(&sbi->extent_lock);
+       sbi->total_ext_tree = 0;
+       atomic_set(&sbi->total_ext_node, 0);
+}
+
+int __init create_extent_cache(void)
+{
+       extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree",
+                       sizeof(struct extent_tree));
+       if (!extent_tree_slab)
+               return -ENOMEM;
+       extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node",
+                       sizeof(struct extent_node));
+       if (!extent_node_slab) {
+               kmem_cache_destroy(extent_tree_slab);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void destroy_extent_cache(void)
+{
+       kmem_cache_destroy(extent_node_slab);
+       kmem_cache_destroy(extent_tree_slab);
+}
+
 const struct address_space_operations f2fs_dblock_aops = {
        .readpage       = f2fs_read_data_page,
        .readpages      = f2fs_read_data_pages,
index e671373cc8ab89610a9d000e5e689fb698b95f0e..f5388f37217e089cf89edf2513760cfdc034b3cf 100644 (file)
@@ -35,6 +35,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
        /* validation check of the segment numbers */
        si->hit_ext = sbi->read_hit_ext;
        si->total_ext = sbi->total_hit_ext;
+       si->ext_tree = sbi->total_ext_tree;
+       si->ext_node = atomic_read(&sbi->total_ext_node);
        si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
        si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
        si->ndirty_dirs = sbi->n_dirty_dirs;
@@ -185,6 +187,9 @@ get_cache:
        si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry);
        for (i = 0; i <= UPDATE_INO; i++)
                si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
+       si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree);
+       si->cache_mem += atomic_read(&sbi->total_ext_node) *
+                                               sizeof(struct extent_node);
 
        si->page_mem = 0;
        npages = NODE_MAPPING(sbi)->nrpages;
@@ -260,13 +265,20 @@ static int stat_show(struct seq_file *s, void *v)
                seq_printf(s, "CP calls: %d\n", si->cp_count);
                seq_printf(s, "GC calls: %d (BG: %d)\n",
                           si->call_count, si->bg_gc);
-               seq_printf(s, "  - data segments : %d\n", si->data_segs);
-               seq_printf(s, "  - node segments : %d\n", si->node_segs);
-               seq_printf(s, "Try to move %d blocks\n", si->tot_blks);
-               seq_printf(s, "  - data blocks : %d\n", si->data_blks);
-               seq_printf(s, "  - node blocks : %d\n", si->node_blks);
+               seq_printf(s, "  - data segments : %d (%d)\n",
+                               si->data_segs, si->bg_data_segs);
+               seq_printf(s, "  - node segments : %d (%d)\n",
+                               si->node_segs, si->bg_node_segs);
+               seq_printf(s, "Try to move %d blocks (BG: %d)\n", si->tot_blks,
+                               si->bg_data_blks + si->bg_node_blks);
+               seq_printf(s, "  - data blocks : %d (%d)\n", si->data_blks,
+                               si->bg_data_blks);
+               seq_printf(s, "  - node blocks : %d (%d)\n", si->node_blks,
+                               si->bg_node_blks);
                seq_printf(s, "\nExtent Hit Ratio: %d / %d\n",
                           si->hit_ext, si->total_ext);
+               seq_printf(s, "\nExtent Tree Count: %d\n", si->ext_tree);
+               seq_printf(s, "\nExtent Node Count: %d\n", si->ext_node);
                seq_puts(s, "\nBalancing F2FS Async:\n");
                seq_printf(s, "  - inmem: %4d, wb: %4d\n",
                           si->inmem_pages, si->wb_pages);
index b74097a7f6d904c53030ba4ba3714c70a0d0bd43..3a3302ab78713a6535296252f4ac35f9966beeee 100644 (file)
@@ -59,9 +59,8 @@ static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
        [S_IFLNK >> S_SHIFT]    = F2FS_FT_SYMLINK,
 };
 
-void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
+void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
 {
-       umode_t mode = inode->i_mode;
        de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
 }
 
@@ -127,22 +126,19 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
                *max_slots = 0;
        while (bit_pos < d->max) {
                if (!test_bit_le(bit_pos, d->bitmap)) {
-                       if (bit_pos == 0)
-                               max_len = 1;
-                       else if (!test_bit_le(bit_pos - 1, d->bitmap))
-                               max_len++;
                        bit_pos++;
+                       max_len++;
                        continue;
                }
+
                de = &d->dentry[bit_pos];
                if (early_match_name(name->len, namehash, de) &&
                        !memcmp(d->filename[bit_pos], name->name, name->len))
                        goto found;
 
-               if (max_slots && *max_slots >= 0 && max_len > *max_slots) {
+               if (max_slots && max_len > *max_slots)
                        *max_slots = max_len;
-                       max_len = 0;
-               }
+               max_len = 0;
 
                /* remain bug on condition */
                if (unlikely(!de->name_len))
@@ -219,14 +215,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
        unsigned int max_depth;
        unsigned int level;
 
+       *res_page = NULL;
+
        if (f2fs_has_inline_dentry(dir))
                return find_in_inline_dir(dir, child, res_page);
 
        if (npages == 0)
                return NULL;
 
-       *res_page = NULL;
-
        name_hash = f2fs_dentry_hash(child);
        max_depth = F2FS_I(dir)->i_current_depth;
 
@@ -285,7 +281,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
        lock_page(page);
        f2fs_wait_on_page_writeback(page, type);
        de->ino = cpu_to_le32(inode->i_ino);
-       set_de_type(de, inode);
+       set_de_type(de, inode->i_mode);
        f2fs_dentry_kunmap(dir, page);
        set_page_dirty(page);
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
@@ -331,14 +327,14 @@ void do_make_empty_dir(struct inode *inode, struct inode *parent,
        de->hash_code = 0;
        de->ino = cpu_to_le32(inode->i_ino);
        memcpy(d->filename[0], ".", 1);
-       set_de_type(de, inode);
+       set_de_type(de, inode->i_mode);
 
        de = &d->dentry[1];
        de->hash_code = 0;
        de->name_len = cpu_to_le16(2);
        de->ino = cpu_to_le32(parent->i_ino);
        memcpy(d->filename[1], "..", 2);
-       set_de_type(de, inode);
+       set_de_type(de, parent->i_mode);
 
        test_and_set_bit_le(0, (void *)d->bitmap);
        test_and_set_bit_le(1, (void *)d->bitmap);
@@ -435,7 +431,7 @@ error:
 void update_parent_metadata(struct inode *dir, struct inode *inode,
                                                unsigned int current_depth)
 {
-       if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
+       if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
                if (S_ISDIR(inode->i_mode)) {
                        inc_nlink(dir);
                        set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
@@ -450,7 +446,7 @@ void update_parent_metadata(struct inode *dir, struct inode *inode,
                set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
        }
 
-       if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
+       if (inode && is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
                clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
 }
 
@@ -474,30 +470,47 @@ next:
        goto next;
 }
 
+void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
+                               const struct qstr *name, f2fs_hash_t name_hash,
+                               unsigned int bit_pos)
+{
+       struct f2fs_dir_entry *de;
+       int slots = GET_DENTRY_SLOTS(name->len);
+       int i;
+
+       de = &d->dentry[bit_pos];
+       de->hash_code = name_hash;
+       de->name_len = cpu_to_le16(name->len);
+       memcpy(d->filename[bit_pos], name->name, name->len);
+       de->ino = cpu_to_le32(ino);
+       set_de_type(de, mode);
+       for (i = 0; i < slots; i++)
+               test_and_set_bit_le(bit_pos + i, (void *)d->bitmap);
+}
+
 /*
  * Caller should grab and release a rwsem by calling f2fs_lock_op() and
  * f2fs_unlock_op().
  */
 int __f2fs_add_link(struct inode *dir, const struct qstr *name,
-                                               struct inode *inode)
+                               struct inode *inode, nid_t ino, umode_t mode)
 {
        unsigned int bit_pos;
        unsigned int level;
        unsigned int current_depth;
        unsigned long bidx, block;
        f2fs_hash_t dentry_hash;
-       struct f2fs_dir_entry *de;
        unsigned int nbucket, nblock;
        size_t namelen = name->len;
        struct page *dentry_page = NULL;
        struct f2fs_dentry_block *dentry_blk = NULL;
+       struct f2fs_dentry_ptr d;
        int slots = GET_DENTRY_SLOTS(namelen);
-       struct page *page;
+       struct page *page = NULL;
        int err = 0;
-       int i;
 
        if (f2fs_has_inline_dentry(dir)) {
-               err = f2fs_add_inline_entry(dir, name, inode);
+               err = f2fs_add_inline_entry(dir, name, inode, ino, mode);
                if (!err || err != -EAGAIN)
                        return err;
                else
@@ -547,30 +560,31 @@ start:
 add_dentry:
        f2fs_wait_on_page_writeback(dentry_page, DATA);
 
-       down_write(&F2FS_I(inode)->i_sem);
-       page = init_inode_metadata(inode, dir, name, NULL);
-       if (IS_ERR(page)) {
-               err = PTR_ERR(page);
-               goto fail;
+       if (inode) {
+               down_write(&F2FS_I(inode)->i_sem);
+               page = init_inode_metadata(inode, dir, name, NULL);
+               if (IS_ERR(page)) {
+                       err = PTR_ERR(page);
+                       goto fail;
+               }
        }
-       de = &dentry_blk->dentry[bit_pos];
-       de->hash_code = dentry_hash;
-       de->name_len = cpu_to_le16(namelen);
-       memcpy(dentry_blk->filename[bit_pos], name->name, name->len);
-       de->ino = cpu_to_le32(inode->i_ino);
-       set_de_type(de, inode);
-       for (i = 0; i < slots; i++)
-               test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
+
+       make_dentry_ptr(&d, (void *)dentry_blk, 1);
+       f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos);
+
        set_page_dirty(dentry_page);
 
-       /* we don't need to mark_inode_dirty now */
-       F2FS_I(inode)->i_pino = dir->i_ino;
-       update_inode(inode, page);
-       f2fs_put_page(page, 1);
+       if (inode) {
+               /* we don't need to mark_inode_dirty now */
+               F2FS_I(inode)->i_pino = dir->i_ino;
+               update_inode(inode, page);
+               f2fs_put_page(page, 1);
+       }
 
        update_parent_metadata(dir, inode, current_depth);
 fail:
-       up_write(&F2FS_I(inode)->i_sem);
+       if (inode)
+               up_write(&F2FS_I(inode)->i_sem);
 
        if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
                update_inode_page(dir);
@@ -669,6 +683,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
        if (bit_pos == NR_DENTRY_IN_BLOCK) {
                truncate_hole(dir, page->index, page->index + 1);
                clear_page_dirty_for_io(page);
+               ClearPagePrivate(page);
                ClearPageUptodate(page);
                inode_dec_dirty_pages(dir);
        }
index 7fa3313ab0e27381b95d48b22da8315e605ef567..c06a25e5cec335d8195cf027623fc8021bca1f38 100644 (file)
@@ -50,6 +50,7 @@
 #define F2FS_MOUNT_FLUSH_MERGE         0x00000400
 #define F2FS_MOUNT_NOBARRIER           0x00000800
 #define F2FS_MOUNT_FASTBOOT            0x00001000
+#define F2FS_MOUNT_EXTENT_CACHE                0x00002000
 
 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
 #define set_opt(sbi, option)   (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -102,6 +103,7 @@ enum {
        CP_UMOUNT,
        CP_FASTBOOT,
        CP_SYNC,
+       CP_RECOVERY,
        CP_DISCARD,
 };
 
@@ -216,6 +218,15 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
 #define F2FS_IOC_RELEASE_VOLATILE_WRITE        _IO(F2FS_IOCTL_MAGIC, 4)
 #define F2FS_IOC_ABORT_VOLATILE_WRITE  _IO(F2FS_IOCTL_MAGIC, 5)
 
+/*
+ * should be same as XFS_IOC_GOINGDOWN.
+ * Flags for going down operation used by FS_IOC_GOINGDOWN
+ */
+#define F2FS_IOC_SHUTDOWN      _IOR('X', 125, __u32)   /* Shutdown */
+#define F2FS_GOING_DOWN_FULLSYNC       0x0     /* going down with full sync */
+#define F2FS_GOING_DOWN_METASYNC       0x1     /* going down with metadata */
+#define F2FS_GOING_DOWN_NOSYNC         0x2     /* going down */
+
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
  * ioctl commands in 32 bit emulation
@@ -273,14 +284,34 @@ enum {
 
 #define MAX_DIR_RA_PAGES       4       /* maximum ra pages of dir */
 
+/* vector size for gang look-up from extent cache that consists of radix tree */
+#define EXT_TREE_VEC_SIZE      64
+
 /* for in-memory extent cache entry */
-#define F2FS_MIN_EXTENT_LEN    16      /* minimum extent length */
+#define F2FS_MIN_EXTENT_LEN    64      /* minimum extent length */
+
+/* number of extent info in extent cache we try to shrink */
+#define EXTENT_CACHE_SHRINK_NUMBER     128
 
 struct extent_info {
-       rwlock_t ext_lock;      /* rwlock for consistency */
-       unsigned int fofs;      /* start offset in a file */
-       u32 blk_addr;           /* start block address of the extent */
-       unsigned int len;       /* length of the extent */
+       unsigned int fofs;              /* start offset in a file */
+       u32 blk;                        /* start block address of the extent */
+       unsigned int len;               /* length of the extent */
+};
+
+struct extent_node {
+       struct rb_node rb_node;         /* rb node located in rb-tree */
+       struct list_head list;          /* node in global extent list of sbi */
+       struct extent_info ei;          /* extent info */
+};
+
+struct extent_tree {
+       nid_t ino;                      /* inode number */
+       struct rb_root root;            /* root of extent info rb-tree */
+       struct extent_node *cached_en;  /* recently accessed extent node */
+       rwlock_t lock;                  /* protect extent info rb-tree */
+       atomic_t refcount;              /* reference count of rb-tree */
+       unsigned int count;             /* # of extent node in rb-tree*/
 };
 
 /*
@@ -309,6 +340,7 @@ struct f2fs_inode_info {
        nid_t i_xattr_nid;              /* node id that contains xattrs */
        unsigned long long xattr_ver;   /* cp version of xattr modification */
        struct extent_info ext;         /* in-memory extent cache entry */
+       rwlock_t ext_lock;              /* rwlock for single extent cache */
        struct inode_entry *dirty_dir;  /* the pointer of dirty dir */
 
        struct radix_tree_root inmem_root;      /* radix tree for inmem pages */
@@ -319,21 +351,51 @@ struct f2fs_inode_info {
 static inline void get_extent_info(struct extent_info *ext,
                                        struct f2fs_extent i_ext)
 {
-       write_lock(&ext->ext_lock);
        ext->fofs = le32_to_cpu(i_ext.fofs);
-       ext->blk_addr = le32_to_cpu(i_ext.blk_addr);
+       ext->blk = le32_to_cpu(i_ext.blk);
        ext->len = le32_to_cpu(i_ext.len);
-       write_unlock(&ext->ext_lock);
 }
 
 static inline void set_raw_extent(struct extent_info *ext,
                                        struct f2fs_extent *i_ext)
 {
-       read_lock(&ext->ext_lock);
        i_ext->fofs = cpu_to_le32(ext->fofs);
-       i_ext->blk_addr = cpu_to_le32(ext->blk_addr);
+       i_ext->blk = cpu_to_le32(ext->blk);
        i_ext->len = cpu_to_le32(ext->len);
-       read_unlock(&ext->ext_lock);
+}
+
+static inline void set_extent_info(struct extent_info *ei, unsigned int fofs,
+                                               u32 blk, unsigned int len)
+{
+       ei->fofs = fofs;
+       ei->blk = blk;
+       ei->len = len;
+}
+
+static inline bool __is_extent_same(struct extent_info *ei1,
+                                               struct extent_info *ei2)
+{
+       return (ei1->fofs == ei2->fofs && ei1->blk == ei2->blk &&
+                                               ei1->len == ei2->len);
+}
+
+static inline bool __is_extent_mergeable(struct extent_info *back,
+                                               struct extent_info *front)
+{
+       return (back->fofs + back->len == front->fofs &&
+                       back->blk + back->len == front->blk);
+}
+
+static inline bool __is_back_mergeable(struct extent_info *cur,
+                                               struct extent_info *back)
+{
+       return __is_extent_mergeable(back, cur);
+}
+
+static inline bool __is_front_mergeable(struct extent_info *cur,
+                                               struct extent_info *front)
+{
+       return __is_extent_mergeable(cur, front);
 }
 
 struct f2fs_nm_info {
@@ -502,6 +564,10 @@ enum page_type {
        META,
        NR_PAGE_TYPE,
        META_FLUSH,
+       INMEM,          /* the below types are used by tracepoints only. */
+       INMEM_DROP,
+       IPU,
+       OPU,
 };
 
 struct f2fs_io_info {
@@ -559,7 +625,6 @@ struct f2fs_sb_info {
        struct mutex cp_mutex;                  /* checkpoint procedure lock */
        struct rw_semaphore cp_rwsem;           /* blocking FS operations */
        struct rw_semaphore node_write;         /* locking node writes */
-       struct mutex writepages;                /* mutex for writepages() */
        wait_queue_head_t cp_wait;
 
        struct inode_management im[MAX_INO_ENTRY];      /* manage inode cache */
@@ -571,6 +636,14 @@ struct f2fs_sb_info {
        struct list_head dir_inode_list;        /* dir inode list */
        spinlock_t dir_inode_lock;              /* for dir inode list lock */
 
+       /* for extent tree cache */
+       struct radix_tree_root extent_tree_root;/* cache extent cache entries */
+       struct rw_semaphore extent_tree_lock;   /* locking extent radix tree */
+       struct list_head extent_list;           /* lru list for shrinker */
+       spinlock_t extent_lock;                 /* locking extent lru list */
+       int total_ext_tree;                     /* extent tree count */
+       atomic_t total_ext_node;                /* extent info count */
+
        /* basic filesystem units */
        unsigned int log_sectors_per_block;     /* log2 sectors per block */
        unsigned int log_blocksize;             /* log2 block size */
@@ -920,12 +993,17 @@ static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag)
        return 0;
 }
 
+static inline block_t __cp_payload(struct f2fs_sb_info *sbi)
+{
+       return le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload);
+}
+
 static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
 {
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
        int offset;
 
-       if (le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload) > 0) {
+       if (__cp_payload(sbi) > 0) {
                if (flag == NAT_BITMAP)
                        return &ckpt->sit_nat_version_bitmap;
                else
@@ -1166,8 +1244,10 @@ enum {
        FI_NEED_IPU,            /* used for ipu per file */
        FI_ATOMIC_FILE,         /* indicate atomic file */
        FI_VOLATILE_FILE,       /* indicate volatile file */
+       FI_FIRST_BLOCK_WRITTEN, /* indicate #0 data block was written */
        FI_DROP_CACHE,          /* drop dirty page cache */
        FI_DATA_EXIST,          /* indicate data exists */
+       FI_INLINE_DOTS,         /* indicate inline dot dentries */
 };
 
 static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
@@ -1204,6 +1284,8 @@ static inline void get_inline_info(struct f2fs_inode_info *fi,
                set_inode_flag(fi, FI_INLINE_DENTRY);
        if (ri->i_inline & F2FS_DATA_EXIST)
                set_inode_flag(fi, FI_DATA_EXIST);
+       if (ri->i_inline & F2FS_INLINE_DOTS)
+               set_inode_flag(fi, FI_INLINE_DOTS);
 }
 
 static inline void set_raw_inline(struct f2fs_inode_info *fi,
@@ -1219,6 +1301,8 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi,
                ri->i_inline |= F2FS_INLINE_DENTRY;
        if (is_inode_flag_set(fi, FI_DATA_EXIST))
                ri->i_inline |= F2FS_DATA_EXIST;
+       if (is_inode_flag_set(fi, FI_INLINE_DOTS))
+               ri->i_inline |= F2FS_INLINE_DOTS;
 }
 
 static inline int f2fs_has_inline_xattr(struct inode *inode)
@@ -1264,6 +1348,11 @@ static inline int f2fs_exist_data(struct inode *inode)
        return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST);
 }
 
+static inline int f2fs_has_inline_dots(struct inode *inode)
+{
+       return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DOTS);
+}
+
 static inline bool f2fs_is_atomic_file(struct inode *inode)
 {
        return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE);
@@ -1274,6 +1363,11 @@ static inline bool f2fs_is_volatile_file(struct inode *inode)
        return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE);
 }
 
+static inline bool f2fs_is_first_block_written(struct inode *inode)
+{
+       return is_inode_flag_set(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
+}
+
 static inline bool f2fs_is_drop_cache(struct inode *inode)
 {
        return is_inode_flag_set(F2FS_I(inode), FI_DROP_CACHE);
@@ -1290,12 +1384,6 @@ static inline int f2fs_has_inline_dentry(struct inode *inode)
        return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DENTRY);
 }
 
-static inline void *inline_dentry_addr(struct page *page)
-{
-       struct f2fs_inode *ri = F2FS_INODE(page);
-       return (void *)&(ri->i_addr[1]);
-}
-
 static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page)
 {
        if (!f2fs_has_inline_dentry(dir))
@@ -1363,7 +1451,7 @@ struct dentry *f2fs_get_parent(struct dentry *child);
  * dir.c
  */
 extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
-void set_de_type(struct f2fs_dir_entry *, struct inode *);
+void set_de_type(struct f2fs_dir_entry *, umode_t);
 struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
                        struct f2fs_dentry_ptr *);
 bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
@@ -1382,7 +1470,10 @@ ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
 void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
                                struct page *, struct inode *);
 int update_dent_inode(struct inode *, const struct qstr *);
-int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
+void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *,
+                       const struct qstr *, f2fs_hash_t , unsigned int);
+int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t,
+                       umode_t);
 void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *,
                                                        struct inode *);
 int f2fs_do_tmpfile(struct inode *, struct inode *);
@@ -1392,7 +1483,7 @@ bool f2fs_empty_dir(struct inode *);
 static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
 {
        return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name,
-                               inode);
+                               inode, inode->i_ino, inode->i_mode);
 }
 
 /*
@@ -1519,14 +1610,22 @@ int f2fs_submit_page_bio(struct f2fs_sb_info *, struct page *,
                                                struct f2fs_io_info *);
 void f2fs_submit_page_mbio(struct f2fs_sb_info *, struct page *,
                                                struct f2fs_io_info *);
+void set_data_blkaddr(struct dnode_of_data *);
 int reserve_new_block(struct dnode_of_data *);
 int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
-void update_extent_cache(struct dnode_of_data *);
+void f2fs_shrink_extent_tree(struct f2fs_sb_info *, int);
+void f2fs_destroy_extent_tree(struct inode *);
+void f2fs_init_extent_cache(struct inode *, struct f2fs_extent *);
+void f2fs_update_extent_cache(struct dnode_of_data *);
+void f2fs_preserve_extent_tree(struct inode *);
 struct page *find_data_page(struct inode *, pgoff_t, bool);
 struct page *get_lock_data_page(struct inode *, pgoff_t);
 struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
 int do_write_data_page(struct page *, struct f2fs_io_info *);
 int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64);
+void init_extent_cache_info(struct f2fs_sb_info *);
+int __init create_extent_cache(void);
+void destroy_extent_cache(void);
 void f2fs_invalidate_page(struct page *, unsigned int, unsigned int);
 int f2fs_release_page(struct page *, gfp_t);
 
@@ -1554,7 +1653,7 @@ struct f2fs_stat_info {
        struct f2fs_sb_info *sbi;
        int all_area_segs, sit_area_segs, nat_area_segs, ssa_area_segs;
        int main_area_segs, main_area_sections, main_area_zones;
-       int hit_ext, total_ext;
+       int hit_ext, total_ext, ext_tree, ext_node;
        int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta;
        int nats, dirty_nats, sits, dirty_sits, fnids;
        int total_count, utilization;
@@ -1566,7 +1665,9 @@ struct f2fs_stat_info {
        int dirty_count, node_pages, meta_pages;
        int prefree_count, call_count, cp_count;
        int tot_segs, node_segs, data_segs, free_segs, free_secs;
+       int bg_node_segs, bg_data_segs;
        int tot_blks, data_blks, node_blks;
+       int bg_data_blks, bg_node_blks;
        int curseg[NR_CURSEG_TYPE];
        int cursec[NR_CURSEG_TYPE];
        int curzone[NR_CURSEG_TYPE];
@@ -1615,31 +1716,36 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
                ((sbi)->block_count[(curseg)->alloc_type]++)
 #define stat_inc_inplace_blocks(sbi)                                   \
                (atomic_inc(&(sbi)->inplace_count))
-#define stat_inc_seg_count(sbi, type)                                  \
+#define stat_inc_seg_count(sbi, type, gc_type)                         \
        do {                                                            \
                struct f2fs_stat_info *si = F2FS_STAT(sbi);             \
                (si)->tot_segs++;                                       \
-               if (type == SUM_TYPE_DATA)                              \
+               if (type == SUM_TYPE_DATA) {                            \
                        si->data_segs++;                                \
-               else                                                    \
+                       si->bg_data_segs += (gc_type == BG_GC) ? 1 : 0; \
+               } else {                                                \
                        si->node_segs++;                                \
+                       si->bg_node_segs += (gc_type == BG_GC) ? 1 : 0; \
+               }                                                       \
        } while (0)
 
 #define stat_inc_tot_blk_count(si, blks)                               \
        (si->tot_blks += (blks))
 
-#define stat_inc_data_blk_count(sbi, blks)                             \
+#define stat_inc_data_blk_count(sbi, blks, gc_type)                    \
        do {                                                            \
                struct f2fs_stat_info *si = F2FS_STAT(sbi);             \
                stat_inc_tot_blk_count(si, blks);                       \
                si->data_blks += (blks);                                \
+               si->bg_data_blks += (gc_type == BG_GC) ? (blks) : 0;    \
        } while (0)
 
-#define stat_inc_node_blk_count(sbi, blks)                             \
+#define stat_inc_node_blk_count(sbi, blks, gc_type)                    \
        do {                                                            \
                struct f2fs_stat_info *si = F2FS_STAT(sbi);             \
                stat_inc_tot_blk_count(si, blks);                       \
                si->node_blks += (blks);                                \
+               si->bg_node_blks += (gc_type == BG_GC) ? (blks) : 0;    \
        } while (0)
 
 int f2fs_build_stats(struct f2fs_sb_info *);
@@ -1661,10 +1767,10 @@ void f2fs_destroy_root_stats(void);
 #define stat_inc_seg_type(sbi, curseg)
 #define stat_inc_block_count(sbi, curseg)
 #define stat_inc_inplace_blocks(sbi)
-#define stat_inc_seg_count(si, type)
+#define stat_inc_seg_count(sbi, type, gc_type)
 #define stat_inc_tot_blk_count(si, blks)
-#define stat_inc_data_blk_count(si, blks)
-#define stat_inc_node_blk_count(sbi, blks)
+#define stat_inc_data_blk_count(sbi, blks, gc_type)
+#define stat_inc_node_blk_count(sbi, blks, gc_type)
 
 static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; }
 static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { }
@@ -1688,6 +1794,7 @@ extern struct kmem_cache *inode_entry_slab;
  */
 bool f2fs_may_inline(struct inode *);
 void read_inline_data(struct page *, struct page *);
+bool truncate_inline_inode(struct page *, u64);
 int f2fs_read_inline_data(struct inode *, struct page *);
 int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
 int f2fs_convert_inline_inode(struct inode *);
@@ -1697,7 +1804,8 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
                                                        struct page **);
 struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
 int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
-int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *);
+int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
+                                               nid_t, umode_t);
 void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
                                                struct inode *, struct inode *);
 bool f2fs_empty_inline_dir(struct inode *);
index df6a0596eccf210dfefbaaca2cf2f60ca57a7f6b..a6f3f618658851943af35ee46e03dd7570d9b47a 100644 (file)
@@ -241,6 +241,8 @@ go_write:
                 * will be used only for fsynced inodes after checkpoint.
                 */
                try_to_fix_pino(inode);
+               clear_inode_flag(fi, FI_APPEND_WRITE);
+               clear_inode_flag(fi, FI_UPDATE_WRITE);
                goto out;
        }
 sync_nodes:
@@ -433,8 +435,12 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
                        continue;
 
                dn->data_blkaddr = NULL_ADDR;
-               update_extent_cache(dn);
+               set_data_blkaddr(dn);
+               f2fs_update_extent_cache(dn);
                invalidate_blocks(sbi, blkaddr);
+               if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
+                       clear_inode_flag(F2FS_I(dn->inode),
+                                               FI_FIRST_BLOCK_WRITTEN);
                nr_free++;
        }
        if (nr_free) {
@@ -454,15 +460,16 @@ void truncate_data_blocks(struct dnode_of_data *dn)
        truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
 }
 
-static int truncate_partial_data_page(struct inode *inode, u64 from)
+static int truncate_partial_data_page(struct inode *inode, u64 from,
+                                                               bool force)
 {
        unsigned offset = from & (PAGE_CACHE_SIZE - 1);
        struct page *page;
 
-       if (!offset)
+       if (!offset && !force)
                return 0;
 
-       page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
+       page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force);
        if (IS_ERR(page))
                return 0;
 
@@ -473,7 +480,8 @@ static int truncate_partial_data_page(struct inode *inode, u64 from)
 
        f2fs_wait_on_page_writeback(page, DATA);
        zero_user(page, offset, PAGE_CACHE_SIZE - offset);
-       set_page_dirty(page);
+       if (!force)
+               set_page_dirty(page);
 out:
        f2fs_put_page(page, 1);
        return 0;
@@ -487,6 +495,7 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        pgoff_t free_from;
        int count = 0, err = 0;
        struct page *ipage;
+       bool truncate_page = false;
 
        trace_f2fs_truncate_blocks_enter(inode, from);
 
@@ -502,7 +511,10 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        }
 
        if (f2fs_has_inline_data(inode)) {
+               if (truncate_inline_inode(ipage, from))
+                       set_page_dirty(ipage);
                f2fs_put_page(ipage, 1);
+               truncate_page = true;
                goto out;
        }
 
@@ -533,7 +545,7 @@ out:
 
        /* lastly zero out the first data page */
        if (!err)
-               err = truncate_partial_data_page(inode, from);
+               err = truncate_partial_data_page(inode, from, truncate_page);
 
        trace_f2fs_truncate_blocks_exit(inode, err);
        return err;
@@ -997,6 +1009,9 @@ static int f2fs_ioc_release_volatile_write(struct file *filp)
        if (!f2fs_is_volatile_file(inode))
                return 0;
 
+       if (!f2fs_is_first_block_written(inode))
+               return truncate_partial_data_page(inode, 0, true);
+
        punch_hole(inode, 0, F2FS_BLKSIZE);
        return 0;
 }
@@ -1029,6 +1044,41 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
        return ret;
 }
 
+static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
+{
+       struct inode *inode = file_inode(filp);
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct super_block *sb = sbi->sb;
+       __u32 in;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (get_user(in, (__u32 __user *)arg))
+               return -EFAULT;
+
+       switch (in) {
+       case F2FS_GOING_DOWN_FULLSYNC:
+               sb = freeze_bdev(sb->s_bdev);
+               if (sb && !IS_ERR(sb)) {
+                       f2fs_stop_checkpoint(sbi);
+                       thaw_bdev(sb->s_bdev, sb);
+               }
+               break;
+       case F2FS_GOING_DOWN_METASYNC:
+               /* do checkpoint only */
+               f2fs_sync_fs(sb, 1);
+               f2fs_stop_checkpoint(sbi);
+               break;
+       case F2FS_GOING_DOWN_NOSYNC:
+               f2fs_stop_checkpoint(sbi);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
@@ -1078,6 +1128,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return f2fs_ioc_release_volatile_write(filp);
        case F2FS_IOC_ABORT_VOLATILE_WRITE:
                return f2fs_ioc_abort_volatile_write(filp);
+       case F2FS_IOC_SHUTDOWN:
+               return f2fs_ioc_shutdown(filp, arg);
        case FITRIM:
                return f2fs_ioc_fitrim(filp, arg);
        default:
index 76adbc3641f1534ead6151b06dd8257219f7911e..ed58211fe79b45f1a980a35e54d500815b0c6704 100644 (file)
@@ -435,7 +435,7 @@ next_step:
                                set_page_dirty(node_page);
                }
                f2fs_put_page(node_page, 1);
-               stat_inc_node_blk_count(sbi, 1);
+               stat_inc_node_blk_count(sbi, 1, gc_type);
        }
 
        if (initial) {
@@ -622,7 +622,7 @@ next_step:
                        if (IS_ERR(data_page))
                                continue;
                        move_data_page(inode, data_page, gc_type);
-                       stat_inc_data_blk_count(sbi, 1);
+                       stat_inc_data_blk_count(sbi, 1, gc_type);
                }
        }
 
@@ -680,7 +680,7 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
        }
        blk_finish_plug(&plug);
 
-       stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
+       stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type);
        stat_inc_call_count(sbi->stat_info);
 
        f2fs_put_page(sum_page, 1);
index 1484c00133cd8e9fe2492c0ee955b7c587c19f97..8140e4f0e538e5ecfc0568664cd098d0ed944d57 100644 (file)
@@ -21,7 +21,7 @@ bool f2fs_may_inline(struct inode *inode)
        if (f2fs_is_atomic_file(inode))
                return false;
 
-       if (!S_ISREG(inode->i_mode))
+       if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
                return false;
 
        if (i_size_read(inode) > MAX_INLINE_DATA)
@@ -50,10 +50,19 @@ void read_inline_data(struct page *page, struct page *ipage)
        SetPageUptodate(page);
 }
 
-static void truncate_inline_data(struct page *ipage)
+bool truncate_inline_inode(struct page *ipage, u64 from)
 {
+       void *addr;
+
+       if (from >= MAX_INLINE_DATA)
+               return false;
+
+       addr = inline_data_addr(ipage);
+
        f2fs_wait_on_page_writeback(ipage, NODE);
-       memset(inline_data_addr(ipage), 0, MAX_INLINE_DATA);
+       memset(addr + from, 0, MAX_INLINE_DATA - from);
+
+       return true;
 }
 
 int f2fs_read_inline_data(struct inode *inode, struct page *page)
@@ -122,7 +131,8 @@ no_update:
        set_page_writeback(page);
        fio.blk_addr = dn->data_blkaddr;
        write_data_page(page, dn, &fio);
-       update_extent_cache(dn);
+       set_data_blkaddr(dn);
+       f2fs_update_extent_cache(dn);
        f2fs_wait_on_page_writeback(page, DATA);
        if (dirty)
                inode_dec_dirty_pages(dn->inode);
@@ -131,7 +141,7 @@ no_update:
        set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE);
 
        /* clear inline data and flag after data writeback */
-       truncate_inline_data(dn->inode_page);
+       truncate_inline_inode(dn->inode_page, 0);
 clear_out:
        stat_dec_inline_inode(dn->inode);
        f2fs_clear_inline_inode(dn->inode);
@@ -245,7 +255,7 @@ process_inline:
        if (f2fs_has_inline_data(inode)) {
                ipage = get_node_page(sbi, inode->i_ino);
                f2fs_bug_on(sbi, IS_ERR(ipage));
-               truncate_inline_data(ipage);
+               truncate_inline_inode(ipage, 0);
                f2fs_clear_inline_inode(inode);
                update_inode(inode, ipage);
                f2fs_put_page(ipage, 1);
@@ -363,7 +373,7 @@ static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
        set_page_dirty(page);
 
        /* clear inline dir and flag after data writeback */
-       truncate_inline_data(ipage);
+       truncate_inline_inode(ipage, 0);
 
        stat_dec_inline_dir(dir);
        clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY);
@@ -380,21 +390,18 @@ out:
 }
 
 int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
-                                               struct inode *inode)
+                       struct inode *inode, nid_t ino, umode_t mode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
        struct page *ipage;
        unsigned int bit_pos;
        f2fs_hash_t name_hash;
-       struct f2fs_dir_entry *de;
        size_t namelen = name->len;
        struct f2fs_inline_dentry *dentry_blk = NULL;
+       struct f2fs_dentry_ptr d;
        int slots = GET_DENTRY_SLOTS(namelen);
-       struct page *page;
+       struct page *page = NULL;
        int err = 0;
-       int i;
-
-       name_hash = f2fs_dentry_hash(name);
 
        ipage = get_node_page(sbi, dir->i_ino);
        if (IS_ERR(ipage))
@@ -410,32 +417,34 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
                goto out;
        }
 
-       down_write(&F2FS_I(inode)->i_sem);
-       page = init_inode_metadata(inode, dir, name, ipage);
-       if (IS_ERR(page)) {
-               err = PTR_ERR(page);
-               goto fail;
+       if (inode) {
+               down_write(&F2FS_I(inode)->i_sem);
+               page = init_inode_metadata(inode, dir, name, ipage);
+               if (IS_ERR(page)) {
+                       err = PTR_ERR(page);
+                       goto fail;
+               }
        }
 
        f2fs_wait_on_page_writeback(ipage, NODE);
-       de = &dentry_blk->dentry[bit_pos];
-       de->hash_code = name_hash;
-       de->name_len = cpu_to_le16(namelen);
-       memcpy(dentry_blk->filename[bit_pos], name->name, name->len);
-       de->ino = cpu_to_le32(inode->i_ino);
-       set_de_type(de, inode);
-       for (i = 0; i < slots; i++)
-               test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
+
+       name_hash = f2fs_dentry_hash(name);
+       make_dentry_ptr(&d, (void *)dentry_blk, 2);
+       f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
+
        set_page_dirty(ipage);
 
        /* we don't need to mark_inode_dirty now */
-       F2FS_I(inode)->i_pino = dir->i_ino;
-       update_inode(inode, page);
-       f2fs_put_page(page, 1);
+       if (inode) {
+               F2FS_I(inode)->i_pino = dir->i_ino;
+               update_inode(inode, page);
+               f2fs_put_page(page, 1);
+       }
 
        update_parent_metadata(dir, inode, 0);
 fail:
-       up_write(&F2FS_I(inode)->i_sem);
+       if (inode)
+               up_write(&F2FS_I(inode)->i_sem);
 
        if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
                update_inode(dir, ipage);
index 2d002e3738a77be52be0f5b810b577e62f017192..e622ec95409ee5eece7e6b763a9584ae648e1f3e 100644 (file)
@@ -51,6 +51,15 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
        }
 }
 
+static bool __written_first_block(struct f2fs_inode *ri)
+{
+       block_t addr = le32_to_cpu(ri->i_addr[0]);
+
+       if (addr != NEW_ADDR && addr != NULL_ADDR)
+               return true;
+       return false;
+}
+
 static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
 {
        if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
@@ -130,7 +139,8 @@ static int do_read_inode(struct inode *inode)
        fi->i_pino = le32_to_cpu(ri->i_pino);
        fi->i_dir_level = ri->i_dir_level;
 
-       get_extent_info(&fi->ext, ri->i_ext);
+       f2fs_init_extent_cache(inode, &ri->i_ext);
+
        get_inline_info(fi, ri);
 
        /* check data exist */
@@ -140,6 +150,9 @@ static int do_read_inode(struct inode *inode)
        /* get rdev by using inline_info */
        __get_inode_rdev(inode, ri);
 
+       if (__written_first_block(ri))
+               set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
+
        f2fs_put_page(node_page, 1);
 
        stat_inc_inline_inode(inode);
@@ -220,7 +233,11 @@ void update_inode(struct inode *inode, struct page *node_page)
        ri->i_links = cpu_to_le32(inode->i_nlink);
        ri->i_size = cpu_to_le64(i_size_read(inode));
        ri->i_blocks = cpu_to_le64(inode->i_blocks);
+
+       read_lock(&F2FS_I(inode)->ext_lock);
        set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
+       read_unlock(&F2FS_I(inode)->ext_lock);
+
        set_raw_inline(F2FS_I(inode), ri);
 
        ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
@@ -328,6 +345,12 @@ void f2fs_evict_inode(struct inode *inode)
 no_delete:
        stat_dec_inline_dir(inode);
        stat_dec_inline_inode(inode);
+
+       /* update extent info in inode */
+       if (inode->i_nlink)
+               f2fs_preserve_extent_tree(inode);
+       f2fs_destroy_extent_tree(inode);
+
        invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino);
        if (xnid)
                invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
index e79639a9787aab0f5d8d8b27fae7354a5cd087b6..407dde3d7a92273a790aefb2ace267f3be0ece74 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 #include <linux/ctype.h>
 #include <linux/dcache.h>
+#include <linux/namei.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -187,6 +188,44 @@ struct dentry *f2fs_get_parent(struct dentry *child)
        return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino));
 }
 
+static int __recover_dot_dentries(struct inode *dir, nid_t pino)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
+       struct qstr dot = QSTR_INIT(".", 1);
+       struct qstr dotdot = QSTR_INIT("..", 2);
+       struct f2fs_dir_entry *de;
+       struct page *page;
+       int err = 0;
+
+       f2fs_lock_op(sbi);
+
+       de = f2fs_find_entry(dir, &dot, &page);
+       if (de) {
+               f2fs_dentry_kunmap(dir, page);
+               f2fs_put_page(page, 0);
+       } else {
+               err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR);
+               if (err)
+                       goto out;
+       }
+
+       de = f2fs_find_entry(dir, &dotdot, &page);
+       if (de) {
+               f2fs_dentry_kunmap(dir, page);
+               f2fs_put_page(page, 0);
+       } else {
+               err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
+       }
+out:
+       if (!err) {
+               clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS);
+               mark_inode_dirty(dir);
+       }
+
+       f2fs_unlock_op(sbi);
+       return err;
+}
+
 static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
                unsigned int flags)
 {
@@ -206,6 +245,16 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
                inode = f2fs_iget(dir->i_sb, ino);
                if (IS_ERR(inode))
                        return ERR_CAST(inode);
+
+               if (f2fs_has_inline_dots(inode)) {
+                       int err;
+
+                       err = __recover_dot_dentries(inode, dir->i_ino);
+                       if (err) {
+                               iget_failed(inode);
+                               return ERR_PTR(err);
+                       }
+               }
        }
 
        return d_splice_alias(inode, dentry);
@@ -247,6 +296,23 @@ fail:
        return err;
 }
 
+static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+       struct page *page;
+
+       page = page_follow_link_light(dentry, nd);
+       if (IS_ERR(page))
+               return page;
+
+       /* this is broken symlink case */
+       if (*nd_get_link(nd) == 0) {
+               kunmap(page);
+               page_cache_release(page);
+               return ERR_PTR(-ENOENT);
+       }
+       return page;
+}
+
 static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
                                        const char *symname)
 {
@@ -276,6 +342,17 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
        d_instantiate(dentry, inode);
        unlock_new_inode(inode);
 
+       /*
+        * Let's flush symlink data in order to avoid broken symlink as much as
+        * possible. Nevertheless, fsyncing is the best way, but there is no
+        * way to get a file descriptor in order to flush that.
+        *
+        * Note that, it needs to do dir->fsync to make this recoverable.
+        * If the symlink path is stored into inline_data, there is no
+        * performance regression.
+        */
+       filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1);
+
        if (IS_DIRSYNC(dir))
                f2fs_sync_fs(sbi->sb, 1);
        return err;
@@ -693,6 +770,8 @@ static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
        f2fs_unlock_op(sbi);
 
        alloc_nid_done(sbi, inode->i_ino);
+
+       stat_inc_inline_inode(inode);
        d_tmpfile(dentry, inode);
        unlock_new_inode(inode);
        return 0;
@@ -729,7 +808,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
 
 const struct inode_operations f2fs_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = page_follow_link_light,
+       .follow_link    = f2fs_follow_link,
        .put_link       = page_put_link,
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
index 97bd9d3db88285e36a24c9d05b73c3a5c5e269f1..8ab0cf1930bd2f4a1aa9ae11827c961b204d8519 100644 (file)
@@ -41,7 +41,9 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
        /* only uses low memory */
        avail_ram = val.totalram - val.totalhigh;
 
-       /* give 25%, 25%, 50%, 50% memory for each components respectively */
+       /*
+        * give 25%, 25%, 50%, 50%, 50% memory for each components respectively
+        */
        if (type == FREE_NIDS) {
                mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >>
                                                        PAGE_CACHE_SHIFT;
@@ -62,6 +64,11 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
                        mem_size += (sbi->im[i].ino_num *
                                sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT;
                res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
+       } else if (type == EXTENT_CACHE) {
+               mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) +
+                               atomic_read(&sbi->total_ext_node) *
+                               sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT;
+               res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
        } else {
                if (sbi->sb->s_bdi->dirty_exceeded)
                        return false;
@@ -494,7 +501,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
 
        /* if inline_data is set, should not report any block indices */
        if (f2fs_has_inline_data(dn->inode) && index) {
-               err = -EINVAL;
+               err = -ENOENT;
                f2fs_put_page(npage[0], 1);
                goto release_out;
        }
@@ -995,6 +1002,7 @@ static int read_node_page(struct page *page, int rw)
        get_node_info(sbi, page->index, &ni);
 
        if (unlikely(ni.blk_addr == NULL_ADDR)) {
+               ClearPageUptodate(page);
                f2fs_put_page(page, 1);
                return -ENOENT;
        }
@@ -1306,6 +1314,7 @@ static int f2fs_write_node_page(struct page *page,
 
        /* This page is already truncated */
        if (unlikely(ni.blk_addr == NULL_ADDR)) {
+               ClearPageUptodate(page);
                dec_page_count(sbi, F2FS_DIRTY_NODES);
                unlock_page(page);
                return 0;
@@ -1821,6 +1830,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
        struct f2fs_nat_block *nat_blk;
        struct nat_entry *ne, *cur;
        struct page *page = NULL;
+       struct f2fs_nm_info *nm_i = NM_I(sbi);
 
        /*
         * there are two steps to flush nat entries:
@@ -1874,7 +1884,9 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
 
        f2fs_bug_on(sbi, set->entry_cnt);
 
+       down_write(&nm_i->nat_tree_lock);
        radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set);
+       up_write(&nm_i->nat_tree_lock);
        kmem_cache_free(nat_entry_set_slab, set);
 }
 
@@ -1902,6 +1914,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
        if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL))
                remove_nats_in_journal(sbi);
 
+       down_write(&nm_i->nat_tree_lock);
        while ((found = __gang_lookup_nat_set(nm_i,
                                        set_idx, SETVEC_SIZE, setvec))) {
                unsigned idx;
@@ -1910,6 +1923,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
                        __adjust_nat_entry_set(setvec[idx], &sets,
                                                        MAX_NAT_JENTRIES(sum));
        }
+       up_write(&nm_i->nat_tree_lock);
 
        /* flush dirty nats in nat entry set */
        list_for_each_entry_safe(set, tmp, &sets, set_list)
index f405bbf2435a50d8608ff901f9eadc76b0b5f6a9..c56026f1725c90f44393ad38ce4ea53d705d349d 100644 (file)
@@ -120,6 +120,7 @@ enum mem_type {
        NAT_ENTRIES,    /* indicates the cached nat entry */
        DIRTY_DENTS,    /* indicates dirty dentry pages */
        INO_ENTRIES,    /* indicates inode entries */
+       EXTENT_CACHE,   /* indicates extent cache */
        BASE_CHECK,     /* check kernel status */
 };
 
index 41afb9534bbdcb66a20ea6650fc71f62a8b99034..8d8ea99f2156278c7423b05f56be54d7006599a0 100644 (file)
@@ -93,10 +93,9 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
        }
 retry:
        de = f2fs_find_entry(dir, &name, &page);
-       if (de && inode->i_ino == le32_to_cpu(de->ino)) {
-               clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
+       if (de && inode->i_ino == le32_to_cpu(de->ino))
                goto out_unmap_put;
-       }
+
        if (de) {
                einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino));
                if (IS_ERR(einode)) {
@@ -115,7 +114,7 @@ retry:
                iput(einode);
                goto retry;
        }
-       err = __f2fs_add_link(dir, &name, inode);
+       err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode);
        if (err)
                goto out_err;
 
@@ -187,11 +186,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
                        goto next;
 
                entry = get_fsync_inode(head, ino_of_node(page));
-               if (entry) {
-                       if (IS_INODE(page) && is_dent_dnode(page))
-                               set_inode_flag(F2FS_I(entry->inode),
-                                                       FI_INC_LINK);
-               } else {
+               if (!entry) {
                        if (IS_INODE(page) && is_dent_dnode(page)) {
                                err = recover_inode_page(sbi, page);
                                if (err)
@@ -212,8 +207,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
                        if (IS_ERR(entry->inode)) {
                                err = PTR_ERR(entry->inode);
                                kmem_cache_free(fsync_entry_slab, entry);
-                               if (err == -ENOENT)
+                               if (err == -ENOENT) {
+                                       err = 0;
                                        goto next;
+                               }
                                break;
                        }
                        list_add_tail(&entry->list, head);
@@ -256,6 +253,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
        struct f2fs_summary_block *sum_node;
        struct f2fs_summary sum;
        struct page *sum_page, *node_page;
+       struct dnode_of_data tdn = *dn;
        nid_t ino, nid;
        struct inode *inode;
        unsigned int offset;
@@ -283,17 +281,15 @@ got_it:
        /* Use the locked dnode page and inode */
        nid = le32_to_cpu(sum.nid);
        if (dn->inode->i_ino == nid) {
-               struct dnode_of_data tdn = *dn;
                tdn.nid = nid;
+               if (!dn->inode_page_locked)
+                       lock_page(dn->inode_page);
                tdn.node_page = dn->inode_page;
                tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
-               truncate_data_blocks_range(&tdn, 1);
-               return 0;
+               goto truncate_out;
        } else if (dn->nid == nid) {
-               struct dnode_of_data tdn = *dn;
                tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
-               truncate_data_blocks_range(&tdn, 1);
-               return 0;
+               goto truncate_out;
        }
 
        /* Get the node page */
@@ -317,18 +313,33 @@ got_it:
        bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
                        le16_to_cpu(sum.ofs_in_node);
 
-       if (ino != dn->inode->i_ino) {
-               truncate_hole(inode, bidx, bidx + 1);
+       /*
+        * if inode page is locked, unlock temporarily, but its reference
+        * count keeps alive.
+        */
+       if (ino == dn->inode->i_ino && dn->inode_page_locked)
+               unlock_page(dn->inode_page);
+
+       set_new_dnode(&tdn, inode, NULL, NULL, 0);
+       if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
+               goto out;
+
+       if (tdn.data_blkaddr == blkaddr)
+               truncate_data_blocks_range(&tdn, 1);
+
+       f2fs_put_dnode(&tdn);
+out:
+       if (ino != dn->inode->i_ino)
                iput(inode);
-       } else {
-               struct dnode_of_data tdn;
-               set_new_dnode(&tdn, inode, dn->inode_page, NULL, 0);
-               if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
-                       return 0;
-               if (tdn.data_blkaddr != NULL_ADDR)
-                       truncate_data_blocks_range(&tdn, 1);
-               f2fs_put_page(tdn.node_page, 1);
-       }
+       else if (dn->inode_page_locked)
+               lock_page(dn->inode_page);
+       return 0;
+
+truncate_out:
+       if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr)
+               truncate_data_blocks_range(&tdn, 1);
+       if (dn->inode->i_ino == nid && !dn->inode_page_locked)
+               unlock_page(dn->inode_page);
        return 0;
 }
 
@@ -384,7 +395,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
                src = datablock_addr(dn.node_page, dn.ofs_in_node);
                dest = datablock_addr(page, dn.ofs_in_node);
 
-               if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) {
+               if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR &&
+                       dest >= MAIN_BLKADDR(sbi) && dest < MAX_BLKADDR(sbi)) {
+
                        if (src == NULL_ADDR) {
                                err = reserve_new_block(&dn);
                                /* We should not get -ENOSPC */
@@ -401,14 +414,13 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
                        /* write dummy data page */
                        recover_data_page(sbi, NULL, &sum, src, dest);
                        dn.data_blkaddr = dest;
-                       update_extent_cache(&dn);
+                       set_data_blkaddr(&dn);
+                       f2fs_update_extent_cache(&dn);
                        recovered++;
                }
                dn.ofs_in_node++;
        }
 
-       /* write node page in place */
-       set_summary(&sum, dn.nid, 0, 0);
        if (IS_INODE(dn.node_page))
                sync_inode_page(&dn);
 
@@ -552,7 +564,7 @@ out:
                mutex_unlock(&sbi->cp_mutex);
        } else if (need_writecp) {
                struct cp_control cpc = {
-                       .reason = CP_SYNC,
+                       .reason = CP_RECOVERY,
                };
                mutex_unlock(&sbi->cp_mutex);
                write_checkpoint(sbi, &cpc);
index daee4ab913daf2bcd2883aeee28ff2e8975d02bd..f939660941bbc7a69fc24d84023e7561134f687b 100644 (file)
@@ -205,6 +205,8 @@ retry:
        list_add_tail(&new->list, &fi->inmem_pages);
        inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
        mutex_unlock(&fi->inmem_lock);
+
+       trace_f2fs_register_inmem_page(page, INMEM);
 }
 
 void commit_inmem_pages(struct inode *inode, bool abort)
@@ -238,11 +240,13 @@ void commit_inmem_pages(struct inode *inode, bool abort)
                                f2fs_wait_on_page_writeback(cur->page, DATA);
                                if (clear_page_dirty_for_io(cur->page))
                                        inode_dec_dirty_pages(inode);
+                               trace_f2fs_commit_inmem_page(cur->page, INMEM);
                                do_write_data_page(cur->page, &fio);
                                submit_bio = true;
                        }
                        f2fs_put_page(cur->page, 1);
                } else {
+                       trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP);
                        put_page(cur->page);
                }
                radix_tree_delete(&fi->inmem_root, cur->page->index);
@@ -277,6 +281,9 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi)
 
 void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
 {
+       /* try to shrink extent cache when there is no enough memory */
+       f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER);
+
        /* check the # of cached NAT entries and prefree segments */
        if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) ||
                        excess_prefree_segs(sbi) ||
@@ -549,7 +556,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 
                end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1);
 
-               if (end - start < cpc->trim_minlen)
+               if (force && end - start < cpc->trim_minlen)
                        continue;
 
                __add_discard_entry(sbi, cpc, start, end);
@@ -1164,6 +1171,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
        curseg = CURSEG_I(sbi, type);
 
        mutex_lock(&curseg->curseg_mutex);
+       mutex_lock(&sit_i->sentry_lock);
 
        /* direct_io'ed data is aligned to the segment for better performance */
        if (direct_io && curseg->next_blkoff)
@@ -1178,7 +1186,6 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
         */
        __add_sum_entry(sbi, type, sum);
 
-       mutex_lock(&sit_i->sentry_lock);
        __refresh_next_blkoff(sbi, curseg);
 
        stat_inc_block_count(sbi, curseg);
@@ -1730,6 +1737,9 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        mutex_lock(&curseg->curseg_mutex);
        mutex_lock(&sit_i->sentry_lock);
 
+       if (!sit_i->dirty_sentries)
+               goto out;
+
        /*
         * add and account sit entries of dirty bitmap in sit entry
         * set temporarily
@@ -1744,9 +1754,6 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL))
                remove_sits_in_journal(sbi);
 
-       if (!sit_i->dirty_sentries)
-               goto out;
-
        /*
         * there are two steps to flush sit entries:
         * #1, flush sit entries to journal in current cold data summary block.
index 7fd35111cf6241c486c22ec4477c71527528fb79..85d7fa7514b2cf1c90429095dc05076be5bdc3dd 100644 (file)
@@ -336,7 +336,8 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
        clear_bit(segno, free_i->free_segmap);
        free_i->free_segments++;
 
-       next = find_next_bit(free_i->free_segmap, MAIN_SEGS(sbi), start_segno);
+       next = find_next_bit(free_i->free_segmap,
+                       start_segno + sbi->segs_per_sec, start_segno);
        if (next >= start_segno + sbi->segs_per_sec) {
                clear_bit(secno, free_i->free_secmap);
                free_i->free_sections++;
index f2fe666a6ea995a29d5aee3f228acaf75e6cf85a..160b88346b2477466f3ed34d4d2dff2509742d7e 100644 (file)
@@ -57,6 +57,8 @@ enum {
        Opt_flush_merge,
        Opt_nobarrier,
        Opt_fastboot,
+       Opt_extent_cache,
+       Opt_noinline_data,
        Opt_err,
 };
 
@@ -78,6 +80,8 @@ static match_table_t f2fs_tokens = {
        {Opt_flush_merge, "flush_merge"},
        {Opt_nobarrier, "nobarrier"},
        {Opt_fastboot, "fastboot"},
+       {Opt_extent_cache, "extent_cache"},
+       {Opt_noinline_data, "noinline_data"},
        {Opt_err, NULL},
 };
 
@@ -367,6 +371,12 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_fastboot:
                        set_opt(sbi, FASTBOOT);
                        break;
+               case Opt_extent_cache:
+                       set_opt(sbi, EXTENT_CACHE);
+                       break;
+               case Opt_noinline_data:
+                       clear_opt(sbi, INLINE_DATA);
+                       break;
                default:
                        f2fs_msg(sb, KERN_ERR,
                                "Unrecognized mount option \"%s\" or missing value",
@@ -392,7 +402,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
        atomic_set(&fi->dirty_pages, 0);
        fi->i_current_depth = 1;
        fi->i_advise = 0;
-       rwlock_init(&fi->ext.ext_lock);
+       rwlock_init(&fi->ext_lock);
        init_rwsem(&fi->i_sem);
        INIT_RADIX_TREE(&fi->inmem_root, GFP_NOFS);
        INIT_LIST_HEAD(&fi->inmem_pages);
@@ -591,6 +601,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",disable_ext_identify");
        if (test_opt(sbi, INLINE_DATA))
                seq_puts(seq, ",inline_data");
+       else
+               seq_puts(seq, ",noinline_data");
        if (test_opt(sbi, INLINE_DENTRY))
                seq_puts(seq, ",inline_dentry");
        if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE))
@@ -599,6 +611,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",nobarrier");
        if (test_opt(sbi, FASTBOOT))
                seq_puts(seq, ",fastboot");
+       if (test_opt(sbi, EXTENT_CACHE))
+               seq_puts(seq, ",extent_cache");
        seq_printf(seq, ",active_logs=%u", sbi->active_logs);
 
        return 0;
@@ -959,7 +973,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
        struct buffer_head *raw_super_buf;
        struct inode *root;
        long err = -EINVAL;
-       bool retry = true;
+       bool retry = true, need_fsck = false;
        char *options = NULL;
        int i;
 
@@ -984,6 +998,7 @@ try_onemore:
        sbi->active_logs = NR_CURSEG_TYPE;
 
        set_opt(sbi, BG_GC);
+       set_opt(sbi, INLINE_DATA);
 
 #ifdef CONFIG_F2FS_FS_XATTR
        set_opt(sbi, XATTR_USER);
@@ -1020,7 +1035,6 @@ try_onemore:
        sbi->raw_super = raw_super;
        sbi->raw_super_buf = raw_super_buf;
        mutex_init(&sbi->gc_mutex);
-       mutex_init(&sbi->writepages);
        mutex_init(&sbi->cp_mutex);
        init_rwsem(&sbi->node_write);
        clear_sbi_flag(sbi, SBI_POR_DOING);
@@ -1072,6 +1086,8 @@ try_onemore:
        INIT_LIST_HEAD(&sbi->dir_inode_list);
        spin_lock_init(&sbi->dir_inode_lock);
 
+       init_extent_cache_info(sbi);
+
        init_ino_entry_info(sbi);
 
        /* setup f2fs internal modules */
@@ -1146,9 +1162,6 @@ try_onemore:
        if (err)
                goto free_proc;
 
-       if (!retry)
-               set_sbi_flag(sbi, SBI_NEED_FSCK);
-
        /* recover fsynced data */
        if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
                /*
@@ -1160,8 +1173,13 @@ try_onemore:
                        err = -EROFS;
                        goto free_kobj;
                }
+
+               if (need_fsck)
+                       set_sbi_flag(sbi, SBI_NEED_FSCK);
+
                err = recover_fsync_data(sbi);
                if (err) {
+                       need_fsck = true;
                        f2fs_msg(sb, KERN_ERR,
                                "Cannot recover all fsync data errno=%ld", err);
                        goto free_kobj;
@@ -1212,7 +1230,7 @@ free_sbi:
 
        /* give only one another chance */
        if (retry) {
-               retry = 0;
+               retry = false;
                shrink_dcache_sb(sb);
                goto try_onemore;
        }
@@ -1278,10 +1296,13 @@ static int __init init_f2fs_fs(void)
        err = create_checkpoint_caches();
        if (err)
                goto free_segment_manager_caches;
+       err = create_extent_cache();
+       if (err)
+               goto free_checkpoint_caches;
        f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj);
        if (!f2fs_kset) {
                err = -ENOMEM;
-               goto free_checkpoint_caches;
+               goto free_extent_cache;
        }
        err = register_filesystem(&f2fs_fs_type);
        if (err)
@@ -1292,6 +1313,8 @@ static int __init init_f2fs_fs(void)
 
 free_kset:
        kset_unregister(f2fs_kset);
+free_extent_cache:
+       destroy_extent_cache();
 free_checkpoint_caches:
        destroy_checkpoint_caches();
 free_segment_manager_caches:
@@ -1309,6 +1332,7 @@ static void __exit exit_f2fs_fs(void)
        remove_proc_entry("fs/f2fs", NULL);
        f2fs_destroy_root_stats();
        unregister_filesystem(&f2fs_fs_type);
+       destroy_extent_cache();
        destroy_checkpoint_caches();
        destroy_segment_manager_caches();
        destroy_node_manager_caches();
index 5072bf9ae0ef841dc613202b998cbab864c969cd..b0fd2f2d0716c487ff703a7b3a1f2759f0b4fd06 100644 (file)
@@ -135,7 +135,8 @@ static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name,
        if (strcmp(name, "") != 0)
                return -EINVAL;
 
-       *((char *)buffer) = F2FS_I(inode)->i_advise;
+       if (buffer)
+               *((char *)buffer) = F2FS_I(inode)->i_advise;
        return sizeof(char);
 }
 
@@ -152,6 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name,
                return -EINVAL;
 
        F2FS_I(inode)->i_advise |= *(char *)value;
+       mark_inode_dirty(inode);
        return 0;
 }
 
index b06c98796afb1b58ce569c046eee72e0d929c9fe..611b5408f6ec48f84d8cd04a4ea5ec1911a968ba 100644 (file)
@@ -9,8 +9,8 @@ static DEFINE_SPINLOCK(pin_lock);
 void pin_remove(struct fs_pin *pin)
 {
        spin_lock(&pin_lock);
-       hlist_del(&pin->m_list);
-       hlist_del(&pin->s_list);
+       hlist_del_init(&pin->m_list);
+       hlist_del_init(&pin->s_list);
        spin_unlock(&pin_lock);
        spin_lock_irq(&pin->wait.lock);
        pin->done = 1;
index 82ef1405260e1cfbe551ffba781fec25f881e918..1f4f9dac6e5af8017e41ab497b92e0515e5ed71c 100644 (file)
@@ -632,14 +632,17 @@ struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
  */
 struct mount *__lookup_mnt_last(struct vfsmount *mnt, struct dentry *dentry)
 {
-       struct mount *p, *res;
-       res = p = __lookup_mnt(mnt, dentry);
+       struct mount *p, *res = NULL;
+       p = __lookup_mnt(mnt, dentry);
        if (!p)
                goto out;
+       if (!(p->mnt.mnt_flags & MNT_UMOUNT))
+               res = p;
        hlist_for_each_entry_continue(p, mnt_hash) {
                if (&p->mnt_parent->mnt != mnt || p->mnt_mountpoint != dentry)
                        break;
-               res = p;
+               if (!(p->mnt.mnt_flags & MNT_UMOUNT))
+                       res = p;
        }
 out:
        return res;
@@ -795,10 +798,8 @@ static void __touch_mnt_namespace(struct mnt_namespace *ns)
 /*
  * vfsmount lock must be held for write
  */
-static void detach_mnt(struct mount *mnt, struct path *old_path)
+static void unhash_mnt(struct mount *mnt)
 {
-       old_path->dentry = mnt->mnt_mountpoint;
-       old_path->mnt = &mnt->mnt_parent->mnt;
        mnt->mnt_parent = mnt;
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        list_del_init(&mnt->mnt_child);
@@ -808,6 +809,26 @@ static void detach_mnt(struct mount *mnt, struct path *old_path)
        mnt->mnt_mp = NULL;
 }
 
+/*
+ * vfsmount lock must be held for write
+ */
+static void detach_mnt(struct mount *mnt, struct path *old_path)
+{
+       old_path->dentry = mnt->mnt_mountpoint;
+       old_path->mnt = &mnt->mnt_parent->mnt;
+       unhash_mnt(mnt);
+}
+
+/*
+ * vfsmount lock must be held for write
+ */
+static void umount_mnt(struct mount *mnt)
+{
+       /* old mountpoint will be dropped when we can do that */
+       mnt->mnt_ex_mountpoint = mnt->mnt_mountpoint;
+       unhash_mnt(mnt);
+}
+
 /*
  * vfsmount lock must be held for write
  */
@@ -1078,6 +1099,13 @@ static void mntput_no_expire(struct mount *mnt)
        rcu_read_unlock();
 
        list_del(&mnt->mnt_instance);
+
+       if (unlikely(!list_empty(&mnt->mnt_mounts))) {
+               struct mount *p, *tmp;
+               list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts,  mnt_child) {
+                       umount_mnt(p);
+               }
+       }
        unlock_mount_hash();
 
        if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) {
@@ -1298,17 +1326,15 @@ static HLIST_HEAD(unmounted);   /* protected by namespace_sem */
 
 static void namespace_unlock(void)
 {
-       struct hlist_head head = unmounted;
+       struct hlist_head head;
 
-       if (likely(hlist_empty(&head))) {
-               up_write(&namespace_sem);
-               return;
-       }
+       hlist_move_list(&unmounted, &head);
 
-       head.first->pprev = &head.first;
-       INIT_HLIST_HEAD(&unmounted);
        up_write(&namespace_sem);
 
+       if (likely(hlist_empty(&head)))
+               return;
+
        synchronize_rcu();
 
        group_pin_kill(&head);
@@ -1319,49 +1345,63 @@ static inline void namespace_lock(void)
        down_write(&namespace_sem);
 }
 
+enum umount_tree_flags {
+       UMOUNT_SYNC = 1,
+       UMOUNT_PROPAGATE = 2,
+       UMOUNT_CONNECTED = 4,
+};
 /*
  * mount_lock must be held
  * namespace_sem must be held for write
- * how = 0 => just this tree, don't propagate
- * how = 1 => propagate; we know that nobody else has reference to any victims
- * how = 2 => lazy umount
  */
-void umount_tree(struct mount *mnt, int how)
+static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
 {
-       HLIST_HEAD(tmp_list);
+       LIST_HEAD(tmp_list);
        struct mount *p;
 
+       if (how & UMOUNT_PROPAGATE)
+               propagate_mount_unlock(mnt);
+
+       /* Gather the mounts to umount */
        for (p = mnt; p; p = next_mnt(p, mnt)) {
-               hlist_del_init_rcu(&p->mnt_hash);
-               hlist_add_head(&p->mnt_hash, &tmp_list);
+               p->mnt.mnt_flags |= MNT_UMOUNT;
+               list_move(&p->mnt_list, &tmp_list);
        }
 
-       hlist_for_each_entry(p, &tmp_list, mnt_hash)
+       /* Hide the mounts from mnt_mounts */
+       list_for_each_entry(p, &tmp_list, mnt_list) {
                list_del_init(&p->mnt_child);
+       }
 
-       if (how)
+       /* Add propogated mounts to the tmp_list */
+       if (how & UMOUNT_PROPAGATE)
                propagate_umount(&tmp_list);
 
-       while (!hlist_empty(&tmp_list)) {
-               p = hlist_entry(tmp_list.first, struct mount, mnt_hash);
-               hlist_del_init_rcu(&p->mnt_hash);
+       while (!list_empty(&tmp_list)) {
+               bool disconnect;
+               p = list_first_entry(&tmp_list, struct mount, mnt_list);
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
                __touch_mnt_namespace(p->mnt_ns);
                p->mnt_ns = NULL;
-               if (how < 2)
+               if (how & UMOUNT_SYNC)
                        p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
 
-               pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt, &unmounted);
+               disconnect = !(((how & UMOUNT_CONNECTED) &&
+                               mnt_has_parent(p) &&
+                               (p->mnt_parent->mnt.mnt_flags & MNT_UMOUNT)) ||
+                              IS_MNT_LOCKED_AND_LAZY(p));
+
+               pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt,
+                                disconnect ? &unmounted : NULL);
                if (mnt_has_parent(p)) {
-                       hlist_del_init(&p->mnt_mp_list);
-                       put_mountpoint(p->mnt_mp);
                        mnt_add_count(p->mnt_parent, -1);
-                       /* old mountpoint will be dropped when we can do that */
-                       p->mnt_ex_mountpoint = p->mnt_mountpoint;
-                       p->mnt_mountpoint = p->mnt.mnt_root;
-                       p->mnt_parent = p;
-                       p->mnt_mp = NULL;
+                       if (!disconnect) {
+                               /* Don't forget about p */
+                               list_add_tail(&p->mnt_child, &p->mnt_parent->mnt_mounts);
+                       } else {
+                               umount_mnt(p);
+                       }
                }
                change_mnt_propagation(p, MS_PRIVATE);
        }
@@ -1447,14 +1487,14 @@ static int do_umount(struct mount *mnt, int flags)
 
        if (flags & MNT_DETACH) {
                if (!list_empty(&mnt->mnt_list))
-                       umount_tree(mnt, 2);
+                       umount_tree(mnt, UMOUNT_PROPAGATE);
                retval = 0;
        } else {
                shrink_submounts(mnt);
                retval = -EBUSY;
                if (!propagate_mount_busy(mnt, 2)) {
                        if (!list_empty(&mnt->mnt_list))
-                               umount_tree(mnt, 1);
+                               umount_tree(mnt, UMOUNT_PROPAGATE|UMOUNT_SYNC);
                        retval = 0;
                }
        }
@@ -1480,13 +1520,20 @@ void __detach_mounts(struct dentry *dentry)
 
        namespace_lock();
        mp = lookup_mountpoint(dentry);
-       if (!mp)
+       if (IS_ERR_OR_NULL(mp))
                goto out_unlock;
 
        lock_mount_hash();
        while (!hlist_empty(&mp->m_list)) {
                mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
-               umount_tree(mnt, 2);
+               if (mnt->mnt.mnt_flags & MNT_UMOUNT) {
+                       struct mount *p, *tmp;
+                       list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts,  mnt_child) {
+                               hlist_add_head(&p->mnt_umount.s_list, &unmounted);
+                               umount_mnt(p);
+                       }
+               }
+               else umount_tree(mnt, UMOUNT_CONNECTED);
        }
        unlock_mount_hash();
        put_mountpoint(mp);
@@ -1648,7 +1695,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
 out:
        if (res) {
                lock_mount_hash();
-               umount_tree(res, 0);
+               umount_tree(res, UMOUNT_SYNC);
                unlock_mount_hash();
        }
        return q;
@@ -1660,8 +1707,11 @@ struct vfsmount *collect_mounts(struct path *path)
 {
        struct mount *tree;
        namespace_lock();
-       tree = copy_tree(real_mount(path->mnt), path->dentry,
-                        CL_COPY_ALL | CL_PRIVATE);
+       if (!check_mnt(real_mount(path->mnt)))
+               tree = ERR_PTR(-EINVAL);
+       else
+               tree = copy_tree(real_mount(path->mnt), path->dentry,
+                                CL_COPY_ALL | CL_PRIVATE);
        namespace_unlock();
        if (IS_ERR(tree))
                return ERR_CAST(tree);
@@ -1672,7 +1722,7 @@ void drop_collected_mounts(struct vfsmount *mnt)
 {
        namespace_lock();
        lock_mount_hash();
-       umount_tree(real_mount(mnt), 0);
+       umount_tree(real_mount(mnt), UMOUNT_SYNC);
        unlock_mount_hash();
        namespace_unlock();
 }
@@ -1855,7 +1905,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
  out_cleanup_ids:
        while (!hlist_empty(&tree_list)) {
                child = hlist_entry(tree_list.first, struct mount, mnt_hash);
-               umount_tree(child, 0);
+               umount_tree(child, UMOUNT_SYNC);
        }
        unlock_mount_hash();
        cleanup_group_ids(source_mnt, NULL);
@@ -2035,7 +2085,7 @@ static int do_loopback(struct path *path, const char *old_name,
        err = graft_tree(mnt, parent, mp);
        if (err) {
                lock_mount_hash();
-               umount_tree(mnt, 0);
+               umount_tree(mnt, UMOUNT_SYNC);
                unlock_mount_hash();
        }
 out2:
@@ -2406,7 +2456,7 @@ void mark_mounts_for_expiry(struct list_head *mounts)
        while (!list_empty(&graveyard)) {
                mnt = list_first_entry(&graveyard, struct mount, mnt_expire);
                touch_mnt_namespace(mnt->mnt_ns);
-               umount_tree(mnt, 1);
+               umount_tree(mnt, UMOUNT_PROPAGATE|UMOUNT_SYNC);
        }
        unlock_mount_hash();
        namespace_unlock();
@@ -2477,7 +2527,7 @@ static void shrink_submounts(struct mount *mnt)
                        m = list_first_entry(&graveyard, struct mount,
                                                mnt_expire);
                        touch_mnt_namespace(m->mnt_ns);
-                       umount_tree(m, 1);
+                       umount_tree(m, UMOUNT_PROPAGATE|UMOUNT_SYNC);
                }
        }
 }
index 260ac8f898a43de82de925d9befad1613a7764fb..6367e1e435c64144a1d9adb94e9c1cfd1f157dfe 100644 (file)
@@ -361,6 +361,46 @@ int propagate_mount_busy(struct mount *mnt, int refcnt)
        return ret;
 }
 
+/*
+ * Clear MNT_LOCKED when it can be shown to be safe.
+ *
+ * mount_lock lock must be held for write
+ */
+void propagate_mount_unlock(struct mount *mnt)
+{
+       struct mount *parent = mnt->mnt_parent;
+       struct mount *m, *child;
+
+       BUG_ON(parent == mnt);
+
+       for (m = propagation_next(parent, parent); m;
+                       m = propagation_next(m, parent)) {
+               child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint);
+               if (child)
+                       child->mnt.mnt_flags &= ~MNT_LOCKED;
+       }
+}
+
+/*
+ * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted.
+ */
+static void mark_umount_candidates(struct mount *mnt)
+{
+       struct mount *parent = mnt->mnt_parent;
+       struct mount *m;
+
+       BUG_ON(parent == mnt);
+
+       for (m = propagation_next(parent, parent); m;
+                       m = propagation_next(m, parent)) {
+               struct mount *child = __lookup_mnt_last(&m->mnt,
+                                               mnt->mnt_mountpoint);
+               if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) {
+                       SET_MNT_MARK(child);
+               }
+       }
+}
+
 /*
  * NOTE: unmounting 'mnt' naturally propagates to all other mounts its
  * parent propagates to.
@@ -378,13 +418,16 @@ static void __propagate_umount(struct mount *mnt)
                struct mount *child = __lookup_mnt_last(&m->mnt,
                                                mnt->mnt_mountpoint);
                /*
-                * umount the child only if the child has no
-                * other children
+                * umount the child only if the child has no children
+                * and the child is marked safe to unmount.
                 */
-               if (child && list_empty(&child->mnt_mounts)) {
+               if (!child || !IS_MNT_MARKED(child))
+                       continue;
+               CLEAR_MNT_MARK(child);
+               if (list_empty(&child->mnt_mounts)) {
                        list_del_init(&child->mnt_child);
-                       hlist_del_init_rcu(&child->mnt_hash);
-                       hlist_add_before_rcu(&child->mnt_hash, &mnt->mnt_hash);
+                       child->mnt.mnt_flags |= MNT_UMOUNT;
+                       list_move_tail(&child->mnt_list, &mnt->mnt_list);
                }
        }
 }
@@ -396,11 +439,14 @@ static void __propagate_umount(struct mount *mnt)
  *
  * vfsmount lock must be held for write
  */
-int propagate_umount(struct hlist_head *list)
+int propagate_umount(struct list_head *list)
 {
        struct mount *mnt;
 
-       hlist_for_each_entry(mnt, list, mnt_hash)
+       list_for_each_entry_reverse(mnt, list, mnt_list)
+               mark_umount_candidates(mnt);
+
+       list_for_each_entry(mnt, list, mnt_list)
                __propagate_umount(mnt);
        return 0;
 }
index 4a246358b03183994461d9eae5d8efe09db2e6dc..7114ce6e6b9ef038f415550b925ac50a95be978c 100644 (file)
@@ -19,6 +19,9 @@
 #define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
 #define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED)
 #define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED)
+#define IS_MNT_LOCKED(m) ((m)->mnt.mnt_flags & MNT_LOCKED)
+#define IS_MNT_LOCKED_AND_LAZY(m) \
+       (((m)->mnt.mnt_flags & (MNT_LOCKED|MNT_SYNC_UMOUNT)) == MNT_LOCKED)
 
 #define CL_EXPIRE              0x01
 #define CL_SLAVE               0x02
@@ -40,14 +43,14 @@ static inline void set_mnt_shared(struct mount *mnt)
 void change_mnt_propagation(struct mount *, int);
 int propagate_mnt(struct mount *, struct mountpoint *, struct mount *,
                struct hlist_head *);
-int propagate_umount(struct hlist_head *);
+int propagate_umount(struct list_head *);
 int propagate_mount_busy(struct mount *, int);
+void propagate_mount_unlock(struct mount *);
 void mnt_release_group_id(struct mount *);
 int get_dominating_id(struct mount *mnt, const struct path *root);
 unsigned int mnt_get_count(struct mount *mnt);
 void mnt_set_mountpoint(struct mount *, struct mountpoint *,
                        struct mount *);
-void umount_tree(struct mount *, int);
 struct mount *copy_tree(struct mount *, struct dentry *, int);
 bool is_path_reachable(struct mount *, struct dentry *,
                         const struct path *root);
index 383ade1a211bfcbec546899d6451171a63e5a13f..9bb0d11729c9cef15cf9ca1e5b6f6c18528da27d 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/of.h>
-#include <linux/pinctrl/pinctrl.h>
 
 #ifdef CONFIG_GPIOLIB
 
@@ -139,53 +138,6 @@ static inline void gpio_unexport(unsigned gpio)
        gpiod_unexport(gpio_to_desc(gpio));
 }
 
-#ifdef CONFIG_PINCTRL
-
-/**
- * struct gpio_pin_range - pin range controlled by a gpio chip
- * @head: list for maintaining set of pin ranges, used internally
- * @pctldev: pinctrl device which handles corresponding pins
- * @range: actual range of pins controlled by a gpio controller
- */
-
-struct gpio_pin_range {
-       struct list_head node;
-       struct pinctrl_dev *pctldev;
-       struct pinctrl_gpio_range range;
-};
-
-int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
-                          unsigned int gpio_offset, unsigned int pin_offset,
-                          unsigned int npins);
-int gpiochip_add_pingroup_range(struct gpio_chip *chip,
-                       struct pinctrl_dev *pctldev,
-                       unsigned int gpio_offset, const char *pin_group);
-void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
-
-#else
-
-static inline int
-gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
-                      unsigned int gpio_offset, unsigned int pin_offset,
-                      unsigned int npins)
-{
-       return 0;
-}
-static inline int
-gpiochip_add_pingroup_range(struct gpio_chip *chip,
-                       struct pinctrl_dev *pctldev,
-                       unsigned int gpio_offset, const char *pin_group)
-{
-       return 0;
-}
-
-static inline void
-gpiochip_remove_pin_ranges(struct gpio_chip *chip)
-{
-}
-
-#endif /* CONFIG_PINCTRL */
-
 #else  /* !CONFIG_GPIOLIB */
 
 static inline bool gpio_is_valid(int number)
index fd23978d93fe35a91afab5f56d3611c0324d9ceb..51cc1deb7af3a38597a665300ef9405d8dbe3cef 100644 (file)
@@ -605,9 +605,4 @@ static inline unsigned long to_bytes(sector_t n)
        return (n << SECTOR_SHIFT);
 }
 
-/*-----------------------------------------------------------------
- * Helper for block layer and dm core operations
- *---------------------------------------------------------------*/
-int dm_underlying_device_busy(struct request_queue *q);
-
 #endif /* _LINUX_DEVICE_MAPPER_H */
index a23556c32703aa9e6563e777853e270f80d03712..591f8c3ef41091c30bc8eadbbe10ffecf232ad9a 100644 (file)
@@ -153,7 +153,7 @@ struct f2fs_orphan_block {
  */
 struct f2fs_extent {
        __le32 fofs;            /* start file offset of the extent */
-       __le32 blk_addr;        /* start block address of the extent */
+       __le32 blk;             /* start block address of the extent */
        __le32 len;             /* lengh of the extent */
 } __packed;
 
@@ -178,6 +178,7 @@ struct f2fs_extent {
 #define F2FS_INLINE_DATA       0x02    /* file inline data flag */
 #define F2FS_INLINE_DENTRY     0x04    /* file inline dentry flag */
 #define F2FS_DATA_EXIST                0x08    /* file inline data exist flag */
+#define F2FS_INLINE_DOTS       0x10    /* file having implicit dot dentries */
 
 #define MAX_INLINE_DATA                (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
                                                F2FS_INLINE_XATTR_ADDRS - 1))
index 9dc4e0384bfb7abe6fc8c60efa402d85c9725b20..3886b3bffd7f75305fc60db553916729cf677a1e 100644 (file)
@@ -13,6 +13,8 @@ struct vfsmount;
 static inline void init_fs_pin(struct fs_pin *p, void (*kill)(struct fs_pin *))
 {
        init_waitqueue_head(&p->wait);
+       INIT_HLIST_NODE(&p->s_list);
+       INIT_HLIST_NODE(&p->m_list);
        p->kill = kill;
 }
 
index 45afc2dee560ccc478462789e8fb4d7e38817d10..3a7c9ffd5ab930b46e7341adfdcc0c5cc446224a 100644 (file)
@@ -16,6 +16,15 @@ struct device;
  */
 struct gpio_desc;
 
+/**
+ * Struct containing an array of descriptors that can be obtained using
+ * gpiod_get_array().
+ */
+struct gpio_descs {
+       unsigned int ndescs;
+       struct gpio_desc *desc[];
+};
+
 #define GPIOD_FLAGS_BIT_DIR_SET                BIT(0)
 #define GPIOD_FLAGS_BIT_DIR_OUT                BIT(1)
 #define GPIOD_FLAGS_BIT_DIR_VAL                BIT(2)
@@ -34,6 +43,9 @@ enum gpiod_flags {
 
 #ifdef CONFIG_GPIOLIB
 
+/* Return the number of GPIOs associated with a device / function */
+int gpiod_count(struct device *dev, const char *con_id);
+
 /* Acquire and dispose GPIOs */
 struct gpio_desc *__must_check __gpiod_get(struct device *dev,
                                         const char *con_id,
@@ -49,7 +61,14 @@ struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev,
                                                        const char *con_id,
                                                        unsigned int index,
                                                        enum gpiod_flags flags);
+struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
+                                               const char *con_id,
+                                               enum gpiod_flags flags);
+struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
+                                                       const char *con_id,
+                                                       enum gpiod_flags flags);
 void gpiod_put(struct gpio_desc *desc);
+void gpiod_put_array(struct gpio_descs *descs);
 
 struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev,
                                              const char *con_id,
@@ -64,7 +83,14 @@ struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev,
 struct gpio_desc *__must_check
 __devm_gpiod_get_index_optional(struct device *dev, const char *con_id,
                              unsigned int index, enum gpiod_flags flags);
+struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
+                                                    const char *con_id,
+                                                    enum gpiod_flags flags);
+struct gpio_descs *__must_check
+devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
+                             enum gpiod_flags flags);
 void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
+void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
 
 int gpiod_get_direction(struct gpio_desc *desc);
 int gpiod_direction_input(struct gpio_desc *desc);
@@ -110,9 +136,15 @@ struct fwnode_handle;
 struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                                         const char *propname);
 struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
+                                           const char *con_id,
                                            struct fwnode_handle *child);
 #else /* CONFIG_GPIOLIB */
 
+static inline int gpiod_count(struct device *dev, const char *con_id)
+{
+       return 0;
+}
+
 static inline struct gpio_desc *__must_check __gpiod_get(struct device *dev,
                                                const char *con_id,
                                                enum gpiod_flags flags)
@@ -142,6 +174,20 @@ __gpiod_get_index_optional(struct device *dev, const char *con_id,
        return ERR_PTR(-ENOSYS);
 }
 
+static inline struct gpio_descs *__must_check
+gpiod_get_array(struct device *dev, const char *con_id,
+               enum gpiod_flags flags)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct gpio_descs *__must_check
+gpiod_get_array_optional(struct device *dev, const char *con_id,
+                        enum gpiod_flags flags)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
 static inline void gpiod_put(struct gpio_desc *desc)
 {
        might_sleep();
@@ -150,6 +196,14 @@ static inline void gpiod_put(struct gpio_desc *desc)
        WARN_ON(1);
 }
 
+static inline void gpiod_put_array(struct gpio_descs *descs)
+{
+       might_sleep();
+
+       /* GPIO can never have been requested */
+       WARN_ON(1);
+}
+
 static inline struct gpio_desc *__must_check
 __devm_gpiod_get(struct device *dev,
                 const char *con_id,
@@ -181,6 +235,20 @@ __devm_gpiod_get_index_optional(struct device *dev, const char *con_id,
        return ERR_PTR(-ENOSYS);
 }
 
+static inline struct gpio_descs *__must_check
+devm_gpiod_get_array(struct device *dev, const char *con_id,
+                    enum gpiod_flags flags)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct gpio_descs *__must_check
+devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
+                             enum gpiod_flags flags)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
 static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
 {
        might_sleep();
@@ -189,6 +257,15 @@ static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
        WARN_ON(1);
 }
 
+static inline void devm_gpiod_put_array(struct device *dev,
+                                       struct gpio_descs *descs)
+{
+       might_sleep();
+
+       /* GPIO can never have been requested */
+       WARN_ON(1);
+}
+
 
 static inline int gpiod_get_direction(const struct gpio_desc *desc)
 {
index c497c62889d1d3bd60ba1d5aaad98714a06897bc..f1b36593ec9f18f0e552a83992be2f03f9c007d5 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/irq.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
+#include <linux/pinctrl/pinctrl.h>
 
 struct device;
 struct gpio_desc;
@@ -173,6 +174,53 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
 
 #endif /* CONFIG_GPIOLIB_IRQCHIP */
 
+#ifdef CONFIG_PINCTRL
+
+/**
+ * struct gpio_pin_range - pin range controlled by a gpio chip
+ * @head: list for maintaining set of pin ranges, used internally
+ * @pctldev: pinctrl device which handles corresponding pins
+ * @range: actual range of pins controlled by a gpio controller
+ */
+
+struct gpio_pin_range {
+       struct list_head node;
+       struct pinctrl_dev *pctldev;
+       struct pinctrl_gpio_range range;
+};
+
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+                          unsigned int gpio_offset, unsigned int pin_offset,
+                          unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+                       struct pinctrl_dev *pctldev,
+                       unsigned int gpio_offset, const char *pin_group);
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+
+#else
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+                      unsigned int gpio_offset, unsigned int pin_offset,
+                      unsigned int npins)
+{
+       return 0;
+}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+                       struct pinctrl_dev *pctldev,
+                       unsigned int gpio_offset, const char *pin_group)
+{
+       return 0;
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_PINCTRL */
+
 struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
                                            const char *label);
 void gpiochip_free_own_desc(struct gpio_desc *desc);
index 3ec06300d5355a02c33b99ea02e0bdf2c1200ddf..5dd60c2e120f944aa0d35fe7aef607fa605d70f5 100644 (file)
@@ -135,9 +135,9 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info,
  * @device: Driver model representation of the device
  * @tx_cfg: HSI TX configuration
  * @rx_cfg: HSI RX configuration
- * e_handler: Callback for handling port events (RX Wake High/Low)
- * pclaimed: Keeps tracks if the clients claimed its associated HSI port
- * nb: Notifier block for port events
+ * @e_handler: Callback for handling port events (RX Wake High/Low)
+ * @pclaimed: Keeps tracks if the clients claimed its associated HSI port
+ * @nb: Notifier block for port events
  */
 struct hsi_client {
        struct device           device;
index c2c561dc011440ee83f395aba0a226ab467204a0..f822c3c113777113958418a4cb4fdca4151ad21f 100644 (file)
@@ -61,6 +61,7 @@ struct mnt_namespace;
 #define MNT_DOOMED             0x1000000
 #define MNT_SYNC_UMOUNT                0x2000000
 #define MNT_MARKED             0x4000000
+#define MNT_UMOUNT             0x8000000
 
 struct vfsmount {
        struct dentry *mnt_root;        /* root of the mounted tree */
@@ -92,6 +93,6 @@ extern struct vfsmount *vfs_kern_mount(struct file_system_type *type,
 extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list);
 extern void mark_mounts_for_expiry(struct list_head *mounts);
 
-extern dev_t name_to_dev_t(char *name);
+extern dev_t name_to_dev_t(const char *name);
 
 #endif /* _LINUX_MOUNT_H */
index 9bfcc18ceab3bf70e884ec8fc90a44264a3f83f3..5f124f685e0752952aafe2758a471c077410452e 100644 (file)
@@ -622,6 +622,38 @@ static inline const char *of_prop_next_string(struct property *prop,
        return NULL;
 }
 
+static inline int of_node_check_flag(struct device_node *n, unsigned long flag)
+{
+       return 0;
+}
+
+static inline int of_node_test_and_set_flag(struct device_node *n,
+                                           unsigned long flag)
+{
+       return 0;
+}
+
+static inline void of_node_set_flag(struct device_node *n, unsigned long flag)
+{
+}
+
+static inline void of_node_clear_flag(struct device_node *n, unsigned long flag)
+{
+}
+
+static inline int of_property_check_flag(struct property *p, unsigned long flag)
+{
+       return 0;
+}
+
+static inline void of_property_set_flag(struct property *p, unsigned long flag)
+{
+}
+
+static inline void of_property_clear_flag(struct property *p, unsigned long flag)
+{
+}
+
 #define of_match_ptr(_ptr)     NULL
 #define of_match_node(_matches, _node) NULL
 #endif /* CONFIG_OF */
index befef42e015bbe8b51e1fd7b3fcf49ae76d90afa..7bc92e0506087d0b97995a9aa74cc56060c12fa8 100644 (file)
@@ -14,6 +14,8 @@
 #ifndef __LINUX_OF_GRAPH_H
 #define __LINUX_OF_GRAPH_H
 
+#include <linux/types.h>
+
 /**
  * struct of_endpoint - the OF graph endpoint data structure
  * @port: identifier (value of reg property) of a port this endpoint belongs to
@@ -26,9 +28,21 @@ struct of_endpoint {
        const struct device_node *local_node;
 };
 
+/**
+ * for_each_endpoint_of_node - iterate over every endpoint in a device node
+ * @parent: parent device node containing ports and endpoints
+ * @child: loop variable pointing to the current endpoint node
+ *
+ * When breaking out of the loop, of_node_put(child) has to be called manually.
+ */
+#define for_each_endpoint_of_node(parent, child) \
+       for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
+            child = of_graph_get_next_endpoint(parent, child))
+
 #ifdef CONFIG_OF
 int of_graph_parse_endpoint(const struct device_node *node,
                                struct of_endpoint *endpoint);
+struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
 struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
                                        struct device_node *previous);
 struct device_node *of_graph_get_remote_port_parent(
@@ -42,6 +56,12 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
        return -ENOSYS;
 }
 
+static inline struct device_node *of_graph_get_port_by_id(
+                                       struct device_node *node, u32 id)
+{
+       return NULL;
+}
+
 static inline struct device_node *of_graph_get_next_endpoint(
                                        const struct device_node *parent,
                                        struct device_node *previous)
index 36f4536b6149054bd547223a44fe81c073baad91..e202dec22e1dd14aaae4a76d7d807d4d7f9a3e7a 100644 (file)
@@ -44,7 +44,11 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
                { NODE,         "NODE" },                               \
                { DATA,         "DATA" },                               \
                { META,         "META" },                               \
-               { META_FLUSH,   "META_FLUSH" })
+               { META_FLUSH,   "META_FLUSH" },                         \
+               { INMEM,        "INMEM" },                              \
+               { INMEM_DROP,   "INMEM_DROP" },                         \
+               { IPU,          "IN-PLACE" },                           \
+               { OPU,          "OUT-OF-PLACE" })
 
 #define F2FS_BIO_MASK(t)       (t & (READA | WRITE_FLUSH_FUA))
 #define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO))
@@ -104,6 +108,7 @@ TRACE_DEFINE_ENUM(CP_DISCARD);
                { CP_UMOUNT,    "Umount" },                             \
                { CP_FASTBOOT,  "Fastboot" },                           \
                { CP_SYNC,      "Sync" },                               \
+               { CP_RECOVERY,  "Recovery" },                           \
                { CP_DISCARD,   "Discard" })
 
 struct victim_sel_policy;
@@ -884,6 +889,13 @@ DEFINE_EVENT(f2fs__page, f2fs_writepage,
        TP_ARGS(page, type)
 );
 
+DEFINE_EVENT(f2fs__page, f2fs_do_write_data_page,
+
+       TP_PROTO(struct page *page, int type),
+
+       TP_ARGS(page, type)
+);
+
 DEFINE_EVENT(f2fs__page, f2fs_readpage,
 
        TP_PROTO(struct page *page, int type),
@@ -905,6 +917,20 @@ DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite,
        TP_ARGS(page, type)
 );
 
+DEFINE_EVENT(f2fs__page, f2fs_register_inmem_page,
+
+       TP_PROTO(struct page *page, int type),
+
+       TP_ARGS(page, type)
+);
+
+DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page,
+
+       TP_PROTO(struct page *page, int type),
+
+       TP_ARGS(page, type)
+);
+
 TRACE_EVENT(f2fs_writepages,
 
        TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type),
@@ -1041,6 +1067,140 @@ TRACE_EVENT(f2fs_issue_flush,
                __entry->nobarrier ? "skip (nobarrier)" : "issue",
                __entry->flush_merge ? " with flush_merge" : "")
 );
+
+TRACE_EVENT(f2fs_lookup_extent_tree_start,
+
+       TP_PROTO(struct inode *inode, unsigned int pgofs),
+
+       TP_ARGS(inode, pgofs),
+
+       TP_STRUCT__entry(
+               __field(dev_t,  dev)
+               __field(ino_t,  ino)
+               __field(unsigned int, pgofs)
+       ),
+
+       TP_fast_assign(
+               __entry->dev = inode->i_sb->s_dev;
+               __entry->ino = inode->i_ino;
+               __entry->pgofs = pgofs;
+       ),
+
+       TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u",
+               show_dev_ino(__entry),
+               __entry->pgofs)
+);
+
+TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end,
+
+       TP_PROTO(struct inode *inode, unsigned int pgofs,
+                                               struct extent_node *en),
+
+       TP_ARGS(inode, pgofs, en),
+
+       TP_CONDITION(en),
+
+       TP_STRUCT__entry(
+               __field(dev_t,  dev)
+               __field(ino_t,  ino)
+               __field(unsigned int, pgofs)
+               __field(unsigned int, fofs)
+               __field(u32, blk)
+               __field(unsigned int, len)
+       ),
+
+       TP_fast_assign(
+               __entry->dev = inode->i_sb->s_dev;
+               __entry->ino = inode->i_ino;
+               __entry->pgofs = pgofs;
+               __entry->fofs = en->ei.fofs;
+               __entry->blk = en->ei.blk;
+               __entry->len = en->ei.len;
+       ),
+
+       TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, "
+               "ext_info(fofs: %u, blk: %u, len: %u)",
+               show_dev_ino(__entry),
+               __entry->pgofs,
+               __entry->fofs,
+               __entry->blk,
+               __entry->len)
+);
+
+TRACE_EVENT(f2fs_update_extent_tree,
+
+       TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr),
+
+       TP_ARGS(inode, pgofs, blkaddr),
+
+       TP_STRUCT__entry(
+               __field(dev_t,  dev)
+               __field(ino_t,  ino)
+               __field(unsigned int, pgofs)
+               __field(u32, blk)
+       ),
+
+       TP_fast_assign(
+               __entry->dev = inode->i_sb->s_dev;
+               __entry->ino = inode->i_ino;
+               __entry->pgofs = pgofs;
+               __entry->blk = blkaddr;
+       ),
+
+       TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, blkaddr = %u",
+               show_dev_ino(__entry),
+               __entry->pgofs,
+               __entry->blk)
+);
+
+TRACE_EVENT(f2fs_shrink_extent_tree,
+
+       TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt,
+                                               unsigned int tree_cnt),
+
+       TP_ARGS(sbi, node_cnt, tree_cnt),
+
+       TP_STRUCT__entry(
+               __field(dev_t,  dev)
+               __field(unsigned int, node_cnt)
+               __field(unsigned int, tree_cnt)
+       ),
+
+       TP_fast_assign(
+               __entry->dev = sbi->sb->s_dev;
+               __entry->node_cnt = node_cnt;
+               __entry->tree_cnt = tree_cnt;
+       ),
+
+       TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u",
+               show_dev(__entry),
+               __entry->node_cnt,
+               __entry->tree_cnt)
+);
+
+TRACE_EVENT(f2fs_destroy_extent_tree,
+
+       TP_PROTO(struct inode *inode, unsigned int node_cnt),
+
+       TP_ARGS(inode, node_cnt),
+
+       TP_STRUCT__entry(
+               __field(dev_t,  dev)
+               __field(ino_t,  ino)
+               __field(unsigned int, node_cnt)
+       ),
+
+       TP_fast_assign(
+               __entry->dev = inode->i_sb->s_dev;
+               __entry->ino = inode->i_ino;
+               __entry->node_cnt = node_cnt;
+       ),
+
+       TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u",
+               show_dev_ino(__entry),
+               __entry->node_cnt)
+);
+
 #endif /* _TRACE_F2FS_H */
 
  /* This part must be outside protection */
index 0421f49a20f7a2af50ca0ab7e7280455d1871367..42febb6bc1d56a00256a62cbb9149c3b5e3c92a7 100644 (file)
@@ -18,14 +18,14 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,
        TP_ARGS(page),
 
        TP_STRUCT__entry(
-               __field(struct page *, page)
+               __field(unsigned long, pfn)
                __field(unsigned long, i_ino)
                __field(unsigned long, index)
                __field(dev_t, s_dev)
        ),
 
        TP_fast_assign(
-               __entry->page = page;
+               __entry->pfn = page_to_pfn(page);
                __entry->i_ino = page->mapping->host->i_ino;
                __entry->index = page->index;
                if (page->mapping->host->i_sb)
@@ -37,8 +37,8 @@ DECLARE_EVENT_CLASS(mm_filemap_op_page_cache,
        TP_printk("dev %d:%d ino %lx page=%p pfn=%lu ofs=%lu",
                MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
                __entry->i_ino,
-               __entry->page,
-               page_to_pfn(__entry->page),
+               pfn_to_page(__entry->pfn),
+               __entry->pfn,
                __entry->index << PAGE_SHIFT)
 );
 
index 4ad10baecd4dcddbb47e503ccc41b1c309aaba3c..81ea598121173bf782c04f7c6bfae63b5207b75e 100644 (file)
@@ -154,18 +154,18 @@ TRACE_EVENT(mm_page_free,
        TP_ARGS(page, order),
 
        TP_STRUCT__entry(
-               __field(        struct page *,  page            )
+               __field(        unsigned long,  pfn             )
                __field(        unsigned int,   order           )
        ),
 
        TP_fast_assign(
-               __entry->page           = page;
+               __entry->pfn            = page_to_pfn(page);
                __entry->order          = order;
        ),
 
        TP_printk("page=%p pfn=%lu order=%d",
-                       __entry->page,
-                       page_to_pfn(__entry->page),
+                       pfn_to_page(__entry->pfn),
+                       __entry->pfn,
                        __entry->order)
 );
 
@@ -176,18 +176,18 @@ TRACE_EVENT(mm_page_free_batched,
        TP_ARGS(page, cold),
 
        TP_STRUCT__entry(
-               __field(        struct page *,  page            )
+               __field(        unsigned long,  pfn             )
                __field(        int,            cold            )
        ),
 
        TP_fast_assign(
-               __entry->page           = page;
+               __entry->pfn            = page_to_pfn(page);
                __entry->cold           = cold;
        ),
 
        TP_printk("page=%p pfn=%lu order=0 cold=%d",
-                       __entry->page,
-                       page_to_pfn(__entry->page),
+                       pfn_to_page(__entry->pfn),
+                       __entry->pfn,
                        __entry->cold)
 );
 
@@ -199,22 +199,22 @@ TRACE_EVENT(mm_page_alloc,
        TP_ARGS(page, order, gfp_flags, migratetype),
 
        TP_STRUCT__entry(
-               __field(        struct page *,  page            )
+               __field(        unsigned long,  pfn             )
                __field(        unsigned int,   order           )
                __field(        gfp_t,          gfp_flags       )
                __field(        int,            migratetype     )
        ),
 
        TP_fast_assign(
-               __entry->page           = page;
+               __entry->pfn            = page ? page_to_pfn(page) : -1UL;
                __entry->order          = order;
                __entry->gfp_flags      = gfp_flags;
                __entry->migratetype    = migratetype;
        ),
 
        TP_printk("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s",
-               __entry->page,
-               __entry->page ? page_to_pfn(__entry->page) : 0,
+               __entry->pfn != -1UL ? pfn_to_page(__entry->pfn) : NULL,
+               __entry->pfn != -1UL ? __entry->pfn : 0,
                __entry->order,
                __entry->migratetype,
                show_gfp_flags(__entry->gfp_flags))
@@ -227,20 +227,20 @@ DECLARE_EVENT_CLASS(mm_page,
        TP_ARGS(page, order, migratetype),
 
        TP_STRUCT__entry(
-               __field(        struct page *,  page            )
+               __field(        unsigned long,  pfn             )
                __field(        unsigned int,   order           )
                __field(        int,            migratetype     )
        ),
 
        TP_fast_assign(
-               __entry->page           = page;
+               __entry->pfn            = page ? page_to_pfn(page) : -1UL;
                __entry->order          = order;
                __entry->migratetype    = migratetype;
        ),
 
        TP_printk("page=%p pfn=%lu order=%u migratetype=%d percpu_refill=%d",
-               __entry->page,
-               __entry->page ? page_to_pfn(__entry->page) : 0,
+               __entry->pfn != -1UL ? pfn_to_page(__entry->pfn) : NULL,
+               __entry->pfn != -1UL ? __entry->pfn : 0,
                __entry->order,
                __entry->migratetype,
                __entry->order == 0)
@@ -260,7 +260,7 @@ DEFINE_EVENT_PRINT(mm_page, mm_page_pcpu_drain,
        TP_ARGS(page, order, migratetype),
 
        TP_printk("page=%p pfn=%lu order=%d migratetype=%d",
-               __entry->page, page_to_pfn(__entry->page),
+               pfn_to_page(__entry->pfn), __entry->pfn,
                __entry->order, __entry->migratetype)
 );
 
@@ -275,7 +275,7 @@ TRACE_EVENT(mm_page_alloc_extfrag,
                alloc_migratetype, fallback_migratetype),
 
        TP_STRUCT__entry(
-               __field(        struct page *,  page                    )
+               __field(        unsigned long,  pfn                     )
                __field(        int,            alloc_order             )
                __field(        int,            fallback_order          )
                __field(        int,            alloc_migratetype       )
@@ -284,7 +284,7 @@ TRACE_EVENT(mm_page_alloc_extfrag,
        ),
 
        TP_fast_assign(
-               __entry->page                   = page;
+               __entry->pfn                    = page_to_pfn(page);
                __entry->alloc_order            = alloc_order;
                __entry->fallback_order         = fallback_order;
                __entry->alloc_migratetype      = alloc_migratetype;
@@ -294,8 +294,8 @@ TRACE_EVENT(mm_page_alloc_extfrag,
        ),
 
        TP_printk("page=%p pfn=%lu alloc_order=%d fallback_order=%d pageblock_order=%d alloc_migratetype=%d fallback_migratetype=%d fragmenting=%d change_ownership=%d",
-               __entry->page,
-               page_to_pfn(__entry->page),
+               pfn_to_page(__entry->pfn),
+               __entry->pfn,
                __entry->alloc_order,
                __entry->fallback_order,
                pageblock_order,
index 69590b6ffc091116911b29dffbb64871bd9f92b1..f66476b96264cec3cc11f7797bd8fb3acac64de7 100644 (file)
@@ -336,18 +336,18 @@ TRACE_EVENT(mm_vmscan_writepage,
        TP_ARGS(page, reclaim_flags),
 
        TP_STRUCT__entry(
-               __field(struct page *, page)
+               __field(unsigned long, pfn)
                __field(int, reclaim_flags)
        ),
 
        TP_fast_assign(
-               __entry->page = page;
+               __entry->pfn = page_to_pfn(page);
                __entry->reclaim_flags = reclaim_flags;
        ),
 
        TP_printk("page=%p pfn=%lu flags=%s",
-               __entry->page,
-               page_to_pfn(__entry->page),
+               pfn_to_page(__entry->pfn),
+               __entry->pfn,
                show_reclaim_flags(__entry->reclaim_flags))
 );
 
index 889f3a5b7b18267d91a81df449098bfd66da2b68..eac8c3641f39a629b5023782c050f84fffaa3036 100644 (file)
@@ -267,9 +267,9 @@ enum {
 #define DM_DEV_SET_GEOMETRY    _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR       4
-#define DM_VERSION_MINOR       30
+#define DM_VERSION_MINOR       31
 #define DM_VERSION_PATCHLEVEL  0
-#define DM_VERSION_EXTRA       "-ioctl (2014-12-22)"
+#define DM_VERSION_EXTRA       "-ioctl (2015-3-12)"
 
 /* Status bits */
 #define DM_READONLY_FLAG       (1 << 0) /* In/Out */
index 3b9df1aa35db366a7063bd5b0cfa0b88efb5fb00..dc24dec6023292ac6f1bd484ce506074d3e53566 100644 (file)
@@ -1047,12 +1047,6 @@ config MEMCG_KMEM
          the kmem extension can use it to guarantee that no group of processes
          will ever exhaust kernel resources alone.
 
-         WARNING: Current implementation lacks reclaim support. That means
-         allocation attempts will fail when close to the limit even if there
-         are plenty of kmem available for reclaim. That makes this option
-         unusable in real life so DO NOT SELECT IT unless for development
-         purposes.
-
 config CGROUP_HUGETLB
        bool "HugeTLB Resource Controller for Control Groups"
        depends on HUGETLB_PAGE
index eb410083e8e075f9ca1829d0db1bf3cb70d17139..8369ffa5f33db24a12703ce74eb7ac437ada96f9 100644 (file)
@@ -207,7 +207,7 @@ done:
  *     bangs.
  */
 
-dev_t name_to_dev_t(char *name)
+dev_t name_to_dev_t(const char *name)
 {
        char s[32];
        char *p;
@@ -226,8 +226,9 @@ dev_t name_to_dev_t(char *name)
 
        if (strncmp(name, "/dev/", 5) != 0) {
                unsigned maj, min;
+               char dummy;
 
-               if (sscanf(name, "%u:%u", &maj, &min) == 2) {
+               if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2) {
                        res = MKDEV(maj, min);
                        if (maj != MAJOR(res) || min != MINOR(res))
                                goto fail;
@@ -286,6 +287,7 @@ fail:
 done:
        return res;
 }
+EXPORT_SYMBOL_GPL(name_to_dev_t);
 
 static int __init root_dev_setup(char *line)
 {
index ba77ab5f64dd9809f5e24f1b079ac5abbefeb495..a0831e1b99f4aabd6c80ea68cedb6350a7a9affc 100644 (file)
@@ -551,7 +551,21 @@ static void print_lockdep_cache(struct lockdep_map *lock)
 
 static void print_lock(struct held_lock *hlock)
 {
-       print_lock_name(hlock_class(hlock));
+       /*
+        * We can be called locklessly through debug_show_all_locks() so be
+        * extra careful, the hlock might have been released and cleared.
+        */
+       unsigned int class_idx = hlock->class_idx;
+
+       /* Don't re-read hlock->class_idx, can't use READ_ONCE() on bitfields: */
+       barrier();
+
+       if (!class_idx || (class_idx - 1) >= MAX_LOCKDEP_KEYS) {
+               printk("<RELEASED>\n");
+               return;
+       }
+
+       print_lock_name(lock_classes + class_idx - 1);
        printk(", at: ");
        print_ip_sym(hlock->acquire_ip);
 }
index f38a1e6922599e037d3f76622f3d7dfd50c874e6..2aaac2c47683d38efa407c139cbe1b6f4e18e678 100644 (file)
@@ -19,7 +19,7 @@
 
 enum {
        CSD_FLAG_LOCK           = 0x01,
-       CSD_FLAG_WAIT           = 0x02,
+       CSD_FLAG_SYNCHRONOUS    = 0x02,
 };
 
 struct call_function_data {
@@ -107,7 +107,7 @@ void __init call_function_init(void)
  */
 static void csd_lock_wait(struct call_single_data *csd)
 {
-       while (csd->flags & CSD_FLAG_LOCK)
+       while (smp_load_acquire(&csd->flags) & CSD_FLAG_LOCK)
                cpu_relax();
 }
 
@@ -121,19 +121,17 @@ static void csd_lock(struct call_single_data *csd)
         * to ->flags with any subsequent assignments to other
         * fields of the specified call_single_data structure:
         */
-       smp_mb();
+       smp_wmb();
 }
 
 static void csd_unlock(struct call_single_data *csd)
 {
-       WARN_ON((csd->flags & CSD_FLAG_WAIT) && !(csd->flags & CSD_FLAG_LOCK));
+       WARN_ON(!(csd->flags & CSD_FLAG_LOCK));
 
        /*
         * ensure we're all done before releasing data:
         */
-       smp_mb();
-
-       csd->flags &= ~CSD_FLAG_LOCK;
+       smp_store_release(&csd->flags, 0);
 }
 
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_single_data, csd_data);
@@ -144,13 +142,16 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_single_data, csd_data);
  * ->func, ->info, and ->flags set.
  */
 static int generic_exec_single(int cpu, struct call_single_data *csd,
-                              smp_call_func_t func, void *info, int wait)
+                              smp_call_func_t func, void *info)
 {
-       struct call_single_data csd_stack = { .flags = 0 };
-       unsigned long flags;
-
-
        if (cpu == smp_processor_id()) {
+               unsigned long flags;
+
+               /*
+                * We can unlock early even for the synchronous on-stack case,
+                * since we're doing this from the same CPU..
+                */
+               csd_unlock(csd);
                local_irq_save(flags);
                func(info);
                local_irq_restore(flags);
@@ -161,21 +162,9 @@ static int generic_exec_single(int cpu, struct call_single_data *csd,
        if ((unsigned)cpu >= nr_cpu_ids || !cpu_online(cpu))
                return -ENXIO;
 
-
-       if (!csd) {
-               csd = &csd_stack;
-               if (!wait)
-                       csd = this_cpu_ptr(&csd_data);
-       }
-
-       csd_lock(csd);
-
        csd->func = func;
        csd->info = info;
 
-       if (wait)
-               csd->flags |= CSD_FLAG_WAIT;
-
        /*
         * The list addition should be visible before sending the IPI
         * handler locks the list to pull the entry off it because of
@@ -190,9 +179,6 @@ static int generic_exec_single(int cpu, struct call_single_data *csd,
        if (llist_add(&csd->llist, &per_cpu(call_single_queue, cpu)))
                arch_send_call_function_single_ipi(cpu);
 
-       if (wait)
-               csd_lock_wait(csd);
-
        return 0;
 }
 
@@ -250,8 +236,17 @@ static void flush_smp_call_function_queue(bool warn_cpu_offline)
        }
 
        llist_for_each_entry_safe(csd, csd_next, entry, llist) {
-               csd->func(csd->info);
-               csd_unlock(csd);
+               smp_call_func_t func = csd->func;
+               void *info = csd->info;
+
+               /* Do we wait until *after* callback? */
+               if (csd->flags & CSD_FLAG_SYNCHRONOUS) {
+                       func(info);
+                       csd_unlock(csd);
+               } else {
+                       csd_unlock(csd);
+                       func(info);
+               }
        }
 
        /*
@@ -274,6 +269,8 @@ static void flush_smp_call_function_queue(bool warn_cpu_offline)
 int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
                             int wait)
 {
+       struct call_single_data *csd;
+       struct call_single_data csd_stack = { .flags = CSD_FLAG_LOCK | CSD_FLAG_SYNCHRONOUS };
        int this_cpu;
        int err;
 
@@ -292,7 +289,16 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
        WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
                     && !oops_in_progress);
 
-       err = generic_exec_single(cpu, NULL, func, info, wait);
+       csd = &csd_stack;
+       if (!wait) {
+               csd = this_cpu_ptr(&csd_data);
+               csd_lock(csd);
+       }
+
+       err = generic_exec_single(cpu, csd, func, info);
+
+       if (wait)
+               csd_lock_wait(csd);
 
        put_cpu();
 
@@ -321,7 +327,15 @@ int smp_call_function_single_async(int cpu, struct call_single_data *csd)
        int err = 0;
 
        preempt_disable();
-       err = generic_exec_single(cpu, csd, csd->func, csd->info, 0);
+
+       /* We could deadlock if we have to wait here with interrupts disabled! */
+       if (WARN_ON_ONCE(csd->flags & CSD_FLAG_LOCK))
+               csd_lock_wait(csd);
+
+       csd->flags = CSD_FLAG_LOCK;
+       smp_wmb();
+
+       err = generic_exec_single(cpu, csd, csd->func, csd->info);
        preempt_enable();
 
        return err;
@@ -433,6 +447,8 @@ void smp_call_function_many(const struct cpumask *mask,
                struct call_single_data *csd = per_cpu_ptr(cfd->csd, cpu);
 
                csd_lock(csd);
+               if (wait)
+                       csd->flags |= CSD_FLAG_SYNCHRONOUS;
                csd->func = func;
                csd->info = info;
                llist_add(&csd->llist, &per_cpu(call_single_queue, cpu));
index e9d0f0c1a04827f0d1cc0f554f6b4dfaabb2c414..16d28756598789504f9a30a07476256a2146b2e3 100644 (file)
@@ -275,7 +275,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
                        }
                        break;
                case 'R':{
-                               int16_t *nwqid = va_arg(ap, int16_t *);
+                               uint16_t *nwqid = va_arg(ap, uint16_t *);
                                struct p9_qid **wqids =
                                    va_arg(ap, struct p9_qid **);
 
@@ -440,7 +440,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
                                                 stbuf->n_gid, stbuf->n_muid);
                        } break;
                case 'V':{
-                               int32_t count = va_arg(ap, int32_t);
+                               uint32_t count = va_arg(ap, uint32_t);
                                struct iov_iter *from =
                                                va_arg(ap, struct iov_iter *);
                                errcode = p9pdu_writef(pdu, proto_version, "d",
@@ -471,7 +471,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
                        }
                        break;
                case 'R':{
-                               int16_t nwqid = va_arg(ap, int);
+                               uint16_t nwqid = va_arg(ap, int);
                                struct p9_qid *wqids =
                                    va_arg(ap, struct p9_qid *);
 
index 3e3d82d8ff70506c2878d4b55bf44269ed26b5cb..bced8c074c1280c6ff175576cb035bf048f18ddd 100644 (file)
@@ -734,6 +734,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts)
        opts->port = P9_PORT;
        opts->rfd = ~0;
        opts->wfd = ~0;
+       opts->privport = 0;
 
        if (!params)
                return 0;
@@ -1013,7 +1014,6 @@ p9_fd_create(struct p9_client *client, const char *addr, char *args)
 {
        int err;
        struct p9_fd_opts opts;
-       struct p9_trans_fd *p;
 
        parse_opts(args, &opts);
 
@@ -1026,7 +1026,6 @@ p9_fd_create(struct p9_client *client, const char *addr, char *args)
        if (err < 0)
                return err;
 
-       p = (struct p9_trans_fd *) client->trans;
        p9_conn_create(client);
 
        return 0;
index 14ad43b5cf89a323f3de9d2ca03879f4b4c9bf35..3533d2a53ab649e634487edfa63dcc965a3ffa07 100644 (file)
@@ -139,6 +139,7 @@ struct p9_rdma_opts {
        int sq_depth;
        int rq_depth;
        long timeout;
+       int privport;
 };
 
 /*
@@ -146,7 +147,10 @@ struct p9_rdma_opts {
  */
 enum {
        /* Options that take integer arguments */
-       Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout, Opt_err,
+       Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout,
+       /* Options that take no argument */
+       Opt_privport,
+       Opt_err,
 };
 
 static match_table_t tokens = {
@@ -154,6 +158,7 @@ static match_table_t tokens = {
        {Opt_sq_depth, "sq=%u"},
        {Opt_rq_depth, "rq=%u"},
        {Opt_timeout, "timeout=%u"},
+       {Opt_privport, "privport"},
        {Opt_err, NULL},
 };
 
@@ -175,6 +180,7 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts)
        opts->sq_depth = P9_RDMA_SQ_DEPTH;
        opts->rq_depth = P9_RDMA_RQ_DEPTH;
        opts->timeout = P9_RDMA_TIMEOUT;
+       opts->privport = 0;
 
        if (!params)
                return 0;
@@ -193,13 +199,13 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts)
                if (!*p)
                        continue;
                token = match_token(p, tokens, args);
-               if (token == Opt_err)
-                       continue;
-               r = match_int(&args[0], &option);
-               if (r < 0) {
-                       p9_debug(P9_DEBUG_ERROR,
-                                "integer field, but no integer?\n");
-                       continue;
+               if ((token != Opt_err) && (token != Opt_privport)) {
+                       r = match_int(&args[0], &option);
+                       if (r < 0) {
+                               p9_debug(P9_DEBUG_ERROR,
+                                        "integer field, but no integer?\n");
+                               continue;
+                       }
                }
                switch (token) {
                case Opt_port:
@@ -214,6 +220,9 @@ static int parse_opts(char *params, struct p9_rdma_opts *opts)
                case Opt_timeout:
                        opts->timeout = option;
                        break;
+               case Opt_privport:
+                       opts->privport = 1;
+                       break;
                default:
                        continue;
                }
@@ -607,6 +616,23 @@ static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req)
        return 0;
 }
 
+static int p9_rdma_bind_privport(struct p9_trans_rdma *rdma)
+{
+       struct sockaddr_in cl = {
+               .sin_family = AF_INET,
+               .sin_addr.s_addr = htonl(INADDR_ANY),
+       };
+       int port, err = -EINVAL;
+
+       for (port = P9_DEF_MAX_RESVPORT; port >= P9_DEF_MIN_RESVPORT; port--) {
+               cl.sin_port = htons((ushort)port);
+               err = rdma_bind_addr(rdma->cm_id, (struct sockaddr *)&cl);
+               if (err != -EADDRINUSE)
+                       break;
+       }
+       return err;
+}
+
 /**
  * trans_create_rdma - Transport method for creating atransport instance
  * @client: client instance
@@ -642,6 +668,16 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args)
        /* Associate the client with the transport */
        client->trans = rdma;
 
+       /* Bind to a privileged port if we need to */
+       if (opts.privport) {
+               err = p9_rdma_bind_privport(rdma);
+               if (err < 0) {
+                       pr_err("%s (%d): problem binding to privport: %d\n",
+                              __func__, task_pid_nr(current), -err);
+                       goto error;
+               }
+       }
+
        /* Resolve the server's address */
        rdma->addr.sin_family = AF_INET;
        rdma->addr.sin_addr.s_addr = in_aton(addr);
index e62bcbbabb5e3cd43717f7980fce3e280aba3ded..9dd49ca67dbc22a905999de21b8355475ba40052 100644 (file)
@@ -525,7 +525,10 @@ static ssize_t p9_mount_tag_show(struct device *dev,
        vdev = dev_to_virtio(dev);
        chan = vdev->priv;
 
-       return snprintf(buf, chan->tag_len + 1, "%s", chan->tag);
+       memcpy(buf, chan->tag, chan->tag_len);
+       buf[chan->tag_len] = 0;
+
+       return chan->tag_len + 1;
 }
 
 static DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL);
index a05b9dbf14c991dd1a90de4f23877bc02b78e1b5..9070dfd6b4adcd247c482589c27d5d4519302b7c 100644 (file)
@@ -1313,7 +1313,8 @@ int hidp_connection_add(struct hidp_connadd_req *req,
                        struct socket *ctrl_sock,
                        struct socket *intr_sock)
 {
-       u32 valid_flags = 0;
+       u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG) |
+                         BIT(HIDP_BOOT_PROTOCOL_MODE);
        struct hidp_session *session;
        struct l2cap_conn *conn;
        struct l2cap_chan *chan;
index 150253cc3c97dd9561610ea780b1b83b871346eb..23219c65c16f77892a30b9b85f38609f1b1dc1f5 100644 (file)
@@ -3,7 +3,7 @@ perf-kmem(1)
 
 NAME
 ----
-perf-kmem - Tool to trace/measure kernel memory(slab) properties
+perf-kmem - Tool to trace/measure kernel memory properties
 
 SYNOPSIS
 --------
@@ -46,6 +46,12 @@ OPTIONS
 --raw-ip::
        Print raw ip instead of symbol
 
+--slab::
+       Analyze SLAB allocator events.
+
+--page::
+       Analyze page allocator events
+
 SEE ALSO
 --------
 linkperf:perf-record[1]
index 4ebf65c7943443291db80f3f3cd36aa9f17e85e6..63ea01349b6e2b6c6bf4fb16ce3722c93d625695 100644 (file)
 #include <linux/string.h>
 #include <locale.h>
 
+static int     kmem_slab;
+static int     kmem_page;
+
+static long    kmem_page_size;
+
 struct alloc_stat;
 typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
 
@@ -226,6 +231,244 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel,
        return 0;
 }
 
+static u64 total_page_alloc_bytes;
+static u64 total_page_free_bytes;
+static u64 total_page_nomatch_bytes;
+static u64 total_page_fail_bytes;
+static unsigned long nr_page_allocs;
+static unsigned long nr_page_frees;
+static unsigned long nr_page_fails;
+static unsigned long nr_page_nomatch;
+
+static bool use_pfn;
+
+#define MAX_MIGRATE_TYPES  6
+#define MAX_PAGE_ORDER     11
+
+static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
+
+struct page_stat {
+       struct rb_node  node;
+       u64             page;
+       int             order;
+       unsigned        gfp_flags;
+       unsigned        migrate_type;
+       u64             alloc_bytes;
+       u64             free_bytes;
+       int             nr_alloc;
+       int             nr_free;
+};
+
+static struct rb_root page_tree;
+static struct rb_root page_alloc_tree;
+static struct rb_root page_alloc_sorted;
+
+static struct page_stat *search_page(unsigned long page, bool create)
+{
+       struct rb_node **node = &page_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct page_stat *data;
+
+       while (*node) {
+               s64 cmp;
+
+               parent = *node;
+               data = rb_entry(*node, struct page_stat, node);
+
+               cmp = data->page - page;
+               if (cmp < 0)
+                       node = &parent->rb_left;
+               else if (cmp > 0)
+                       node = &parent->rb_right;
+               else
+                       return data;
+       }
+
+       if (!create)
+               return NULL;
+
+       data = zalloc(sizeof(*data));
+       if (data != NULL) {
+               data->page = page;
+
+               rb_link_node(&data->node, parent, node);
+               rb_insert_color(&data->node, &page_tree);
+       }
+
+       return data;
+}
+
+static int page_stat_cmp(struct page_stat *a, struct page_stat *b)
+{
+       if (a->page > b->page)
+               return -1;
+       if (a->page < b->page)
+               return 1;
+       if (a->order > b->order)
+               return -1;
+       if (a->order < b->order)
+               return 1;
+       if (a->migrate_type > b->migrate_type)
+               return -1;
+       if (a->migrate_type < b->migrate_type)
+               return 1;
+       if (a->gfp_flags > b->gfp_flags)
+               return -1;
+       if (a->gfp_flags < b->gfp_flags)
+               return 1;
+       return 0;
+}
+
+static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool create)
+{
+       struct rb_node **node = &page_alloc_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct page_stat *data;
+
+       while (*node) {
+               s64 cmp;
+
+               parent = *node;
+               data = rb_entry(*node, struct page_stat, node);
+
+               cmp = page_stat_cmp(data, stat);
+               if (cmp < 0)
+                       node = &parent->rb_left;
+               else if (cmp > 0)
+                       node = &parent->rb_right;
+               else
+                       return data;
+       }
+
+       if (!create)
+               return NULL;
+
+       data = zalloc(sizeof(*data));
+       if (data != NULL) {
+               data->page = stat->page;
+               data->order = stat->order;
+               data->gfp_flags = stat->gfp_flags;
+               data->migrate_type = stat->migrate_type;
+
+               rb_link_node(&data->node, parent, node);
+               rb_insert_color(&data->node, &page_alloc_tree);
+       }
+
+       return data;
+}
+
+static bool valid_page(u64 pfn_or_page)
+{
+       if (use_pfn && pfn_or_page == -1UL)
+               return false;
+       if (!use_pfn && pfn_or_page == 0)
+               return false;
+       return true;
+}
+
+static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
+                                               struct perf_sample *sample)
+{
+       u64 page;
+       unsigned int order = perf_evsel__intval(evsel, sample, "order");
+       unsigned int gfp_flags = perf_evsel__intval(evsel, sample, "gfp_flags");
+       unsigned int migrate_type = perf_evsel__intval(evsel, sample,
+                                                      "migratetype");
+       u64 bytes = kmem_page_size << order;
+       struct page_stat *stat;
+       struct page_stat this = {
+               .order = order,
+               .gfp_flags = gfp_flags,
+               .migrate_type = migrate_type,
+       };
+
+       if (use_pfn)
+               page = perf_evsel__intval(evsel, sample, "pfn");
+       else
+               page = perf_evsel__intval(evsel, sample, "page");
+
+       nr_page_allocs++;
+       total_page_alloc_bytes += bytes;
+
+       if (!valid_page(page)) {
+               nr_page_fails++;
+               total_page_fail_bytes += bytes;
+
+               return 0;
+       }
+
+       /*
+        * This is to find the current page (with correct gfp flags and
+        * migrate type) at free event.
+        */
+       stat = search_page(page, true);
+       if (stat == NULL)
+               return -ENOMEM;
+
+       stat->order = order;
+       stat->gfp_flags = gfp_flags;
+       stat->migrate_type = migrate_type;
+
+       this.page = page;
+       stat = search_page_alloc_stat(&this, true);
+       if (stat == NULL)
+               return -ENOMEM;
+
+       stat->nr_alloc++;
+       stat->alloc_bytes += bytes;
+
+       order_stats[order][migrate_type]++;
+
+       return 0;
+}
+
+static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
+                                               struct perf_sample *sample)
+{
+       u64 page;
+       unsigned int order = perf_evsel__intval(evsel, sample, "order");
+       u64 bytes = kmem_page_size << order;
+       struct page_stat *stat;
+       struct page_stat this = {
+               .order = order,
+       };
+
+       if (use_pfn)
+               page = perf_evsel__intval(evsel, sample, "pfn");
+       else
+               page = perf_evsel__intval(evsel, sample, "page");
+
+       nr_page_frees++;
+       total_page_free_bytes += bytes;
+
+       stat = search_page(page, false);
+       if (stat == NULL) {
+               pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
+                         page, order);
+
+               nr_page_nomatch++;
+               total_page_nomatch_bytes += bytes;
+
+               return 0;
+       }
+
+       this.page = page;
+       this.gfp_flags = stat->gfp_flags;
+       this.migrate_type = stat->migrate_type;
+
+       rb_erase(&stat->node, &page_tree);
+       free(stat);
+
+       stat = search_page_alloc_stat(&this, false);
+       if (stat == NULL)
+               return -ENOENT;
+
+       stat->nr_free++;
+       stat->free_bytes += bytes;
+
+       return 0;
+}
+
 typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
                                  struct perf_sample *sample);
 
@@ -270,8 +513,9 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc)
                return 100.0 - (100.0 * n_req / n_alloc);
 }
 
-static void __print_result(struct rb_root *root, struct perf_session *session,
-                          int n_lines, int is_caller)
+static void __print_slab_result(struct rb_root *root,
+                               struct perf_session *session,
+                               int n_lines, int is_caller)
 {
        struct rb_node *next;
        struct machine *machine = &session->machines.host;
@@ -323,9 +567,56 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
        printf("%.105s\n", graph_dotted_line);
 }
 
-static void print_summary(void)
+static const char * const migrate_type_str[] = {
+       "UNMOVABL",
+       "RECLAIM",
+       "MOVABLE",
+       "RESERVED",
+       "CMA/ISLT",
+       "UNKNOWN",
+};
+
+static void __print_page_result(struct rb_root *root,
+                               struct perf_session *session __maybe_unused,
+                               int n_lines)
+{
+       struct rb_node *next = rb_first(root);
+       const char *format;
+
+       printf("\n%.80s\n", graph_dotted_line);
+       printf(" %-16s | Total alloc (KB) | Hits      | Order | Mig.type | GFP flags\n",
+              use_pfn ? "PFN" : "Page");
+       printf("%.80s\n", graph_dotted_line);
+
+       if (use_pfn)
+               format = " %16llu | %'16llu | %'9d | %5d | %8s |  %08lx\n";
+       else
+               format = " %016llx | %'16llu | %'9d | %5d | %8s |  %08lx\n";
+
+       while (next && n_lines--) {
+               struct page_stat *data;
+
+               data = rb_entry(next, struct page_stat, node);
+
+               printf(format, (unsigned long long)data->page,
+                      (unsigned long long)data->alloc_bytes / 1024,
+                      data->nr_alloc, data->order,
+                      migrate_type_str[data->migrate_type],
+                      (unsigned long)data->gfp_flags);
+
+               next = rb_next(next);
+       }
+
+       if (n_lines == -1)
+               printf(" ...              | ...              | ...       | ...   | ...      | ...     \n");
+
+       printf("%.80s\n", graph_dotted_line);
+}
+
+static void print_slab_summary(void)
 {
-       printf("\nSUMMARY\n=======\n");
+       printf("\nSUMMARY (SLAB allocator)");
+       printf("\n========================\n");
        printf("Total bytes requested: %'lu\n", total_requested);
        printf("Total bytes allocated: %'lu\n", total_allocated);
        printf("Total bytes wasted on internal fragmentation: %'lu\n",
@@ -335,13 +626,73 @@ static void print_summary(void)
        printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
 }
 
-static void print_result(struct perf_session *session)
+static void print_page_summary(void)
+{
+       int o, m;
+       u64 nr_alloc_freed = nr_page_frees - nr_page_nomatch;
+       u64 total_alloc_freed_bytes = total_page_free_bytes - total_page_nomatch_bytes;
+
+       printf("\nSUMMARY (page allocator)");
+       printf("\n========================\n");
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation requests",
+              nr_page_allocs, total_page_alloc_bytes / 1024);
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free requests",
+              nr_page_frees, total_page_free_bytes / 1024);
+       printf("\n");
+
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total alloc+freed requests",
+              nr_alloc_freed, (total_alloc_freed_bytes) / 1024);
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total alloc-only requests",
+              nr_page_allocs - nr_alloc_freed,
+              (total_page_alloc_bytes - total_alloc_freed_bytes) / 1024);
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total free-only requests",
+              nr_page_nomatch, total_page_nomatch_bytes / 1024);
+       printf("\n");
+
+       printf("%-30s: %'16lu   [ %'16"PRIu64" KB ]\n", "Total allocation failures",
+              nr_page_fails, total_page_fail_bytes / 1024);
+       printf("\n");
+
+       printf("%5s  %12s  %12s  %12s  %12s  %12s\n", "Order",  "Unmovable",
+              "Reclaimable", "Movable", "Reserved", "CMA/Isolated");
+       printf("%.5s  %.12s  %.12s  %.12s  %.12s  %.12s\n", graph_dotted_line,
+              graph_dotted_line, graph_dotted_line, graph_dotted_line,
+              graph_dotted_line, graph_dotted_line);
+
+       for (o = 0; o < MAX_PAGE_ORDER; o++) {
+               printf("%5d", o);
+               for (m = 0; m < MAX_MIGRATE_TYPES - 1; m++) {
+                       if (order_stats[o][m])
+                               printf("  %'12d", order_stats[o][m]);
+                       else
+                               printf("  %12c", '.');
+               }
+               printf("\n");
+       }
+}
+
+static void print_slab_result(struct perf_session *session)
 {
        if (caller_flag)
-               __print_result(&root_caller_sorted, session, caller_lines, 1);
+               __print_slab_result(&root_caller_sorted, session, caller_lines, 1);
+       if (alloc_flag)
+               __print_slab_result(&root_alloc_sorted, session, alloc_lines, 0);
+       print_slab_summary();
+}
+
+static void print_page_result(struct perf_session *session)
+{
        if (alloc_flag)
-               __print_result(&root_alloc_sorted, session, alloc_lines, 0);
-       print_summary();
+               __print_page_result(&page_alloc_sorted, session, alloc_lines);
+       print_page_summary();
+}
+
+static void print_result(struct perf_session *session)
+{
+       if (kmem_slab)
+               print_slab_result(session);
+       if (kmem_page)
+               print_page_result(session);
 }
 
 struct sort_dimension {
@@ -353,8 +704,8 @@ struct sort_dimension {
 static LIST_HEAD(caller_sort);
 static LIST_HEAD(alloc_sort);
 
-static void sort_insert(struct rb_root *root, struct alloc_stat *data,
-                       struct list_head *sort_list)
+static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
+                            struct list_head *sort_list)
 {
        struct rb_node **new = &(root->rb_node);
        struct rb_node *parent = NULL;
@@ -383,8 +734,8 @@ static void sort_insert(struct rb_root *root, struct alloc_stat *data,
        rb_insert_color(&data->node, root);
 }
 
-static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
-                         struct list_head *sort_list)
+static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted,
+                              struct list_head *sort_list)
 {
        struct rb_node *node;
        struct alloc_stat *data;
@@ -396,26 +747,79 @@ static void __sort_result(struct rb_root *root, struct rb_root *root_sorted,
 
                rb_erase(node, root);
                data = rb_entry(node, struct alloc_stat, node);
-               sort_insert(root_sorted, data, sort_list);
+               sort_slab_insert(root_sorted, data, sort_list);
+       }
+}
+
+static void sort_page_insert(struct rb_root *root, struct page_stat *data)
+{
+       struct rb_node **new = &root->rb_node;
+       struct rb_node *parent = NULL;
+
+       while (*new) {
+               struct page_stat *this;
+               int cmp = 0;
+
+               this = rb_entry(*new, struct page_stat, node);
+               parent = *new;
+
+               /* TODO: support more sort key */
+               cmp = data->alloc_bytes - this->alloc_bytes;
+
+               if (cmp > 0)
+                       new = &parent->rb_left;
+               else
+                       new = &parent->rb_right;
+       }
+
+       rb_link_node(&data->node, parent, new);
+       rb_insert_color(&data->node, root);
+}
+
+static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted)
+{
+       struct rb_node *node;
+       struct page_stat *data;
+
+       for (;;) {
+               node = rb_first(root);
+               if (!node)
+                       break;
+
+               rb_erase(node, root);
+               data = rb_entry(node, struct page_stat, node);
+               sort_page_insert(root_sorted, data);
        }
 }
 
 static void sort_result(void)
 {
-       __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort);
-       __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort);
+       if (kmem_slab) {
+               __sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
+                                  &alloc_sort);
+               __sort_slab_result(&root_caller_stat, &root_caller_sorted,
+                                  &caller_sort);
+       }
+       if (kmem_page) {
+               __sort_page_result(&page_alloc_tree, &page_alloc_sorted);
+       }
 }
 
 static int __cmd_kmem(struct perf_session *session)
 {
        int err = -EINVAL;
+       struct perf_evsel *evsel;
        const struct perf_evsel_str_handler kmem_tracepoints[] = {
+               /* slab allocator */
                { "kmem:kmalloc",               perf_evsel__process_alloc_event, },
                { "kmem:kmem_cache_alloc",      perf_evsel__process_alloc_event, },
                { "kmem:kmalloc_node",          perf_evsel__process_alloc_node_event, },
                { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
                { "kmem:kfree",                 perf_evsel__process_free_event, },
                { "kmem:kmem_cache_free",       perf_evsel__process_free_event, },
+               /* page allocator */
+               { "kmem:mm_page_alloc",         perf_evsel__process_page_alloc_event, },
+               { "kmem:mm_page_free",          perf_evsel__process_page_free_event, },
        };
 
        if (!perf_session__has_traces(session, "kmem record"))
@@ -426,10 +830,20 @@ static int __cmd_kmem(struct perf_session *session)
                goto out;
        }
 
+       evlist__for_each(session->evlist, evsel) {
+               if (!strcmp(perf_evsel__name(evsel), "kmem:mm_page_alloc") &&
+                   perf_evsel__field(evsel, "pfn")) {
+                       use_pfn = true;
+                       break;
+               }
+       }
+
        setup_pager();
        err = perf_session__process_events(session);
-       if (err != 0)
+       if (err != 0) {
+               pr_err("error during process events: %d\n", err);
                goto out;
+       }
        sort_result();
        print_result(session);
 out:
@@ -612,6 +1026,22 @@ static int parse_alloc_opt(const struct option *opt __maybe_unused,
        return 0;
 }
 
+static int parse_slab_opt(const struct option *opt __maybe_unused,
+                         const char *arg __maybe_unused,
+                         int unset __maybe_unused)
+{
+       kmem_slab = (kmem_page + 1);
+       return 0;
+}
+
+static int parse_page_opt(const struct option *opt __maybe_unused,
+                         const char *arg __maybe_unused,
+                         int unset __maybe_unused)
+{
+       kmem_page = (kmem_slab + 1);
+       return 0;
+}
+
 static int parse_line_opt(const struct option *opt __maybe_unused,
                          const char *arg, int unset __maybe_unused)
 {
@@ -634,6 +1064,8 @@ static int __cmd_record(int argc, const char **argv)
 {
        const char * const record_args[] = {
        "record", "-a", "-R", "-c", "1",
+       };
+       const char * const slab_events[] = {
        "-e", "kmem:kmalloc",
        "-e", "kmem:kmalloc_node",
        "-e", "kmem:kfree",
@@ -641,10 +1073,19 @@ static int __cmd_record(int argc, const char **argv)
        "-e", "kmem:kmem_cache_alloc_node",
        "-e", "kmem:kmem_cache_free",
        };
+       const char * const page_events[] = {
+       "-e", "kmem:mm_page_alloc",
+       "-e", "kmem:mm_page_free",
+       };
        unsigned int rec_argc, i, j;
        const char **rec_argv;
 
        rec_argc = ARRAY_SIZE(record_args) + argc - 1;
+       if (kmem_slab)
+               rec_argc += ARRAY_SIZE(slab_events);
+       if (kmem_page)
+               rec_argc += ARRAY_SIZE(page_events);
+
        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
        if (rec_argv == NULL)
@@ -653,6 +1094,15 @@ static int __cmd_record(int argc, const char **argv)
        for (i = 0; i < ARRAY_SIZE(record_args); i++)
                rec_argv[i] = strdup(record_args[i]);
 
+       if (kmem_slab) {
+               for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
+                       rec_argv[i] = strdup(slab_events[j]);
+       }
+       if (kmem_page) {
+               for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
+                       rec_argv[i] = strdup(page_events[j]);
+       }
+
        for (j = 1; j < (unsigned int)argc; j++, i++)
                rec_argv[i] = argv[j];
 
@@ -679,6 +1129,10 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
        OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
        OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
+       OPT_CALLBACK_NOOPT(0, "slab", NULL, NULL, "Analyze slab allocator",
+                          parse_slab_opt),
+       OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
+                          parse_page_opt),
        OPT_END()
        };
        const char *const kmem_subcommands[] = { "record", "stat", NULL };
@@ -695,6 +1149,9 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
        if (!argc)
                usage_with_options(kmem_usage, kmem_options);
 
+       if (kmem_slab == 0 && kmem_page == 0)
+               kmem_slab = 1;  /* for backward compatibility */
+
        if (!strncmp(argv[0], "rec", 3)) {
                symbol__init(NULL);
                return __cmd_record(argc, argv);
@@ -706,6 +1163,17 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
        if (session == NULL)
                return -1;
 
+       if (kmem_page) {
+               struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+
+               if (evsel == NULL || evsel->tp_format == NULL) {
+                       pr_err("invalid event found.. aborting\n");
+                       return -1;
+               }
+
+               kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
+       }
+
        symbol__init(&session->header.env);
 
        if (!strcmp(argv[0], "stat")) {
index 30545ce2c712d699a00b13b2bbcc63d4f2fc5ee3..d8bb616ff57c29b38c05ac134d35e88f646f42cd 100644 (file)
@@ -332,6 +332,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
        else {
                result->offset += pp->offset;
                result->line += pp->line;
+               result->retprobe = pp->retprobe;
                ret = 0;
        }
 
@@ -654,65 +655,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
        return ntevs;
 }
 
-/*
- * Find a src file from a DWARF tag path. Prepend optional source path prefix
- * and chop off leading directories that do not exist. Result is passed back as
- * a newly allocated path on success.
- * Return 0 if file was found and readable, -errno otherwise.
- */
-static int get_real_path(const char *raw_path, const char *comp_dir,
-                        char **new_path)
-{
-       const char *prefix = symbol_conf.source_prefix;
-
-       if (!prefix) {
-               if (raw_path[0] != '/' && comp_dir)
-                       /* If not an absolute path, try to use comp_dir */
-                       prefix = comp_dir;
-               else {
-                       if (access(raw_path, R_OK) == 0) {
-                               *new_path = strdup(raw_path);
-                               return *new_path ? 0 : -ENOMEM;
-                       } else
-                               return -errno;
-               }
-       }
-
-       *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
-       if (!*new_path)
-               return -ENOMEM;
-
-       for (;;) {
-               sprintf(*new_path, "%s/%s", prefix, raw_path);
-
-               if (access(*new_path, R_OK) == 0)
-                       return 0;
-
-               if (!symbol_conf.source_prefix) {
-                       /* In case of searching comp_dir, don't retry */
-                       zfree(new_path);
-                       return -errno;
-               }
-
-               switch (errno) {
-               case ENAMETOOLONG:
-               case ENOENT:
-               case EROFS:
-               case EFAULT:
-                       raw_path = strchr(++raw_path, '/');
-                       if (!raw_path) {
-                               zfree(new_path);
-                               return -ENOENT;
-                       }
-                       continue;
-
-               default:
-                       zfree(new_path);
-                       return -errno;
-               }
-       }
-}
-
 #define LINEBUF_SIZE 256
 #define NR_ADDITIONAL_LINES 2
 
index e3074230f236ef13b077586bdd3678bde6525513..b5bf9d5efeaf2dc32317573de059c1d73367cc7c 100644 (file)
@@ -855,11 +855,22 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
 static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
 {
        int ret = 0;
+       char *fpath;
 
        if (intlist__empty(pf->lcache)) {
+               const char *comp_dir;
+
+               comp_dir = cu_get_comp_dir(&pf->cu_die);
+               ret = get_real_path(pf->fname, comp_dir, &fpath);
+               if (ret < 0) {
+                       pr_warning("Failed to find source file path.\n");
+                       return ret;
+               }
+
                /* Matching lazy line pattern */
-               ret = find_lazy_match_lines(pf->lcache, pf->fname,
+               ret = find_lazy_match_lines(pf->lcache, fpath,
                                            pf->pev->point.lazy_line);
+               free(fpath);
                if (ret <= 0)
                        return ret;
        }
@@ -1055,7 +1066,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
                        if (pp->function)
                                ret = find_probe_point_by_func(pf);
                        else if (pp->lazy_line)
-                               ret = find_probe_point_lazy(NULL, pf);
+                               ret = find_probe_point_lazy(&pf->cu_die, pf);
                        else {
                                pf->lno = pp->line;
                                ret = find_probe_point_by_line(pf);
@@ -1622,3 +1633,61 @@ found:
        return (ret < 0) ? ret : lf.found;
 }
 
+/*
+ * Find a src file from a DWARF tag path. Prepend optional source path prefix
+ * and chop off leading directories that do not exist. Result is passed back as
+ * a newly allocated path on success.
+ * Return 0 if file was found and readable, -errno otherwise.
+ */
+int get_real_path(const char *raw_path, const char *comp_dir,
+                        char **new_path)
+{
+       const char *prefix = symbol_conf.source_prefix;
+
+       if (!prefix) {
+               if (raw_path[0] != '/' && comp_dir)
+                       /* If not an absolute path, try to use comp_dir */
+                       prefix = comp_dir;
+               else {
+                       if (access(raw_path, R_OK) == 0) {
+                               *new_path = strdup(raw_path);
+                               return *new_path ? 0 : -ENOMEM;
+                       } else
+                               return -errno;
+               }
+       }
+
+       *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
+       if (!*new_path)
+               return -ENOMEM;
+
+       for (;;) {
+               sprintf(*new_path, "%s/%s", prefix, raw_path);
+
+               if (access(*new_path, R_OK) == 0)
+                       return 0;
+
+               if (!symbol_conf.source_prefix) {
+                       /* In case of searching comp_dir, don't retry */
+                       zfree(new_path);
+                       return -errno;
+               }
+
+               switch (errno) {
+               case ENAMETOOLONG:
+               case ENOENT:
+               case EROFS:
+               case EFAULT:
+                       raw_path = strchr(++raw_path, '/');
+                       if (!raw_path) {
+                               zfree(new_path);
+                               return -ENOENT;
+                       }
+                       continue;
+
+               default:
+                       zfree(new_path);
+                       return -errno;
+               }
+       }
+}
index 92590b2c7e1ce650080652839e5506bf98d9093c..ebf8c8c814531ff4efaf5a7461c2ef6a7414df69 100644 (file)
@@ -55,6 +55,10 @@ extern int debuginfo__find_available_vars_at(struct debuginfo *dbg,
                                             struct variable_list **vls,
                                             int max_points, bool externs);
 
+/* Find a src file from a DWARF tag path */
+int get_real_path(const char *raw_path, const char *comp_dir,
+                        char **new_path);
+
 struct probe_finder {
        struct perf_probe_event *pev;           /* Target probe event */
 
index f0a7918178ddb890f85847b036b1f2588759a000..ddf63569df5ae166e466901aa1a60e1b194fe508 100644 (file)
@@ -1,6 +1,6 @@
 .PHONY: all all_32 all_64 check_build32 clean run_tests
 
-TARGETS_C_BOTHBITS := sigreturn
+TARGETS_C_BOTHBITS := sigreturn single_step_syscall
 
 BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32)
 BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64)
index 3d3ec65f3e7ceed3308b7eee8e51351e40f047dd..3fc19b3768121ad0e3846297a17470b03d8fa4e7 100644 (file)
@@ -3,9 +3,11 @@
 # This is deliberately minimal.  IMO kselftests should provide a standard
 # script here.
 ./sigreturn_32 || exit 1
+./single_step_syscall_32 || exit 1
 
 if [[ "$uname -p" -eq "x86_64" ]]; then
     ./sigreturn_64 || exit 1
+    ./single_step_syscall_64 || exit 1
 fi
 
 exit 0
diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c
new file mode 100644 (file)
index 0000000..50c2635
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * single_step_syscall.c - single-steps various x86 syscalls
+ * Copyright (c) 2014-2015 Andrew Lutomirski
+ *
+ * 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.
+ *
+ * This is a very simple series of tests that makes system calls with
+ * the TF flag set.  This exercises some nasty kernel code in the
+ * SYSENTER case: SYSENTER does not clear TF, so SYSENTER with TF set
+ * immediately issues #DB from CPL 0.  This requires special handling in
+ * the kernel.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <asm/ldt.h>
+#include <err.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+                      int flags)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = handler;
+       sa.sa_flags = SA_SIGINFO | flags;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+static volatile sig_atomic_t sig_traps;
+
+#ifdef __x86_64__
+# define REG_IP REG_RIP
+# define WIDTH "q"
+#else
+# define REG_IP REG_EIP
+# define WIDTH "l"
+#endif
+
+static unsigned long get_eflags(void)
+{
+       unsigned long eflags;
+       asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags));
+       return eflags;
+}
+
+static void set_eflags(unsigned long eflags)
+{
+       asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH
+                     : : "rm" (eflags) : "flags");
+}
+
+#define X86_EFLAGS_TF (1UL << 8)
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+       ucontext_t *ctx = (ucontext_t*)ctx_void;
+
+       if (get_eflags() & X86_EFLAGS_TF) {
+               set_eflags(get_eflags() & ~X86_EFLAGS_TF);
+               printf("[WARN]\tSIGTRAP handler had TF set\n");
+               _exit(1);
+       }
+
+       sig_traps++;
+
+       if (sig_traps == 10000 || sig_traps == 10001) {
+               printf("[WARN]\tHit %d SIGTRAPs with si_addr 0x%lx, ip 0x%lx\n",
+                      (int)sig_traps,
+                      (unsigned long)info->si_addr,
+                      (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
+       }
+}
+
+static void check_result(void)
+{
+       unsigned long new_eflags = get_eflags();
+       set_eflags(new_eflags & ~X86_EFLAGS_TF);
+
+       if (!sig_traps) {
+               printf("[FAIL]\tNo SIGTRAP\n");
+               exit(1);
+       }
+
+       if (!(new_eflags & X86_EFLAGS_TF)) {
+               printf("[FAIL]\tTF was cleared\n");
+               exit(1);
+       }
+
+       printf("[OK]\tSurvived with TF set and %d traps\n", (int)sig_traps);
+       sig_traps = 0;
+}
+
+int main()
+{
+       int tmp;
+
+       sethandler(SIGTRAP, sigtrap, 0);
+
+       printf("[RUN]\tSet TF and check nop\n");
+       set_eflags(get_eflags() | X86_EFLAGS_TF);
+       asm volatile ("nop");
+       check_result();
+
+#ifdef __x86_64__
+       printf("[RUN]\tSet TF and check syscall-less opportunistic sysret\n");
+       set_eflags(get_eflags() | X86_EFLAGS_TF);
+       extern unsigned char post_nop[];
+       asm volatile ("pushf" WIDTH "\n\t"
+                     "pop" WIDTH " %%r11\n\t"
+                     "nop\n\t"
+                     "post_nop:"
+                     : : "c" (post_nop) : "r11");
+       check_result();
+#endif
+
+       printf("[RUN]\tSet TF and check int80\n");
+       set_eflags(get_eflags() | X86_EFLAGS_TF);
+       asm volatile ("int $0x80" : "=a" (tmp) : "a" (SYS_getpid));
+       check_result();
+
+       /*
+        * This test is particularly interesting if fast syscalls use
+        * SYSENTER: it triggers a nasty design flaw in SYSENTER.
+        * Specifically, SYSENTER does not clear TF, so either SYSENTER
+        * or the next instruction traps at CPL0.  (Of course, Intel
+        * mostly forgot to document exactly what happens here.)  So we
+        * get a CPL0 fault with usergs (on 64-bit kernels) and possibly
+        * no stack.  The only sane way the kernel can possibly handle
+        * it is to clear TF on return from the #DB handler, but this
+        * happens way too early to set TF in the saved pt_regs, so the
+        * kernel has to do something clever to avoid losing track of
+        * the TF bit.
+        *
+        * Needless to say, we've had bugs in this area.
+        */
+       syscall(SYS_getpid);  /* Force symbol binding without TF set. */
+       printf("[RUN]\tSet TF and check a fast syscall\n");
+       set_eflags(get_eflags() | X86_EFLAGS_TF);
+       syscall(SYS_getpid);
+       check_result();
+
+       /* Now make sure that another fast syscall doesn't set TF again. */
+       printf("[RUN]\tFast syscall with TF cleared\n");
+       fflush(stdout);  /* Force a syscall */
+       if (get_eflags() & X86_EFLAGS_TF) {
+               printf("[FAIL]\tTF is now set\n");
+               exit(1);
+       }
+       if (sig_traps) {
+               printf("[FAIL]\tGot SIGTRAP\n");
+               exit(1);
+       }
+       printf("[OK]\tNothing unexpected happened\n");
+
+       return 0;
+}