diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-04 01:22:16 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-04 01:22:16 +0200 |
commit | 76ca7d1cca761bb9712dfcad9a27d70b520874ae (patch) | |
tree | 4af495beaeee26b126e3dc6f0aa644e8e0b9e24e | |
parent | Merge tag 'vfio-v3.15-rc1' of git://github.com/awilliam/linux-vfio (diff) | |
parent | Documentation/SubmittingPatches: remove references to patch-scripts (diff) | |
download | linux-76ca7d1cca761bb9712dfcad9a27d70b520874ae.tar.xz linux-76ca7d1cca761bb9712dfcad9a27d70b520874ae.zip |
Merge branch 'akpm' (incoming from Andrew)
Merge first patch-bomb from Andrew Morton:
- Various misc bits
- kmemleak fixes
- small befs, codafs, cifs, efs, freexxfs, hfsplus, minixfs, reiserfs things
- fanotify
- I appear to have become SuperH maintainer
- ocfs2 updates
- direct-io tweaks
- a bit of the MM queue
- printk updates
- MAINTAINERS maintenance
- some backlight things
- lib/ updates
- checkpatch updates
- the rtc queue
- nilfs2 updates
- Small Documentation/ updates
* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (237 commits)
Documentation/SubmittingPatches: remove references to patch-scripts
Documentation/SubmittingPatches: update some dead URLs
Documentation/filesystems/ntfs.txt: remove changelog reference
Documentation/kmemleak.txt: updates
fs/reiserfs/super.c: add __init to init_inodecache
fs/reiserfs: move prototype declaration to header file
fs/hfsplus/attributes.c: add __init to hfsplus_create_attr_tree_cache()
fs/hfsplus/extents.c: fix concurrent acess of alloc_blocks
fs/hfsplus/extents.c: remove unused variable in hfsplus_get_block
nilfs2: update project's web site in nilfs2.txt
nilfs2: update MAINTAINERS file entries fix
nilfs2: verify metadata sizes read from disk
nilfs2: add FITRIM ioctl support for nilfs2
nilfs2: add nilfs_sufile_trim_fs to trim clean segs
nilfs2: implementation of NILFS_IOCTL_SET_SUINFO ioctl
nilfs2: add nilfs_sufile_set_suinfo to update segment usage
nilfs2: add struct nilfs_suinfo_update and flags
nilfs2: update MAINTAINERS file entries
fs/coda/inode.c: add __init to init_inodecache()
BEFS: logging cleanup
...
276 files changed, 5246 insertions, 3074 deletions
@@ -2564,6 +2564,10 @@ N: Wolfgang Muees E: wolfgang@iksw-muees.de D: Auerswald USB driver +N: Paul Mundt +E: paul.mundt@gmail.com +D: SuperH maintainer + N: Ian A. Murdock E: imurdock@gnu.ai.mit.edu D: Creator of Debian distribution @@ -2707,6 +2711,9 @@ N: Greg Page E: gpage@sovereign.org D: IPX development and support +N: Venkatesh Pallipadi (Venki) +D: x86/HPET + N: David Parsons E: orc@pell.chi.il.us D: improved memory detection code. diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 26b1e31d5a13..2a8e89e13e45 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -14,7 +14,10 @@ Read Documentation/SubmitChecklist for a list of items to check before submitting code. If you are submitting a driver, also read Documentation/SubmittingDrivers. - +Many of these steps describe the default behavior of the git version +control system; if you use git to prepare your patches, you'll find much +of the mechanical work done for you, though you'll still need to prepare +and document a sensible set of patches. -------------------------------------------- SECTION 1 - CREATING AND SENDING YOUR CHANGE @@ -25,7 +28,9 @@ SECTION 1 - CREATING AND SENDING YOUR CHANGE 1) "diff -up" ------------ -Use "diff -up" or "diff -uprN" to create patches. +Use "diff -up" or "diff -uprN" to create patches. git generates patches +in this form by default; if you're using git, you can skip this section +entirely. All changes to the Linux kernel occur in the form of patches, as generated by diff(1). When creating your patch, make sure to create it @@ -66,19 +71,14 @@ Make sure your patch does not include any extra files which do not belong in a patch submission. Make sure to review your patch -after- generated it with diff(1), to ensure accuracy. -If your changes produce a lot of deltas, you may want to look into -splitting them into individual patches which modify things in -logical stages. This will facilitate easier reviewing by other -kernel developers, very important if you want your patch accepted. -There are a number of scripts which can aid in this: - -Quilt: -http://savannah.nongnu.org/projects/quilt +If your changes produce a lot of deltas, you need to split them into +individual patches which modify things in logical stages; see section +#3. This will facilitate easier reviewing by other kernel developers, +very important if you want your patch accepted. -Andrew Morton's patch scripts: -http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz -Instead of these scripts, quilt is the recommended patch management -tool (see above). +If you're using git, "git rebase -i" can help you with this process. If +you're not using git, quilt <http://savannah.nongnu.org/projects/quilt> +is another popular alternative. @@ -106,8 +106,21 @@ I.e., the patch (series) and its description should be self-contained. This benefits both the patch merger(s) and reviewers. Some reviewers probably didn't even receive earlier versions of the patch. +Describe your changes in imperative mood, e.g. "make xyzzy do frotz" +instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy +to do frotz", as if you are giving orders to the codebase to change +its behaviour. + If the patch fixes a logged bug entry, refer to that bug entry by -number and URL. +number and URL. If the patch follows from a mailing list discussion, +give a URL to the mailing list archive; use the https://lkml.kernel.org/ +redirector with a Message-Id, to ensure that the links cannot become +stale. + +However, try to make your explanation understandable without external +resources. In addition to giving a URL to a mailing list archive or +bug, summarize the relevant points of the discussion that led to the +patch as submitted. If you want to refer to a specific commit, don't just refer to the SHA-1 ID of the commit. Please also include the oneline summary of @@ -594,7 +607,8 @@ patch. If you are going to include a diffstat after the "---" marker, please use diffstat options "-p 1 -w 70" so that filenames are listed from the top of the kernel source tree and don't use too much horizontal -space (easily fit in 80 columns, maybe with some indentation). +space (easily fit in 80 columns, maybe with some indentation). (git +generates appropriate diffstats by default.) See more details on the proper patch format in the following references. @@ -725,7 +739,7 @@ SECTION 3 - REFERENCES ---------------------- Andrew Morton, "The perfect patch" (tpp). - <http://userweb.kernel.org/~akpm/stuff/tpp.txt> + <http://www.ozlabs.org/~akpm/stuff/tpp.txt> Jeff Garzik, "Linux kernel patch submission format". <http://linux.yyz.us/patch-format.html> @@ -738,7 +752,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer". <http://www.kroah.com/log/linux/maintainer-05.html> NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people! - <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2> + <https://lkml.org/lkml/2005/7/11/336> Kernel Documentation/CodingStyle: <http://users.sosdg.org/~qiyong/lxr/source/Documentation/CodingStyle> diff --git a/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt b/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt index 7cb9dbf34878..6983aad376c3 100644 --- a/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt +++ b/Documentation/devicetree/bindings/rtc/sunxi-rtc.txt @@ -3,7 +3,7 @@ RTC controller for the Allwinner A10/A20 Required properties: -- compatible : Should be "allwinner,sun4i-rtc" or "allwinner,sun7i-a20-rtc" +- compatible : Should be "allwinner,sun4i-a10-rtc" or "allwinner,sun7i-a20-rtc" - reg: physical base address of the controller and length of memory mapped region. - interrupts: IRQ line for the RTC. @@ -11,7 +11,7 @@ Required properties: Example: rtc: rtc@01c20d00 { - compatible = "allwinner,sun4i-rtc"; + compatible = "allwinner,sun4i-a10-rtc"; reg = <0x01c20d00 0x20>; interrupts = <24>; }; diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt index 06887d46ccf2..41c3d332acc9 100644 --- a/Documentation/filesystems/nilfs2.txt +++ b/Documentation/filesystems/nilfs2.txt @@ -25,9 +25,8 @@ available from the following download page. At least "mkfs.nilfs2", cleaner or garbage collector) are required. Details on the tools are described in the man pages included in the package. -Project web page: http://www.nilfs.org/en/ -Download page: http://www.nilfs.org/en/download.html -Git tree web page: http://www.nilfs.org/git/ +Project web page: http://nilfs.sourceforge.net/ +Download page: http://nilfs.sourceforge.net/en/download.html List info: http://vger.kernel.org/vger-lists.html#linux-nilfs Caveats @@ -111,6 +110,13 @@ Table of NILFS2 specific ioctls nilfs_resize utilities and by nilfs_cleanerd daemon. + NILFS_IOCTL_SET_SUINFO Modify segment usage info of requested + segments. This ioctl is used by + nilfs_cleanerd daemon to skip unnecessary + cleaning operation of segments and reduce + performance penalty or wear of flash device + due to redundant move of in-use blocks. + NILFS_IOCTL_GET_SUSTAT Return segment usage statistics. This ioctl is used in lssu, nilfs_resize utilities and by nilfs_cleanerd daemon. diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index 791af8dac065..61947facfc07 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -455,8 +455,6 @@ not have this problem with odd numbers of sectors. ChangeLog ========= -Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. - 2.1.30: - Fix writev() (it kept writing the first segment over and over again instead of moving onto subsequent segments). diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index fe2b7ae6f962..0f3a1390bf00 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -295,9 +295,9 @@ in the beginning of ->setattr unconditionally. ->clear_inode() and ->delete_inode() are gone; ->evict_inode() should be used instead. It gets called whenever the inode is evicted, whether it has remaining links or not. Caller does *not* evict the pagecache or inode-associated -metadata buffers; getting rid of those is responsibility of method, as it had -been for ->delete_inode(). Caller makes sure async writeback cannot be running -for the inode while (or after) ->evict_inode() is called. +metadata buffers; the method has to use truncate_inode_pages_final() to get rid +of those. Caller makes sure async writeback cannot be running for the inode while +(or after) ->evict_inode() is called. ->drop_inode() returns int now; it's called on final iput() with inode->i_lock held and it returns true if filesystems wants the inode to be diff --git a/Documentation/ja_JP/SubmittingPatches b/Documentation/ja_JP/SubmittingPatches index 97f78dd0c085..5d6ae639bfa0 100644 --- a/Documentation/ja_JP/SubmittingPatches +++ b/Documentation/ja_JP/SubmittingPatches @@ -98,11 +98,6 @@ dontdiff ファイルには Linux カーネルのビルドプロセスの過程� Quilt: http://savannah.nongnu.org/projects/quilt -Andrew Morton's patch scripts: -http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz -このリンクの先のスクリプトの代わりとして、quilt がパッチマネジメント -ツールとして推奨されています(上のリンクを見てください)。 - 2) パッチに対する説明 パッチの中の変更点に対する技術的な詳細について説明してください。 @@ -695,7 +690,7 @@ gcc においては、マクロと同じくらい軽いです。 ---------------------- Andrew Morton, "The perfect patch" (tpp). - <http://userweb.kernel.org/~akpm/stuff/tpp.txt> + <http://www.ozlabs.org/~akpm/stuff/tpp.txt> Jeff Garzik, "Linux kernel patch submission format". <http://linux.yyz.us/patch-format.html> @@ -707,7 +702,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer". <http://www.kroah.com/log/2006/01/11/> NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people! - <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2> + <https://lkml.org/lkml/2005/7/11/336> Kernel Documentation/CodingStyle: <http://users.sosdg.org/~qiyong/lxr/source/Documentation/CodingStyle> diff --git a/Documentation/kmemleak.txt b/Documentation/kmemleak.txt index b6e39739a36d..a7563ec4ea7b 100644 --- a/Documentation/kmemleak.txt +++ b/Documentation/kmemleak.txt @@ -11,9 +11,7 @@ with the difference that the orphan objects are not freed but only reported via /sys/kernel/debug/kmemleak. A similar method is used by the Valgrind tool (memcheck --leak-check) to detect the memory leaks in user-space applications. - -Please check DEBUG_KMEMLEAK dependencies in lib/Kconfig.debug for supported -architectures. +Kmemleak is supported on x86, arm, powerpc, sparc, sh, microblaze, ppc, mips, s390, metag and tile. Usage ----- @@ -53,7 +51,8 @@ Memory scanning parameters can be modified at run-time by writing to the (default 600, 0 to stop the automatic scanning) scan - trigger a memory scan clear - clear list of current memory leak suspects, done by - marking all current reported unreferenced objects grey + marking all current reported unreferenced objects grey, + or free all kmemleak objects if kmemleak has been disabled. dump=<addr> - dump information about the object found at <addr> Kmemleak can also be disabled at boot-time by passing "kmemleak=off" on @@ -68,7 +67,7 @@ Basic Algorithm The memory allocations via kmalloc, vmalloc, kmem_cache_alloc and friends are traced and the pointers, together with additional -information like size and stack trace, are stored in a prio search tree. +information like size and stack trace, are stored in a rbtree. The corresponding freeing function calls are tracked and the pointers removed from the kmemleak data structures. @@ -84,7 +83,7 @@ The scanning algorithm steps: 1. mark all objects as white (remaining white objects will later be considered orphan) 2. scan the memory starting with the data section and stacks, checking - the values against the addresses stored in the prio search tree. If + the values against the addresses stored in the rbtree. If a pointer to a white object is found, the object is added to the gray list 3. scan the gray objects for matching addresses (some white objects @@ -120,6 +119,18 @@ Then as usual to get your report with: # cat /sys/kernel/debug/kmemleak +Freeing kmemleak internal objects +--------------------------------- + +To allow access to previosuly found memory leaks after kmemleak has been +disabled by the user or due to an fatal error, internal kmemleak objects +won't be freed when kmemleak is disabled, and those objects may occupy +a large part of physical memory. + +In this situation, you may reclaim memory with: + + # echo clear > /sys/kernel/debug/kmemleak + Kmemleak API ------------ diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index d614a9b6a280..dd9d0e33b443 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -175,18 +175,39 @@ Setting this to zero disables periodic writeback altogether. drop_caches -Writing to this will cause the kernel to drop clean caches, dentries and -inodes from memory, causing that memory to become free. +Writing to this will cause the kernel to drop clean caches, as well as +reclaimable slab objects like dentries and inodes. Once dropped, their +memory becomes free. To free pagecache: echo 1 > /proc/sys/vm/drop_caches -To free dentries and inodes: +To free reclaimable slab objects (includes dentries and inodes): echo 2 > /proc/sys/vm/drop_caches -To free pagecache, dentries and inodes: +To free slab objects and pagecache: echo 3 > /proc/sys/vm/drop_caches -As this is a non-destructive operation and dirty objects are not freeable, the -user should run `sync' first. +This is a non-destructive operation and will not free any dirty objects. +To increase the number of objects freed by this operation, the user may run +`sync' prior to writing to /proc/sys/vm/drop_caches. This will minimize the +number of dirty objects on the system and create more candidates to be +dropped. + +This file is not a means to control the growth of the various kernel caches +(inodes, dentries, pagecache, etc...) These objects are automatically +reclaimed by the kernel when memory is needed elsewhere on the system. + +Use of this file can cause performance problems. Since it discards cached +objects, it may cost a significant amount of I/O and CPU to recreate the +dropped objects, especially if they were under heavy use. Because of this, +use outside of a testing or debugging environment is not recommended. + +You may see informational messages in your kernel log when this file is +used: + + cat (1234): drop_caches: 3 + +These are informational only. They do not mean that anything is wrong +with your system. To disable them, echo 4 (bit 3) into drop_caches. ============================================================== diff --git a/Documentation/zh_CN/SubmittingPatches b/Documentation/zh_CN/SubmittingPatches index be0bd4725062..1d3a10f8746b 100644 --- a/Documentation/zh_CN/SubmittingPatches +++ b/Documentation/zh_CN/SubmittingPatches @@ -82,10 +82,6 @@ Documentation/SubmittingDrivers 。 Quilt: http://savannah.nongnu.org/projects/quilt -Andrew Morton 的补丁脚本: -http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz -作为这些脚本的替代,quilt 是值得推荐的补丁管理工具(看上面的链接)。 - 2)描述你的改动。 描述你的改动包含的技术细节。 @@ -394,7 +390,7 @@ Static inline 函数相比宏来说,是好得多的选择。Static inline 函� ---------------- Andrew Morton, "The perfect patch" (tpp). - <http://userweb.kernel.org/~akpm/stuff/tpp.txt> + <http://www.ozlabs.org/~akpm/stuff/tpp.txt> Jeff Garzik, "Linux kernel patch submission format". <http://linux.yyz.us/patch-format.html> @@ -406,7 +402,7 @@ Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer". <http://www.kroah.com/log/2006/01/11/> NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people! - <http://marc.theaimsgroup.com/?l=linux-kernel&m=112112749912944&w=2> + <https://lkml.org/lkml/2005/7/11/336> Kernel Documentation/CodingStyle: <http://sosdg.org/~coywolf/lxr/source/Documentation/CodingStyle> diff --git a/MAINTAINERS b/MAINTAINERS index 346744599b4f..e1af73272fd7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1704,6 +1704,8 @@ F: drivers/net/wireless/b43legacy/ BACKLIGHT CLASS/SUBSYSTEM M: Jingoo Han <jg1.han@samsung.com> +M: Bryan Wu <cooloney@gmail.com> +M: Lee Jones <lee.jones@linaro.org> S: Maintained F: drivers/video/backlight/ F: include/linux/backlight.h @@ -2728,6 +2730,31 @@ F: include/linux/device-mapper.h F: include/linux/dm-*.h F: include/uapi/linux/dm-*.h +DIALOG SEMICONDUCTOR DRIVERS +M: Support Opensource <support.opensource@diasemi.com> +W: http://www.dialog-semiconductor.com/products +S: Supported +F: Documentation/hwmon/da90?? +F: drivers/gpio/gpio-da90??.c +F: drivers/hwmon/da90??-hwmon.c +F: drivers/input/misc/da90??_onkey.c +F: drivers/input/touchscreen/da9052_tsi.c +F: drivers/leds/leds-da90??.c +F: drivers/mfd/da903x.c +F: drivers/mfd/da90??-*.c +F: drivers/power/da9052-battery.c +F: drivers/regulator/da903x.c +F: drivers/regulator/da9???-regulator.[ch] +F: drivers/rtc/rtc-da90??.c +F: drivers/video/backlight/da90??_bl.c +F: drivers/watchdog/da90??_wdt.c +F: include/linux/mfd/da903x.h +F: include/linux/mfd/da9052/ +F: include/linux/mfd/da9055/ +F: include/linux/mfd/da9063/ +F: include/sound/da[79]*.h +F: sound/soc/codecs/da[79]*.[ch] + DIGI NEO AND CLASSIC PCI PRODUCTS M: Lidza Louina <lidza.louina@gmail.com> L: driverdev-devel@linuxdriverproject.org @@ -4128,8 +4155,7 @@ F: include/linux/hpet.h F: include/uapi/linux/hpet.h HPET: x86 -M: "Venkatesh Pallipadi (Venki)" <venki@google.com> -S: Maintained +S: Orphan F: arch/x86/kernel/hpet.c F: arch/x86/include/asm/hpet.h @@ -4620,7 +4646,7 @@ F: arch/x86/kernel/tboot.c INTEL WIRELESS WIMAX CONNECTION 2400 M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> M: linux-wimax@intel.com -L: wimax@linuxwimax.org +L: wimax@linuxwimax.org (subscribers-only) S: Supported W: http://linuxwimax.org F: Documentation/wimax/README.i2400m @@ -5734,7 +5760,6 @@ F: fs/imgdafs/ MICROBLAZE ARCHITECTURE M: Michal Simek <monstr@monstr.eu> -L: microblaze-uclinux@itee.uq.edu.au (moderated for non-subscribers) W: http://www.monstr.eu/fdt/ T: git git://git.monstr.eu/linux-2.6-microblaze.git S: Supported @@ -6156,10 +6181,10 @@ F: include/uapi/linux/nfs* F: include/uapi/linux/sunrpc/ NILFS2 FILESYSTEM -M: KONISHI Ryusuke <konishi.ryusuke@lab.ntt.co.jp> +M: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> L: linux-nilfs@vger.kernel.org -W: http://www.nilfs.org/en/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2.git +W: http://nilfs.sourceforge.net/ +T: git git://github.com/konis/nilfs2.git S: Supported F: Documentation/filesystems/nilfs2.txt F: fs/nilfs2/ @@ -8476,12 +8501,10 @@ S: Maintained F: drivers/net/ethernet/dlink/sundance.c SUPERH -M: Paul Mundt <lethal@linux-sh.org> L: linux-sh@vger.kernel.org W: http://www.linux-sh.org Q: http://patchwork.kernel.org/project/linux-sh/list/ -T: git git://github.com/pmundt/linux-sh.git sh-latest -S: Supported +S: Orphan F: Documentation/sh/ F: arch/sh/ F: drivers/sh/ @@ -8765,6 +8788,7 @@ M: Max Filippov <jcmvbkbc@gmail.com> L: linux-xtensa@linux-xtensa.org S: Maintained F: arch/xtensa/ +F: drivers/irqchip/irq-xtensa-* THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER M: Hans Verkuil <hverkuil@xs4all.nl> @@ -9656,7 +9680,7 @@ F: drivers/media/rc/winbond-cir.c WIMAX STACK M: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> M: linux-wimax@intel.com -L: wimax@linuxwimax.org +L: wimax@linuxwimax.org (subscribers-only) S: Supported W: http://linuxwimax.org F: Documentation/wimax/README.wimax diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 7753be0c86d7..9321681cc45a 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -415,7 +415,7 @@ }; rtc: rtc@01c20d00 { - compatible = "allwinner,sun4i-rtc"; + compatible = "allwinner,sun4i-a10-rtc"; reg = <0x01c20d00 0x20>; interrupts = <24>; }; diff --git a/arch/score/Kconfig b/arch/score/Kconfig index c75d06aa27c3..4ac8cae5727c 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -22,27 +22,21 @@ choice config ARCH_SCORE7 bool "SCORE7 processor" select SYS_SUPPORTS_32BIT_KERNEL - select CPU_SCORE7 select GENERIC_HAS_IOMAP config MACH_SPCT6600 bool "SPCT6600 series based machines" select SYS_SUPPORTS_32BIT_KERNEL - select CPU_SCORE7 select GENERIC_HAS_IOMAP config SCORE_SIM bool "Score simulator" select SYS_SUPPORTS_32BIT_KERNEL - select CPU_SCORE7 select GENERIC_HAS_IOMAP endchoice endmenu -config CPU_SCORE7 - bool - config NO_DMA bool default y diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 25c5a932f9fe..669df51a82e3 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -252,7 +252,7 @@ static struct sh_mobile_sdhi_info sdhi_info = { static struct resource sdhi_resources[] = { [0] = { .start = 0xffe50000, - .end = 0xffe501ff, + .end = 0xffe500ff, .flags = IORESOURCE_MEM, }, [1] = { diff --git a/arch/sh/drivers/pci/pcie-sh7786.h b/arch/sh/drivers/pci/pcie-sh7786.h index 1ee054e47eae..4a6ff55f759b 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.h +++ b/arch/sh/drivers/pci/pcie-sh7786.h @@ -145,9 +145,6 @@ /* PCIERMSGIER */ #define SH4A_PCIERMSGIER (0x004040) /* R/W - 0x0000 0000 32 */ -/* PCIEPHYCTLR */ -#define SH4A_PCIEPHYCTLR (0x010000) /* R/W - 0x0000 0000 32 */ - /* PCIEPHYADRR */ #define SH4A_PCIEPHYADRR (0x010004) /* R/W - 0x0000 0000 32 */ #define BITS_ACK (24) // Rev1.171 diff --git a/arch/sh/include/asm/syscalls_32.h b/arch/sh/include/asm/syscalls_32.h index 4f97df87d7d5..4f643aa718e3 100644 --- a/arch/sh/include/asm/syscalls_32.h +++ b/arch/sh/include/asm/syscalls_32.h @@ -9,15 +9,9 @@ struct pt_regs; -asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); -asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); -asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); +asmlinkage int sys_sigreturn(void); +asmlinkage int sys_rt_sigreturn(void); +asmlinkage int sys_sh_pipe(void); asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf, size_t count, long dummy, loff_t pos); asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf, diff --git a/arch/sh/include/asm/traps_32.h b/arch/sh/include/asm/traps_32.h index cfd55ff9dff2..17e129fe459c 100644 --- a/arch/sh/include/asm/traps_32.h +++ b/arch/sh/include/asm/traps_32.h @@ -42,18 +42,10 @@ static inline void trigger_address_error(void) asmlinkage void do_address_error(struct pt_regs *regs, unsigned long writeaccess, unsigned long address); -asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); -asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); -asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); -asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs); +asmlinkage void do_divide_error(unsigned long r4); +asmlinkage void do_reserved_inst(void); +asmlinkage void do_illegal_slot_inst(void); +asmlinkage void do_exception_error(void); #define BUILD_TRAP_HANDLER(name) \ asmlinkage void name##_trap_handler(unsigned long r4, unsigned long r5, \ diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c index e84a43229b9c..5c0e3c335161 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c @@ -132,7 +132,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP103]), CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP102]), CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP220]), - CLKDEV_CON_ID("rspi2", &mstp_clks[MSTP127]), + CLKDEV_DEV_ID("rspi.2", &mstp_clks[MSTP127]), }; int __init arch_clk_init(void) diff --git a/arch/sh/kernel/dumpstack.c b/arch/sh/kernel/dumpstack.c index b959f5592604..8dfe645bcc4b 100644 --- a/arch/sh/kernel/dumpstack.c +++ b/arch/sh/kernel/dumpstack.c @@ -115,7 +115,7 @@ static int print_trace_stack(void *data, char *name) */ static void print_trace_address(void *data, unsigned long addr, int reliable) { - printk(data); + printk("%s", (char *)data); printk_address(addr, reliable); } diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index ca46834294b7..13047a4facd2 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S @@ -193,10 +193,10 @@ syscall_trace_entry: ! Reload R0-R4 from kernel stack, where the ! parent may have modified them using ! ptrace(POKEUSR). (Note that R0-R2 are - ! used by the system call handler directly - ! from the kernel stack anyway, so don't need - ! to be reloaded here.) This allows the parent - ! to rewrite system calls and args on the fly. + ! reloaded from the kernel stack by syscall_call + ! below, so don't need to be reloaded here.) + ! This allows the parent to rewrite system calls + ! and args on the fly. mov.l @(OFF_R4,r15), r4 ! arg0 mov.l @(OFF_R5,r15), r5 mov.l @(OFF_R6,r15), r6 @@ -357,8 +357,15 @@ syscall_call: mov.l 3f, r8 ! Load the address of sys_call_table add r8, r3 mov.l @r3, r8 + mov.l @(OFF_R2,r15), r2 + mov.l @(OFF_R1,r15), r1 + mov.l @(OFF_R0,r15), r0 + mov.l r2, @-r15 + mov.l r1, @-r15 + mov.l r0, @-r15 jsr @r8 ! jump to specific syscall handler nop + add #12, r15 mov.l @(OFF_R0,r15), r12 ! save r0 mov.l r0, @(OFF_R0,r15) ! save the return value ! diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index 6af6e7c5cac8..594cd371aa28 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -148,11 +148,9 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p return err; } -asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage int sys_sigreturn(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct pt_regs *regs = current_pt_regs(); struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15]; sigset_t set; int r0; @@ -180,11 +178,9 @@ badframe: return 0; } -asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage int sys_rt_sigreturn(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct pt_regs *regs = current_pt_regs(); struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15]; sigset_t set; int r0; diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c index 497bab3a0401..b66d1c62eb19 100644 --- a/arch/sh/kernel/sys_sh32.c +++ b/arch/sh/kernel/sys_sh32.c @@ -21,17 +21,14 @@ * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way Unix traditionally does this, though. */ -asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage int sys_sh_pipe(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); int fd[2]; int error; error = do_pipe_flags(fd, 0); if (!error) { - regs->regs[1] = fd[1]; + current_pt_regs()->regs[1] = fd[1]; return fd[0]; } return error; diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 68e99f09171d..ff639342a8be 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -594,9 +594,7 @@ int is_dsp_inst(struct pt_regs *regs) #endif /* CONFIG_SH_DSP */ #ifdef CONFIG_CPU_SH2A -asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage void do_divide_error(unsigned long r4) { siginfo_t info; @@ -613,11 +611,9 @@ asmlinkage void do_divide_error(unsigned long r4, unsigned long r5, } #endif -asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage void do_reserved_inst(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct pt_regs *regs = current_pt_regs(); unsigned long error_code; struct task_struct *tsk = current; @@ -701,11 +697,9 @@ static int emulate_branch(unsigned short inst, struct pt_regs *regs) } #endif -asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage void do_illegal_slot_inst(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); + struct pt_regs *regs = current_pt_regs(); unsigned long inst; struct task_struct *tsk = current; @@ -730,15 +724,12 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5, die_if_no_fixup("illegal slot instruction", regs, inst); } -asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs __regs) +asmlinkage void do_exception_error(void) { - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); long ex; ex = lookup_exception_vector(); - die_if_kernel("exception", regs, ex); + die_if_kernel("exception", current_pt_regs(), ex); } void per_cpu_trap_init(void) diff --git a/arch/sh/math-emu/math.c b/arch/sh/math-emu/math.c index b876780c1e1c..04aa55fa8c75 100644 --- a/arch/sh/math-emu/math.c +++ b/arch/sh/math-emu/math.c @@ -574,24 +574,6 @@ static int ieee_fpe_handler(struct pt_regs *regs) return 0; } -asmlinkage void do_fpu_error(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs regs) -{ - struct task_struct *tsk = current; - siginfo_t info; - - if (ieee_fpe_handler (®s)) - return; - - regs.pc += 2; - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = FPE_FLTINV; - info.si_addr = (void __user *)regs.pc; - force_sig_info(SIGFPE, &info, tsk); -} - /** * fpu_init - Initialize FPU registers * @fpu: Pointer to software emulated FPU registers. diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index db933decc39c..2e565f8e5165 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -573,6 +573,18 @@ config RTC_DRV_DS1305 This driver can also be built as a module. If so, the module will be called rtc-ds1305. +config RTC_DRV_DS1347 + tristate "Dallas/Maxim DS1347" + help + If you say yes here you get support for the + Dallas/Maxim DS1347 chips. + + This driver only supports the RTC feature, and not other chip + features such as alarms. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1347. + config RTC_DRV_DS1390 tristate "Dallas/Maxim DS1390/93/94" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b427bf7dd20d..40a09915c8f6 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o +obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 544be722937c..c2eff6082363 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -584,6 +584,9 @@ enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer) void rtc_update_irq(struct rtc_device *rtc, unsigned long num, unsigned long events) { + if (unlikely(IS_ERR_OR_NULL(rtc))) + return; + pm_stay_awake(rtc->dev.parent); schedule_work(&rtc->irqwork); } diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c index 4af016985890..9f38eda69154 100644 --- a/drivers/rtc/rtc-as3722.c +++ b/drivers/rtc/rtc-as3722.c @@ -242,9 +242,8 @@ static int as3722_rtc_resume(struct device *dev) } #endif -static const struct dev_pm_ops as3722_rtc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(as3722_rtc_suspend, as3722_rtc_resume) -}; +static SIMPLE_DEV_PM_OPS(as3722_rtc_pm_ops, as3722_rtc_suspend, + as3722_rtc_resume); static struct platform_driver as3722_rtc_driver = { .probe = as3722_rtc_probe, diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index 3161ab5263ed..aee3387fb099 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -204,10 +204,8 @@ static int __init at32_rtc_probe(struct platform_device *pdev) rtc = devm_kzalloc(&pdev->dev, sizeof(struct rtc_at32ap700x), GFP_KERNEL); - if (!rtc) { - dev_dbg(&pdev->dev, "out of memory\n"); + if (!rtc) return -ENOMEM; - } regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index cae212f30d65..0963c9309c74 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -837,7 +837,7 @@ static void __exit cmos_do_remove(struct device *dev) cmos->dev = NULL; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int cmos_suspend(struct device *dev) { @@ -935,8 +935,6 @@ static int cmos_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume); - #else static inline int cmos_poweroff(struct device *dev) @@ -946,6 +944,8 @@ static inline int cmos_poweroff(struct device *dev) #endif +static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume); + /*----------------------------------------------------------------*/ /* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus. @@ -1088,11 +1088,9 @@ static struct pnp_driver cmos_pnp_driver = { /* flag ensures resume() gets called, and stops syslog spam */ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, -#ifdef CONFIG_PM_SLEEP .driver = { .pm = &cmos_pm_ops, }, -#endif }; #endif /* CONFIG_PNP */ diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index 73f157519dff..869cae273799 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -43,8 +43,6 @@ struct coh901331_port { struct rtc_device *rtc; struct clk *clk; - u32 phybase; - u32 physize; void __iomem *virtbase; int irq; #ifdef CONFIG_PM_SLEEP @@ -173,19 +171,9 @@ static int __init coh901331_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - rtap->phybase = res->start; - rtap->physize = resource_size(res); - - if (devm_request_mem_region(&pdev->dev, rtap->phybase, rtap->physize, - "rtc-coh901331") == NULL) - return -EBUSY; - - rtap->virtbase = devm_ioremap(&pdev->dev, rtap->phybase, rtap->physize); - if (!rtap->virtbase) - return -ENOMEM; + rtap->virtbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtap->virtbase)) + return PTR_ERR(rtap->virtbase); rtap->irq = platform_get_irq(pdev, 0); if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0, diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 4385ca4503da..a1cbf64242a5 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -26,7 +26,6 @@ struct da9052_rtc { struct rtc_device *rtc; struct da9052 *da9052; - int irq; }; static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable) @@ -240,8 +239,7 @@ static int da9052_rtc_probe(struct platform_device *pdev) rtc->da9052 = dev_get_drvdata(pdev->dev.parent); platform_set_drvdata(pdev, rtc); - rtc->irq = DA9052_IRQ_ALARM; - ret = da9052_request_irq(rtc->da9052, rtc->irq, "ALM", + ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM", da9052_rtc_irq, rtc); if (ret != 0) { rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c index 48cb2ac3bd3e..a825491331c8 100644 --- a/drivers/rtc/rtc-da9055.c +++ b/drivers/rtc/rtc-da9055.c @@ -302,7 +302,9 @@ static int da9055_rtc_probe(struct platform_device *pdev) } alm_irq = platform_get_irq_byname(pdev, "ALM"); - alm_irq = regmap_irq_get_virq(rtc->da9055->irq_data, alm_irq); + if (alm_irq < 0) + return alm_irq; + ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL, da9055_rtc_alm_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index 24677ef8c39a..c0a3b59f65a2 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -119,8 +119,6 @@ static DEFINE_SPINLOCK(davinci_rtc_lock); struct davinci_rtc { struct rtc_device *rtc; void __iomem *base; - resource_size_t pbase; - size_t base_size; int irq; }; @@ -482,14 +480,12 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct davinci_rtc *davinci_rtc; - struct resource *res, *mem; + struct resource *res; int ret = 0; davinci_rtc = devm_kzalloc(&pdev->dev, sizeof(struct davinci_rtc), GFP_KERNEL); - if (!davinci_rtc) { - dev_dbg(dev, "could not allocate memory for private data\n"); + if (!davinci_rtc) return -ENOMEM; - } davinci_rtc->irq = platform_get_irq(pdev, 0); if (davinci_rtc->irq < 0) { @@ -498,28 +494,9 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no mem resource\n"); - return -EINVAL; - } - - davinci_rtc->pbase = res->start; - davinci_rtc->base_size = resource_size(res); - - mem = devm_request_mem_region(dev, davinci_rtc->pbase, - davinci_rtc->base_size, pdev->name); - if (!mem) { - dev_err(dev, "RTC registers at %08x are not free\n", - davinci_rtc->pbase); - return -EBUSY; - } - - davinci_rtc->base = devm_ioremap(dev, davinci_rtc->pbase, - davinci_rtc->base_size); - if (!davinci_rtc->base) { - dev_err(dev, "unable to ioremap MEM resource\n"); - return -ENOMEM; - } + davinci_rtc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(davinci_rtc->base)) + return PTR_ERR(davinci_rtc->base); platform_set_drvdata(pdev, davinci_rtc); diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 2dd586a19b59..129add77065d 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -756,19 +756,17 @@ static int ds1305_probe(struct spi_device *spi) status = devm_request_irq(&spi->dev, spi->irq, ds1305_irq, 0, dev_name(&ds1305->rtc->dev), ds1305); if (status < 0) { - dev_dbg(&spi->dev, "request_irq %d --> %d\n", + dev_err(&spi->dev, "request_irq %d --> %d\n", spi->irq, status); - return status; + } else { + device_set_wakeup_capable(&spi->dev, 1); } - - device_set_wakeup_capable(&spi->dev, 1); } /* export NVRAM */ status = sysfs_create_bin_file(&spi->dev.kobj, &nvram); if (status < 0) { - dev_dbg(&spi->dev, "register nvram --> %d\n", status); - return status; + dev_err(&spi->dev, "register nvram --> %d\n", status); } return 0; diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4e75345a559a..f03d5ba96db1 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -154,6 +154,7 @@ static const struct chip_desc chips[last_ds_type] = { .alarm = 1, }, [mcp7941x] = { + .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, .nvram_size = 0x40, @@ -606,6 +607,178 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { /*----------------------------------------------------------------------*/ +/* + * Alarm support for mcp7941x devices. + */ + +#define MCP7941X_REG_CONTROL 0x07 +# define MCP7941X_BIT_ALM0_EN 0x10 +# define MCP7941X_BIT_ALM1_EN 0x20 +#define MCP7941X_REG_ALARM0_BASE 0x0a +#define MCP7941X_REG_ALARM0_CTRL 0x0d +#define MCP7941X_REG_ALARM1_BASE 0x11 +#define MCP7941X_REG_ALARM1_CTRL 0x14 +# define MCP7941X_BIT_ALMX_IF (1 << 3) +# define MCP7941X_BIT_ALMX_C0 (1 << 4) +# define MCP7941X_BIT_ALMX_C1 (1 << 5) +# define MCP7941X_BIT_ALMX_C2 (1 << 6) +# define MCP7941X_BIT_ALMX_POL (1 << 7) +# define MCP7941X_MSK_ALMX_MATCH (MCP7941X_BIT_ALMX_C0 | \ + MCP7941X_BIT_ALMX_C1 | \ + MCP7941X_BIT_ALMX_C2) + +static void mcp7941x_work(struct work_struct *work) +{ + struct ds1307 *ds1307 = container_of(work, struct ds1307, work); + struct i2c_client *client = ds1307->client; + int reg, ret; + + mutex_lock(&ds1307->rtc->ops_lock); + + /* Check and clear alarm 0 interrupt flag. */ + reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_ALARM0_CTRL); + if (reg < 0) + goto out; + if (!(reg & MCP7941X_BIT_ALMX_IF)) + goto out; + reg &= ~MCP7941X_BIT_ALMX_IF; + ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_ALARM0_CTRL, reg); + if (ret < 0) + goto out; + + /* Disable alarm 0. */ + reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL); + if (reg < 0) + goto out; + reg &= ~MCP7941X_BIT_ALM0_EN; + ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg); + if (ret < 0) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + if (test_bit(HAS_ALARM, &ds1307->flags)) + enable_irq(client->irq); + mutex_unlock(&ds1307->rtc->ops_lock); +} + +static int mcp7941x_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + u8 *regs = ds1307->regs; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read control and alarm 0 registers. */ + ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + if (ret < 0) + return ret; + + t->enabled = !!(regs[0] & MCP7941X_BIT_ALM0_EN); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f); + t->time.tm_min = bcd2bin(ds1307->regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(ds1307->regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(ds1307->regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(ds1307->regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(ds1307->regs[8] & 0x1f) - 1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d polarity=%d irq=%d match=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, + !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_POL), + !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_IF), + (ds1307->regs[6] & MCP7941X_MSK_ALMX_MATCH) >> 4); + + return 0; +} + +static int mcp7941x_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + unsigned char *regs = ds1307->regs; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control and alarm 0 registers. */ + ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + if (ret < 0) + return ret; + + /* Set alarm 0, using 24-hour and day-of-month modes. */ + regs[3] = bin2bcd(t->time.tm_sec); + regs[4] = bin2bcd(t->time.tm_min); + regs[5] = bin2bcd(t->time.tm_hour); + regs[6] = bin2bcd(t->time.tm_wday) + 1; + regs[7] = bin2bcd(t->time.tm_mday); + regs[8] = bin2bcd(t->time.tm_mon) + 1; + + /* Clear the alarm 0 interrupt flag. */ + regs[6] &= ~MCP7941X_BIT_ALMX_IF; + /* Set alarm match: second, minute, hour, day, date, month. */ + regs[6] |= MCP7941X_MSK_ALMX_MATCH; + + if (t->enabled) + regs[0] |= MCP7941X_BIT_ALM0_EN; + else + regs[0] &= ~MCP7941X_BIT_ALM0_EN; + + ret = ds1307->write_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + if (ret < 0) + return ret; + + return 0; +} + +static int mcp7941x_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + int reg; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL); + if (reg < 0) + return reg; + + if (enabled) + reg |= MCP7941X_BIT_ALM0_EN; + else + reg &= ~MCP7941X_BIT_ALM0_EN; + + return i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg); +} + +static const struct rtc_class_ops mcp7941x_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = mcp7941x_read_alarm, + .set_alarm = mcp7941x_set_alarm, + .alarm_irq_enable = mcp7941x_alarm_irq_enable, +}; + +/*----------------------------------------------------------------------*/ + static ssize_t ds1307_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, @@ -678,6 +851,7 @@ static int ds1307_probe(struct i2c_client *client, [ds_1339] = DS1339_BIT_BBSQI, [ds_3231] = DS3231_BIT_BBSQW, }; + const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA) && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) @@ -816,6 +990,13 @@ static int ds1307_probe(struct i2c_client *client, case ds_1388: ds1307->offset = 1; /* Seconds starts at 1 */ break; + case mcp7941x: + rtc_ops = &mcp7941x_rtc_ops; + if (ds1307->client->irq > 0 && chip->alarm) { + INIT_WORK(&ds1307->work, mcp7941x_work); + want_irq = true; + } + break; default: break; } @@ -927,55 +1108,61 @@ read_rtc: bin2bcd(tmp)); } + device_set_wakeup_capable(&client->dev, want_irq); ds1307->rtc = devm_rtc_device_register(&client->dev, client->name, - &ds13xx_rtc_ops, THIS_MODULE); + rtc_ops, THIS_MODULE); if (IS_ERR(ds1307->rtc)) { - err = PTR_ERR(ds1307->rtc); - dev_err(&client->dev, - "unable to register the class device\n"); - goto exit; + return PTR_ERR(ds1307->rtc); } if (want_irq) { err = request_irq(client->irq, ds1307_irq, IRQF_SHARED, ds1307->rtc->name, client); if (err) { - dev_err(&client->dev, - "unable to request IRQ!\n"); - goto exit; - } + client->irq = 0; + dev_err(&client->dev, "unable to request IRQ!\n"); + } else { - device_set_wakeup_capable(&client->dev, 1); - set_bit(HAS_ALARM, &ds1307->flags); - dev_dbg(&client->dev, "got IRQ %d\n", client->irq); + set_bit(HAS_ALARM, &ds1307->flags); + dev_dbg(&client->dev, "got IRQ %d\n", client->irq); + } } if (chip->nvram_size) { + ds1307->nvram = devm_kzalloc(&client->dev, sizeof(struct bin_attribute), GFP_KERNEL); if (!ds1307->nvram) { - err = -ENOMEM; - goto err_irq; + dev_err(&client->dev, "cannot allocate memory for nvram sysfs\n"); + } else { + + ds1307->nvram->attr.name = "nvram"; + ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; + + sysfs_bin_attr_init(ds1307->nvram); + + ds1307->nvram->read = ds1307_nvram_read; + ds1307->nvram->write = ds1307_nvram_write; + ds1307->nvram->size = chip->nvram_size; + ds1307->nvram_offset = chip->nvram_offset; + + err = sysfs_create_bin_file(&client->dev.kobj, + ds1307->nvram); + if (err) { + dev_err(&client->dev, + "unable to create sysfs file: %s\n", + ds1307->nvram->attr.name); + } else { + set_bit(HAS_NVRAM, &ds1307->flags); + dev_info(&client->dev, "%zu bytes nvram\n", + ds1307->nvram->size); + } } - ds1307->nvram->attr.name = "nvram"; - ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; - sysfs_bin_attr_init(ds1307->nvram); - ds1307->nvram->read = ds1307_nvram_read; - ds1307->nvram->write = ds1307_nvram_write; - ds1307->nvram->size = chip->nvram_size; - ds1307->nvram_offset = chip->nvram_offset; - err = sysfs_create_bin_file(&client->dev.kobj, ds1307->nvram); - if (err) - goto err_irq; - set_bit(HAS_NVRAM, &ds1307->flags); - dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size); } return 0; -err_irq: - free_irq(client->irq, client); exit: return err; } diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c new file mode 100644 index 000000000000..c82b4c050326 --- /dev/null +++ b/drivers/rtc/rtc-ds1347.c @@ -0,0 +1,166 @@ +/* rtc-ds1347.c + * + * Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +/* Registers in ds1347 rtc */ + +#define DS1347_SECONDS_REG 0x01 +#define DS1347_MINUTES_REG 0x03 +#define DS1347_HOURS_REG 0x05 +#define DS1347_DATE_REG 0x07 +#define DS1347_MONTH_REG 0x09 +#define DS1347_DAY_REG 0x0B +#define DS1347_YEAR_REG 0x0D +#define DS1347_CONTROL_REG 0x0F +#define DS1347_STATUS_REG 0x17 +#define DS1347_CLOCK_BURST 0x3F + +static int ds1347_read_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + + *data = address | 0x80; + + return spi_write_then_read(spi, data, 1, data, 1); +} + +static int ds1347_write_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = address & 0x7F; + buf[1] = data; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int ds1347_read_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + int err; + unsigned char buf[8]; + + buf[0] = DS1347_CLOCK_BURST | 0x80; + + err = spi_write_then_read(spi, buf, 1, buf, 8); + if (err) + return err; + + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1]); + dt->tm_hour = bcd2bin(buf[2] & 0x3F); + dt->tm_mday = bcd2bin(buf[3]); + dt->tm_mon = bcd2bin(buf[4]) - 1; + dt->tm_wday = bcd2bin(buf[5]) - 1; + dt->tm_year = bcd2bin(buf[6]) + 100; + + return rtc_valid_tm(dt); +} + +static int ds1347_set_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[9]; + + buf[0] = DS1347_CLOCK_BURST & 0x7F; + buf[1] = bin2bcd(dt->tm_sec); + buf[2] = bin2bcd(dt->tm_min); + buf[3] = (bin2bcd(dt->tm_hour) & 0x3F); + buf[4] = bin2bcd(dt->tm_mday); + buf[5] = bin2bcd(dt->tm_mon + 1); + buf[6] = bin2bcd(dt->tm_wday + 1); + + /* year in linux is from 1900 i.e in range of 100 + in rtc it is from 00 to 99 */ + dt->tm_year = dt->tm_year % 100; + + buf[7] = bin2bcd(dt->tm_year); + buf[8] = bin2bcd(0x00); + + /* write the rtc settings */ + return spi_write_then_read(spi, buf, 9, NULL, 0); +} + +static const struct rtc_class_ops ds1347_rtc_ops = { + .read_time = ds1347_read_time, + .set_time = ds1347_set_time, +}; + +static int ds1347_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char data; + int res; + + /* spi setup with ds1347 in mode 3 and bits per word as 8 */ + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + /* RTC Settings */ + res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data); + if (res) + return res; + + /* Disable the write protect of rtc */ + ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data); + data = data & ~(1<<7); + ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data); + + /* Enable the oscillator , disable the oscillator stop flag, + and glitch filter to reduce current consumption */ + ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data); + data = data & 0x1B; + ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data); + + /* display the settings */ + ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data); + dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data); + + ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data); + dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data); + + rtc = devm_rtc_device_register(&spi->dev, "ds1347", + &ds1347_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + return 0; +} + +static struct spi_driver ds1347_driver = { + .driver = { + .name = "ds1347", + .owner = THIS_MODULE, + }, + .probe = ds1347_probe, +}; + +module_spi_driver(ds1347_driver); + +MODULE_DESCRIPTION("DS1347 SPI RTC DRIVER"); +MODULE_AUTHOR("Raghavendra C Ganiga <ravi23ganiga@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index be9d8c0a7e3a..e67bfcb3a1aa 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -132,10 +132,9 @@ static int ds1390_probe(struct spi_device *spi) spi_setup(spi); chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, "unable to allocate device memory\n"); + if (!chip) return -ENOMEM; - } + spi_set_drvdata(spi, chip); res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp); diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index bc7b4fcf603c..b13d1399b81a 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -371,8 +371,7 @@ ds1511_interrupt(int irq, void *dev_id) events |= RTC_UF; else events |= RTC_AF; - if (likely(pdata->rtc)) - rtc_update_irq(pdata->rtc, 1, events); + rtc_update_irq(pdata->rtc, 1, events); } spin_unlock(&pdata->lock); return events ? IRQ_HANDLED : IRQ_NONE; @@ -473,7 +472,6 @@ static struct bin_attribute ds1511_nvram_attr = { static int ds1511_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; struct resource *res; struct rtc_plat_data *pdata; int ret = 0; @@ -512,6 +510,12 @@ static int ds1511_rtc_probe(struct platform_device *pdev) spin_lock_init(&pdata->lock); platform_set_drvdata(pdev, pdata); + + pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &ds1511_rtc_ops, THIS_MODULE); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + /* * if the platform has an interrupt in mind for this device, * then by all means, set it @@ -526,15 +530,12 @@ static int ds1511_rtc_probe(struct platform_device *pdev) } } - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &ds1511_rtc_ops, - THIS_MODULE); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); - pdata->rtc = rtc; - ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr); + if (ret) + dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n", + ds1511_nvram_attr.attr.name); - return ret; + return 0; } static int ds1511_rtc_remove(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index fd31571941f5..ab56893aac73 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -206,8 +206,7 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) events |= RTC_UF; else events |= RTC_AF; - if (likely(pdata->rtc)) - rtc_update_irq(pdata->rtc, 1, events); + rtc_update_irq(pdata->rtc, 1, events); } spin_unlock(&pdata->lock); return events ? IRQ_HANDLED : IRQ_NONE; @@ -278,7 +277,6 @@ static struct bin_attribute ds1553_nvram_attr = { static int ds1553_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc; struct resource *res; unsigned int cen, sec; struct rtc_plat_data *pdata; @@ -311,6 +309,12 @@ static int ds1553_rtc_probe(struct platform_device *pdev) spin_lock_init(&pdata->lock); pdata->last_jiffies = jiffies; platform_set_drvdata(pdev, pdata); + + pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &ds1553_rtc_ops, THIS_MODULE); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + if (pdata->irq > 0) { writeb(0, ioaddr + RTC_INTERRUPTS); if (devm_request_irq(&pdev->dev, pdata->irq, @@ -321,15 +325,12 @@ static int ds1553_rtc_probe(struct platform_device *pdev) } } - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &ds1553_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); - pdata->rtc = rtc; - ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); + if (ret) + dev_err(&pdev->dev, "unable to create sysfs file: %s\n", + ds1553_nvram_attr.attr.name); - return ret; + return 0; } static int ds1553_rtc_remove(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 18e2d8471472..a4888dbca2e1 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -177,8 +177,9 @@ static int ds1672_probe(struct i2c_client *client, /* read control register */ err = ds1672_get_control(client, &control); - if (err) - goto exit_devreg; + if (err) { + dev_warn(&client->dev, "Unable to read the control register\n"); + } if (control & DS1672_REG_CONTROL_EOSC) dev_warn(&client->dev, "Oscillator not enabled. " @@ -187,12 +188,10 @@ static int ds1672_probe(struct i2c_client *client, /* Register sysfs hooks */ err = device_create_file(&client->dev, &dev_attr_control); if (err) - goto exit_devreg; + dev_err(&client->dev, "Unable to create sysfs entry: %s\n", + dev_attr_control.attr.name); return 0; - - exit_devreg: - return err; } static struct i2c_device_id ds1672_id[] = { diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 5a1f3b2a8f1e..942103dac30f 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -204,8 +204,11 @@ static int ds1742_rtc_probe(struct platform_device *pdev) return PTR_ERR(rtc); ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr); + if (ret) + dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n", + pdata->nvram_attr.attr.name); - return ret; + return 0; } static int ds1742_rtc_remove(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index b83bb5a527f8..adaf06c41479 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -57,6 +57,7 @@ struct ds3232 { * in the remove function. */ struct mutex mutex; + bool suspended; int exiting; }; @@ -345,7 +346,15 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id) struct ds3232 *ds3232 = i2c_get_clientdata(client); disable_irq_nosync(irq); - schedule_work(&ds3232->work); + + /* + * If rtc as a wakeup source, can't schedule the work + * at system resume flow, because at this time the i2c bus + * has not been resumed. + */ + if (!ds3232->suspended) + schedule_work(&ds3232->work); + return IRQ_HANDLED; } @@ -363,22 +372,26 @@ static void ds3232_work(struct work_struct *work) if (stat & DS3232_REG_SR_A1F) { control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); - if (control < 0) - goto out; - /* disable alarm1 interrupt */ - control &= ~(DS3232_REG_CR_A1IE); - i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); - - /* clear the alarm pend flag */ - stat &= ~DS3232_REG_SR_A1F; - i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); - - rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + if (control < 0) { + pr_warn("Read DS3232 Control Register error." + "Disable IRQ%d.\n", client->irq); + } else { + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + i2c_smbus_write_byte_data(client, DS3232_REG_CR, + control); + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + + if (!ds3232->exiting) + enable_irq(client->irq); + } } -out: - if (!ds3232->exiting) - enable_irq(client->irq); unlock: mutex_unlock(&ds3232->mutex); } @@ -411,23 +424,17 @@ static int ds3232_probe(struct i2c_client *client, if (ret) return ret; - ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, - &ds3232_rtc_ops, THIS_MODULE); - if (IS_ERR(ds3232->rtc)) { - dev_err(&client->dev, "unable to register the class device\n"); - return PTR_ERR(ds3232->rtc); - } - - if (client->irq >= 0) { - ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, 0, - "ds3232", client); + if (client->irq > 0) { + ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, + IRQF_SHARED, "ds3232", client); if (ret) { dev_err(&client->dev, "unable to request IRQ\n"); - return ret; } + device_init_wakeup(&client->dev, 1); } - - return 0; + ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, + &ds3232_rtc_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(ds3232->rtc); } static int ds3232_remove(struct i2c_client *client) @@ -446,6 +453,42 @@ static int ds3232_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int ds3232_suspend(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (device_can_wakeup(dev)) { + ds3232->suspended = true; + irq_set_irq_wake(client->irq, 1); + } + + return 0; +} + +static int ds3232_resume(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (ds3232->suspended) { + ds3232->suspended = false; + + /* Clear the hardware alarm pend flag */ + schedule_work(&ds3232->work); + + irq_set_irq_wake(client->irq, 0); + } + + return 0; +} +#endif + +static const struct dev_pm_ops ds3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) +}; + static const struct i2c_device_id ds3232_id[] = { { "ds3232", 0 }, { } @@ -456,6 +499,7 @@ static struct i2c_driver ds3232_driver = { .driver = { .name = "rtc-ds3232", .owner = THIS_MODULE, + .pm = &ds3232_pm_ops, }, .probe = ds3232_probe, .remove = ds3232_remove, diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index abd7f9091f34..cd741c77e085 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -401,7 +401,9 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) imxdi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(imxdi->clk)) return PTR_ERR(imxdi->clk); - clk_prepare_enable(imxdi->clk); + rc = clk_prepare_enable(imxdi->clk); + if (rc) + return rc; /* * Initialize dryice hardware diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index 7854a656628f..7e5ead936a04 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -26,7 +26,6 @@ #include <linux/rtc.h> #include <linux/i2c.h> #include <linux/bcd.h> -#include <linux/rtc.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/regmap.h> diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 1b126d2513de..08f5160fb6d4 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -38,7 +38,6 @@ #define JZ_RTC_CTRL_ENABLE BIT(0) struct jz4740_rtc { - struct resource *mem; void __iomem *base; struct rtc_device *rtc; @@ -216,6 +215,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) int ret; struct jz4740_rtc *rtc; uint32_t scratchpad; + struct resource *mem; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -227,25 +227,10 @@ static int jz4740_rtc_probe(struct platform_device *pdev) return -ENOENT; } - rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rtc->mem) { - dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); - return -ENOENT; - } - - rtc->mem = devm_request_mem_region(&pdev->dev, rtc->mem->start, - resource_size(rtc->mem), pdev->name); - if (!rtc->mem) { - dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - return -EBUSY; - } - - rtc->base = devm_ioremap_nocache(&pdev->dev, rtc->mem->start, - resource_size(rtc->mem)); - if (!rtc->base) { - dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); - return -EBUSY; - } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); spin_lock_init(&rtc->lock); diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c index bfdbcb82d069..f130c08c98f8 100644 --- a/drivers/rtc/rtc-lpc32xx.c +++ b/drivers/rtc/rtc-lpc32xx.c @@ -211,10 +211,9 @@ static int lpc32xx_rtc_probe(struct platform_device *pdev) } rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); - if (unlikely(!rtc)) { - dev_err(&pdev->dev, "Can't allocate memory\n"); + if (unlikely(!rtc)) return -ENOMEM; - } + rtc->irq = rtcirq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 77ea9896b5ba..0765606a2d14 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -23,6 +23,8 @@ #define MC13XXX_RTCDAY 22 #define MC13XXX_RTCDAYA 23 +#define SEC_PER_DAY (24 * 60 * 60) + struct mc13xxx_rtc { struct rtc_device *rtc; struct mc13xxx *mc13xxx; @@ -42,15 +44,15 @@ static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev, return func(priv->mc13xxx, irq); } -static int mc13xxx_rtc_irq_enable(struct device *dev, - unsigned int enabled, int irq) +static int mc13xxx_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) { struct mc13xxx_rtc *priv = dev_get_drvdata(dev); int ret; mc13xxx_lock(priv->mc13xxx); - ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, irq); + ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA); mc13xxx_unlock(priv->mc13xxx); @@ -61,44 +63,27 @@ static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct mc13xxx_rtc *priv = dev_get_drvdata(dev); unsigned int seconds, days1, days2; - unsigned long s1970; - int ret; - - mc13xxx_lock(priv->mc13xxx); - - if (!priv->valid) { - ret = -ENODATA; - goto out; - } - ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1); - if (unlikely(ret)) - goto out; - - ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds); - if (unlikely(ret)) - goto out; - - ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2); -out: - mc13xxx_unlock(priv->mc13xxx); + if (!priv->valid) + return -ENODATA; - if (ret) - return ret; + do { + int ret; - if (days2 == days1 + 1) { - if (seconds >= 86400 / 2) - days2 = days1; - else - days1 = days2; - } + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1); + if (ret) + return ret; - if (days1 != days2) - return -EIO; + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds); + if (ret) + return ret; - s1970 = days1 * 86400 + seconds; + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2); + if (ret) + return ret; + } while (days1 != days2); - rtc_time_to_tm(s1970, tm); + rtc_time_to_tm(days1 * SEC_PER_DAY + seconds, tm); return rtc_valid_tm(tm); } @@ -110,8 +95,8 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs) unsigned int alarmseconds; int ret; - seconds = secs % 86400; - days = secs / 86400; + seconds = secs % SEC_PER_DAY; + days = secs / SEC_PER_DAY; mc13xxx_lock(priv->mc13xxx); @@ -123,7 +108,7 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs) if (unlikely(ret)) goto out; - if (alarmseconds < 86400) { + if (alarmseconds < SEC_PER_DAY) { ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff); if (unlikely(ret)) @@ -147,18 +132,21 @@ static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs) goto out; /* restore alarm */ - if (alarmseconds < 86400) { + if (alarmseconds < SEC_PER_DAY) { ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, alarmseconds); if (unlikely(ret)) goto out; } - ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST); - if (unlikely(ret)) - goto out; + if (!priv->valid) { + ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST); + if (unlikely(ret)) + goto out; + + ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST); + } - ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST); out: priv->valid = !ret; @@ -180,7 +168,7 @@ static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds); if (unlikely(ret)) goto out; - if (seconds >= 86400) { + if (seconds >= SEC_PER_DAY) { ret = -ENODATA; goto out; } @@ -201,7 +189,7 @@ out: alarm->enabled = enabled; alarm->pending = pending; - s1970 = days * 86400 + seconds; + s1970 = days * SEC_PER_DAY + seconds; rtc_time_to_tm(s1970, &alarm->time); dev_dbg(dev, "%s: %lu\n", __func__, s1970); @@ -239,8 +227,8 @@ static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) if (unlikely(ret)) goto out; - seconds = s1970 % 86400; - days = s1970 / 86400; + seconds = s1970 % SEC_PER_DAY; + days = s1970 / SEC_PER_DAY; ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days); if (unlikely(ret)) @@ -259,8 +247,6 @@ static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev) struct mc13xxx_rtc *priv = dev; struct mc13xxx *mc13xxx = priv->mc13xxx; - dev_dbg(&priv->rtc->dev, "Alarm\n"); - rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF); mc13xxx_irq_ack(mc13xxx, irq); @@ -273,8 +259,6 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev) struct mc13xxx_rtc *priv = dev; struct mc13xxx *mc13xxx = priv->mc13xxx; - dev_dbg(&priv->rtc->dev, "1HZ\n"); - rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF); mc13xxx_irq_ack(mc13xxx, irq); @@ -282,12 +266,6 @@ static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev) return IRQ_HANDLED; } -static int mc13xxx_rtc_alarm_irq_enable(struct device *dev, - unsigned int enabled) -{ - return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_TODA); -} - static const struct rtc_class_ops mc13xxx_rtc_ops = { .read_time = mc13xxx_rtc_read_time, .set_mmss = mc13xxx_rtc_set_mmss, @@ -301,7 +279,6 @@ static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev) struct mc13xxx_rtc *priv = dev; struct mc13xxx *mc13xxx = priv->mc13xxx; - dev_dbg(&priv->rtc->dev, "RTCRST\n"); priv->valid = 0; mc13xxx_irq_mask(mc13xxx, irq); @@ -314,7 +291,6 @@ static int __init mc13xxx_rtc_probe(struct platform_device *pdev) int ret; struct mc13xxx_rtc *priv; struct mc13xxx *mc13xxx; - int rtcrst_pending; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -322,60 +298,47 @@ static int __init mc13xxx_rtc_probe(struct platform_device *pdev) mc13xxx = dev_get_drvdata(pdev->dev.parent); priv->mc13xxx = mc13xxx; + priv->valid = 1; platform_set_drvdata(pdev, priv); mc13xxx_lock(mc13xxx); + mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST); + ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST, mc13xxx_rtc_reset_handler, DRIVER_NAME, priv); if (ret) - goto err_reset_irq_request; - - ret = mc13xxx_irq_status(mc13xxx, MC13XXX_IRQ_RTCRST, - NULL, &rtcrst_pending); - if (ret) - goto err_reset_irq_status; - - priv->valid = !rtcrst_pending; + goto err_irq_request; - ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_1HZ, + ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_1HZ, mc13xxx_rtc_update_handler, DRIVER_NAME, priv); if (ret) - goto err_update_irq_request; + goto err_irq_request; ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA, mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv); if (ret) - goto err_alarm_irq_request; + goto err_irq_request; mc13xxx_unlock(mc13xxx); priv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &mc13xxx_rtc_ops, THIS_MODULE); - if (IS_ERR(priv->rtc)) { - ret = PTR_ERR(priv->rtc); + &mc13xxx_rtc_ops, THIS_MODULE); - mc13xxx_lock(mc13xxx); - - mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv); -err_alarm_irq_request: - - mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv); -err_update_irq_request: - -err_reset_irq_status: + return 0; - mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv); -err_reset_irq_request: +err_irq_request: + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv); + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv); + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv); - mc13xxx_unlock(mc13xxx); - } + mc13xxx_unlock(mc13xxx); return ret; } -static int __exit mc13xxx_rtc_remove(struct platform_device *pdev) +static int mc13xxx_rtc_remove(struct platform_device *pdev) { struct mc13xxx_rtc *priv = platform_get_drvdata(pdev); @@ -404,7 +367,7 @@ MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable); static struct platform_driver mc13xxx_rtc_driver = { .id_table = mc13xxx_rtc_idtable, - .remove = __exit_p(mc13xxx_rtc_remove), + .remove = mc13xxx_rtc_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/rtc/rtc-moxart.c b/drivers/rtc/rtc-moxart.c index c29dee0946e6..c31846238871 100644 --- a/drivers/rtc/rtc-moxart.c +++ b/drivers/rtc/rtc-moxart.c @@ -247,10 +247,8 @@ static int moxart_rtc_probe(struct platform_device *pdev) int ret = 0; moxart_rtc = devm_kzalloc(&pdev->dev, sizeof(*moxart_rtc), GFP_KERNEL); - if (!moxart_rtc) { - dev_err(&pdev->dev, "devm_kzalloc failed\n"); + if (!moxart_rtc) return -ENOMEM; - } moxart_rtc->gpio_data = of_get_named_gpio(pdev->dev.of_node, "gpio-rtc-data", 0); diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index 248653c74b80..a53da0958e95 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -229,10 +229,9 @@ static int __init nuc900_rtc_probe(struct platform_device *pdev) nuc900_rtc = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_rtc), GFP_KERNEL); - if (!nuc900_rtc) { - dev_err(&pdev->dev, "kzalloc nuc900_rtc failed\n"); + if (!nuc900_rtc) return -ENOMEM; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nuc900_rtc->rtc_reg = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(nuc900_rtc->rtc_reg)) diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c index fffb7d3449d7..c360d62fb3f6 100644 --- a/drivers/rtc/rtc-palmas.c +++ b/drivers/rtc/rtc-palmas.c @@ -348,9 +348,8 @@ static int palmas_rtc_resume(struct device *dev) } #endif -static const struct dev_pm_ops palmas_rtc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume) -}; +static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend, + palmas_rtc_resume); #ifdef CONFIG_OF static struct of_device_id of_palmas_rtc_match[] = { diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 03f8f75d5af2..197699f358c7 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -9,18 +9,16 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - +#include <linux/of.h> #include <linux/module.h> #include <linux/init.h> #include <linux/rtc.h> +#include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/mfd/pm8xxx/core.h> -#include <linux/mfd/pm8xxx/rtc.h> - - /* RTC Register offsets from RTC CTRL REG */ #define PM8XXX_ALARM_CTRL_OFFSET 0x01 #define PM8XXX_RTC_WRITE_OFFSET 0x02 @@ -37,6 +35,8 @@ /** * struct pm8xxx_rtc - rtc driver internal structure * @rtc: rtc device for this driver. + * @regmap: regmap used to access RTC registers + * @allow_set_time: indicates whether writing to the RTC is allowed * @rtc_alarm_irq: rtc alarm irq number. * @rtc_base: address of rtc control register. * @rtc_read_base: base address of read registers. @@ -48,55 +48,19 @@ */ struct pm8xxx_rtc { struct rtc_device *rtc; + struct regmap *regmap; + bool allow_set_time; int rtc_alarm_irq; int rtc_base; int rtc_read_base; int rtc_write_base; int alarm_rw_base; - u8 ctrl_reg; + u8 ctrl_reg; struct device *rtc_dev; spinlock_t ctrl_reg_lock; }; /* - * The RTC registers need to be read/written one byte at a time. This is a - * hardware limitation. - */ -static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, - int base, int count) -{ - int i, rc; - struct device *parent = rtc_dd->rtc_dev->parent; - - for (i = 0; i < count; i++) { - rc = pm8xxx_readb(parent, base + i, &rtc_val[i]); - if (rc < 0) { - dev_err(rtc_dd->rtc_dev, "PMIC read failed\n"); - return rc; - } - } - - return 0; -} - -static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, - int base, int count) -{ - int i, rc; - struct device *parent = rtc_dd->rtc_dev->parent; - - for (i = 0; i < count; i++) { - rc = pm8xxx_writeb(parent, base + i, rtc_val[i]); - if (rc < 0) { - dev_err(rtc_dd->rtc_dev, "PMIC write failed\n"); - return rc; - } - } - - return 0; -} - -/* * Steps to write the RTC registers. * 1. Disable alarm if enabled. * 2. Write 0x00 to LSB. @@ -107,9 +71,12 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) { int rc, i; unsigned long secs, irq_flags; - u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg; + u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, ctrl_reg; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + if (!rtc_dd->allow_set_time) + return -EACCES; + rtc_tm_to_time(tm, &secs); for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) { @@ -125,47 +92,43 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) { alarm_enabled = 1; ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); - if (rc < 0) { - dev_err(dev, "Write to RTC control register " - "failed\n"); + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } rtc_dd->ctrl_reg = ctrl_reg; - } else + } else { spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); + } /* Write 0 to Byte[0] */ - reg = 0; - rc = pm8xxx_write_wrapper(rtc_dd, ®, rtc_dd->rtc_write_base, 1); - if (rc < 0) { + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, 0); + if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); goto rtc_rw_fail; } /* Write Byte[1], Byte[2], Byte[3] */ - rc = pm8xxx_write_wrapper(rtc_dd, value + 1, - rtc_dd->rtc_write_base + 1, 3); - if (rc < 0) { + rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->rtc_write_base + 1, + &value[1], sizeof(value) - 1); + if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); goto rtc_rw_fail; } /* Write Byte[0] */ - rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1); - if (rc < 0) { + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_write_base, value[0]); + if (rc) { dev_err(dev, "Write to RTC write data register failed\n"); goto rtc_rw_fail; } if (alarm_enabled) { ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); - if (rc < 0) { - dev_err(dev, "Write to RTC control register " - "failed\n"); + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } rtc_dd->ctrl_reg = ctrl_reg; @@ -181,13 +144,14 @@ rtc_rw_fail: static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) { int rc; - u8 value[NUM_8_BIT_RTC_REGS], reg; + u8 value[NUM_8_BIT_RTC_REGS]; unsigned long secs; + unsigned int reg; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); - rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base, - NUM_8_BIT_RTC_REGS); - if (rc < 0) { + rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base, + value, sizeof(value)); + if (rc) { dev_err(dev, "RTC read data register failed\n"); return rc; } @@ -196,16 +160,16 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) * Read the LSB again and check if there has been a carry over. * If there is, redo the read operation. */ - rc = pm8xxx_read_wrapper(rtc_dd, ®, rtc_dd->rtc_read_base, 1); + rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_read_base, ®); if (rc < 0) { dev_err(dev, "RTC read data register failed\n"); return rc; } if (unlikely(reg < value[0])) { - rc = pm8xxx_read_wrapper(rtc_dd, value, - rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS); - if (rc < 0) { + rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->rtc_read_base, + value, sizeof(value)); + if (rc) { dev_err(dev, "RTC read data register failed\n"); return rc; } @@ -222,8 +186,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) } dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n", - secs, tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_mday, tm->tm_mon, tm->tm_year); + secs, tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_mday, tm->tm_mon, tm->tm_year); return 0; } @@ -244,19 +208,22 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); - rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base, - NUM_8_BIT_RTC_REGS); - if (rc < 0) { + rc = regmap_bulk_write(rtc_dd->regmap, rtc_dd->alarm_rw_base, value, + sizeof(value)); + if (rc) { dev_err(dev, "Write to RTC ALARM register failed\n"); goto rtc_rw_fail; } ctrl_reg = rtc_dd->ctrl_reg; - ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : - (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); - if (rc < 0) { + if (alarm->enabled) + ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; + else + ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } @@ -264,9 +231,9 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) rtc_dd->ctrl_reg = ctrl_reg; dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", - alarm->time.tm_hour, alarm->time.tm_min, - alarm->time.tm_sec, alarm->time.tm_mday, - alarm->time.tm_mon, alarm->time.tm_year); + alarm->time.tm_hour, alarm->time.tm_min, + alarm->time.tm_sec, alarm->time.tm_mday, + alarm->time.tm_mon, alarm->time.tm_year); rtc_rw_fail: spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); return rc; @@ -279,9 +246,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) unsigned long secs; struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); - rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base, - NUM_8_BIT_RTC_REGS); - if (rc < 0) { + rc = regmap_bulk_read(rtc_dd->regmap, rtc_dd->alarm_rw_base, value, + sizeof(value)); + if (rc) { dev_err(dev, "RTC alarm time read failed\n"); return rc; } @@ -297,9 +264,9 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) } dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", - alarm->time.tm_hour, alarm->time.tm_min, - alarm->time.tm_sec, alarm->time.tm_mday, - alarm->time.tm_mon, alarm->time.tm_year); + alarm->time.tm_hour, alarm->time.tm_min, + alarm->time.tm_sec, alarm->time.tm_mday, + alarm->time.tm_mon, alarm->time.tm_year); return 0; } @@ -312,12 +279,16 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) u8 ctrl_reg; spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags); + ctrl_reg = rtc_dd->ctrl_reg; - ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : - (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); - if (rc < 0) { + if (enable) + ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; + else + ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; + + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; } @@ -329,8 +300,9 @@ rtc_rw_fail: return rc; } -static struct rtc_class_ops pm8xxx_rtc_ops = { +static const struct rtc_class_ops pm8xxx_rtc_ops = { .read_time = pm8xxx_rtc_read_time, + .set_time = pm8xxx_rtc_set_time, .set_alarm = pm8xxx_rtc_set_alarm, .read_alarm = pm8xxx_rtc_read_alarm, .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable, @@ -339,7 +311,7 @@ static struct rtc_class_ops pm8xxx_rtc_ops = { static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) { struct pm8xxx_rtc *rtc_dd = dev_id; - u8 ctrl_reg; + unsigned int ctrl_reg; int rc; unsigned long irq_flags; @@ -351,11 +323,11 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) ctrl_reg = rtc_dd->ctrl_reg; ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); - if (rc < 0) { + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); - dev_err(rtc_dd->rtc_dev, "Write to RTC control register " - "failed\n"); + dev_err(rtc_dd->rtc_dev, + "Write to RTC control register failed\n"); goto rtc_alarm_handled; } @@ -363,61 +335,71 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); /* Clear RTC alarm register */ - rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + - PM8XXX_ALARM_CTRL_OFFSET, 1); - if (rc < 0) { - dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read " - "failed\n"); + rc = regmap_read(rtc_dd->regmap, + rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET, + &ctrl_reg); + if (rc) { + dev_err(rtc_dd->rtc_dev, + "RTC Alarm control register read failed\n"); goto rtc_alarm_handled; } ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + - PM8XXX_ALARM_CTRL_OFFSET, 1); - if (rc < 0) - dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register" - " failed\n"); + rc = regmap_write(rtc_dd->regmap, + rtc_dd->rtc_base + PM8XXX_ALARM_CTRL_OFFSET, + ctrl_reg); + if (rc) + dev_err(rtc_dd->rtc_dev, + "Write to RTC Alarm control register failed\n"); rtc_alarm_handled: return IRQ_HANDLED; } +/* + * Hardcoded RTC bases until IORESOURCE_REG mapping is figured out + */ +static const struct of_device_id pm8xxx_id_table[] = { + { .compatible = "qcom,pm8921-rtc", .data = (void *) 0x11D }, + { .compatible = "qcom,pm8058-rtc", .data = (void *) 0x1E8 }, + { }, +}; +MODULE_DEVICE_TABLE(of, pm8xxx_id_table); + static int pm8xxx_rtc_probe(struct platform_device *pdev) { int rc; - u8 ctrl_reg; - bool rtc_write_enable = false; + unsigned int ctrl_reg; struct pm8xxx_rtc *rtc_dd; - struct resource *rtc_resource; - const struct pm8xxx_rtc_platform_data *pdata = - dev_get_platdata(&pdev->dev); + const struct of_device_id *match; - if (pdata != NULL) - rtc_write_enable = pdata->rtc_write_enable; + match = of_match_node(pm8xxx_id_table, pdev->dev.of_node); + if (!match) + return -ENXIO; rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL); - if (rtc_dd == NULL) { - dev_err(&pdev->dev, "Unable to allocate memory!\n"); + if (rtc_dd == NULL) return -ENOMEM; - } /* Initialise spinlock to protect RTC control register */ spin_lock_init(&rtc_dd->ctrl_reg_lock); + rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc_dd->regmap) { + dev_err(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } + rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); if (rtc_dd->rtc_alarm_irq < 0) { dev_err(&pdev->dev, "Alarm IRQ resource absent!\n"); return -ENXIO; } - rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO, - "pmic_rtc_base"); - if (!(rtc_resource && rtc_resource->start)) { - dev_err(&pdev->dev, "RTC IO resource absent!\n"); - return -ENXIO; - } + rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node, + "allow-set-time"); - rtc_dd->rtc_base = rtc_resource->start; + rtc_dd->rtc_base = (long) match->data; /* Setup RTC register addresses */ rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET; @@ -427,64 +409,52 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) rtc_dd->rtc_dev = &pdev->dev; /* Check if the RTC is on, else turn it on */ - rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); - if (rc < 0) { + rc = regmap_read(rtc_dd->regmap, rtc_dd->rtc_base, &ctrl_reg); + if (rc) { dev_err(&pdev->dev, "RTC control register read failed!\n"); return rc; } if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { ctrl_reg |= PM8xxx_RTC_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); - if (rc < 0) { - dev_err(&pdev->dev, "Write to RTC control register " - "failed\n"); + rc = regmap_write(rtc_dd->regmap, rtc_dd->rtc_base, ctrl_reg); + if (rc) { + dev_err(&pdev->dev, + "Write to RTC control register failed\n"); return rc; } } rtc_dd->ctrl_reg = ctrl_reg; - if (rtc_write_enable == true) - pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time; platform_set_drvdata(pdev, rtc_dd); + device_init_wakeup(&pdev->dev, 1); + /* Register the RTC device */ rtc_dd->rtc = devm_rtc_device_register(&pdev->dev, "pm8xxx_rtc", - &pm8xxx_rtc_ops, THIS_MODULE); + &pm8xxx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc_dd->rtc)) { dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n", - __func__, PTR_ERR(rtc_dd->rtc)); + __func__, PTR_ERR(rtc_dd->rtc)); return PTR_ERR(rtc_dd->rtc); } /* Request the alarm IRQ */ - rc = request_any_context_irq(rtc_dd->rtc_alarm_irq, - pm8xxx_alarm_trigger, IRQF_TRIGGER_RISING, - "pm8xxx_rtc_alarm", rtc_dd); + rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->rtc_alarm_irq, + pm8xxx_alarm_trigger, + IRQF_TRIGGER_RISING, + "pm8xxx_rtc_alarm", rtc_dd); if (rc < 0) { dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc); return rc; } - device_init_wakeup(&pdev->dev, 1); - dev_dbg(&pdev->dev, "Probe success !!\n"); return 0; } -static int pm8xxx_rtc_remove(struct platform_device *pdev) -{ - struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev); - - device_init_wakeup(&pdev->dev, 0); - free_irq(rtc_dd->rtc_alarm_irq, rtc_dd); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int pm8xxx_rtc_resume(struct device *dev) { @@ -507,15 +477,17 @@ static int pm8xxx_rtc_suspend(struct device *dev) } #endif -static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume); +static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, + pm8xxx_rtc_suspend, + pm8xxx_rtc_resume); static struct platform_driver pm8xxx_rtc_driver = { .probe = pm8xxx_rtc_probe, - .remove = pm8xxx_rtc_remove, .driver = { - .name = PM8XXX_RTC_DEV_NAME, - .owner = THIS_MODULE, - .pm = &pm8xxx_rtc_pm_ops, + .name = "rtc-pm8xxx", + .owner = THIS_MODULE, + .pm = &pm8xxx_rtc_pm_ops, + .of_match_table = pm8xxx_id_table, }, }; diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 1a779a67ff66..e9ac5a43be1a 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -395,6 +395,12 @@ static int rv3029c2_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL)) return -ENODEV; + rc = rv3029c2_i2c_get_sr(client, buf); + if (rc < 0) { + dev_err(&client->dev, "reading status failed\n"); + return rc; + } + rtc = devm_rtc_device_register(&client->dev, client->name, &rv3029c2_rtc_ops, THIS_MODULE); @@ -403,12 +409,6 @@ static int rv3029c2_probe(struct i2c_client *client, i2c_set_clientdata(client, rtc); - rc = rv3029c2_i2c_get_sr(client, buf); - if (rc < 0) { - dev_err(&client->dev, "reading status failed\n"); - return rc; - } - return 0; } diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index 8fa23eabcb68..e6298e02b400 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -551,7 +551,6 @@ static int rx8025_probe(struct i2c_client *client, rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL); if (!rx8025) { - dev_err(&adapter->dev, "failed to alloc memory\n"); err = -ENOMEM; goto errout; } diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index c4cde9c08f1f..4958a363b2c7 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -48,8 +48,8 @@ struct s3c_rtc_drv_data { static struct clk *rtc_clk; static void __iomem *s3c_rtc_base; -static int s3c_rtc_alarmno = NO_IRQ; -static int s3c_rtc_tickno = NO_IRQ; +static int s3c_rtc_alarmno; +static int s3c_rtc_tickno; static enum s3c_cpu_type s3c_rtc_cpu_type; static DEFINE_SPINLOCK(s3c_rtc_pie_lock); diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c index 3eb3642ae299..76e38007ba90 100644 --- a/drivers/rtc/rtc-sirfsoc.c +++ b/drivers/rtc/rtc-sirfsoc.c @@ -264,12 +264,8 @@ static int sirfsoc_rtc_probe(struct platform_device *pdev) rtcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_rtc_drv), GFP_KERNEL); - if (rtcdrv == NULL) { - dev_err(&pdev->dev, - "%s: can't alloc mem for drv struct\n", - pdev->name); + if (rtcdrv == NULL) return -ENOMEM; - } err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base); if (err) { @@ -335,39 +331,29 @@ static int sirfsoc_rtc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int sirfsoc_rtc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); + struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev); rtcdrv->overflow_rtc = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE); rtcdrv->saved_counter = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); rtcdrv->saved_overflow_rtc = rtcdrv->overflow_rtc; - if (device_may_wakeup(&pdev->dev) && !enable_irq_wake(rtcdrv->irq)) + if (device_may_wakeup(dev) && !enable_irq_wake(rtcdrv->irq)) rtcdrv->irq_wake = 1; return 0; } -static int sirfsoc_rtc_freeze(struct device *dev) -{ - sirfsoc_rtc_suspend(dev); - - return 0; -} - -static int sirfsoc_rtc_thaw(struct device *dev) +static int sirfsoc_rtc_resume(struct device *dev) { u32 tmp; - struct sirfsoc_rtc_drv *rtcdrv; - rtcdrv = dev_get_drvdata(dev); + struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev); /* - * if resume from snapshot and the rtc power is losed, + * if resume from snapshot and the rtc power is lost, * restroe the rtc settings */ if (SIRFSOC_RTC_CLK != sirfsoc_rtc_iobrg_readl( @@ -407,57 +393,23 @@ static int sirfsoc_rtc_thaw(struct device *dev) sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc, rtcdrv->rtc_base + RTC_SW_VALUE); - return 0; -} - -static int sirfsoc_rtc_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); - sirfsoc_rtc_thaw(dev); - if (device_may_wakeup(&pdev->dev) && rtcdrv->irq_wake) { + if (device_may_wakeup(dev) && rtcdrv->irq_wake) { disable_irq_wake(rtcdrv->irq); rtcdrv->irq_wake = 0; } return 0; } - -static int sirfsoc_rtc_restore(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); - - if (device_may_wakeup(&pdev->dev) && rtcdrv->irq_wake) { - disable_irq_wake(rtcdrv->irq); - rtcdrv->irq_wake = 0; - } - return 0; -} - -#else -#define sirfsoc_rtc_suspend NULL -#define sirfsoc_rtc_resume NULL -#define sirfsoc_rtc_freeze NULL -#define sirfsoc_rtc_thaw NULL -#define sirfsoc_rtc_restore NULL #endif -static const struct dev_pm_ops sirfsoc_rtc_pm_ops = { - .suspend = sirfsoc_rtc_suspend, - .resume = sirfsoc_rtc_resume, - .freeze = sirfsoc_rtc_freeze, - .thaw = sirfsoc_rtc_thaw, - .restore = sirfsoc_rtc_restore, -}; +static SIMPLE_DEV_PM_OPS(sirfsoc_rtc_pm_ops, + sirfsoc_rtc_suspend, sirfsoc_rtc_resume); static struct platform_driver sirfsoc_rtc_driver = { .driver = { .name = "sirfsoc-rtc", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &sirfsoc_rtc_pm_ops, -#endif .of_match_table = sirfsoc_rtc_of_match, }, .probe = sirfsoc_rtc_probe, diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index c492cf0ab8cd..d2cdb9823a15 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -365,10 +365,8 @@ static int spear_rtc_probe(struct platform_device *pdev) } config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); - if (!config) { - dev_err(&pdev->dev, "out of memory\n"); + if (!config) return -ENOMEM; - } /* alarm irqs */ irq = platform_get_irq(pdev, 0); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index a176ba614683..35ed49ea1f81 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -214,8 +214,7 @@ static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id) events |= RTC_UF; else events |= RTC_AF; - if (likely(pdata->rtc)) - rtc_update_irq(pdata->rtc, 1, events); + rtc_update_irq(pdata->rtc, 1, events); } spin_unlock(&pdata->lock); return events ? IRQ_HANDLED : IRQ_NONE; diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c index 68a35284e5ad..b6f21f73d508 100644 --- a/drivers/rtc/rtc-sunxi.c +++ b/drivers/rtc/rtc-sunxi.c @@ -428,7 +428,7 @@ static const struct rtc_class_ops sunxi_rtc_ops = { }; static const struct of_device_id sunxi_rtc_dt_ids[] = { - { .compatible = "allwinner,sun4i-rtc", .data = &data_year_param[0] }, + { .compatible = "allwinner,sun4i-a10-rtc", .data = &data_year_param[0] }, { .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] }, { /* sentinel */ }, }; diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c index 7746e65b93f2..6599c20bc454 100644 --- a/drivers/rtc/rtc-test.c +++ b/drivers/rtc/rtc-test.c @@ -104,20 +104,17 @@ static int test_probe(struct platform_device *plat_dev) rtc = devm_rtc_device_register(&plat_dev->dev, "test", &test_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { - err = PTR_ERR(rtc); - return err; + return PTR_ERR(rtc); } err = device_create_file(&plat_dev->dev, &dev_attr_irq); if (err) - goto err; + dev_err(&plat_dev->dev, "Unable to create sysfs entry: %s\n", + dev_attr_irq.attr.name); platform_set_drvdata(plat_dev, rtc); return 0; - -err: - return err; } static int test_remove(struct platform_device *plat_dev) diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 4f87234e0dee..2e678c681b13 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -176,8 +176,8 @@ static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id) tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_NOP); } spin_unlock(&pdata->lock); - if (likely(pdata->rtc)) - rtc_update_irq(pdata->rtc, 1, events); + rtc_update_irq(pdata->rtc, 1, events); + return IRQ_HANDLED; } diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index df2ef3eba7cd..051da968da6d 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -79,7 +79,6 @@ struct vt8500_rtc { void __iomem *regbase; - struct resource *res; int irq_alarm; struct rtc_device *rtc; spinlock_t lock; /* Protects this structure */ @@ -209,6 +208,7 @@ static const struct rtc_class_ops vt8500_rtc_ops = { static int vt8500_rtc_probe(struct platform_device *pdev) { struct vt8500_rtc *vt8500_rtc; + struct resource *res; int ret; vt8500_rtc = devm_kzalloc(&pdev->dev, @@ -219,34 +219,16 @@ static int vt8500_rtc_probe(struct platform_device *pdev) spin_lock_init(&vt8500_rtc->lock); platform_set_drvdata(pdev, vt8500_rtc); - vt8500_rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!vt8500_rtc->res) { - dev_err(&pdev->dev, "No I/O memory resource defined\n"); - return -ENXIO; - } - vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0); if (vt8500_rtc->irq_alarm < 0) { dev_err(&pdev->dev, "No alarm IRQ resource defined\n"); return vt8500_rtc->irq_alarm; } - vt8500_rtc->res = devm_request_mem_region(&pdev->dev, - vt8500_rtc->res->start, - resource_size(vt8500_rtc->res), - "vt8500-rtc"); - if (vt8500_rtc->res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - return -EBUSY; - } - - vt8500_rtc->regbase = devm_ioremap(&pdev->dev, vt8500_rtc->res->start, - resource_size(vt8500_rtc->res)); - if (!vt8500_rtc->regbase) { - dev_err(&pdev->dev, "Unable to map RTC I/O memory\n"); - ret = -EBUSY; - goto err_return; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vt8500_rtc->regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vt8500_rtc->regbase)) + return PTR_ERR(vt8500_rtc->regbase); /* Enable RTC and set it to 24-hour mode */ writel(VT8500_RTC_CR_ENABLE, diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 365dc6505148..b1de58e0b3d0 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -660,7 +660,7 @@ static int x1205_probe(struct i2c_client *client, err = x1205_sysfs_register(&client->dev); if (err) - return err; + dev_err(&client->dev, "Unable to create sysfs entries\n"); return 0; } diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c index 26003d3c1be7..7c4fd97a7fa0 100644 --- a/drivers/staging/lustre/lustre/llite/llite_lib.c +++ b/drivers/staging/lustre/lustre/llite/llite_lib.c @@ -1877,7 +1877,7 @@ void ll_delete_inode(struct inode *inode) cl_sync_file_range(inode, 0, OBD_OBJECT_EOF, CL_FSYNC_DISCARD, 1); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); /* Workaround for LU-118 */ if (inode->i_data.nrpages) { diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c index ee0c0a982e4e..ec5350f2c28a 100644 --- a/drivers/video/backlight/aat2870_bl.c +++ b/drivers/video/backlight/aat2870_bl.c @@ -149,8 +149,6 @@ static int aat2870_bl_probe(struct platform_device *pdev) sizeof(struct aat2870_bl_driver_data), GFP_KERNEL); if (!aat2870_bl) { - dev_err(&pdev->dev, - "Failed to allocate memory for aat2870 backlight\n"); ret = -ENOMEM; goto out; } diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 9d656717d0f7..be8d83deca7d 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -224,10 +224,8 @@ static int adp8860_led_probe(struct i2c_client *client) led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds, GFP_KERNEL); - if (led == NULL) { - dev_err(&client->dev, "failed to alloc memory\n"); + if (led == NULL) return -ENOMEM; - } ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); ret = adp8860_write(client, ADP8860_ISCT1, diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c index 63707205326b..251af4d38d86 100644 --- a/drivers/video/backlight/adp8870_bl.c +++ b/drivers/video/backlight/adp8870_bl.c @@ -246,10 +246,8 @@ static int adp8870_led_probe(struct i2c_client *client) led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led), GFP_KERNEL); - if (led == NULL) { - dev_err(&client->dev, "failed to alloc memory\n"); + if (led == NULL) return -ENOMEM; - } ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); if (ret) diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 5d05555fe841..27d3cf255e78 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -34,13 +34,15 @@ static const char *const backlight_types[] = { defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) /* This callback gets called when something important happens inside a * framebuffer driver. We're looking if that important event is blanking, - * and if it is, we're switching backlight power as well ... + * and if it is and necessary, we're switching backlight power as well ... */ static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct backlight_device *bd; struct fb_event *evdata = data; + int node = evdata->info->node; + int fb_blank = 0; /* If we aren't interested in this event, skip it immediately ... */ if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK) @@ -51,12 +53,24 @@ static int fb_notifier_callback(struct notifier_block *self, if (bd->ops) if (!bd->ops->check_fb || bd->ops->check_fb(bd, evdata->info)) { - bd->props.fb_blank = *(int *)evdata->data; - if (bd->props.fb_blank == FB_BLANK_UNBLANK) - bd->props.state &= ~BL_CORE_FBBLANK; - else - bd->props.state |= BL_CORE_FBBLANK; - backlight_update_status(bd); + fb_blank = *(int *)evdata->data; + if (fb_blank == FB_BLANK_UNBLANK && + !bd->fb_bl_on[node]) { + bd->fb_bl_on[node] = true; + if (!bd->use_count++) { + bd->props.state &= ~BL_CORE_FBBLANK; + bd->props.fb_blank = FB_BLANK_UNBLANK; + backlight_update_status(bd); + } + } else if (fb_blank != FB_BLANK_UNBLANK && + bd->fb_bl_on[node]) { + bd->fb_bl_on[node] = false; + if (!(--bd->use_count)) { + bd->props.state |= BL_CORE_FBBLANK; + bd->props.fb_blank = fb_blank; + backlight_update_status(bd); + } + } } mutex_unlock(&bd->ops_lock); return 0; diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c index db8db5fa6583..51d18d637e2b 100644 --- a/drivers/video/backlight/corgi_lcd.c +++ b/drivers/video/backlight/corgi_lcd.c @@ -543,10 +543,8 @@ static int corgi_lcd_probe(struct spi_device *spi) } lcd = devm_kzalloc(&spi->dev, sizeof(struct corgi_lcd), GFP_KERNEL); - if (!lcd) { - dev_err(&spi->dev, "failed to allocate memory\n"); + if (!lcd) return -ENOMEM; - } lcd->spi_dev = spi; diff --git a/drivers/video/backlight/hx8357.c b/drivers/video/backlight/hx8357.c index 985e854e244b..23f50b92a930 100644 --- a/drivers/video/backlight/hx8357.c +++ b/drivers/video/backlight/hx8357.c @@ -587,10 +587,8 @@ static int hx8357_probe(struct spi_device *spi) int i, ret; lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) { - dev_err(&spi->dev, "Couldn't allocate lcd internal structure!\n"); + if (!lcd) return -ENOMEM; - } ret = spi_setup(spi); if (ret < 0) { diff --git a/drivers/video/backlight/ili922x.c b/drivers/video/backlight/ili922x.c index 73464e4b4c74..ea67fe199e34 100644 --- a/drivers/video/backlight/ili922x.c +++ b/drivers/video/backlight/ili922x.c @@ -482,10 +482,8 @@ static int ili922x_probe(struct spi_device *spi) u16 reg = 0; ili = devm_kzalloc(&spi->dev, sizeof(*ili), GFP_KERNEL); - if (!ili) { - dev_err(&spi->dev, "cannot alloc priv data\n"); + if (!ili) return -ENOMEM; - } ili->spi = spi; spi_set_drvdata(spi, ili); diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c index e2b8b40a9bd9..2cf39e6d519d 100644 --- a/drivers/video/backlight/ili9320.c +++ b/drivers/video/backlight/ili9320.c @@ -219,10 +219,8 @@ int ili9320_probe_spi(struct spi_device *spi, /* allocate and initialse our state */ ili = devm_kzalloc(&spi->dev, sizeof(struct ili9320), GFP_KERNEL); - if (ili == NULL) { - dev_err(dev, "no memory for device\n"); + if (ili == NULL) return -ENOMEM; - } ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1); diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 63e763828e0e..5fa2649c9631 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -181,11 +181,8 @@ static int l4f00242t03_probe(struct spi_device *spi) priv = devm_kzalloc(&spi->dev, sizeof(struct l4f00242t03_priv), GFP_KERNEL); - - if (priv == NULL) { - dev_err(&spi->dev, "No memory for this device.\n"); + if (priv == NULL) return -ENOMEM; - } spi_set_drvdata(spi, priv); spi->bits_per_word = 9; diff --git a/drivers/video/backlight/lm3533_bl.c b/drivers/video/backlight/lm3533_bl.c index 187d1c283c1d..cff1fbe89a1b 100644 --- a/drivers/video/backlight/lm3533_bl.c +++ b/drivers/video/backlight/lm3533_bl.c @@ -296,11 +296,8 @@ static int lm3533_bl_probe(struct platform_device *pdev) } bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); - if (!bl) { - dev_err(&pdev->dev, - "failed to allocate memory for backlight\n"); + if (!bl) return -ENOMEM; - } bl->lm3533 = lm3533; bl->id = pdev->id; diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index de8832504f68..14590c54aedf 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -168,10 +168,8 @@ static int lms283gf05_probe(struct spi_device *spi) st = devm_kzalloc(&spi->dev, sizeof(struct lms283gf05_state), GFP_KERNEL); - if (st == NULL) { - dev_err(&spi->dev, "No memory for device state\n"); + if (st == NULL) return -ENOMEM; - } ld = devm_lcd_device_register(&spi->dev, "lms283gf05", &spi->dev, st, &lms_ops); diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c index d01884d4f1bf..c3d2e209fc8f 100644 --- a/drivers/video/backlight/platform_lcd.c +++ b/drivers/video/backlight/platform_lcd.c @@ -94,10 +94,8 @@ static int platform_lcd_probe(struct platform_device *pdev) plcd = devm_kzalloc(&pdev->dev, sizeof(struct platform_lcd), GFP_KERNEL); - if (!plcd) { - dev_err(dev, "no memory for state\n"); + if (!plcd) return -ENOMEM; - } plcd->us = dev; plcd->pdata = pdata; diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c index cbba37e6836e..595dcf561020 100644 --- a/drivers/video/backlight/tps65217_bl.c +++ b/drivers/video/backlight/tps65217_bl.c @@ -200,7 +200,6 @@ tps65217_bl_parse_dt(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { - dev_err(&pdev->dev, "failed to allocate platform data\n"); err = ERR_PTR(-ENOMEM); goto err; } @@ -296,10 +295,8 @@ static int tps65217_bl_probe(struct platform_device *pdev) tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl), GFP_KERNEL); - if (tps65217_bl == NULL) { - dev_err(&pdev->dev, "allocation of struct tps65217_bl failed\n"); + if (tps65217_bl == NULL) return -ENOMEM; - } tps65217_bl->tps = tps; tps65217_bl->dev = &pdev->dev; diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index bb7991c7e5c7..53161ec058a7 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -451,7 +451,7 @@ void v9fs_evict_inode(struct inode *inode) { struct v9fs_inode *v9inode = V9FS_I(inode); - truncate_inode_pages(inode->i_mapping, 0); + truncate_inode_pages_final(inode->i_mapping); clear_inode(inode); filemap_fdatawrite(inode->i_mapping); diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 0e092d08680e..96df91e8c334 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -259,7 +259,7 @@ affs_evict_inode(struct inode *inode) { unsigned long cache_page; pr_debug("AFFS: evict_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (!inode->i_nlink) { inode->i_size = 0; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index ce25d755b7aa..294671288449 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -422,7 +422,7 @@ void afs_evict_inode(struct inode *inode) ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); afs_give_up_callback(vnode); diff --git a/fs/befs/Makefile b/fs/befs/Makefile index 2f370bd7a50d..8b9f66642a83 100644 --- a/fs/befs/Makefile +++ b/fs/befs/Makefile @@ -3,5 +3,5 @@ # obj-$(CONFIG_BEFS_FS) += befs.o - +ccflags-$(CONFIG_BEFS_DEBUG) += -DDEBUG befs-objs := datastream.o btree.o super.o inode.o debug.o io.o linuxvfs.o diff --git a/fs/befs/befs.h b/fs/befs/befs.h index b26642839156..3a7813ab8c95 100644 --- a/fs/befs/befs.h +++ b/fs/befs/befs.h @@ -88,8 +88,11 @@ enum befs_err { /****************************/ /* debug.c */ +__printf(2, 3) void befs_error(const struct super_block *sb, const char *fmt, ...); +__printf(2, 3) void befs_warning(const struct super_block *sb, const char *fmt, ...); +__printf(2, 3) void befs_debug(const struct super_block *sb, const char *fmt, ...); void befs_dump_super_block(const struct super_block *sb, befs_super_block *); diff --git a/fs/befs/btree.c b/fs/befs/btree.c index 74e397db0b8b..a2cd305a993a 100644 --- a/fs/befs/btree.c +++ b/fs/befs/btree.c @@ -137,7 +137,7 @@ befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, struct buffer_head *bh = NULL; befs_disk_btree_super *od_sup = NULL; - befs_debug(sb, "---> befs_btree_read_super()"); + befs_debug(sb, "---> %s", __func__); bh = befs_read_datastream(sb, ds, 0, NULL); @@ -162,11 +162,11 @@ befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, goto error; } - befs_debug(sb, "<--- befs_btree_read_super()"); + befs_debug(sb, "<--- %s", __func__); return BEFS_OK; error: - befs_debug(sb, "<--- befs_btree_read_super() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } @@ -195,16 +195,16 @@ befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, { uint off = 0; - befs_debug(sb, "---> befs_bt_read_node()"); + befs_debug(sb, "---> %s", __func__); if (node->bh) brelse(node->bh); node->bh = befs_read_datastream(sb, ds, node_off, &off); if (!node->bh) { - befs_error(sb, "befs_bt_read_node() failed to read " - "node at %Lu", node_off); - befs_debug(sb, "<--- befs_bt_read_node() ERROR"); + befs_error(sb, "%s failed to read " + "node at %llu", __func__, node_off); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } @@ -221,7 +221,7 @@ befs_bt_read_node(struct super_block *sb, befs_data_stream * ds, node->head.all_key_length = fs16_to_cpu(sb, node->od_node->all_key_length); - befs_debug(sb, "<--- befs_btree_read_node()"); + befs_debug(sb, "<--- %s", __func__); return BEFS_OK; } @@ -252,7 +252,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, befs_off_t node_off; int res; - befs_debug(sb, "---> befs_btree_find() Key: %s", key); + befs_debug(sb, "---> %s Key: %s", __func__, key); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, @@ -263,7 +263,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, this_node = kmalloc(sizeof (befs_btree_node), GFP_NOFS); if (!this_node) { - befs_error(sb, "befs_btree_find() failed to allocate %u " + befs_error(sb, "befs_btree_find() failed to allocate %zu " "bytes of memory", sizeof (befs_btree_node)); goto error; } @@ -274,7 +274,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, node_off = bt_super.root_node_ptr; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " - "node at %Lu", node_off); + "node at %llu", node_off); goto error_alloc; } @@ -285,7 +285,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, /* if no match, go to overflow node */ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { befs_error(sb, "befs_btree_find() failed to read " - "node at %Lu", node_off); + "node at %llu", node_off); goto error_alloc; } } @@ -298,11 +298,11 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, kfree(this_node); if (res != BEFS_BT_MATCH) { - befs_debug(sb, "<--- befs_btree_find() Key %s not found", key); + befs_debug(sb, "<--- %s Key %s not found", __func__, key); *value = 0; return BEFS_BT_NOT_FOUND; } - befs_debug(sb, "<--- befs_btree_find() Found key %s, value %Lu", + befs_debug(sb, "<--- %s Found key %s, value %llu", __func__, key, *value); return BEFS_OK; @@ -310,7 +310,7 @@ befs_btree_find(struct super_block *sb, befs_data_stream * ds, kfree(this_node); error: *value = 0; - befs_debug(sb, "<--- befs_btree_find() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } @@ -343,7 +343,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node, char *thiskey; fs64 *valarray; - befs_debug(sb, "---> befs_find_key() %s", findkey); + befs_debug(sb, "---> %s %s", __func__, findkey); *value = 0; @@ -355,7 +355,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node, eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len); if (eq < 0) { - befs_debug(sb, "<--- befs_find_key() %s not found", findkey); + befs_debug(sb, "<--- %s %s not found", __func__, findkey); return BEFS_BT_NOT_FOUND; } @@ -373,8 +373,8 @@ befs_find_key(struct super_block *sb, befs_btree_node * node, findkey_len); if (eq == 0) { - befs_debug(sb, "<--- befs_find_key() found %s at %d", - thiskey, mid); + befs_debug(sb, "<--- %s found %s at %d", + __func__, thiskey, mid); *value = fs64_to_cpu(sb, valarray[mid]); return BEFS_BT_MATCH; @@ -388,7 +388,7 @@ befs_find_key(struct super_block *sb, befs_btree_node * node, *value = fs64_to_cpu(sb, valarray[mid + 1]); else *value = fs64_to_cpu(sb, valarray[mid]); - befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid); + befs_debug(sb, "<--- %s found %s at %d", __func__, thiskey, mid); return BEFS_BT_PARMATCH; } @@ -428,7 +428,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, uint key_sum = 0; - befs_debug(sb, "---> befs_btree_read()"); + befs_debug(sb, "---> %s", __func__); if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) { befs_error(sb, @@ -437,7 +437,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, } if ((this_node = kmalloc(sizeof (befs_btree_node), GFP_NOFS)) == NULL) { - befs_error(sb, "befs_btree_read() failed to allocate %u " + befs_error(sb, "befs_btree_read() failed to allocate %zu " "bytes of memory", sizeof (befs_btree_node)); goto error; } @@ -452,7 +452,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, kfree(this_node); *value = 0; *keysize = 0; - befs_debug(sb, "<--- befs_btree_read() Tree is EMPTY"); + befs_debug(sb, "<--- %s Tree is EMPTY", __func__); return BEFS_BT_EMPTY; } else if (res == BEFS_ERR) { goto error_alloc; @@ -467,7 +467,8 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, *keysize = 0; *value = 0; befs_debug(sb, - "<--- befs_btree_read() END of keys at %Lu", + "<--- %s END of keys at %llu", __func__, + (unsigned long long) key_sum + this_node->head.all_key_count); brelse(this_node->bh); kfree(this_node); @@ -478,8 +479,8 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, node_off = this_node->head.right; if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) { - befs_error(sb, "befs_btree_read() failed to read " - "node at %Lu", node_off); + befs_error(sb, "%s failed to read node at %llu", + __func__, (unsigned long long)node_off); goto error_alloc; } } @@ -492,11 +493,13 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen); - befs_debug(sb, "Read [%Lu,%d]: keysize %d", node_off, cur_key, keylen); + befs_debug(sb, "Read [%llu,%d]: keysize %d", + (long long unsigned int)node_off, (int)cur_key, + (int)keylen); if (bufsize < keylen + 1) { - befs_error(sb, "befs_btree_read() keybuf too small (%u) " - "for key of size %d", bufsize, keylen); + befs_error(sb, "%s keybuf too small (%zu) " + "for key of size %d", __func__, bufsize, keylen); brelse(this_node->bh); goto error_alloc; }; @@ -506,13 +509,13 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, *keysize = keylen; keybuf[keylen] = '\0'; - befs_debug(sb, "Read [%Lu,%d]: Key \"%.*s\", Value %Lu", node_off, + befs_debug(sb, "Read [%llu,%d]: Key \"%.*s\", Value %llu", node_off, cur_key, keylen, keybuf, *value); brelse(this_node->bh); kfree(this_node); - befs_debug(sb, "<--- befs_btree_read()"); + befs_debug(sb, "<--- %s", __func__); return BEFS_OK; @@ -522,7 +525,7 @@ befs_btree_read(struct super_block *sb, befs_data_stream * ds, error: *keysize = 0; *value = 0; - befs_debug(sb, "<--- befs_btree_read() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } @@ -547,26 +550,26 @@ befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, befs_off_t * node_off) { - befs_debug(sb, "---> befs_btree_seekleaf()"); + befs_debug(sb, "---> %s", __func__); if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { - befs_error(sb, "befs_btree_seekleaf() failed to read " - "node at %Lu", *node_off); + befs_error(sb, "%s failed to read " + "node at %llu", __func__, *node_off); goto error; } - befs_debug(sb, "Seekleaf to root node %Lu", *node_off); + befs_debug(sb, "Seekleaf to root node %llu", *node_off); if (this_node->head.all_key_count == 0 && befs_leafnode(this_node)) { - befs_debug(sb, "<--- befs_btree_seekleaf() Tree is EMPTY"); + befs_debug(sb, "<--- %s Tree is EMPTY", __func__); return BEFS_BT_EMPTY; } while (!befs_leafnode(this_node)) { if (this_node->head.all_key_count == 0) { - befs_debug(sb, "befs_btree_seekleaf() encountered " - "an empty interior node: %Lu. Using Overflow " - "node: %Lu", *node_off, + befs_debug(sb, "%s encountered " + "an empty interior node: %llu. Using Overflow " + "node: %llu", __func__, *node_off, this_node->head.overflow); *node_off = this_node->head.overflow; } else { @@ -574,19 +577,19 @@ befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds, *node_off = fs64_to_cpu(sb, valarray[0]); } if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) { - befs_error(sb, "befs_btree_seekleaf() failed to read " - "node at %Lu", *node_off); + befs_error(sb, "%s failed to read " + "node at %llu", __func__, *node_off); goto error; } - befs_debug(sb, "Seekleaf to child node %Lu", *node_off); + befs_debug(sb, "Seekleaf to child node %llu", *node_off); } - befs_debug(sb, "Node %Lu is a leaf node", *node_off); + befs_debug(sb, "Node %llu is a leaf node", *node_off); return BEFS_OK; error: - befs_debug(sb, "<--- befs_btree_seekleaf() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } diff --git a/fs/befs/datastream.c b/fs/befs/datastream.c index 59096b5e0fc7..c467bebd50af 100644 --- a/fs/befs/datastream.c +++ b/fs/befs/datastream.c @@ -52,26 +52,25 @@ befs_read_datastream(struct super_block *sb, befs_data_stream * ds, befs_block_run run; befs_blocknr_t block; /* block coresponding to pos */ - befs_debug(sb, "---> befs_read_datastream() %Lu", pos); + befs_debug(sb, "---> %s %llu", __func__, pos); block = pos >> BEFS_SB(sb)->block_shift; if (off) *off = pos - (block << BEFS_SB(sb)->block_shift); if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) { befs_error(sb, "BeFS: Error finding disk addr of block %lu", - block); - befs_debug(sb, "<--- befs_read_datastream() ERROR"); + (unsigned long)block); + befs_debug(sb, "<--- %s ERROR", __func__); return NULL; } bh = befs_bread_iaddr(sb, run); if (!bh) { befs_error(sb, "BeFS: Error reading block %lu from datastream", - block); + (unsigned long)block); return NULL; } - befs_debug(sb, "<--- befs_read_datastream() read data, starting at %Lu", - pos); + befs_debug(sb, "<--- %s read data, starting at %llu", __func__, pos); return bh; } @@ -106,7 +105,8 @@ befs_fblock2brun(struct super_block *sb, befs_data_stream * data, } else { befs_error(sb, "befs_fblock2brun() was asked to find block %lu, " - "which is not mapped by the datastream\n", fblock); + "which is not mapped by the datastream\n", + (unsigned long)fblock); err = BEFS_ERR; } return err; @@ -128,14 +128,14 @@ befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff, befs_off_t bytes_read = 0; /* bytes readed */ u16 plen; struct buffer_head *bh = NULL; - befs_debug(sb, "---> befs_read_lsymlink() length: %Lu", len); + befs_debug(sb, "---> %s length: %llu", __func__, len); while (bytes_read < len) { bh = befs_read_datastream(sb, ds, bytes_read, NULL); if (!bh) { befs_error(sb, "BeFS: Error reading datastream block " - "starting from %Lu", bytes_read); - befs_debug(sb, "<--- befs_read_lsymlink() ERROR"); + "starting from %llu", bytes_read); + befs_debug(sb, "<--- %s ERROR", __func__); return bytes_read; } @@ -146,7 +146,8 @@ befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff, bytes_read += plen; } - befs_debug(sb, "<--- befs_read_lsymlink() read %u bytes", bytes_read); + befs_debug(sb, "<--- %s read %u bytes", __func__, (unsigned int) + bytes_read); return bytes_read; } @@ -169,7 +170,7 @@ befs_count_blocks(struct super_block * sb, befs_data_stream * ds) befs_blocknr_t metablocks; /* FS metadata blocks */ befs_sb_info *befs_sb = BEFS_SB(sb); - befs_debug(sb, "---> befs_count_blocks()"); + befs_debug(sb, "---> %s", __func__); datablocks = ds->size >> befs_sb->block_shift; if (ds->size & (befs_sb->block_size - 1)) @@ -206,7 +207,7 @@ befs_count_blocks(struct super_block * sb, befs_data_stream * ds) } blocks = datablocks + metablocks; - befs_debug(sb, "<--- befs_count_blocks() %u blocks", blocks); + befs_debug(sb, "<--- %s %u blocks", __func__, (unsigned int)blocks); return blocks; } @@ -251,11 +252,11 @@ befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, befs_blocknr_t max_block = data->max_direct_range >> BEFS_SB(sb)->block_shift; - befs_debug(sb, "---> befs_find_brun_direct(), find %lu", blockno); + befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno); if (blockno > max_block) { - befs_error(sb, "befs_find_brun_direct() passed block outside of" - "direct region"); + befs_error(sb, "%s passed block outside of direct region", + __func__); return BEFS_ERR; } @@ -267,13 +268,14 @@ befs_find_brun_direct(struct super_block *sb, befs_data_stream * data, run->start = array[i].start + offset; run->len = array[i].len - offset; - befs_debug(sb, "---> befs_find_brun_direct(), " - "found %lu at direct[%d]", blockno, i); + befs_debug(sb, "---> %s, " + "found %lu at direct[%d]", __func__, + (unsigned long)blockno, i); return BEFS_OK; } } - befs_debug(sb, "---> befs_find_brun_direct() ERROR"); + befs_debug(sb, "---> %s ERROR", __func__); return BEFS_ERR; } @@ -316,7 +318,7 @@ befs_find_brun_indirect(struct super_block *sb, befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect); int arraylen = befs_iaddrs_per_block(sb); - befs_debug(sb, "---> befs_find_brun_indirect(), find %lu", blockno); + befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno); indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift; search_blk = blockno - indir_start_blk; @@ -325,10 +327,9 @@ befs_find_brun_indirect(struct super_block *sb, for (i = 0; i < indirect.len; i++) { indirblock = befs_bread(sb, indirblockno + i); if (indirblock == NULL) { - befs_debug(sb, - "---> befs_find_brun_indirect() failed to " - "read disk block %lu from the indirect brun", - indirblockno + i); + befs_debug(sb, "---> %s failed to read " + "disk block %lu from the indirect brun", + __func__, (unsigned long)indirblockno + i); return BEFS_ERR; } @@ -348,9 +349,10 @@ befs_find_brun_indirect(struct super_block *sb, brelse(indirblock); befs_debug(sb, - "<--- befs_find_brun_indirect() found " - "file block %lu at indirect[%d]", - blockno, j + (i * arraylen)); + "<--- %s found file block " + "%lu at indirect[%d]", __func__, + (unsigned long)blockno, + j + (i * arraylen)); return BEFS_OK; } sum += len; @@ -360,10 +362,10 @@ befs_find_brun_indirect(struct super_block *sb, } /* Only fallthrough is an error */ - befs_error(sb, "BeFS: befs_find_brun_indirect() failed to find " - "file block %lu", blockno); + befs_error(sb, "BeFS: %s failed to find " + "file block %lu", __func__, (unsigned long)blockno); - befs_debug(sb, "<--- befs_find_brun_indirect() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return BEFS_ERR; } @@ -444,7 +446,7 @@ befs_find_brun_dblindirect(struct super_block *sb, size_t diblklen = iblklen * befs_iaddrs_per_block(sb) * BEFS_DBLINDIR_BRUN_LEN; - befs_debug(sb, "---> befs_find_brun_dblindirect() find %lu", blockno); + befs_debug(sb, "---> %s find %lu", __func__, (unsigned long)blockno); /* First, discover which of the double_indir->indir blocks * contains pos. Then figure out how much of pos that @@ -460,8 +462,9 @@ befs_find_brun_dblindirect(struct super_block *sb, dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb); if (dbl_which_block > data->double_indirect.len) { befs_error(sb, "The double-indirect index calculated by " - "befs_read_brun_dblindirect(), %d, is outside the range " - "of the double-indirect block", dblindir_indx); + "%s, %d, is outside the range " + "of the double-indirect block", __func__, + dblindir_indx); return BEFS_ERR; } @@ -469,10 +472,10 @@ befs_find_brun_dblindirect(struct super_block *sb, befs_bread(sb, iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); if (dbl_indir_block == NULL) { - befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " - "double-indirect block at blockno %lu", - iaddr2blockno(sb, - &data->double_indirect) + + befs_error(sb, "%s couldn't read the " + "double-indirect block at blockno %lu", __func__, + (unsigned long) + iaddr2blockno(sb, &data->double_indirect) + dbl_which_block); brelse(dbl_indir_block); return BEFS_ERR; @@ -489,16 +492,16 @@ befs_find_brun_dblindirect(struct super_block *sb, which_block = indir_indx / befs_iaddrs_per_block(sb); if (which_block > indir_run.len) { befs_error(sb, "The indirect index calculated by " - "befs_read_brun_dblindirect(), %d, is outside the range " - "of the indirect block", indir_indx); + "%s, %d, is outside the range " + "of the indirect block", __func__, indir_indx); return BEFS_ERR; } indir_block = befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block); if (indir_block == NULL) { - befs_error(sb, "befs_read_brun_dblindirect() couldn't read the " - "indirect block at blockno %lu", + befs_error(sb, "%s couldn't read the indirect block " + "at blockno %lu", __func__, (unsigned long) iaddr2blockno(sb, &indir_run) + which_block); brelse(indir_block); return BEFS_ERR; @@ -519,7 +522,7 @@ befs_find_brun_dblindirect(struct super_block *sb, run->len -= offset; befs_debug(sb, "Found file block %lu in double_indirect[%d][%d]," - " double_indirect_leftover = %lu", + " double_indirect_leftover = %lu", (unsigned long) blockno, dblindir_indx, indir_indx, dblindir_leftover); return BEFS_OK; diff --git a/fs/befs/debug.c b/fs/befs/debug.c index 622e73775c83..4de7cffcd662 100644 --- a/fs/befs/debug.c +++ b/fs/befs/debug.c @@ -10,6 +10,7 @@ * debug functions */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #ifdef __KERNEL__ #include <stdarg.h> @@ -23,43 +24,30 @@ #include "befs.h" -#define ERRBUFSIZE 1024 - void befs_error(const struct super_block *sb, const char *fmt, ...) { + struct va_format vaf; va_list args; - char *err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL); - if (err_buf == NULL) { - printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); - return; - } va_start(args, fmt); - vsnprintf(err_buf, ERRBUFSIZE, fmt, args); + vaf.fmt = fmt; + vaf.va = &args; + pr_err("(%s): %pV\n", sb->s_id, &vaf); va_end(args); - - printk(KERN_ERR "BeFS(%s): %s\n", sb->s_id, err_buf); - kfree(err_buf); } void befs_warning(const struct super_block *sb, const char *fmt, ...) { + struct va_format vaf; va_list args; - char *err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL); - if (err_buf == NULL) { - printk(KERN_ERR "could not allocate %d bytes\n", ERRBUFSIZE); - return; - } va_start(args, fmt); - vsnprintf(err_buf, ERRBUFSIZE, fmt, args); + vaf.fmt = fmt; + vaf.va = &args; + pr_warn("(%s): %pV\n", sb->s_id, &vaf); va_end(args); - - printk(KERN_WARNING "BeFS(%s): %s\n", sb->s_id, err_buf); - - kfree(err_buf); } void @@ -67,25 +55,13 @@ befs_debug(const struct super_block *sb, const char *fmt, ...) { #ifdef CONFIG_BEFS_DEBUG + struct va_format vaf; va_list args; - char *err_buf = NULL; - - if (BEFS_SB(sb)->mount_opts.debug) { - err_buf = kmalloc(ERRBUFSIZE, GFP_KERNEL); - if (err_buf == NULL) { - printk(KERN_ERR "could not allocate %d bytes\n", - ERRBUFSIZE); - return; - } - - va_start(args, fmt); - vsnprintf(err_buf, ERRBUFSIZE, fmt, args); - va_end(args); - - printk(KERN_DEBUG "BeFS(%s): %s\n", sb->s_id, err_buf); - - kfree(err_buf); - } + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + pr_debug("(%s): %pV\n", sb->s_id, &vaf); + va_end(args); #endif //CONFIG_BEFS_DEBUG } @@ -109,9 +85,9 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode) befs_debug(sb, " gid %u", fs32_to_cpu(sb, inode->gid)); befs_debug(sb, " mode %08x", fs32_to_cpu(sb, inode->mode)); befs_debug(sb, " flags %08x", fs32_to_cpu(sb, inode->flags)); - befs_debug(sb, " create_time %Lu", + befs_debug(sb, " create_time %llu", fs64_to_cpu(sb, inode->create_time)); - befs_debug(sb, " last_modified_time %Lu", + befs_debug(sb, " last_modified_time %llu", fs64_to_cpu(sb, inode->last_modified_time)); tmp_run = fsrun_to_cpu(sb, inode->parent); @@ -137,7 +113,7 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode) tmp_run.allocation_group, tmp_run.start, tmp_run.len); } - befs_debug(sb, " max_direct_range %Lu", + befs_debug(sb, " max_direct_range %llu", fs64_to_cpu(sb, inode->data.datastream. max_direct_range)); @@ -147,7 +123,7 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode) tmp_run.allocation_group, tmp_run.start, tmp_run.len); - befs_debug(sb, " max_indirect_range %Lu", + befs_debug(sb, " max_indirect_range %llu", fs64_to_cpu(sb, inode->data.datastream. max_indirect_range)); @@ -158,12 +134,12 @@ befs_dump_inode(const struct super_block *sb, befs_inode * inode) tmp_run.allocation_group, tmp_run.start, tmp_run.len); - befs_debug(sb, " max_double_indirect_range %Lu", + befs_debug(sb, " max_double_indirect_range %llu", fs64_to_cpu(sb, inode->data.datastream. max_double_indirect_range)); - befs_debug(sb, " size %Lu", + befs_debug(sb, " size %llu", fs64_to_cpu(sb, inode->data.datastream.size)); } @@ -191,8 +167,8 @@ befs_dump_super_block(const struct super_block *sb, befs_super_block * sup) befs_debug(sb, " block_size %u", fs32_to_cpu(sb, sup->block_size)); befs_debug(sb, " block_shift %u", fs32_to_cpu(sb, sup->block_shift)); - befs_debug(sb, " num_blocks %Lu", fs64_to_cpu(sb, sup->num_blocks)); - befs_debug(sb, " used_blocks %Lu", fs64_to_cpu(sb, sup->used_blocks)); + befs_debug(sb, " num_blocks %llu", fs64_to_cpu(sb, sup->num_blocks)); + befs_debug(sb, " used_blocks %llu", fs64_to_cpu(sb, sup->used_blocks)); befs_debug(sb, " magic2 %08x", fs32_to_cpu(sb, sup->magic2)); befs_debug(sb, " blocks_per_ag %u", @@ -206,8 +182,8 @@ befs_dump_super_block(const struct super_block *sb, befs_super_block * sup) befs_debug(sb, " log_blocks %u, %hu, %hu", tmp_run.allocation_group, tmp_run.start, tmp_run.len); - befs_debug(sb, " log_start %Ld", fs64_to_cpu(sb, sup->log_start)); - befs_debug(sb, " log_end %Ld", fs64_to_cpu(sb, sup->log_end)); + befs_debug(sb, " log_start %lld", fs64_to_cpu(sb, sup->log_start)); + befs_debug(sb, " log_end %lld", fs64_to_cpu(sb, sup->log_end)); befs_debug(sb, " magic3 %08x", fs32_to_cpu(sb, sup->magic3)); diff --git a/fs/befs/inode.c b/fs/befs/inode.c index 94c17f9a9576..fa4b718de597 100644 --- a/fs/befs/inode.c +++ b/fs/befs/inode.c @@ -25,7 +25,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode, /* check magic header. */ if (magic1 != BEFS_INODE_MAGIC1) { befs_error(sb, - "Inode has a bad magic header - inode = %lu", inode); + "Inode has a bad magic header - inode = %lu", + (unsigned long)inode); return BEFS_BAD_INODE; } @@ -34,8 +35,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode, */ if (inode != iaddr2blockno(sb, &ino_num)) { befs_error(sb, "inode blocknr field disagrees with vfs " - "VFS: %lu, Inode %lu", - inode, iaddr2blockno(sb, &ino_num)); + "VFS: %lu, Inode %lu", (unsigned long) + inode, (unsigned long)iaddr2blockno(sb, &ino_num)); return BEFS_BAD_INODE; } @@ -44,7 +45,8 @@ befs_check_inode(struct super_block *sb, befs_inode * raw_inode, */ if (!(flags & BEFS_INODE_IN_USE)) { - befs_error(sb, "inode is not used - inode = %lu", inode); + befs_error(sb, "inode is not used - inode = %lu", + (unsigned long)inode); return BEFS_BAD_INODE; } diff --git a/fs/befs/io.c b/fs/befs/io.c index ddef98aa255d..0408a3d601d0 100644 --- a/fs/befs/io.c +++ b/fs/befs/io.c @@ -30,9 +30,9 @@ befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr) befs_blocknr_t block = 0; befs_sb_info *befs_sb = BEFS_SB(sb); - befs_debug(sb, "---> Enter befs_read_iaddr() " - "[%u, %hu, %hu]", - iaddr.allocation_group, iaddr.start, iaddr.len); + befs_debug(sb, "---> Enter %s " + "[%u, %hu, %hu]", __func__, iaddr.allocation_group, + iaddr.start, iaddr.len); if (iaddr.allocation_group > befs_sb->num_ags) { befs_error(sb, "BEFS: Invalid allocation group %u, max is %u", @@ -42,20 +42,21 @@ befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr) block = iaddr2blockno(sb, &iaddr); - befs_debug(sb, "befs_read_iaddr: offset = %lu", block); + befs_debug(sb, "%s: offset = %lu", __func__, (unsigned long)block); bh = sb_bread(sb, block); if (bh == NULL) { - befs_error(sb, "Failed to read block %lu", block); + befs_error(sb, "Failed to read block %lu", + (unsigned long)block); goto error; } - befs_debug(sb, "<--- befs_read_iaddr()"); + befs_debug(sb, "<--- %s", __func__); return bh; error: - befs_debug(sb, "<--- befs_read_iaddr() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return NULL; } @@ -64,20 +65,21 @@ befs_bread(struct super_block *sb, befs_blocknr_t block) { struct buffer_head *bh = NULL; - befs_debug(sb, "---> Enter befs_read() %Lu", block); + befs_debug(sb, "---> Enter %s %lu", __func__, (unsigned long)block); bh = sb_bread(sb, block); if (bh == NULL) { - befs_error(sb, "Failed to read block %lu", block); + befs_error(sb, "Failed to read block %lu", + (unsigned long)block); goto error; } - befs_debug(sb, "<--- befs_read()"); + befs_debug(sb, "<--- %s", __func__); return bh; error: - befs_debug(sb, "<--- befs_read() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return NULL; } diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 845d2d690ce2..5188f1222987 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -5,6 +5,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/slab.h> #include <linux/fs.h> @@ -39,7 +41,6 @@ static struct dentry *befs_lookup(struct inode *, struct dentry *, unsigned int) static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); -static int befs_init_inodecache(void); static void befs_destroy_inodecache(void); static void *befs_follow_link(struct dentry *, struct nameidata *); static void *befs_fast_follow_link(struct dentry *, struct nameidata *); @@ -131,26 +132,28 @@ befs_get_block(struct inode *inode, sector_t block, ulong disk_off; befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld", - inode->i_ino, block); + (unsigned long)inode->i_ino, (long)block); if (block < 0) { befs_error(sb, "befs_get_block() was asked for a block " "number less than zero: block %ld in inode %lu", - block, inode->i_ino); + (long)block, (unsigned long)inode->i_ino); return -EIO; } if (create) { befs_error(sb, "befs_get_block() was asked to write to " - "block %ld in inode %lu", block, inode->i_ino); + "block %ld in inode %lu", (long)block, + (unsigned long)inode->i_ino); return -EPERM; } res = befs_fblock2brun(sb, ds, block, &run); if (res != BEFS_OK) { befs_error(sb, - "<--- befs_get_block() for inode %lu, block " - "%ld ERROR", inode->i_ino, block); + "<--- %s for inode %lu, block %ld ERROR", + __func__, (unsigned long)inode->i_ino, + (long)block); return -EFBIG; } @@ -158,8 +161,9 @@ befs_get_block(struct inode *inode, sector_t block, map_bh(bh_result, inode->i_sb, disk_off); - befs_debug(sb, "<--- befs_get_block() for inode %lu, block %ld, " - "disk address %lu", inode->i_ino, block, disk_off); + befs_debug(sb, "<--- %s for inode %lu, block %ld, disk address %lu", + __func__, (unsigned long)inode->i_ino, (long)block, + (unsigned long)disk_off); return 0; } @@ -176,15 +180,15 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) char *utfname; const char *name = dentry->d_name.name; - befs_debug(sb, "---> befs_lookup() " - "name %s inode %ld", dentry->d_name.name, dir->i_ino); + befs_debug(sb, "---> %s name %s inode %ld", __func__, + dentry->d_name.name, dir->i_ino); /* Convert to UTF-8 */ if (BEFS_SB(sb)->nls) { ret = befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen); if (ret < 0) { - befs_debug(sb, "<--- befs_lookup() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return ERR_PTR(ret); } ret = befs_btree_find(sb, ds, utfname, &offset); @@ -195,12 +199,12 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) } if (ret == BEFS_BT_NOT_FOUND) { - befs_debug(sb, "<--- befs_lookup() %s not found", + befs_debug(sb, "<--- %s %s not found", __func__, dentry->d_name.name); return ERR_PTR(-ENOENT); } else if (ret != BEFS_OK || offset == 0) { - befs_warning(sb, "<--- befs_lookup() Error"); + befs_warning(sb, "<--- %s Error", __func__); return ERR_PTR(-ENODATA); } @@ -210,7 +214,7 @@ befs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) d_add(dentry, inode); - befs_debug(sb, "<--- befs_lookup()"); + befs_debug(sb, "<--- %s", __func__); return NULL; } @@ -228,26 +232,25 @@ befs_readdir(struct file *file, struct dir_context *ctx) char keybuf[BEFS_NAME_LEN + 1]; const char *dirname = file->f_path.dentry->d_name.name; - befs_debug(sb, "---> befs_readdir() " - "name %s, inode %ld, ctx->pos %Ld", - dirname, inode->i_ino, ctx->pos); + befs_debug(sb, "---> %s name %s, inode %ld, ctx->pos %lld", + __func__, dirname, inode->i_ino, ctx->pos); more: result = befs_btree_read(sb, ds, ctx->pos, BEFS_NAME_LEN + 1, keybuf, &keysize, &value); if (result == BEFS_ERR) { - befs_debug(sb, "<--- befs_readdir() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); befs_error(sb, "IO error reading %s (inode %lu)", dirname, inode->i_ino); return -EIO; } else if (result == BEFS_BT_END) { - befs_debug(sb, "<--- befs_readdir() END"); + befs_debug(sb, "<--- %s END", __func__); return 0; } else if (result == BEFS_BT_EMPTY) { - befs_debug(sb, "<--- befs_readdir() Empty directory"); + befs_debug(sb, "<--- %s Empty directory", __func__); return 0; } @@ -260,7 +263,7 @@ more: result = befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen); if (result < 0) { - befs_debug(sb, "<--- befs_readdir() ERROR"); + befs_debug(sb, "<--- %s ERROR", __func__); return result; } if (!dir_emit(ctx, nlsname, nlsnamelen, @@ -277,7 +280,7 @@ more: ctx->pos++; goto more; - befs_debug(sb, "<--- befs_readdir() pos %Ld", ctx->pos); + befs_debug(sb, "<--- %s pos %lld", __func__, ctx->pos); return 0; } @@ -321,7 +324,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) struct inode *inode; long ret = -EIO; - befs_debug(sb, "---> befs_read_inode() " "inode = %lu", ino); + befs_debug(sb, "---> %s inode = %lu", __func__, ino); inode = iget_locked(sb, ino); if (!inode) @@ -428,7 +431,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) } brelse(bh); - befs_debug(sb, "<--- befs_read_inode()"); + befs_debug(sb, "<--- %s", __func__); unlock_new_inode(inode); return inode; @@ -437,7 +440,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) unacquire_none: iget_failed(inode); - befs_debug(sb, "<--- befs_read_inode() - Bad inode"); + befs_debug(sb, "<--- %s - Bad inode", __func__); return ERR_PTR(ret); } @@ -445,7 +448,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) * * Taken from NFS implementation by Al Viro. */ -static int +static int __init befs_init_inodecache(void) { befs_inode_cachep = kmem_cache_create("befs_inode_cache", @@ -454,11 +457,9 @@ befs_init_inodecache(void) SLAB_MEM_SPREAD), init_once); if (befs_inode_cachep == NULL) { - printk(KERN_ERR "befs_init_inodecache: " - "Couldn't initialize inode slabcache\n"); + pr_err("%s: Couldn't initialize inode slabcache\n", __func__); return -ENOMEM; } - return 0; } @@ -544,16 +545,16 @@ befs_utf2nls(struct super_block *sb, const char *in, */ int maxlen = in_len + 1; - befs_debug(sb, "---> utf2nls()"); + befs_debug(sb, "---> %s", __func__); if (!nls) { - befs_error(sb, "befs_utf2nls called with no NLS table loaded"); + befs_error(sb, "%s called with no NLS table loaded", __func__); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { - befs_error(sb, "befs_utf2nls() cannot allocate memory"); + befs_error(sb, "%s cannot allocate memory", __func__); *out_len = 0; return -ENOMEM; } @@ -575,14 +576,14 @@ befs_utf2nls(struct super_block *sb, const char *in, result[o] = '\0'; *out_len = o; - befs_debug(sb, "<--- utf2nls()"); + befs_debug(sb, "<--- %s", __func__); return o; conv_err: befs_error(sb, "Name using character set %s contains a character that " "cannot be converted to unicode.", nls->charset); - befs_debug(sb, "<--- utf2nls()"); + befs_debug(sb, "<--- %s", __func__); kfree(result); return -EILSEQ; } @@ -623,16 +624,17 @@ befs_nls2utf(struct super_block *sb, const char *in, * in special cases */ int maxlen = (3 * in_len) + 1; - befs_debug(sb, "---> nls2utf()\n"); + befs_debug(sb, "---> %s\n", __func__); if (!nls) { - befs_error(sb, "befs_nls2utf called with no NLS table loaded."); + befs_error(sb, "%s called with no NLS table loaded.", + __func__); return -EINVAL; } *out = result = kmalloc(maxlen, GFP_NOFS); if (!*out) { - befs_error(sb, "befs_nls2utf() cannot allocate memory"); + befs_error(sb, "%s cannot allocate memory", __func__); *out_len = 0; return -ENOMEM; } @@ -653,14 +655,14 @@ befs_nls2utf(struct super_block *sb, const char *in, result[o] = '\0'; *out_len = o; - befs_debug(sb, "<--- nls2utf()"); + befs_debug(sb, "<--- %s", __func__); return i; conv_err: befs_error(sb, "Name using charecter set %s contains a charecter that " "cannot be converted to unicode.", nls->charset); - befs_debug(sb, "<--- nls2utf()"); + befs_debug(sb, "<--- %s", __func__); kfree(result); return -EILSEQ; } @@ -715,8 +717,8 @@ parse_options(char *options, befs_mount_options * opts) if (option >= 0) uid = make_kuid(current_user_ns(), option); if (!uid_valid(uid)) { - printk(KERN_ERR "BeFS: Invalid uid %d, " - "using default\n", option); + pr_err("Invalid uid %d, " + "using default\n", option); break; } opts->uid = uid; @@ -729,8 +731,8 @@ parse_options(char *options, befs_mount_options * opts) if (option >= 0) gid = make_kgid(current_user_ns(), option); if (!gid_valid(gid)) { - printk(KERN_ERR "BeFS: Invalid gid %d, " - "using default\n", option); + pr_err("Invalid gid %d, " + "using default\n", option); break; } opts->gid = gid; @@ -740,8 +742,8 @@ parse_options(char *options, befs_mount_options * opts) kfree(opts->iocharset); opts->iocharset = match_strdup(&args[0]); if (!opts->iocharset) { - printk(KERN_ERR "BeFS: allocation failure for " - "iocharset string\n"); + pr_err("allocation failure for " + "iocharset string\n"); return 0; } break; @@ -749,8 +751,8 @@ parse_options(char *options, befs_mount_options * opts) opts->debug = 1; break; default: - printk(KERN_ERR "BeFS: Unrecognized mount option \"%s\" " - "or missing value\n", p); + pr_err("Unrecognized mount option \"%s\" " + "or missing value\n", p); return 0; } } @@ -791,22 +793,20 @@ befs_fill_super(struct super_block *sb, void *data, int silent) save_mount_options(sb, data); - sb->s_fs_info = kmalloc(sizeof (*befs_sb), GFP_KERNEL); + sb->s_fs_info = kzalloc(sizeof(*befs_sb), GFP_KERNEL); if (sb->s_fs_info == NULL) { - printk(KERN_ERR - "BeFS(%s): Unable to allocate memory for private " + pr_err("(%s): Unable to allocate memory for private " "portion of superblock. Bailing.\n", sb->s_id); goto unacquire_none; } befs_sb = BEFS_SB(sb); - memset(befs_sb, 0, sizeof(befs_sb_info)); if (!parse_options((char *) data, &befs_sb->mount_opts)) { befs_error(sb, "cannot parse mount options"); goto unacquire_priv_sbp; } - befs_debug(sb, "---> befs_fill_super()"); + befs_debug(sb, "---> %s", __func__); #ifndef CONFIG_BEFS_RW if (!(sb->s_flags & MS_RDONLY)) { @@ -854,7 +854,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent) goto unacquire_priv_sbp; if( befs_sb->num_blocks > ~((sector_t)0) ) { - befs_error(sb, "blocks count: %Lu " + befs_error(sb, "blocks count: %llu " "is larger than the host can use", befs_sb->num_blocks); goto unacquire_priv_sbp; @@ -924,7 +924,7 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf) struct super_block *sb = dentry->d_sb; u64 id = huge_encode_dev(sb->s_bdev->bd_dev); - befs_debug(sb, "---> befs_statfs()"); + befs_debug(sb, "---> %s", __func__); buf->f_type = BEFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; @@ -937,7 +937,7 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_fsid.val[1] = (u32)(id >> 32); buf->f_namelen = BEFS_NAME_LEN; - befs_debug(sb, "<--- befs_statfs()"); + befs_debug(sb, "<--- %s", __func__); return 0; } @@ -963,7 +963,7 @@ init_befs_fs(void) { int err; - printk(KERN_INFO "BeFS version: %s\n", BEFS_VERSION); + pr_info("version: %s\n", BEFS_VERSION); err = befs_init_inodecache(); if (err) diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 8defc6b3f9a2..29aa5cf6639b 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -172,7 +172,7 @@ static void bfs_evict_inode(struct inode *inode) dprintf("ino=%08lx\n", ino); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); invalidate_inode_buffers(inode); clear_inode(inode); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 67be2951b98a..0f59799fa105 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -46,10 +46,15 @@ #endif static int load_elf_binary(struct linux_binprm *bprm); -static int load_elf_library(struct file *); static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long); +#ifdef CONFIG_USELIB +static int load_elf_library(struct file *); +#else +#define load_elf_library NULL +#endif + /* * If we don't support core dumping, then supply a NULL so we * don't even try. @@ -1005,6 +1010,7 @@ out_free_ph: goto out; } +#ifdef CONFIG_USELIB /* This is really simpleminded and specialized - we are loading an a.out library that is given an ELF header. */ static int load_elf_library(struct file *file) @@ -1083,6 +1089,7 @@ out_free_ph: out: return error; } +#endif /* #ifdef CONFIG_USELIB */ #ifdef CONFIG_ELF_CORE /* diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 1c740e152f38..b60500300dd7 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -656,6 +656,7 @@ static ssize_t bm_status_write(struct file * file, const char __user * buffer, mutex_unlock(&root->d_inode->i_mutex); dput(root); + break; default: return res; } return count; diff --git a/fs/block_dev.c b/fs/block_dev.c index 1e86823a9cbd..ba0d2b05bb78 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -83,7 +83,7 @@ void kill_bdev(struct block_device *bdev) { struct address_space *mapping = bdev->bd_inode->i_mapping; - if (mapping->nrpages == 0) + if (mapping->nrpages == 0 && mapping->nrshadows == 0) return; invalidate_bh_lrus(); @@ -419,7 +419,7 @@ static void bdev_evict_inode(struct inode *inode) { struct block_device *bdev = &BDEV_I(inode)->bdev; struct list_head *p; - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); invalidate_inode_buffers(inode); /* is it needed here? */ clear_inode(inode); spin_lock(&bdev_lock); @@ -1523,7 +1523,7 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, ssize_t err; err = generic_write_sync(file, pos, ret); - if (err < 0 && ret > 0) + if (err < 0) ret = err; } blk_finish_plug(&plug); diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index b01fb6c527e3..d43c544d3b68 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -472,7 +472,7 @@ static noinline int add_ra_bio_pages(struct inode *inode, rcu_read_lock(); page = radix_tree_lookup(&mapping->page_tree, pg_index); rcu_read_unlock(); - if (page) { + if (page && !radix_tree_exceptional_entry(page)) { misses++; if (misses > 4) break; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0165b8672f09..7331a230e30b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1797,7 +1797,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, BTRFS_I(inode)->last_sub_trans = root->log_transid; if (num_written > 0) { err = generic_write_sync(file, pos, num_written); - if (err < 0 && num_written > 0) + if (err < 0) num_written = err; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d3d44486290b..49ec1398879f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4593,7 +4593,7 @@ static void evict_inode_truncate_pages(struct inode *inode) struct rb_node *node; ASSERT(inode->i_state & I_FREEING); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); write_lock(&map_tree->lock); while (!RB_EMPTY_ROOT(&map_tree->map)) { diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index ebaff368120d..4b1fb5ca65b8 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c @@ -265,24 +265,22 @@ static int cachefiles_read_backing_file_one(struct cachefiles_object *object, goto nomem_monitor; } - ret = add_to_page_cache(newpage, bmapping, - netpage->index, cachefiles_gfp); + ret = add_to_page_cache_lru(newpage, bmapping, + netpage->index, cachefiles_gfp); if (ret == 0) goto installed_new_backing_page; if (ret != -EEXIST) goto nomem_page; } - /* we've installed a new backing page, so now we need to add it - * to the LRU list and start it reading */ + /* we've installed a new backing page, so now we need to start + * it reading */ installed_new_backing_page: _debug("- new %p", newpage); backpage = newpage; newpage = NULL; - lru_cache_add_file(backpage); - read_backing_page: ret = bmapping->a_ops->readpage(NULL, backpage); if (ret < 0) @@ -510,24 +508,23 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, goto nomem; } - ret = add_to_page_cache(newpage, bmapping, - netpage->index, cachefiles_gfp); + ret = add_to_page_cache_lru(newpage, bmapping, + netpage->index, + cachefiles_gfp); if (ret == 0) goto installed_new_backing_page; if (ret != -EEXIST) goto nomem; } - /* we've installed a new backing page, so now we need to add it - * to the LRU list and start it reading */ + /* we've installed a new backing page, so now we need + * to start it reading */ installed_new_backing_page: _debug("- new %p", newpage); backpage = newpage; newpage = NULL; - lru_cache_add_file(backpage); - reread_backing_page: ret = bmapping->a_ops->readpage(NULL, backpage); if (ret < 0) @@ -538,8 +535,8 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, monitor_backing_page: _debug("- monitor add"); - ret = add_to_page_cache(netpage, op->mapping, netpage->index, - cachefiles_gfp); + ret = add_to_page_cache_lru(netpage, op->mapping, + netpage->index, cachefiles_gfp); if (ret < 0) { if (ret == -EEXIST) { page_cache_release(netpage); @@ -549,8 +546,6 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, goto nomem; } - lru_cache_add_file(netpage); - /* install a monitor */ page_cache_get(netpage); monitor->netfs_page = netpage; @@ -613,8 +608,8 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, backing_page_already_uptodate: _debug("- uptodate"); - ret = add_to_page_cache(netpage, op->mapping, netpage->index, - cachefiles_gfp); + ret = add_to_page_cache_lru(netpage, op->mapping, + netpage->index, cachefiles_gfp); if (ret < 0) { if (ret == -EEXIST) { page_cache_release(netpage); @@ -631,8 +626,6 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, fscache_mark_page_cached(op, netpage); - lru_cache_add_file(netpage); - /* the netpage is unlocked and marked up to date here */ fscache_end_io(op, netpage, 0); page_cache_release(netpage); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 849f6132b327..ab8ad2546c3e 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -286,7 +286,7 @@ cifs_destroy_inode(struct inode *inode) static void cifs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); cifs_fscache_release_inode_cookie(inode); } @@ -1005,7 +1005,7 @@ cifs_init_once(void *inode) init_rwsem(&cifsi->lock_sem); } -static int +static int __init cifs_init_inodecache(void) { cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h index b7143cf783ac..381c993b1427 100644 --- a/fs/coda/coda_int.h +++ b/fs/coda/coda_int.h @@ -10,7 +10,7 @@ extern int coda_hard; extern int coda_fake_statfs; void coda_destroy_inodecache(void); -int coda_init_inodecache(void); +int __init coda_init_inodecache(void); int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync); void coda_sysctl_init(void); void coda_sysctl_clean(void); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 506de34a4ef3..626abc02b694 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -73,7 +73,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -int coda_init_inodecache(void) +int __init coda_init_inodecache(void) { coda_inode_cachep = kmem_cache_create("coda_inode_cache", sizeof(struct coda_inode_info), @@ -250,7 +250,7 @@ static void coda_put_super(struct super_block *sb) static void coda_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); coda_cache_clear_inode(inode); } diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 06610cf94d57..a1f801c14fbc 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -195,8 +195,7 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i struct page *page = NULL; if (blocknr + i < devsize) { - page = read_mapping_page_async(mapping, blocknr + i, - NULL); + page = read_mapping_page(mapping, blocknr + i, NULL); /* synchronous error? */ if (IS_ERR(page)) page = NULL; diff --git a/fs/direct-io.c b/fs/direct-io.c index 160a5489a939..6e6bff375244 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -664,7 +664,6 @@ static inline int dio_new_bio(struct dio *dio, struct dio_submit *sdio, goto out; sector = start_sector << (sdio->blkbits - 9); nr_pages = min(sdio->pages_in_io, bio_get_nr_vecs(map_bh->b_bdev)); - nr_pages = min(nr_pages, BIO_MAX_PAGES); BUG_ON(nr_pages <= 0); dio_bio_alloc(dio, sdio, map_bh->b_bdev, sector, nr_pages); sdio->boundary = 0; diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 9fd702f5bfb2..9280202e488c 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -59,10 +59,22 @@ int drop_caches_sysctl_handler(ctl_table *table, int write, if (ret) return ret; if (write) { - if (sysctl_drop_caches & 1) + static int stfu; + + if (sysctl_drop_caches & 1) { iterate_supers(drop_pagecache_sb, NULL); - if (sysctl_drop_caches & 2) + count_vm_event(DROP_PAGECACHE); + } + if (sysctl_drop_caches & 2) { drop_slab(); + count_vm_event(DROP_SLAB); + } + if (!stfu) { + pr_info("%s (%d): drop_caches: %d\n", + current->comm, task_pid_nr(current), + sysctl_drop_caches); + } + stfu |= sysctl_drop_caches & 4; } return 0; } diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index e879cf8ff0b1..afa1b81c3418 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -132,7 +132,7 @@ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) */ static void ecryptfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); iput(ecryptfs_inode_to_lower(inode)); } diff --git a/fs/efs/super.c b/fs/efs/super.c index 50215bbd6463..f8def1acf08c 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -91,7 +91,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { efs_inode_cachep = kmem_cache_create("efs_inode_cache", sizeof(struct efs_inode_info), diff --git a/fs/exec.c b/fs/exec.c index 4f59402fdda5..25dfeba6d55f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -97,6 +97,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt) module_put(fmt->module); } +#ifdef CONFIG_USELIB /* * Note that a shared library must be both readable and executable due to * security reasons. @@ -156,6 +157,7 @@ exit: out: return error; } +#endif /* #ifdef CONFIG_USELIB */ #ifdef CONFIG_MMU /* diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index ee4317faccb1..d1c244d67667 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1486,7 +1486,7 @@ void exofs_evict_inode(struct inode *inode) struct ore_io_state *ios; int ret; - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); /* TODO: should do better here */ if (inode->i_nlink || is_bad_inode(inode)) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 94ed36849b71..b1d2a4675d42 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -78,7 +78,7 @@ void ext2_evict_inode(struct inode * inode) dquot_drop(inode); } - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (want_delete) { sb_start_intwrite(inode->i_sb); diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 384b6ebb655f..efce2bbfb5e5 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -228,7 +228,7 @@ void ext3_evict_inode (struct inode *inode) log_wait_commit(journal, commit_tid); filemap_write_and_wait(&inode->i_data); } - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); ext3_discard_reservation(inode); rsv = ei->i_block_alloc_info; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 1a5073959f32..6db7f7db7777 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -153,7 +153,7 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov, ssize_t err; err = generic_write_sync(file, iocb->ki_pos - ret, ret); - if (err < 0 && ret > 0) + if (err < 0) ret = err; } blk_finish_plug(&plug); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 24bfd7ff3049..175c3f933816 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -215,7 +215,7 @@ void ext4_evict_inode(struct inode *inode) jbd2_complete_transaction(journal, commit_tid); filemap_write_and_wait(&inode->i_data); } - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count)); goto no_delete; @@ -226,7 +226,7 @@ void ext4_evict_inode(struct inode *inode) if (ext4_should_order_data(inode)) ext4_begin_ordered_truncate(inode, 0); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count)); if (is_bad_inode(inode)) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 4d67ed736dca..28cea76d78c6 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -260,7 +260,7 @@ void f2fs_evict_inode(struct inode *inode) struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); trace_f2fs_evict_inode(inode); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (inode->i_ino == F2FS_NODE_INO(sbi) || inode->i_ino == F2FS_META_INO(sbi)) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 854b578f6695..c68d9f27135e 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -490,7 +490,7 @@ EXPORT_SYMBOL_GPL(fat_build_inode); static void fat_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (!inode->i_nlink) { inode->i_size = 0; fat_truncate_blocks(inode, 0); diff --git a/fs/filesystems.c b/fs/filesystems.c index 92567d95ba6a..5797d45a78cb 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -121,6 +121,7 @@ int unregister_filesystem(struct file_system_type * fs) EXPORT_SYMBOL(unregister_filesystem); +#ifdef CONFIG_SYSFS_SYSCALL static int fs_index(const char __user * __name) { struct file_system_type * tmp; @@ -199,6 +200,7 @@ SYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2) } return retval; } +#endif int __init get_filesystem_list(char *buf) { diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index f47df72cef17..363e3ae25f6b 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -354,7 +354,7 @@ static void vxfs_i_callback(struct rcu_head *head) void vxfs_evict_inode(struct inode *ip) { - truncate_inode_pages(&ip->i_data, 0); + truncate_inode_pages_final(&ip->i_data); clear_inode(ip); call_rcu(&ip->i_rcu, vxfs_i_callback); } diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index 25d4099a4aea..99c7f0a37af4 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -192,7 +192,7 @@ vxfs_inode_by_name(struct inode *dip, struct dentry *dp) * vxfs_lookup - lookup pathname component * @dip: dir in which we lookup * @dp: dentry we lookup - * @nd: lookup nameidata + * @flags: lookup flags * * Description: * vxfs_lookup tries to lookup the pathname component described diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d754e3cf99a8..a16315957ef3 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -89,16 +89,29 @@ static inline struct inode *wb_inode(struct list_head *head) #define CREATE_TRACE_POINTS #include <trace/events/writeback.h> +static void bdi_wakeup_thread(struct backing_dev_info *bdi) +{ + spin_lock_bh(&bdi->wb_lock); + if (test_bit(BDI_registered, &bdi->state)) + mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); + spin_unlock_bh(&bdi->wb_lock); +} + static void bdi_queue_work(struct backing_dev_info *bdi, struct wb_writeback_work *work) { trace_writeback_queue(bdi, work); spin_lock_bh(&bdi->wb_lock); + if (!test_bit(BDI_registered, &bdi->state)) { + if (work->done) + complete(work->done); + goto out_unlock; + } list_add_tail(&work->list, &bdi->work_list); - spin_unlock_bh(&bdi->wb_lock); - mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); +out_unlock: + spin_unlock_bh(&bdi->wb_lock); } static void @@ -114,7 +127,7 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { trace_writeback_nowork(bdi); - mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); + bdi_wakeup_thread(bdi); return; } @@ -161,7 +174,7 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi) * writeback as soon as there is no other work to do. */ trace_writeback_wake_background(bdi); - mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); + bdi_wakeup_thread(bdi); } /* @@ -1017,7 +1030,7 @@ void bdi_writeback_workfn(struct work_struct *work) current->flags |= PF_SWAPWRITE; if (likely(!current_is_workqueue_rescuer() || - list_empty(&bdi->bdi_list))) { + !test_bit(BDI_registered, &bdi->state))) { /* * The normal path. Keep writing back @bdi until its * work_list is empty. Note that this path is also taken @@ -1039,10 +1052,10 @@ void bdi_writeback_workfn(struct work_struct *work) trace_writeback_pages_written(pages_written); } - if (!list_empty(&bdi->work_list) || - (wb_has_dirty_io(wb) && dirty_writeback_interval)) - queue_delayed_work(bdi_wq, &wb->dwork, - msecs_to_jiffies(dirty_writeback_interval * 10)); + if (!list_empty(&bdi->work_list)) + mod_delayed_work(bdi_wq, &wb->dwork, 0); + else if (wb_has_dirty_io(wb) && dirty_writeback_interval) + bdi_wakeup_thread_delayed(bdi); current->flags &= ~PF_SWAPWRITE; } diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d468643a68b2..9c761b611c54 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -123,7 +123,7 @@ static void fuse_destroy_inode(struct inode *inode) static void fuse_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (inode->i_sb->s_flags & MS_ACTIVE) { struct fuse_conn *fc = get_fuse_conn(inode); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 60f60f6181f3..24410cd9a82a 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1558,7 +1558,7 @@ out_unlock: fs_warn(sdp, "gfs2_evict_inode: %d\n", error); out: /* Case 3 starts here */ - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); gfs2_rs_delete(ip, NULL); gfs2_ordered_del_inode(ip); clear_inode(inode); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 380ab31b5e0f..9e2fecd62f62 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -547,7 +547,7 @@ out: void hfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) { HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL; diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c index 0f47890299c4..caf89a7be0a1 100644 --- a/fs/hfsplus/attributes.c +++ b/fs/hfsplus/attributes.c @@ -11,7 +11,7 @@ static struct kmem_cache *hfsplus_attr_tree_cachep; -int hfsplus_create_attr_tree_cache(void) +int __init hfsplus_create_attr_tree_cache(void) { if (hfsplus_attr_tree_cachep) return -EEXIST; diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index fbb212fbb1ef..a7aafb35b624 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -227,10 +227,8 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, u32 ablock, dblock, mask; sector_t sector; int was_dirty = 0; - int shift; /* Convert inode block to disk allocation block */ - shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; ablock = iblock >> sbi->fs_shift; if (iblock >= hip->fs_blocks) { @@ -498,11 +496,13 @@ int hfsplus_file_extend(struct inode *inode) goto insert_extent; } out: - mutex_unlock(&hip->extents_lock); if (!res) { hip->alloc_blocks += len; + mutex_unlock(&hip->extents_lock); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); + return 0; } + mutex_unlock(&hip->extents_lock); return res; insert_extent: @@ -556,11 +556,13 @@ void hfsplus_file_truncate(struct inode *inode) blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> HFSPLUS_SB(sb)->alloc_blksz_shift; + + mutex_lock(&hip->extents_lock); + alloc_cnt = hip->alloc_blocks; if (blk_cnt == alloc_cnt) - goto out; + goto out_unlock; - mutex_lock(&hip->extents_lock); res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); if (res) { mutex_unlock(&hip->extents_lock); @@ -592,10 +594,10 @@ void hfsplus_file_truncate(struct inode *inode) hfs_brec_remove(&fd); } hfs_find_exit(&fd); - mutex_unlock(&hip->extents_lock); hip->alloc_blocks = blk_cnt; -out: +out_unlock: + mutex_unlock(&hip->extents_lock); hip->phys_size = inode->i_size; hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 62d571eb69ba..83dc29286b10 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -367,7 +367,7 @@ typedef int (*search_strategy_t)(struct hfs_bnode *, */ /* attributes.c */ -int hfsplus_create_attr_tree_cache(void); +int __init hfsplus_create_attr_tree_cache(void); void hfsplus_destroy_attr_tree_cache(void); hfsplus_attr_entry *hfsplus_alloc_attr_entry(void); void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry_p); diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 80875aa640ef..a6abf87d79d0 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -161,7 +161,7 @@ static int hfsplus_write_inode(struct inode *inode, static void hfsplus_evict_inode(struct inode *inode) { hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (HFSPLUS_IS_RSRC(inode)) { HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL; diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index fe649d325b1f..9c470fde9878 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -230,7 +230,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) static void hostfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (HOSTFS_I(inode)->fd != -1) { close_file(&HOSTFS_I(inode)->fd); diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 9edeeb0ea97e..50a427313835 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -304,7 +304,7 @@ void hpfs_write_if_changed(struct inode *inode) void hpfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (!inode->i_nlink) { hpfs_lock(inode->i_sb); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index d19b30ababf1..204027520937 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -366,7 +366,13 @@ static void truncate_hugepages(struct inode *inode, loff_t lstart) static void hugetlbfs_evict_inode(struct inode *inode) { + struct resv_map *resv_map; + truncate_hugepages(inode, 0); + resv_map = (struct resv_map *)inode->i_mapping->private_data; + /* root inode doesn't have the resv_map, so we should check it */ + if (resv_map) + resv_map_release(&resv_map->refs); clear_inode(inode); } @@ -476,6 +482,11 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev) { struct inode *inode; + struct resv_map *resv_map; + + resv_map = resv_map_alloc(); + if (!resv_map) + return NULL; inode = new_inode(sb); if (inode) { @@ -487,7 +498,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - INIT_LIST_HEAD(&inode->i_mapping->private_list); + inode->i_mapping->private_data = resv_map; info = HUGETLBFS_I(inode); /* * The policy is initialized here even if we are creating a @@ -517,7 +528,9 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, break; } lockdep_annotate_inode_mutex_key(inode); - } + } else + kref_put(&resv_map->refs, resv_map_release); + return inode; } diff --git a/fs/inode.c b/fs/inode.c index 4bcdad3c9361..e6905152c39f 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -503,6 +503,7 @@ void clear_inode(struct inode *inode) */ spin_lock_irq(&inode->i_data.tree_lock); BUG_ON(inode->i_data.nrpages); + BUG_ON(inode->i_data.nrshadows); spin_unlock_irq(&inode->i_data.tree_lock); BUG_ON(!list_empty(&inode->i_data.private_list)); BUG_ON(!(inode->i_state & I_FREEING)); @@ -548,8 +549,7 @@ static void evict(struct inode *inode) if (op->evict_inode) { op->evict_inode(inode); } else { - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); } if (S_ISBLK(inode->i_mode) && inode->i_bdev) diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index a69e426435dd..f73991522672 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -242,7 +242,7 @@ void jffs2_evict_inode (struct inode *inode) jffs2_dbg(1, "%s(): ino #%lu mode %o\n", __func__, inode->i_ino, inode->i_mode); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); jffs2_do_clear_inode(c, f); } @@ -687,7 +687,7 @@ unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, struct inode *inode = OFNI_EDONI_2SFFJ(f); struct page *pg; - pg = read_cache_page_async(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, + pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); if (IS_ERR(pg)) return (void *)pg; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index f4aab719add5..6f8fe72c2a7a 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -154,7 +154,7 @@ void jfs_evict_inode(struct inode *inode) dquot_initialize(inode); if (JFS_IP(inode)->fileset == FILESYSTEM_I) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (test_cflag(COMMIT_Freewmap, inode)) jfs_free_zero_link(inode); @@ -168,7 +168,7 @@ void jfs_evict_inode(struct inode *inode) dquot_free_inode(inode); } } else { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); } clear_inode(inode); dquot_drop(inode); diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index e55126f85bd2..abb0f1f53d93 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -355,7 +355,7 @@ void kernfs_evict_inode(struct inode *inode) { struct kernfs_node *kn = inode->i_private; - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); kernfs_put(kn); } diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 9a59cbade2fb..48140315f627 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -2180,7 +2180,7 @@ void logfs_evict_inode(struct inode *inode) do_delete_inode(inode); } } - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); /* Cheaper version of write_inode. All changes are concealed in diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 0332109162a5..0ad2ec9601de 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -26,7 +26,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data); static void minix_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (!inode->i_nlink) { inode->i_size = 0; minix_truncate(inode); @@ -86,7 +86,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { minix_inode_cachep = kmem_cache_create("minix_inode_cache", sizeof(struct minix_inode_info), diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 2cf2ebecb55f..ee59d35ff069 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -296,7 +296,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) static void ncp_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (S_ISDIR(inode->i_mode)) { diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 56ff823ca82e..65d849bdf77a 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -1213,7 +1213,7 @@ static u64 pnfs_num_cont_bytes(struct inode *inode, pgoff_t idx) end = DIV_ROUND_UP(i_size_read(inode), PAGE_CACHE_SIZE); if (end != NFS_I(inode)->npages) { rcu_read_lock(); - end = radix_tree_next_hole(&mapping->page_tree, idx + 1, ULONG_MAX); + end = page_cache_next_hole(mapping, idx + 1, ULONG_MAX); rcu_read_unlock(); } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 360114ae8b82..c4702baa22b8 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(nfs_clear_inode); void nfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); nfs_clear_inode(inode); } diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 808f29574412..6f340f02f2ba 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -90,7 +90,7 @@ static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc) */ static void nfs4_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); pnfs_return_layout(inode); pnfs_destroy_layout(NFS_I(inode)); diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 06cddd572264..2645be435e75 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -71,10 +71,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (gid_eq(new->fsgid, INVALID_GID)) new->fsgid = exp->ex_anon_gid; - ret = set_groups(new, gi); + set_groups(new, gi); put_group_info(gi); - if (ret < 0) - goto error; if (!uid_eq(new->fsuid, GLOBAL_ROOT_UID)) new->cap_effective = cap_drop_nfsd_set(new->cap_effective); @@ -89,7 +87,6 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) oom: ret = -ENOMEM; -error: abort_creds(new); return ret; } diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c index deaa3d33a0aa..0d58075f34e2 100644 --- a/fs/nilfs2/cpfile.c +++ b/fs/nilfs2/cpfile.c @@ -942,6 +942,18 @@ int nilfs_cpfile_read(struct super_block *sb, size_t cpsize, struct inode *cpfile; int err; + if (cpsize > sb->s_blocksize) { + printk(KERN_ERR + "NILFS: too large checkpoint size: %zu bytes.\n", + cpsize); + return -EINVAL; + } else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) { + printk(KERN_ERR + "NILFS: too small checkpoint size: %zu bytes.\n", + cpsize); + return -EINVAL; + } + cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO); if (unlikely(!cpfile)) return -ENOMEM; diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c index fa0f80308c2d..0d5fada91191 100644 --- a/fs/nilfs2/dat.c +++ b/fs/nilfs2/dat.c @@ -484,6 +484,18 @@ int nilfs_dat_read(struct super_block *sb, size_t entry_size, struct nilfs_dat_info *di; int err; + if (entry_size > sb->s_blocksize) { + printk(KERN_ERR + "NILFS: too large DAT entry size: %zu bytes.\n", + entry_size); + return -EINVAL; + } else if (entry_size < NILFS_MIN_DAT_ENTRY_SIZE) { + printk(KERN_ERR + "NILFS: too small DAT entry size: %zu bytes.\n", + entry_size); + return -EINVAL; + } + dat = nilfs_iget_locked(sb, NULL, NILFS_DAT_INO); if (unlikely(!dat)) return -ENOMEM; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 7e350c562e0e..b9c5726120e3 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -783,16 +783,14 @@ void nilfs_evict_inode(struct inode *inode) int ret; if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) { - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); nilfs_clear_inode(inode); return; } nilfs_transaction_begin(sb, &ti, 0); /* never fails */ - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); /* TODO: some of the following operations may fail. */ nilfs_truncate_bmap(ii, 0); diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 2b34021948e4..422fb54b7377 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -1072,6 +1072,48 @@ out: } /** + * nilfs_ioctl_trim_fs() - trim ioctl handle function + * @inode: inode object + * @argp: pointer on argument from userspace + * + * Decription: nilfs_ioctl_trim_fs is the FITRIM ioctl handle function. It + * checks the arguments from userspace and calls nilfs_sufile_trim_fs, which + * performs the actual trim operation. + * + * Return Value: On success, 0 is returned or negative error code, otherwise. + */ +static int nilfs_ioctl_trim_fs(struct inode *inode, void __user *argp) +{ + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct request_queue *q = bdev_get_queue(nilfs->ns_bdev); + struct fstrim_range range; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!blk_queue_discard(q)) + return -EOPNOTSUPP; + + if (copy_from_user(&range, argp, sizeof(range))) + return -EFAULT; + + range.minlen = max_t(u64, range.minlen, q->limits.discard_granularity); + + down_read(&nilfs->ns_segctor_sem); + ret = nilfs_sufile_trim_fs(nilfs->ns_sufile, &range); + up_read(&nilfs->ns_segctor_sem); + + if (ret < 0) + return ret; + + if (copy_to_user(argp, &range, sizeof(range))) + return -EFAULT; + + return 0; +} + +/** * nilfs_ioctl_set_alloc_range - limit range of segments to be allocated * @inode: inode object * @argp: pointer on argument from userspace @@ -1163,6 +1205,95 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, return ret; } +/** + * nilfs_ioctl_set_suinfo - set segment usage info + * @inode: inode object + * @filp: file object + * @cmd: ioctl's request code + * @argp: pointer on argument from userspace + * + * Description: Expects an array of nilfs_suinfo_update structures + * encapsulated in nilfs_argv and updates the segment usage info + * according to the flags in nilfs_suinfo_update. + * + * Return Value: On success, 0 is returned. On error, one of the + * following negative error codes is returned. + * + * %-EPERM - Not enough permissions + * + * %-EFAULT - Error copying input data + * + * %-EIO - I/O error. + * + * %-ENOMEM - Insufficient amount of memory available. + * + * %-EINVAL - Invalid values in input (segment number, flags or nblocks) + */ +static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp, + unsigned int cmd, void __user *argp) +{ + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; + struct nilfs_transaction_info ti; + struct nilfs_argv argv; + size_t len; + void __user *base; + void *kbuf; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + ret = -EFAULT; + if (copy_from_user(&argv, argp, sizeof(argv))) + goto out; + + ret = -EINVAL; + if (argv.v_size < sizeof(struct nilfs_suinfo_update)) + goto out; + + if (argv.v_nmembs > nilfs->ns_nsegments) + goto out; + + if (argv.v_nmembs >= UINT_MAX / argv.v_size) + goto out; + + len = argv.v_size * argv.v_nmembs; + if (!len) { + ret = 0; + goto out; + } + + base = (void __user *)(unsigned long)argv.v_base; + kbuf = vmalloc(len); + if (!kbuf) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(kbuf, base, len)) { + ret = -EFAULT; + goto out_free; + } + + nilfs_transaction_begin(inode->i_sb, &ti, 0); + ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size, + argv.v_nmembs); + if (unlikely(ret < 0)) + nilfs_transaction_abort(inode->i_sb); + else + nilfs_transaction_commit(inode->i_sb); /* never fails */ + +out_free: + vfree(kbuf); +out: + mnt_drop_write_file(filp); + return ret; +} + long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -1189,6 +1320,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return nilfs_ioctl_get_info(inode, filp, cmd, argp, sizeof(struct nilfs_suinfo), nilfs_ioctl_do_get_suinfo); + case NILFS_IOCTL_SET_SUINFO: + return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp); case NILFS_IOCTL_GET_SUSTAT: return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); case NILFS_IOCTL_GET_VINFO: @@ -1205,6 +1338,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return nilfs_ioctl_resize(inode, filp, argp); case NILFS_IOCTL_SET_ALLOC_RANGE: return nilfs_ioctl_set_alloc_range(inode, argp); + case FITRIM: + return nilfs_ioctl_trim_fs(inode, argp); default: return -ENOTTY; } @@ -1228,6 +1363,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case NILFS_IOCTL_GET_CPINFO: case NILFS_IOCTL_GET_CPSTAT: case NILFS_IOCTL_GET_SUINFO: + case NILFS_IOCTL_SET_SUINFO: case NILFS_IOCTL_GET_SUSTAT: case NILFS_IOCTL_GET_VINFO: case NILFS_IOCTL_GET_BDESCS: @@ -1235,6 +1371,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case NILFS_IOCTL_SYNC: case NILFS_IOCTL_RESIZE: case NILFS_IOCTL_SET_ALLOC_RANGE: + case FITRIM: break; default: return -ENOIOCTLCMD; diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 3127e9f438a7..2a869c35c362 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -870,6 +870,289 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, } /** + * nilfs_sufile_set_suinfo - sets segment usage info + * @sufile: inode of segment usage file + * @buf: array of suinfo_update + * @supsz: byte size of suinfo_update + * @nsup: size of suinfo_update array + * + * Description: Takes an array of nilfs_suinfo_update structs and updates + * segment usage accordingly. Only the fields indicated by the sup_flags + * are updated. + * + * Return Value: On success, 0 is returned. On error, one of the + * following negative error codes is returned. + * + * %-EIO - I/O error. + * + * %-ENOMEM - Insufficient amount of memory available. + * + * %-EINVAL - Invalid values in input (segment number, flags or nblocks) + */ +ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf, + unsigned supsz, size_t nsup) +{ + struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; + struct buffer_head *header_bh, *bh; + struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup; + struct nilfs_segment_usage *su; + void *kaddr; + unsigned long blkoff, prev_blkoff; + int cleansi, cleansu, dirtysi, dirtysu; + long ncleaned = 0, ndirtied = 0; + int ret = 0; + + if (unlikely(nsup == 0)) + return ret; + + for (sup = buf; sup < supend; sup = (void *)sup + supsz) { + if (sup->sup_segnum >= nilfs->ns_nsegments + || (sup->sup_flags & + (~0UL << __NR_NILFS_SUINFO_UPDATE_FIELDS)) + || (nilfs_suinfo_update_nblocks(sup) && + sup->sup_sui.sui_nblocks > + nilfs->ns_blocks_per_segment)) + return -EINVAL; + } + + down_write(&NILFS_MDT(sufile)->mi_sem); + + ret = nilfs_sufile_get_header_block(sufile, &header_bh); + if (ret < 0) + goto out_sem; + + sup = buf; + blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum); + ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh); + if (ret < 0) + goto out_header; + + for (;;) { + kaddr = kmap_atomic(bh->b_page); + su = nilfs_sufile_block_get_segment_usage( + sufile, sup->sup_segnum, bh, kaddr); + + if (nilfs_suinfo_update_lastmod(sup)) + su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod); + + if (nilfs_suinfo_update_nblocks(sup)) + su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks); + + if (nilfs_suinfo_update_flags(sup)) { + /* + * Active flag is a virtual flag projected by running + * nilfs kernel code - drop it not to write it to + * disk. + */ + sup->sup_sui.sui_flags &= + ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE); + + cleansi = nilfs_suinfo_clean(&sup->sup_sui); + cleansu = nilfs_segment_usage_clean(su); + dirtysi = nilfs_suinfo_dirty(&sup->sup_sui); + dirtysu = nilfs_segment_usage_dirty(su); + + if (cleansi && !cleansu) + ++ncleaned; + else if (!cleansi && cleansu) + --ncleaned; + + if (dirtysi && !dirtysu) + ++ndirtied; + else if (!dirtysi && dirtysu) + --ndirtied; + + su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags); + } + + kunmap_atomic(kaddr); + + sup = (void *)sup + supsz; + if (sup >= supend) + break; + + prev_blkoff = blkoff; + blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum); + if (blkoff == prev_blkoff) + continue; + + /* get different block */ + mark_buffer_dirty(bh); + put_bh(bh); + ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh); + if (unlikely(ret < 0)) + goto out_mark; + } + mark_buffer_dirty(bh); + put_bh(bh); + + out_mark: + if (ncleaned || ndirtied) { + nilfs_sufile_mod_counter(header_bh, (u64)ncleaned, + (u64)ndirtied); + NILFS_SUI(sufile)->ncleansegs += ncleaned; + } + nilfs_mdt_mark_dirty(sufile); + out_header: + put_bh(header_bh); + out_sem: + up_write(&NILFS_MDT(sufile)->mi_sem); + return ret; +} + +/** + * nilfs_sufile_trim_fs() - trim ioctl handle function + * @sufile: inode of segment usage file + * @range: fstrim_range structure + * + * start: First Byte to trim + * len: number of Bytes to trim from start + * minlen: minimum extent length in Bytes + * + * Decription: nilfs_sufile_trim_fs goes through all segments containing bytes + * from start to start+len. start is rounded up to the next block boundary + * and start+len is rounded down. For each clean segment blkdev_issue_discard + * function is invoked. + * + * Return Value: On success, 0 is returned or negative error code, otherwise. + */ +int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range) +{ + struct the_nilfs *nilfs = sufile->i_sb->s_fs_info; + struct buffer_head *su_bh; + struct nilfs_segment_usage *su; + void *kaddr; + size_t n, i, susz = NILFS_MDT(sufile)->mi_entry_size; + sector_t seg_start, seg_end, start_block, end_block; + sector_t start = 0, nblocks = 0; + u64 segnum, segnum_end, minlen, len, max_blocks, ndiscarded = 0; + int ret = 0; + unsigned int sects_per_block; + + sects_per_block = (1 << nilfs->ns_blocksize_bits) / + bdev_logical_block_size(nilfs->ns_bdev); + len = range->len >> nilfs->ns_blocksize_bits; + minlen = range->minlen >> nilfs->ns_blocksize_bits; + max_blocks = ((u64)nilfs->ns_nsegments * nilfs->ns_blocks_per_segment); + + if (!len || range->start >= max_blocks << nilfs->ns_blocksize_bits) + return -EINVAL; + + start_block = (range->start + nilfs->ns_blocksize - 1) >> + nilfs->ns_blocksize_bits; + + /* + * range->len can be very large (actually, it is set to + * ULLONG_MAX by default) - truncate upper end of the range + * carefully so as not to overflow. + */ + if (max_blocks - start_block < len) + end_block = max_blocks - 1; + else + end_block = start_block + len - 1; + + segnum = nilfs_get_segnum_of_block(nilfs, start_block); + segnum_end = nilfs_get_segnum_of_block(nilfs, end_block); + + down_read(&NILFS_MDT(sufile)->mi_sem); + + while (segnum <= segnum_end) { + n = nilfs_sufile_segment_usages_in_block(sufile, segnum, + segnum_end); + + ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, + &su_bh); + if (ret < 0) { + if (ret != -ENOENT) + goto out_sem; + /* hole */ + segnum += n; + continue; + } + + kaddr = kmap_atomic(su_bh->b_page); + su = nilfs_sufile_block_get_segment_usage(sufile, segnum, + su_bh, kaddr); + for (i = 0; i < n; ++i, ++segnum, su = (void *)su + susz) { + if (!nilfs_segment_usage_clean(su)) + continue; + + nilfs_get_segment_range(nilfs, segnum, &seg_start, + &seg_end); + + if (!nblocks) { + /* start new extent */ + start = seg_start; + nblocks = seg_end - seg_start + 1; + continue; + } + + if (start + nblocks == seg_start) { + /* add to previous extent */ + nblocks += seg_end - seg_start + 1; + continue; + } + + /* discard previous extent */ + if (start < start_block) { + nblocks -= start_block - start; + start = start_block; + } + + if (nblocks >= minlen) { + kunmap_atomic(kaddr); + + ret = blkdev_issue_discard(nilfs->ns_bdev, + start * sects_per_block, + nblocks * sects_per_block, + GFP_NOFS, 0); + if (ret < 0) { + put_bh(su_bh); + goto out_sem; + } + + ndiscarded += nblocks; + kaddr = kmap_atomic(su_bh->b_page); + su = nilfs_sufile_block_get_segment_usage( + sufile, segnum, su_bh, kaddr); + } + + /* start new extent */ + start = seg_start; + nblocks = seg_end - seg_start + 1; + } + kunmap_atomic(kaddr); + put_bh(su_bh); + } + + + if (nblocks) { + /* discard last extent */ + if (start < start_block) { + nblocks -= start_block - start; + start = start_block; + } + if (start + nblocks > end_block + 1) + nblocks = end_block - start + 1; + + if (nblocks >= minlen) { + ret = blkdev_issue_discard(nilfs->ns_bdev, + start * sects_per_block, + nblocks * sects_per_block, + GFP_NOFS, 0); + if (!ret) + ndiscarded += nblocks; + } + } + +out_sem: + up_read(&NILFS_MDT(sufile)->mi_sem); + + range->len = ndiscarded << nilfs->ns_blocksize_bits; + return ret; +} + +/** * nilfs_sufile_read - read or get sufile inode * @sb: super block instance * @susize: size of a segment usage entry @@ -886,6 +1169,18 @@ int nilfs_sufile_read(struct super_block *sb, size_t susize, void *kaddr; int err; + if (susize > sb->s_blocksize) { + printk(KERN_ERR + "NILFS: too large segment usage size: %zu bytes.\n", + susize); + return -EINVAL; + } else if (susize < NILFS_MIN_SEGMENT_USAGE_SIZE) { + printk(KERN_ERR + "NILFS: too small segment usage size: %zu bytes.\n", + susize); + return -EINVAL; + } + sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO); if (unlikely(!sufile)) return -ENOMEM; diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h index e84bc5b51fc1..b8afd72f2379 100644 --- a/fs/nilfs2/sufile.h +++ b/fs/nilfs2/sufile.h @@ -44,6 +44,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum, int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *); ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned, size_t); +ssize_t nilfs_sufile_set_suinfo(struct inode *, void *, unsigned , size_t); int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *, void (*dofunc)(struct inode *, __u64, @@ -65,6 +66,7 @@ void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs); int nilfs_sufile_read(struct super_block *sb, size_t susize, struct nilfs_inode *raw_inode, struct inode **inodep); +int nilfs_sufile_trim_fs(struct inode *sufile, struct fstrim_range *range); /** * nilfs_sufile_scrap - make a segment garbage diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 94c451ce6d24..8ba8229ba076 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -399,6 +399,16 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs, return -EINVAL; nilfs->ns_inode_size = le16_to_cpu(sbp->s_inode_size); + if (nilfs->ns_inode_size > nilfs->ns_blocksize) { + printk(KERN_ERR "NILFS: too large inode size: %d bytes.\n", + nilfs->ns_inode_size); + return -EINVAL; + } else if (nilfs->ns_inode_size < NILFS_MIN_INODE_SIZE) { + printk(KERN_ERR "NILFS: too small inode size: %d bytes.\n", + nilfs->ns_inode_size); + return -EINVAL; + } + nilfs->ns_first_ino = le32_to_cpu(sbp->s_first_ino); nilfs->ns_blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment); diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index dc638f786d5c..ee9cb3795c2b 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -60,8 +60,8 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS -static int fanotify_get_response_from_access(struct fsnotify_group *group, - struct fanotify_event_info *event) +static int fanotify_get_response(struct fsnotify_group *group, + struct fanotify_perm_event_info *event) { int ret; @@ -142,6 +142,40 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, return false; } +struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask, + struct path *path) +{ + struct fanotify_event_info *event; + +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (mask & FAN_ALL_PERM_EVENTS) { + struct fanotify_perm_event_info *pevent; + + pevent = kmem_cache_alloc(fanotify_perm_event_cachep, + GFP_KERNEL); + if (!pevent) + return NULL; + event = &pevent->fae; + pevent->response = 0; + goto init; + } +#endif + event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL); + if (!event) + return NULL; +init: __maybe_unused + fsnotify_init_event(&event->fse, inode, mask); + event->tgid = get_pid(task_tgid(current)); + if (path) { + event->path = *path; + path_get(&event->path); + } else { + event->path.mnt = NULL; + event->path.dentry = NULL; + } + return event; +} + static int fanotify_handle_event(struct fsnotify_group *group, struct inode *inode, struct fsnotify_mark *inode_mark, @@ -171,25 +205,11 @@ static int fanotify_handle_event(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); - event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL); + event = fanotify_alloc_event(inode, mask, data); if (unlikely(!event)) return -ENOMEM; fsn_event = &event->fse; - fsnotify_init_event(fsn_event, inode, mask); - event->tgid = get_pid(task_tgid(current)); - if (data_type == FSNOTIFY_EVENT_PATH) { - struct path *path = data; - event->path = *path; - path_get(&event->path); - } else { - event->path.mnt = NULL; - event->path.dentry = NULL; - } -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - event->response = 0; -#endif - ret = fsnotify_add_notify_event(group, fsn_event, fanotify_merge); if (ret) { /* Permission events shouldn't be merged */ @@ -202,7 +222,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS if (mask & FAN_ALL_PERM_EVENTS) { - ret = fanotify_get_response_from_access(group, event); + ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event)); fsnotify_destroy_event(group, fsn_event); } #endif @@ -225,6 +245,13 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event) event = FANOTIFY_E(fsn_event); path_put(&event->path); put_pid(event->tgid); +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (fsn_event->mask & FAN_ALL_PERM_EVENTS) { + kmem_cache_free(fanotify_perm_event_cachep, + FANOTIFY_PE(fsn_event)); + return; + } +#endif kmem_cache_free(fanotify_event_cachep, event); } diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 32a2f034fb94..2a5fb14115df 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -3,13 +3,12 @@ #include <linux/slab.h> extern struct kmem_cache *fanotify_event_cachep; +extern struct kmem_cache *fanotify_perm_event_cachep; /* - * Lifetime of the structure differs for normal and permission events. In both - * cases the structure is allocated in fanotify_handle_event(). For normal - * events the structure is freed immediately after reporting it to userspace. - * For permission events we free it only after we receive response from - * userspace. + * Structure for normal fanotify events. It gets allocated in + * fanotify_handle_event() and freed when the information is retrieved by + * userspace */ struct fanotify_event_info { struct fsnotify_event fse; @@ -19,12 +18,33 @@ struct fanotify_event_info { */ struct path path; struct pid *tgid; +}; + #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - u32 response; /* userspace answer to question */ -#endif +/* + * Structure for permission fanotify events. It gets allocated and freed in + * fanotify_handle_event() since we wait there for user response. When the + * information is retrieved by userspace the structure is moved from + * group->notification_list to group->fanotify_data.access_list to wait for + * user response. + */ +struct fanotify_perm_event_info { + struct fanotify_event_info fae; + int response; /* userspace answer to question */ + int fd; /* fd we passed to userspace for this event */ }; +static inline struct fanotify_perm_event_info * +FANOTIFY_PE(struct fsnotify_event *fse) +{ + return container_of(fse, struct fanotify_perm_event_info, fae.fse); +} +#endif + static inline struct fanotify_event_info *FANOTIFY_E(struct fsnotify_event *fse) { return container_of(fse, struct fanotify_event_info, fse); } + +struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask, + struct path *path); diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 287a22c04149..4e565c814309 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -28,14 +28,8 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; static struct kmem_cache *fanotify_mark_cache __read_mostly; -static struct kmem_cache *fanotify_response_event_cache __read_mostly; struct kmem_cache *fanotify_event_cachep __read_mostly; - -struct fanotify_response_event { - struct list_head list; - __s32 fd; - struct fanotify_event_info *event; -}; +struct kmem_cache *fanotify_perm_event_cachep __read_mostly; /* * Get an fsnotify notification event if one exists and is small @@ -135,33 +129,34 @@ static int fill_event_metadata(struct fsnotify_group *group, } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS -static struct fanotify_response_event *dequeue_re(struct fsnotify_group *group, - __s32 fd) +static struct fanotify_perm_event_info *dequeue_event( + struct fsnotify_group *group, int fd) { - struct fanotify_response_event *re, *return_re = NULL; + struct fanotify_perm_event_info *event, *return_e = NULL; - mutex_lock(&group->fanotify_data.access_mutex); - list_for_each_entry(re, &group->fanotify_data.access_list, list) { - if (re->fd != fd) + spin_lock(&group->fanotify_data.access_lock); + list_for_each_entry(event, &group->fanotify_data.access_list, + fae.fse.list) { + if (event->fd != fd) continue; - list_del_init(&re->list); - return_re = re; + list_del_init(&event->fae.fse.list); + return_e = event; break; } - mutex_unlock(&group->fanotify_data.access_mutex); + spin_unlock(&group->fanotify_data.access_lock); - pr_debug("%s: found return_re=%p\n", __func__, return_re); + pr_debug("%s: found return_re=%p\n", __func__, return_e); - return return_re; + return return_e; } static int process_access_response(struct fsnotify_group *group, struct fanotify_response *response_struct) { - struct fanotify_response_event *re; - __s32 fd = response_struct->fd; - __u32 response = response_struct->response; + struct fanotify_perm_event_info *event; + int fd = response_struct->fd; + int response = response_struct->response; pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group, fd, response); @@ -181,58 +176,15 @@ static int process_access_response(struct fsnotify_group *group, if (fd < 0) return -EINVAL; - re = dequeue_re(group, fd); - if (!re) + event = dequeue_event(group, fd); + if (!event) return -ENOENT; - re->event->response = response; - + event->response = response; wake_up(&group->fanotify_data.access_waitq); - kmem_cache_free(fanotify_response_event_cache, re); - - return 0; -} - -static int prepare_for_access_response(struct fsnotify_group *group, - struct fsnotify_event *event, - __s32 fd) -{ - struct fanotify_response_event *re; - - if (!(event->mask & FAN_ALL_PERM_EVENTS)) - return 0; - - re = kmem_cache_alloc(fanotify_response_event_cache, GFP_KERNEL); - if (!re) - return -ENOMEM; - - re->event = FANOTIFY_E(event); - re->fd = fd; - - mutex_lock(&group->fanotify_data.access_mutex); - - if (atomic_read(&group->fanotify_data.bypass_perm)) { - mutex_unlock(&group->fanotify_data.access_mutex); - kmem_cache_free(fanotify_response_event_cache, re); - FANOTIFY_E(event)->response = FAN_ALLOW; - return 0; - } - - list_add_tail(&re->list, &group->fanotify_data.access_list); - mutex_unlock(&group->fanotify_data.access_mutex); - - return 0; -} - -#else -static int prepare_for_access_response(struct fsnotify_group *group, - struct fsnotify_event *event, - __s32 fd) -{ return 0; } - #endif static ssize_t copy_event_to_user(struct fsnotify_group *group, @@ -247,7 +199,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ret = fill_event_metadata(group, &fanotify_event_metadata, event, &f); if (ret < 0) - goto out; + return ret; fd = fanotify_event_metadata.fd; ret = -EFAULT; @@ -255,9 +207,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, fanotify_event_metadata.event_len)) goto out_close_fd; - ret = prepare_for_access_response(group, event, fd); - if (ret) - goto out_close_fd; +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (event->mask & FAN_ALL_PERM_EVENTS) + FANOTIFY_PE(event)->fd = fd; +#endif if (fd != FAN_NOFD) fd_install(fd, f); @@ -268,13 +221,6 @@ out_close_fd: put_unused_fd(fd); fput(f); } -out: -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (event->mask & FAN_ALL_PERM_EVENTS) { - FANOTIFY_E(event)->response = FAN_DENY; - wake_up(&group->fanotify_data.access_waitq); - } -#endif return ret; } @@ -314,35 +260,50 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, kevent = get_one_event(group, count); mutex_unlock(&group->notification_mutex); - if (kevent) { + if (IS_ERR(kevent)) { ret = PTR_ERR(kevent); - if (IS_ERR(kevent)) + break; + } + + if (!kevent) { + ret = -EAGAIN; + if (file->f_flags & O_NONBLOCK) break; - ret = copy_event_to_user(group, kevent, buf); - /* - * Permission events get destroyed after we - * receive response - */ - if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) - fsnotify_destroy_event(group, kevent); - if (ret < 0) + + ret = -ERESTARTSYS; + if (signal_pending(current)) + break; + + if (start != buf) break; - buf += ret; - count -= ret; + schedule(); continue; } - ret = -EAGAIN; - if (file->f_flags & O_NONBLOCK) - break; - ret = -ERESTARTSYS; - if (signal_pending(current)) - break; - - if (start != buf) - break; - - schedule(); + ret = copy_event_to_user(group, kevent, buf); + /* + * Permission events get queued to wait for response. Other + * events can be destroyed now. + */ + if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) { + fsnotify_destroy_event(group, kevent); + if (ret < 0) + break; + } else { +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (ret < 0) { + FANOTIFY_PE(kevent)->response = FAN_DENY; + wake_up(&group->fanotify_data.access_waitq); + break; + } + spin_lock(&group->fanotify_data.access_lock); + list_add_tail(&kevent->list, + &group->fanotify_data.access_list); + spin_unlock(&group->fanotify_data.access_lock); +#endif + } + buf += ret; + count -= ret; } finish_wait(&group->notification_waitq, &wait); @@ -383,22 +344,21 @@ static int fanotify_release(struct inode *ignored, struct file *file) struct fsnotify_group *group = file->private_data; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - struct fanotify_response_event *re, *lre; + struct fanotify_perm_event_info *event, *next; - mutex_lock(&group->fanotify_data.access_mutex); + spin_lock(&group->fanotify_data.access_lock); atomic_inc(&group->fanotify_data.bypass_perm); - list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { - pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, - re, re->event); + list_for_each_entry_safe(event, next, &group->fanotify_data.access_list, + fae.fse.list) { + pr_debug("%s: found group=%p event=%p\n", __func__, group, + event); - list_del_init(&re->list); - re->event->response = FAN_ALLOW; - - kmem_cache_free(fanotify_response_event_cache, re); + list_del_init(&event->fae.fse.list); + event->response = FAN_ALLOW; } - mutex_unlock(&group->fanotify_data.access_mutex); + spin_unlock(&group->fanotify_data.access_lock); wake_up(&group->fanotify_data.access_waitq); #endif @@ -731,21 +691,16 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->fanotify_data.user = user; atomic_inc(&user->fanotify_listeners); - oevent = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL); + oevent = fanotify_alloc_event(NULL, FS_Q_OVERFLOW, NULL); if (unlikely(!oevent)) { fd = -ENOMEM; goto out_destroy_group; } group->overflow_event = &oevent->fse; - fsnotify_init_event(group->overflow_event, NULL, FS_Q_OVERFLOW); - oevent->tgid = get_pid(task_tgid(current)); - oevent->path.mnt = NULL; - oevent->path.dentry = NULL; group->fanotify_data.f_flags = event_f_flags; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - oevent->response = 0; - mutex_init(&group->fanotify_data.access_mutex); + spin_lock_init(&group->fanotify_data.access_lock); init_waitqueue_head(&group->fanotify_data.access_waitq); INIT_LIST_HEAD(&group->fanotify_data.access_list); atomic_set(&group->fanotify_data.bypass_perm, 0); @@ -920,9 +875,11 @@ COMPAT_SYSCALL_DEFINE6(fanotify_mark, static int __init fanotify_user_setup(void) { fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); - fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, - SLAB_PANIC); fanotify_event_cachep = KMEM_CACHE(fanotify_event_info, SLAB_PANIC); +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + fanotify_perm_event_cachep = KMEM_CACHE(fanotify_perm_event_info, + SLAB_PANIC); +#endif return 0; } diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index ffb9b3675736..9d8153ebacfb 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2259,7 +2259,7 @@ void ntfs_evict_big_inode(struct inode *vi) { ntfs_inode *ni = NTFS_I(vi); - truncate_inode_pages(&vi->i_data, 0); + truncate_inode_pages_final(&vi->i_data); clear_inode(vi); #ifdef NTFS_RW diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 555f4cddefe3..7e8282dcea2a 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -205,6 +205,7 @@ static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head *di_bh, di->i_mode = cpu_to_le16(inode->i_mode); di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, di_bh); diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index e2edff38be52..b4deb5f750d9 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -5728,6 +5728,7 @@ int ocfs2_remove_btree_range(struct inode *inode, } ocfs2_et_update_clusters(et, -len); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_journal_dirty(handle, et->et_root_bh); @@ -6932,6 +6933,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); spin_unlock(&oi->ip_lock); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_dinode_new_extent_list(inode, di); ocfs2_journal_dirty(handle, di_bh); @@ -7208,6 +7210,7 @@ int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh, di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec); di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_journal_dirty(handle, di_bh); out_commit: diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index aeb44e879c51..d310d12a9adc 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -571,7 +571,6 @@ static void ocfs2_dio_end_io(struct kiocb *iocb, { struct inode *inode = file_inode(iocb->ki_filp); int level; - wait_queue_head_t *wq = ocfs2_ioend_wq(inode); /* this io's submitter should not have unlocked this before we could */ BUG_ON(!ocfs2_iocb_is_rw_locked(iocb)); @@ -582,10 +581,7 @@ static void ocfs2_dio_end_io(struct kiocb *iocb, if (ocfs2_iocb_is_unaligned_aio(iocb)) { ocfs2_iocb_clear_unaligned_aio(iocb); - if (atomic_dec_and_test(&OCFS2_I(inode)->ip_unaligned_aio) && - waitqueue_active(wq)) { - wake_up_all(wq); - } + mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio); } ocfs2_iocb_clear_rw_locked(iocb); @@ -2043,6 +2039,7 @@ out_write_size: inode->i_mtime = inode->i_ctime = CURRENT_TIME; di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec); di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_journal_dirty(handle, wc->w_di_bh); ocfs2_commit_trans(osb, handle); diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index f671e49beb34..6cae155d54df 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -102,9 +102,4 @@ enum ocfs2_iocb_lock_bits { #define ocfs2_iocb_is_unaligned_aio(iocb) \ test_bit(OCFS2_IOCB_UNALIGNED_IO, (unsigned long *)&iocb->private) -#define OCFS2_IOEND_WQ_HASH_SZ 37 -#define ocfs2_ioend_wq(v) (&ocfs2__ioend_wq[((unsigned long)(v)) %\ - OCFS2_IOEND_WQ_HASH_SZ]) -extern wait_queue_head_t ocfs2__ioend_wq[OCFS2_IOEND_WQ_HASH_SZ]; - #endif /* OCFS2_FILE_H */ diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index 5b704c63a103..1edcb141f639 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -90,7 +90,6 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh, * information for this bh as it's not marked locally * uptodate. */ ret = -EIO; - put_bh(bh); mlog_errno(ret); } @@ -420,7 +419,6 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb, if (!buffer_uptodate(bh)) { ret = -EIO; - put_bh(bh); mlog_errno(ret); } diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 2cd2406b4140..eb649d23a4de 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -262,17 +262,17 @@ static void o2net_update_recv_stats(struct o2net_sock_container *sc) #endif /* CONFIG_OCFS2_FS_STATS */ -static inline int o2net_reconnect_delay(void) +static inline unsigned int o2net_reconnect_delay(void) { return o2nm_single_cluster->cl_reconnect_delay_ms; } -static inline int o2net_keepalive_delay(void) +static inline unsigned int o2net_keepalive_delay(void) { return o2nm_single_cluster->cl_keepalive_delay_ms; } -static inline int o2net_idle_timeout(void) +static inline unsigned int o2net_idle_timeout(void) { return o2nm_single_cluster->cl_idle_timeout_ms; } @@ -1964,18 +1964,30 @@ static void o2net_listen_data_ready(struct sock *sk, int bytes) goto out; } - /* ->sk_data_ready is also called for a newly established child socket - * before it has been accepted and the acceptor has set up their - * data_ready.. we only want to queue listen work for our listening - * socket */ + /* This callback may called twice when a new connection + * is being established as a child socket inherits everything + * from a parent LISTEN socket, including the data_ready cb of + * the parent. This leads to a hazard. In o2net_accept_one() + * we are still initializing the child socket but have not + * changed the inherited data_ready callback yet when + * data starts arriving. + * We avoid this hazard by checking the state. + * For the listening socket, the state will be TCP_LISTEN; for the new + * socket, will be TCP_ESTABLISHED. Also, in this case, + * sk->sk_user_data is not a valid function pointer. + */ + if (sk->sk_state == TCP_LISTEN) { mlog(ML_TCP, "bytes: %d\n", bytes); queue_work(o2net_wq, &o2net_listen_work); + } else { + ready = NULL; } out: read_unlock(&sk->sk_callback_lock); - ready(sk, bytes); + if (ready != NULL) + ready(sk, bytes); } static int o2net_open_listening_sock(__be32 addr, __be16 port) diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 0d3a97d2d5f6..e2e05a106beb 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -37,7 +37,6 @@ #include "dlmglue.h" #include "file.h" #include "inode.h" -#include "super.h" #include "ocfs2_trace.h" void ocfs2_dentry_attach_gen(struct dentry *dentry) @@ -346,52 +345,6 @@ out_attach: return ret; } -DEFINE_SPINLOCK(dentry_list_lock); - -/* We limit the number of dentry locks to drop in one go. We have - * this limit so that we don't starve other users of ocfs2_wq. */ -#define DL_INODE_DROP_COUNT 64 - -/* Drop inode references from dentry locks */ -static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count) -{ - struct ocfs2_dentry_lock *dl; - - spin_lock(&dentry_list_lock); - while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) { - dl = osb->dentry_lock_list; - osb->dentry_lock_list = dl->dl_next; - spin_unlock(&dentry_list_lock); - iput(dl->dl_inode); - kfree(dl); - spin_lock(&dentry_list_lock); - } - spin_unlock(&dentry_list_lock); -} - -void ocfs2_drop_dl_inodes(struct work_struct *work) -{ - struct ocfs2_super *osb = container_of(work, struct ocfs2_super, - dentry_lock_work); - - __ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT); - /* - * Don't queue dropping if umount is in progress. We flush the - * list in ocfs2_dismount_volume - */ - spin_lock(&dentry_list_lock); - if (osb->dentry_lock_list && - !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED)) - queue_work(ocfs2_wq, &osb->dentry_lock_work); - spin_unlock(&dentry_list_lock); -} - -/* Flush the whole work queue */ -void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb) -{ - __ocfs2_drop_dl_inodes(osb, -1); -} - /* * ocfs2_dentry_iput() and friends. * @@ -416,24 +369,16 @@ void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb) static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb, struct ocfs2_dentry_lock *dl) { + iput(dl->dl_inode); ocfs2_simple_drop_lockres(osb, &dl->dl_lockres); ocfs2_lock_res_free(&dl->dl_lockres); - - /* We leave dropping of inode reference to ocfs2_wq as that can - * possibly lead to inode deletion which gets tricky */ - spin_lock(&dentry_list_lock); - if (!osb->dentry_lock_list && - !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED)) - queue_work(ocfs2_wq, &osb->dentry_lock_work); - dl->dl_next = osb->dentry_lock_list; - osb->dentry_lock_list = dl; - spin_unlock(&dentry_list_lock); + kfree(dl); } void ocfs2_dentry_lock_put(struct ocfs2_super *osb, struct ocfs2_dentry_lock *dl) { - int unlock; + int unlock = 0; BUG_ON(dl->dl_count == 0); diff --git a/fs/ocfs2/dcache.h b/fs/ocfs2/dcache.h index b79eff709958..55f58892b153 100644 --- a/fs/ocfs2/dcache.h +++ b/fs/ocfs2/dcache.h @@ -29,13 +29,8 @@ extern const struct dentry_operations ocfs2_dentry_ops; struct ocfs2_dentry_lock { - /* Use count of dentry lock */ unsigned int dl_count; - union { - /* Linked list of dentry locks to release */ - struct ocfs2_dentry_lock *dl_next; - u64 dl_parent_blkno; - }; + u64 dl_parent_blkno; /* * The ocfs2_dentry_lock keeps an inode reference until @@ -49,14 +44,9 @@ struct ocfs2_dentry_lock { int ocfs2_dentry_attach_lock(struct dentry *dentry, struct inode *inode, u64 parent_blkno); -extern spinlock_t dentry_list_lock; - void ocfs2_dentry_lock_put(struct ocfs2_super *osb, struct ocfs2_dentry_lock *dl); -void ocfs2_drop_dl_inodes(struct work_struct *work); -void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb); - struct dentry *ocfs2_find_local_alias(struct inode *inode, u64 parent_blkno, int skip_unhashed); diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 91a7e85ac8fd..0717662b4aef 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -2957,6 +2957,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, ocfs2_init_dir_trailer(dir, dirdata_bh, i); } + ocfs2_update_inode_fsync_trans(handle, dir, 1); ocfs2_journal_dirty(handle, dirdata_bh); if (ocfs2_supports_indexed_dirs(osb) && !dx_inline) { @@ -3005,6 +3006,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, di->i_size = cpu_to_le64(sb->s_blocksize); di->i_ctime = di->i_mtime = cpu_to_le64(dir->i_ctime.tv_sec); di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(dir->i_ctime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, dir, 1); /* * This should never fail as our extent list is empty and all @@ -3338,6 +3340,7 @@ do_extend: } else { de->rec_len = cpu_to_le16(sb->s_blocksize); } + ocfs2_update_inode_fsync_trans(handle, dir, 1); ocfs2_journal_dirty(handle, new_bh); dir_i_size += dir->i_sb->s_blocksize; @@ -3896,6 +3899,7 @@ out_commit: dquot_free_space_nodirty(dir, ocfs2_clusters_to_bytes(dir->i_sb, 1)); + ocfs2_update_inode_fsync_trans(handle, dir, 1); ocfs2_commit_trans(osb, handle); out: @@ -4134,6 +4138,7 @@ static int ocfs2_expand_inline_dx_root(struct inode *dir, mlog_errno(ret); did_quota = 0; + ocfs2_update_inode_fsync_trans(handle, dir, 1); ocfs2_journal_dirty(handle, dx_root_bh); out_commit: @@ -4401,6 +4406,7 @@ static int ocfs2_dx_dir_remove_index(struct inode *dir, di->i_dyn_features = cpu_to_le16(OCFS2_I(dir)->ip_dyn_features); spin_unlock(&OCFS2_I(dir)->ip_lock); di->i_dx_root = cpu_to_le64(0ULL); + ocfs2_update_inode_fsync_trans(handle, dir, 1); ocfs2_journal_dirty(handle, di_bh); diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 33660a4a52fa..c973690dc0bc 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -1123,7 +1123,6 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, struct dlm_ctxt *dlm = NULL; char *local = NULL; int status = 0; - int locked = 0; qr = (struct dlm_query_region *) msg->buf; @@ -1132,10 +1131,8 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, /* buffer used in dlm_mast_regions() */ local = kmalloc(sizeof(qr->qr_regions), GFP_KERNEL); - if (!local) { - status = -ENOMEM; - goto bail; - } + if (!local) + return -ENOMEM; status = -EINVAL; @@ -1144,16 +1141,15 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, if (!dlm) { mlog(ML_ERROR, "Node %d queried hb regions on domain %s " "before join domain\n", qr->qr_node, qr->qr_domain); - goto bail; + goto out_domain_lock; } spin_lock(&dlm->spinlock); - locked = 1; if (dlm->joining_node != qr->qr_node) { mlog(ML_ERROR, "Node %d queried hb regions on domain %s " "but joining node is %d\n", qr->qr_node, qr->qr_domain, dlm->joining_node); - goto bail; + goto out_dlm_lock; } /* Support for global heartbeat was added in 1.1 */ @@ -1163,14 +1159,15 @@ static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, "but active dlm protocol is %d.%d\n", qr->qr_node, qr->qr_domain, dlm->dlm_locking_proto.pv_major, dlm->dlm_locking_proto.pv_minor); - goto bail; + goto out_dlm_lock; } status = dlm_match_regions(dlm, qr, local, sizeof(qr->qr_regions)); -bail: - if (locked) - spin_unlock(&dlm->spinlock); +out_dlm_lock: + spin_unlock(&dlm->spinlock); + +out_domain_lock: spin_unlock(&dlm_domain_lock); kfree(local); @@ -1877,19 +1874,19 @@ static int dlm_join_domain(struct dlm_ctxt *dlm) goto bail; } - status = dlm_debug_init(dlm); + status = dlm_launch_thread(dlm); if (status < 0) { mlog_errno(status); goto bail; } - status = dlm_launch_thread(dlm); + status = dlm_launch_recovery_thread(dlm); if (status < 0) { mlog_errno(status); goto bail; } - status = dlm_launch_recovery_thread(dlm); + status = dlm_debug_init(dlm); if (status < 0) { mlog_errno(status); goto bail; diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 7035af09cc03..fe29f7978f81 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -537,7 +537,10 @@ master_here: /* success! see if any other nodes need recovery */ mlog(0, "DONE mastering recovery of %s:%u here(this=%u)!\n", dlm->name, dlm->reco.dead_node, dlm->node_num); - dlm_reset_recovery(dlm); + spin_lock(&dlm->spinlock); + __dlm_reset_recovery(dlm); + dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE; + spin_unlock(&dlm->spinlock); } dlm_end_recovery(dlm); @@ -695,6 +698,14 @@ static int dlm_remaster_locks(struct dlm_ctxt *dlm, u8 dead_node) if (all_nodes_done) { int ret; + /* Set this flag on recovery master to avoid + * a new recovery for another dead node start + * before the recovery is not done. That may + * cause recovery hung.*/ + spin_lock(&dlm->spinlock); + dlm->reco.state |= DLM_RECO_STATE_FINALIZE; + spin_unlock(&dlm->spinlock); + /* all nodes are now in DLM_RECO_NODE_DATA_DONE state * just send a finalize message to everyone and * clean up */ @@ -1750,13 +1761,13 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm, struct dlm_migratable_lockres *mres) { struct dlm_migratable_lock *ml; - struct list_head *queue; + struct list_head *queue, *iter; struct list_head *tmpq = NULL; struct dlm_lock *newlock = NULL; struct dlm_lockstatus *lksb = NULL; int ret = 0; int i, j, bad; - struct dlm_lock *lock = NULL; + struct dlm_lock *lock; u8 from = O2NM_MAX_NODES; unsigned int added = 0; __be64 c; @@ -1791,14 +1802,16 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm, /* MIGRATION ONLY! */ BUG_ON(!(mres->flags & DLM_MRES_MIGRATION)); + lock = NULL; spin_lock(&res->spinlock); for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) { tmpq = dlm_list_idx_to_ptr(res, j); - list_for_each_entry(lock, tmpq, list) { - if (lock->ml.cookie != ml->cookie) - lock = NULL; - else + list_for_each(iter, tmpq) { + lock = list_entry(iter, + struct dlm_lock, list); + if (lock->ml.cookie == ml->cookie) break; + lock = NULL; } if (lock) break; @@ -2882,8 +2895,8 @@ int dlm_finalize_reco_handler(struct o2net_msg *msg, u32 len, void *data, BUG(); } dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE; + __dlm_reset_recovery(dlm); spin_unlock(&dlm->spinlock); - dlm_reset_recovery(dlm); dlm_kick_recovery_thread(dlm); break; default: diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 19986959d149..6bd690b5a061 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3144,22 +3144,60 @@ out: return 0; } +static void ocfs2_process_blocked_lock(struct ocfs2_super *osb, + struct ocfs2_lock_res *lockres); + /* Mark the lockres as being dropped. It will no longer be * queued if blocking, but we still may have to wait on it * being dequeued from the downconvert thread before we can consider * it safe to drop. * * You can *not* attempt to call cluster_lock on this lockres anymore. */ -void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres) +void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb, + struct ocfs2_lock_res *lockres) { int status; struct ocfs2_mask_waiter mw; - unsigned long flags; + unsigned long flags, flags2; ocfs2_init_mask_waiter(&mw); spin_lock_irqsave(&lockres->l_lock, flags); lockres->l_flags |= OCFS2_LOCK_FREEING; + if (lockres->l_flags & OCFS2_LOCK_QUEUED && current == osb->dc_task) { + /* + * We know the downconvert is queued but not in progress + * because we are the downconvert thread and processing + * different lock. So we can just remove the lock from the + * queue. This is not only an optimization but also a way + * to avoid the following deadlock: + * ocfs2_dentry_post_unlock() + * ocfs2_dentry_lock_put() + * ocfs2_drop_dentry_lock() + * iput() + * ocfs2_evict_inode() + * ocfs2_clear_inode() + * ocfs2_mark_lockres_freeing() + * ... blocks waiting for OCFS2_LOCK_QUEUED + * since we are the downconvert thread which + * should clear the flag. + */ + spin_unlock_irqrestore(&lockres->l_lock, flags); + spin_lock_irqsave(&osb->dc_task_lock, flags2); + list_del_init(&lockres->l_blocked_list); + osb->blocked_lock_count--; + spin_unlock_irqrestore(&osb->dc_task_lock, flags2); + /* + * Warn if we recurse into another post_unlock call. Strictly + * speaking it isn't a problem but we need to be careful if + * that happens (stack overflow, deadlocks, ...) so warn if + * ocfs2 grows a path for which this can happen. + */ + WARN_ON_ONCE(lockres->l_ops->post_unlock); + /* Since the lock is freeing we don't do much in the fn below */ + ocfs2_process_blocked_lock(osb, lockres); + return; + } while (lockres->l_flags & OCFS2_LOCK_QUEUED) { lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_QUEUED, 0); spin_unlock_irqrestore(&lockres->l_lock, flags); @@ -3180,7 +3218,7 @@ void ocfs2_simple_drop_lockres(struct ocfs2_super *osb, { int ret; - ocfs2_mark_lockres_freeing(lockres); + ocfs2_mark_lockres_freeing(osb, lockres); ret = ocfs2_drop_lock(osb, lockres); if (ret) mlog_errno(ret); diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h index 1d596d8c4a4a..d293a22c32c5 100644 --- a/fs/ocfs2/dlmglue.h +++ b/fs/ocfs2/dlmglue.h @@ -157,7 +157,8 @@ int ocfs2_refcount_lock(struct ocfs2_refcount_tree *ref_tree, int ex); void ocfs2_refcount_unlock(struct ocfs2_refcount_tree *ref_tree, int ex); -void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres); +void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb, + struct ocfs2_lock_res *lockres); void ocfs2_simple_drop_lockres(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 51632c40e896..ff33c5ef87f2 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -175,9 +175,13 @@ static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end, int datasync) { int err = 0; - journal_t *journal; struct inode *inode = file->f_mapping->host; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_inode_info *oi = OCFS2_I(inode); + journal_t *journal = osb->journal->j_journal; + int ret; + tid_t commit_tid; + bool needs_barrier = false; trace_ocfs2_sync_file(inode, file, file->f_path.dentry, OCFS2_I(inode)->ip_blkno, @@ -192,29 +196,19 @@ static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end, if (err) return err; - /* - * Probably don't need the i_mutex at all in here, just putting it here - * to be consistent with how fsync used to be called, someone more - * familiar with the fs could possibly remove it. - */ - mutex_lock(&inode->i_mutex); - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) { - /* - * We still have to flush drive's caches to get data to the - * platter - */ - if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER) - blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); - goto bail; + commit_tid = datasync ? oi->i_datasync_tid : oi->i_sync_tid; + if (journal->j_flags & JBD2_BARRIER && + !jbd2_trans_will_send_data_barrier(journal, commit_tid)) + needs_barrier = true; + err = jbd2_complete_transaction(journal, commit_tid); + if (needs_barrier) { + ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); + if (!err) + err = ret; } - journal = osb->journal->j_journal; - err = jbd2_journal_force_commit(journal); - -bail: if (err) mlog_errno(err); - mutex_unlock(&inode->i_mutex); return (err < 0) ? -EIO : 0; } @@ -292,6 +286,7 @@ int ocfs2_update_inode_atime(struct inode *inode, inode->i_atime = CURRENT_TIME; di->i_atime = cpu_to_le64(inode->i_atime.tv_sec); di->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, bh); out_commit: @@ -341,6 +336,7 @@ int ocfs2_simple_size_update(struct inode *inode, if (ret < 0) mlog_errno(ret); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_commit_trans(osb, handle); out: return ret; @@ -435,6 +431,7 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, di->i_size = cpu_to_le64(new_i_size); di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec); di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, fe_bh); @@ -650,7 +647,7 @@ restarted_transaction: mlog_errno(status); goto leave; } - + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_journal_dirty(handle, bh); spin_lock(&OCFS2_I(inode)->ip_lock); @@ -743,6 +740,7 @@ static handle_t *ocfs2_zero_start_ordered_transaction(struct inode *inode, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) mlog_errno(ret); + ocfs2_update_inode_fsync_trans(handle, inode, 1); out: if (ret) { @@ -840,6 +838,7 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from, di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); di->i_mtime_nsec = di->i_ctime_nsec; ocfs2_journal_dirty(handle, di_bh); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); } @@ -1344,6 +1343,7 @@ static int __ocfs2_write_remove_suid(struct inode *inode, di = (struct ocfs2_dinode *) bh->b_data; di->i_mode = cpu_to_le16(inode->i_mode); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, bh); @@ -1576,6 +1576,7 @@ static int ocfs2_zero_partial_clusters(struct inode *inode, if (ret) mlog_errno(ret); } + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_commit_trans(osb, handle); out: @@ -2061,13 +2062,6 @@ out: return ret; } -static void ocfs2_aiodio_wait(struct inode *inode) -{ - wait_queue_head_t *wq = ocfs2_ioend_wq(inode); - - wait_event(*wq, (atomic_read(&OCFS2_I(inode)->ip_unaligned_aio) == 0)); -} - static int ocfs2_is_io_unaligned(struct inode *inode, size_t count, loff_t pos) { int blockmask = inode->i_sb->s_blocksize - 1; @@ -2345,10 +2339,8 @@ relock: * Wait on previous unaligned aio to complete before * proceeding. */ - ocfs2_aiodio_wait(inode); - - /* Mark the iocb as needing a decrement in ocfs2_dio_end_io */ - atomic_inc(&OCFS2_I(inode)->ip_unaligned_aio); + mutex_lock(&OCFS2_I(inode)->ip_unaligned_aio); + /* Mark the iocb as needing an unlock in ocfs2_dio_end_io */ ocfs2_iocb_set_unaligned_aio(iocb); } @@ -2428,7 +2420,7 @@ out_dio: if (unaligned_dio) { ocfs2_iocb_clear_unaligned_aio(iocb); - atomic_dec(&OCFS2_I(inode)->ip_unaligned_aio); + mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio); } out: @@ -2645,7 +2637,16 @@ static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int whence) case SEEK_SET: break; case SEEK_END: - offset += inode->i_size; + /* SEEK_END requires the OCFS2 inode lock for the file + * because it references the file's size. + */ + ret = ocfs2_inode_lock(inode, NULL, 0); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + offset += i_size_read(inode); + ocfs2_inode_unlock(inode, 0); break; case SEEK_CUR: if (offset == 0) { diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index f29a90fde619..437de7f768c6 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -130,6 +130,7 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags, struct inode *inode = NULL; struct super_block *sb = osb->sb; struct ocfs2_find_inode_args args; + journal_t *journal = OCFS2_SB(sb)->journal->j_journal; trace_ocfs2_iget_begin((unsigned long long)blkno, flags, sysfile_type); @@ -169,6 +170,32 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags, goto bail; } + /* + * Set transaction id's of transactions that have to be committed + * to finish f[data]sync. We set them to currently running transaction + * as we cannot be sure that the inode or some of its metadata isn't + * part of the transaction - the inode could have been reclaimed and + * now it is reread from disk. + */ + if (journal) { + transaction_t *transaction; + tid_t tid; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + + read_lock(&journal->j_state_lock); + if (journal->j_running_transaction) + transaction = journal->j_running_transaction; + else + transaction = journal->j_committing_transaction; + if (transaction) + tid = transaction->t_tid; + else + tid = journal->j_commit_sequence; + read_unlock(&journal->j_state_lock); + oi->i_sync_tid = tid; + oi->i_datasync_tid = tid; + } + bail: if (!IS_ERR(inode)) { trace_ocfs2_iget_end(inode, @@ -804,11 +831,13 @@ static int ocfs2_inode_is_valid_to_delete(struct inode *inode) goto bail; } - /* If we're coming from downconvert_thread we can't go into our own - * voting [hello, deadlock city!], so unforuntately we just - * have to skip deleting this guy. That's OK though because - * the node who's doing the actual deleting should handle it - * anyway. */ + /* + * If we're coming from downconvert_thread we can't go into our own + * voting [hello, deadlock city!] so we cannot delete the inode. But + * since we dropped last inode ref when downconverting dentry lock, + * we cannot have the file open and thus the node doing unlink will + * take care of deleting the inode. + */ if (current == osb->dc_task) goto bail; @@ -822,12 +851,6 @@ static int ocfs2_inode_is_valid_to_delete(struct inode *inode) goto bail_unlock; } - /* If we have allowd wipe of this inode for another node, it - * will be marked here so we can safely skip it. Recovery will - * cleanup any inodes we might inadvertently skip here. */ - if (oi->ip_flags & OCFS2_INODE_SKIP_DELETE) - goto bail_unlock; - ret = 1; bail_unlock: spin_unlock(&oi->ip_lock); @@ -941,7 +964,7 @@ static void ocfs2_cleanup_delete_inode(struct inode *inode, (unsigned long long)OCFS2_I(inode)->ip_blkno, sync_data); if (sync_data) filemap_write_and_wait(inode->i_mapping); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); } static void ocfs2_delete_inode(struct inode *inode) @@ -960,8 +983,6 @@ static void ocfs2_delete_inode(struct inode *inode) if (is_bad_inode(inode) || !OCFS2_I(inode)->ip_blkno) goto bail; - dquot_initialize(inode); - if (!ocfs2_inode_is_valid_to_delete(inode)) { /* It's probably not necessary to truncate_inode_pages * here but we do it for safety anyway (it will most @@ -970,6 +991,8 @@ static void ocfs2_delete_inode(struct inode *inode) goto bail; } + dquot_initialize(inode); + /* We want to block signals in delete_inode as the lock and * messaging paths may return us -ERESTARTSYS. Which would * cause us to exit early, resulting in inodes being orphaned @@ -1057,6 +1080,7 @@ static void ocfs2_clear_inode(struct inode *inode) { int status; struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); clear_inode(inode); trace_ocfs2_clear_inode((unsigned long long)oi->ip_blkno, @@ -1073,9 +1097,9 @@ static void ocfs2_clear_inode(struct inode *inode) /* Do these before all the other work so that we don't bounce * the downconvert thread while waiting to destroy the locks. */ - ocfs2_mark_lockres_freeing(&oi->ip_rw_lockres); - ocfs2_mark_lockres_freeing(&oi->ip_inode_lockres); - ocfs2_mark_lockres_freeing(&oi->ip_open_lockres); + ocfs2_mark_lockres_freeing(osb, &oi->ip_rw_lockres); + ocfs2_mark_lockres_freeing(osb, &oi->ip_inode_lockres); + ocfs2_mark_lockres_freeing(osb, &oi->ip_open_lockres); ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap, &oi->ip_la_data_resv); @@ -1157,7 +1181,7 @@ void ocfs2_evict_inode(struct inode *inode) (OCFS2_I(inode)->ip_flags & OCFS2_INODE_MAYBE_ORPHANED)) { ocfs2_delete_inode(inode); } else { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); } ocfs2_clear_inode(inode); } @@ -1260,6 +1284,7 @@ int ocfs2_mark_inode_dirty(handle_t *handle, fe->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); ocfs2_journal_dirty(handle, bh); + ocfs2_update_inode_fsync_trans(handle, inode, 1); leave: return status; } diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 621fc73bf23d..a6c991c0fc98 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -44,7 +44,7 @@ struct ocfs2_inode_info struct rw_semaphore ip_xattr_sem; /* Number of outstanding AIO's which are not page aligned */ - atomic_t ip_unaligned_aio; + struct mutex ip_unaligned_aio; /* These fields are protected by ip_lock */ spinlock_t ip_lock; @@ -73,6 +73,13 @@ struct ocfs2_inode_info u32 ip_dir_lock_gen; struct ocfs2_alloc_reservation ip_la_data_resv; + + /* + * Transactions that contain inode's metadata needed to complete + * fsync and fdatasync, respectively. + */ + tid_t i_sync_tid; + tid_t i_datasync_tid; }; /* @@ -84,8 +91,6 @@ struct ocfs2_inode_info #define OCFS2_INODE_BITMAP 0x00000004 /* This inode has been wiped from disk */ #define OCFS2_INODE_DELETED 0x00000008 -/* Another node is deleting, so our delete is a nop */ -#define OCFS2_INODE_SKIP_DELETE 0x00000010 /* Has the inode been orphaned on another node? * * This hints to ocfs2_drop_inode that it should clear i_nlink before @@ -100,11 +105,11 @@ struct ocfs2_inode_info * rely on ocfs2_delete_inode to sort things out under the proper * cluster locks. */ -#define OCFS2_INODE_MAYBE_ORPHANED 0x00000020 +#define OCFS2_INODE_MAYBE_ORPHANED 0x00000010 /* Does someone have the file open O_DIRECT */ -#define OCFS2_INODE_OPEN_DIRECT 0x00000040 +#define OCFS2_INODE_OPEN_DIRECT 0x00000020 /* Tell the inode wipe code it's not in orphan dir */ -#define OCFS2_INODE_SKIP_ORPHAN_DIR 0x00000080 +#define OCFS2_INODE_SKIP_ORPHAN_DIR 0x00000040 static inline struct ocfs2_inode_info *OCFS2_I(struct inode *inode) { diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 8ca3c29accbf..490229f43731 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -413,11 +413,12 @@ int ocfs2_info_handle_freeinode(struct inode *inode, } status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i); - if (status < 0) - goto bail; iput(inode_alloc); inode_alloc = NULL; + + if (status < 0) + goto bail; } o2info_set_request_filled(&oifi->ifi_req); diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 44fc3e530c3d..03ea9314fecd 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -2132,12 +2132,6 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb, iter = oi->ip_next_orphan; spin_lock(&oi->ip_lock); - /* The remote delete code may have set these on the - * assumption that the other node would wipe them - * successfully. If they are still in the node's - * orphan dir, we need to reset that state. */ - oi->ip_flags &= ~(OCFS2_INODE_DELETED|OCFS2_INODE_SKIP_DELETE); - /* Set the proper information to get us going into * ocfs2_delete_inode. */ oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED; diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 9ff4e8cf9d97..7f8cde94abfe 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -626,4 +626,15 @@ static inline int ocfs2_begin_ordered_truncate(struct inode *inode, new_size); } +static inline void ocfs2_update_inode_fsync_trans(handle_t *handle, + struct inode *inode, + int datasync) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + + oi->i_sync_tid = handle->h_transaction->t_tid; + if (datasync) + oi->i_datasync_tid = handle->h_transaction->t_tid; +} + #endif /* OCFS2_JOURNAL_H */ diff --git a/fs/ocfs2/locks.c b/fs/ocfs2/locks.c index e57c804069ea..6b6d092b0998 100644 --- a/fs/ocfs2/locks.c +++ b/fs/ocfs2/locks.c @@ -82,6 +82,8 @@ static int ocfs2_do_flock(struct file *file, struct inode *inode, } ret = flock_lock_file_wait(file, fl); + if (ret) + ocfs2_file_unlock(file); out: mutex_unlock(&fp->fp_mutex); diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index 64c304d668f0..599eb4c4c8be 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -151,6 +151,7 @@ static int __ocfs2_move_extent(handle_t *handle, old_blkno, len); } + ocfs2_update_inode_fsync_trans(handle, inode, 0); out: ocfs2_free_path(path); return ret; @@ -690,8 +691,11 @@ static int ocfs2_move_extent(struct ocfs2_move_extents_context *context, ret = ocfs2_block_group_set_bits(handle, gb_inode, gd, gd_bh, goal_bit, len); - if (ret) + if (ret) { + ocfs2_rollback_alloc_dinode_counts(gb_inode, gb_bh, len, + le16_to_cpu(gd->bg_chain)); mlog_errno(ret); + } /* * Here we should write the new page out first if we are @@ -957,6 +961,7 @@ static int ocfs2_move_extents(struct ocfs2_move_extents_context *context) inode->i_ctime = CURRENT_TIME; di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, di_bh); diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 3683643f3f0e..2060fc398445 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -450,7 +450,6 @@ leave: brelse(new_fe_bh); brelse(parent_fe_bh); - kfree(si.name); kfree(si.value); ocfs2_free_dir_lookup_result(&lookup); @@ -495,6 +494,7 @@ static int __ocfs2_mknod_locked(struct inode *dir, struct ocfs2_dinode *fe = NULL; struct ocfs2_extent_list *fel; u16 feat; + struct ocfs2_inode_info *oi = OCFS2_I(inode); *new_fe_bh = NULL; @@ -576,8 +576,8 @@ static int __ocfs2_mknod_locked(struct inode *dir, mlog_errno(status); } - status = 0; /* error in ocfs2_create_new_inode_locks is not - * critical */ + oi->i_sync_tid = handle->h_transaction->t_tid; + oi->i_datasync_tid = handle->h_transaction->t_tid; leave: if (status < 0) { @@ -1855,7 +1855,6 @@ bail: brelse(new_fe_bh); brelse(parent_fe_bh); - kfree(si.name); kfree(si.value); ocfs2_free_dir_lookup_result(&lookup); if (inode_ac) @@ -2481,6 +2480,7 @@ int ocfs2_mv_orphaned_inode_to_new(struct inode *dir, di->i_orphaned_slot = 0; set_nlink(inode, 1); ocfs2_set_links_count(di, inode->i_nlink); + ocfs2_update_inode_fsync_trans(handle, inode, 1); ocfs2_journal_dirty(handle, di_bh); status = ocfs2_add_entry(handle, dentry, inode, diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 553f53cc73ae..8d64a97a9d5e 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/wait.h> #include <linux/list.h> +#include <linux/llist.h> #include <linux/rbtree.h> #include <linux/workqueue.h> #include <linux/kref.h> @@ -274,19 +275,16 @@ enum ocfs2_mount_options OCFS2_MOUNT_HB_GLOBAL = 1 << 14, /* Global heartbeat */ }; -#define OCFS2_OSB_SOFT_RO 0x0001 -#define OCFS2_OSB_HARD_RO 0x0002 -#define OCFS2_OSB_ERROR_FS 0x0004 -#define OCFS2_OSB_DROP_DENTRY_LOCK_IMMED 0x0008 - -#define OCFS2_DEFAULT_ATIME_QUANTUM 60 +#define OCFS2_OSB_SOFT_RO 0x0001 +#define OCFS2_OSB_HARD_RO 0x0002 +#define OCFS2_OSB_ERROR_FS 0x0004 +#define OCFS2_DEFAULT_ATIME_QUANTUM 60 struct ocfs2_journal; struct ocfs2_slot_info; struct ocfs2_recovery_map; struct ocfs2_replay_map; struct ocfs2_quota_recovery; -struct ocfs2_dentry_lock; struct ocfs2_super { struct task_struct *commit_task; @@ -414,10 +412,9 @@ struct ocfs2_super struct list_head blocked_lock_list; unsigned long blocked_lock_count; - /* List of dentry locks to release. Anyone can add locks to - * the list, ocfs2_wq processes the list */ - struct ocfs2_dentry_lock *dentry_lock_list; - struct work_struct dentry_lock_work; + /* List of dquot structures to drop last reference to */ + struct llist_head dquot_drop_list; + struct work_struct dquot_drop_work; wait_queue_head_t osb_mount_event; @@ -449,6 +446,8 @@ struct ocfs2_super /* rb tree root for refcount lock. */ struct rb_root osb_rf_lock_tree; struct ocfs2_refcount_tree *osb_ref_tree_lru; + + struct mutex system_file_mutex; }; #define OCFS2_SB(sb) ((struct ocfs2_super *)(sb)->s_fs_info) @@ -579,18 +578,6 @@ static inline void ocfs2_set_osb_flag(struct ocfs2_super *osb, spin_unlock(&osb->osb_lock); } - -static inline unsigned long ocfs2_test_osb_flag(struct ocfs2_super *osb, - unsigned long flag) -{ - unsigned long ret; - - spin_lock(&osb->osb_lock); - ret = osb->osb_flags & flag; - spin_unlock(&osb->osb_lock); - return ret; -} - static inline void ocfs2_set_ro_flag(struct ocfs2_super *osb, int hard) { diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h index d5ab56cbe5c5..f266d67df3c6 100644 --- a/fs/ocfs2/quota.h +++ b/fs/ocfs2/quota.h @@ -28,6 +28,7 @@ struct ocfs2_dquot { unsigned int dq_use_count; /* Number of nodes having reference to this entry in global quota file */ s64 dq_origspace; /* Last globally synced space usage */ s64 dq_originodes; /* Last globally synced inode usage */ + struct llist_node list; /* Member of list of dquots to drop */ }; /* Description of one chunk to recover in memory */ @@ -110,6 +111,7 @@ int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, int ocfs2_create_local_dquot(struct dquot *dquot); int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot); int ocfs2_local_write_dquot(struct dquot *dquot); +void ocfs2_drop_dquot_refs(struct work_struct *work); extern const struct dquot_operations ocfs2_quota_operations; extern struct quota_format_type ocfs2_quota_format; diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index d7b5108789e2..b990a62cff50 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c @@ -10,6 +10,7 @@ #include <linux/jiffies.h> #include <linux/writeback.h> #include <linux/workqueue.h> +#include <linux/llist.h> #include <cluster/masklog.h> @@ -679,6 +680,27 @@ static int ocfs2_calc_qdel_credits(struct super_block *sb, int type) OCFS2_INODE_UPDATE_CREDITS; } +void ocfs2_drop_dquot_refs(struct work_struct *work) +{ + struct ocfs2_super *osb = container_of(work, struct ocfs2_super, + dquot_drop_work); + struct llist_node *list; + struct ocfs2_dquot *odquot, *next_odquot; + + list = llist_del_all(&osb->dquot_drop_list); + llist_for_each_entry_safe(odquot, next_odquot, list, list) { + /* Drop the reference we acquired in ocfs2_dquot_release() */ + dqput(&odquot->dq_dquot); + } +} + +/* + * Called when the last reference to dquot is dropped. If we are called from + * downconvert thread, we cannot do all the handling here because grabbing + * quota lock could deadlock (the node holding the quota lock could need some + * other cluster lock to proceed but with blocked downconvert thread we cannot + * release any lock). + */ static int ocfs2_release_dquot(struct dquot *dquot) { handle_t *handle; @@ -694,6 +716,19 @@ static int ocfs2_release_dquot(struct dquot *dquot) /* Check whether we are not racing with some other dqget() */ if (atomic_read(&dquot->dq_count) > 1) goto out; + /* Running from downconvert thread? Postpone quota processing to wq */ + if (current == osb->dc_task) { + /* + * Grab our own reference to dquot and queue it for delayed + * dropping. Quota code rechecks after calling + * ->release_dquot() and won't free dquot structure. + */ + dqgrab(dquot); + /* First entry on list -> queue work */ + if (llist_add(&OCFS2_DQUOT(dquot)->list, &osb->dquot_drop_list)) + queue_work(ocfs2_wq, &osb->dquot_drop_work); + goto out; + } status = ocfs2_lock_global_qf(oinfo, 1); if (status < 0) goto out; diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c index ca5ce14cbddc..5c8343fe7438 100644 --- a/fs/ocfs2/stackglue.c +++ b/fs/ocfs2/stackglue.c @@ -603,11 +603,25 @@ static struct kobj_attribute ocfs2_attr_cluster_stack = ocfs2_cluster_stack_show, ocfs2_cluster_stack_store); + + +static ssize_t ocfs2_dlm_recover_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static struct kobj_attribute ocfs2_attr_dlm_recover_support = + __ATTR(dlm_recover_callback_support, S_IRUGO, + ocfs2_dlm_recover_show, NULL); + static struct attribute *ocfs2_attrs[] = { &ocfs2_attr_max_locking_protocol.attr, &ocfs2_attr_loaded_cluster_plugins.attr, &ocfs2_attr_active_cluster_plugin.attr, &ocfs2_attr_cluster_stack.attr, + &ocfs2_attr_dlm_recover_support.attr, NULL, }; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 47ae2663a6f5..0cb889a17ae1 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -771,6 +771,7 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, spin_unlock(&OCFS2_I(alloc_inode)->ip_lock); i_size_write(alloc_inode, le64_to_cpu(fe->i_size)); alloc_inode->i_blocks = ocfs2_inode_sector_count(alloc_inode); + ocfs2_update_inode_fsync_trans(handle, alloc_inode, 0); status = 0; @@ -1607,6 +1608,21 @@ out: return ret; } +void ocfs2_rollback_alloc_dinode_counts(struct inode *inode, + struct buffer_head *di_bh, + u32 num_bits, + u16 chain) +{ + u32 tmp_used; + struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data; + struct ocfs2_chain_list *cl; + + cl = (struct ocfs2_chain_list *)&di->id2.i_chain; + tmp_used = le32_to_cpu(di->id1.bitmap1.i_used); + di->id1.bitmap1.i_used = cpu_to_le32(tmp_used - num_bits); + le32_add_cpu(&cl->cl_recs[chain].c_free, num_bits); +} + static int ocfs2_bg_discontig_fix_by_rec(struct ocfs2_suballoc_result *res, struct ocfs2_extent_rec *rec, struct ocfs2_chain_list *cl) @@ -1707,8 +1723,12 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, ret = ocfs2_block_group_set_bits(handle, alloc_inode, gd, group_bh, res->sr_bit_offset, res->sr_bits); - if (ret < 0) + if (ret < 0) { + ocfs2_rollback_alloc_dinode_counts(alloc_inode, ac->ac_bh, + res->sr_bits, + le16_to_cpu(gd->bg_chain)); mlog_errno(ret); + } out_loc_only: *bits_left = le16_to_cpu(gd->bg_free_bits_count); @@ -1838,6 +1858,8 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, res->sr_bit_offset, res->sr_bits); if (status < 0) { + ocfs2_rollback_alloc_dinode_counts(alloc_inode, + ac->ac_bh, res->sr_bits, chain); mlog_errno(status); goto bail; } @@ -2091,7 +2113,7 @@ int ocfs2_find_new_inode_loc(struct inode *dir, ac->ac_find_loc_priv = res; *fe_blkno = res->sr_blkno; - + ocfs2_update_inode_fsync_trans(handle, dir, 0); out: if (handle) ocfs2_commit_trans(OCFS2_SB(dir->i_sb), handle); @@ -2149,6 +2171,8 @@ int ocfs2_claim_new_inode_at_loc(handle_t *handle, res->sr_bit_offset, res->sr_bits); if (ret < 0) { + ocfs2_rollback_alloc_dinode_counts(ac->ac_inode, + ac->ac_bh, res->sr_bits, chain); mlog_errno(ret); goto out; } @@ -2870,6 +2894,7 @@ int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res) status = ocfs2_inode_lock(inode_alloc_inode, &alloc_bh, 0); if (status < 0) { mutex_unlock(&inode_alloc_inode->i_mutex); + iput(inode_alloc_inode); mlog(ML_ERROR, "lock on alloc inode on slot %u failed %d\n", (u32)suballoc_slot, status); goto bail; diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index 218d8036b3e7..2d2501767c0c 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -91,6 +91,10 @@ int ocfs2_alloc_dinode_update_counts(struct inode *inode, struct buffer_head *di_bh, u32 num_bits, u16 chain); +void ocfs2_rollback_alloc_dinode_counts(struct inode *inode, + struct buffer_head *di_bh, + u32 num_bits, + u16 chain); int ocfs2_block_group_set_bits(handle_t *handle, struct inode *alloc_inode, struct ocfs2_group_desc *bg, diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 49d84f80f36c..1aecd626e645 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -561,6 +561,9 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb) if (!oi) return NULL; + oi->i_sync_tid = 0; + oi->i_datasync_tid = 0; + jbd2_journal_init_jbd_inode(&oi->ip_jinode, &oi->vfs_inode); return &oi->vfs_inode; } @@ -1238,30 +1241,11 @@ static struct dentry *ocfs2_mount(struct file_system_type *fs_type, return mount_bdev(fs_type, flags, dev_name, data, ocfs2_fill_super); } -static void ocfs2_kill_sb(struct super_block *sb) -{ - struct ocfs2_super *osb = OCFS2_SB(sb); - - /* Failed mount? */ - if (!osb || atomic_read(&osb->vol_state) == VOLUME_DISABLED) - goto out; - - /* Prevent further queueing of inode drop events */ - spin_lock(&dentry_list_lock); - ocfs2_set_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED); - spin_unlock(&dentry_list_lock); - /* Wait for work to finish and/or remove it */ - cancel_work_sync(&osb->dentry_lock_work); -out: - kill_block_super(sb); -} - static struct file_system_type ocfs2_fs_type = { .owner = THIS_MODULE, .name = "ocfs2", .mount = ocfs2_mount, - .kill_sb = ocfs2_kill_sb, - + .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV|FS_RENAME_DOES_D_MOVE, .next = NULL }; @@ -1612,14 +1596,9 @@ static int ocfs2_show_options(struct seq_file *s, struct dentry *root) return 0; } -wait_queue_head_t ocfs2__ioend_wq[OCFS2_IOEND_WQ_HASH_SZ]; - static int __init ocfs2_init(void) { - int status, i; - - for (i = 0; i < OCFS2_IOEND_WQ_HASH_SZ; i++) - init_waitqueue_head(&ocfs2__ioend_wq[i]); + int status; status = init_ocfs2_uptodate_cache(); if (status < 0) @@ -1761,7 +1740,7 @@ static void ocfs2_inode_init_once(void *data) ocfs2_extent_map_init(&oi->vfs_inode); INIT_LIST_HEAD(&oi->ip_io_markers); oi->ip_dir_start_lookup = 0; - atomic_set(&oi->ip_unaligned_aio, 0); + mutex_init(&oi->ip_unaligned_aio); init_rwsem(&oi->ip_alloc_sem); init_rwsem(&oi->ip_xattr_sem); mutex_init(&oi->ip_io_mutex); @@ -1932,17 +1911,16 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) debugfs_remove(osb->osb_ctxt); - /* - * Flush inode dropping work queue so that deletes are - * performed while the filesystem is still working - */ - ocfs2_drop_all_dl_inodes(osb); - /* Orphan scan should be stopped as early as possible */ ocfs2_orphan_scan_stop(osb); ocfs2_disable_quotas(osb); + /* All dquots should be freed by now */ + WARN_ON(!llist_empty(&osb->dquot_drop_list)); + /* Wait for worker to be done with the work structure in osb */ + cancel_work_sync(&osb->dquot_drop_work); + ocfs2_shutdown_local_alloc(osb); /* This will disable recovery and flush any recovery work. */ @@ -2077,7 +2055,6 @@ static int ocfs2_initialize_super(struct super_block *sb, struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; struct inode *inode = NULL; struct ocfs2_journal *journal; - __le32 uuid_net_key; struct ocfs2_super *osb; u64 total_blocks; @@ -2123,6 +2100,8 @@ static int ocfs2_initialize_super(struct super_block *sb, spin_lock_init(&osb->osb_xattr_lock); ocfs2_init_steal_slots(osb); + mutex_init(&osb->system_file_mutex); + atomic_set(&osb->alloc_stats.moves, 0); atomic_set(&osb->alloc_stats.local_data, 0); atomic_set(&osb->alloc_stats.bitmap_data, 0); @@ -2276,8 +2255,8 @@ static int ocfs2_initialize_super(struct super_block *sb, INIT_WORK(&journal->j_recovery_work, ocfs2_complete_recovery); journal->j_state = OCFS2_JOURNAL_FREE; - INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes); - osb->dentry_lock_list = NULL; + INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs); + init_llist_head(&osb->dquot_drop_list); /* get some pseudo constants for clustersize bits */ osb->s_clustersize_bits = @@ -2311,8 +2290,6 @@ static int ocfs2_initialize_super(struct super_block *sb, goto bail; } - memcpy(&uuid_net_key, di->id2.i_super.s_uuid, sizeof(uuid_net_key)); - strncpy(osb->vol_label, di->id2.i_super.s_label, 63); osb->vol_label[63] = '\0'; osb->root_blkno = le64_to_cpu(di->id2.i_super.s_root_blkno); diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index f053688d22a3..af155c183123 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -113,9 +113,11 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, } else arr = get_local_system_inode(osb, type, slot); + mutex_lock(&osb->system_file_mutex); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ inode = igrab(inode); + mutex_unlock(&osb->system_file_mutex); BUG_ON(!inode); return inode; @@ -129,6 +131,7 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, *arr = igrab(inode); BUG_ON(!*arr); } + mutex_unlock(&osb->system_file_mutex); return inode; } diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 185fa3b7f962..016f01df3825 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -369,7 +369,7 @@ static void ocfs2_xattr_bucket_free(struct ocfs2_xattr_bucket *bucket) * them fully. */ static int ocfs2_init_xattr_bucket(struct ocfs2_xattr_bucket *bucket, - u64 xb_blkno) + u64 xb_blkno, int new) { int i, rc = 0; @@ -383,9 +383,16 @@ static int ocfs2_init_xattr_bucket(struct ocfs2_xattr_bucket *bucket, } if (!ocfs2_buffer_uptodate(INODE_CACHE(bucket->bu_inode), - bucket->bu_bhs[i])) - ocfs2_set_new_buffer_uptodate(INODE_CACHE(bucket->bu_inode), - bucket->bu_bhs[i]); + bucket->bu_bhs[i])) { + if (new) + ocfs2_set_new_buffer_uptodate(INODE_CACHE(bucket->bu_inode), + bucket->bu_bhs[i]); + else { + set_buffer_uptodate(bucket->bu_bhs[i]); + ocfs2_set_buffer_uptodate(INODE_CACHE(bucket->bu_inode), + bucket->bu_bhs[i]); + } + } } if (rc) @@ -2602,6 +2609,7 @@ int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh) oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL); di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); spin_unlock(&oi->ip_lock); + ocfs2_update_inode_fsync_trans(handle, inode, 0); ocfs2_journal_dirty(handle, di_bh); out_commit: @@ -3200,8 +3208,15 @@ meta_guess: clusters_add += 1; } } else { - meta_add += 1; credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; + if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { + struct ocfs2_extent_list *el = &def_xv.xv.xr_list; + meta_add += ocfs2_extend_meta_needed(el); + credits += ocfs2_calc_extend_credits(inode->i_sb, + el); + } else { + meta_add += 1; + } } out: if (clusters_need) @@ -3614,6 +3629,7 @@ int ocfs2_xattr_set(struct inode *inode, } ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt); + ocfs2_update_inode_fsync_trans(ctxt.handle, inode, 0); ocfs2_commit_trans(osb, ctxt.handle); @@ -4294,7 +4310,7 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, trace_ocfs2_xattr_create_index_block((unsigned long long)blkno); - ret = ocfs2_init_xattr_bucket(xs->bucket, blkno); + ret = ocfs2_init_xattr_bucket(xs->bucket, blkno, 1); if (ret) { mlog_errno(ret); goto out; @@ -4638,7 +4654,7 @@ static int ocfs2_divide_xattr_bucket(struct inode *inode, * Even if !new_bucket_head, we're overwriting t_bucket. Thus, * there's no need to read it. */ - ret = ocfs2_init_xattr_bucket(t_bucket, new_blk); + ret = ocfs2_init_xattr_bucket(t_bucket, new_blk, new_bucket_head); if (ret) { mlog_errno(ret); goto out; @@ -4804,7 +4820,7 @@ static int ocfs2_cp_xattr_bucket(struct inode *inode, * Even if !t_is_new, we're overwriting t_bucket. Thus, * there's no need to read it. */ - ret = ocfs2_init_xattr_bucket(t_bucket, t_blkno); + ret = ocfs2_init_xattr_bucket(t_bucket, t_blkno, t_is_new); if (ret) goto out; @@ -5476,6 +5492,7 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode, ret = ocfs2_truncate_log_append(osb, handle, blkno, len); if (ret) mlog_errno(ret); + ocfs2_update_inode_fsync_trans(handle, inode, 0); out_commit: ocfs2_commit_trans(osb, handle); @@ -6830,7 +6847,7 @@ static int ocfs2_reflink_xattr_bucket(handle_t *handle, break; } - ret = ocfs2_init_xattr_bucket(args->new_bucket, new_blkno); + ret = ocfs2_init_xattr_bucket(args->new_bucket, new_blkno, 1); if (ret) { mlog_errno(ret); break; diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index d8b0afde2179..ec58c7659183 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -183,7 +183,7 @@ int omfs_sync_inode(struct inode *inode) */ static void omfs_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (inode->i_nlink) diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 124fc43c7090..8f20e3404fd2 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -35,7 +35,7 @@ static void proc_evict_inode(struct inode *inode) const struct proc_ns_operations *ns_ops; void *ns; - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); /* Stop tracking associated processes */ diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index cfc8dcc16043..9cd5f63715c0 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -528,7 +528,7 @@ restart: if (atomic_read(&dquot->dq_count)) { DEFINE_WAIT(wait); - atomic_inc(&dquot->dq_count); + dqgrab(dquot); prepare_to_wait(&dquot->dq_wait_unused, &wait, TASK_UNINTERRUPTIBLE); spin_unlock(&dq_list_lock); @@ -632,7 +632,7 @@ int dquot_writeback_dquots(struct super_block *sb, int type) /* Now we have active dquot from which someone is * holding reference so we can safely just increase * use count */ - atomic_inc(&dquot->dq_count); + dqgrab(dquot); spin_unlock(&dq_list_lock); dqstats_inc(DQST_LOOKUPS); err = sb->dq_op->write_dquot(dquot); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index ad62bdbb451e..bc8b8009897d 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -35,7 +35,7 @@ void reiserfs_evict_inode(struct inode *inode) if (!inode->i_nlink && !is_bad_inode(inode)) dquot_initialize(inode); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (inode->i_nlink) goto no_delete; diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h index 8d06adf89948..83d4eac8059a 100644 --- a/fs/reiserfs/reiserfs.h +++ b/fs/reiserfs/reiserfs.h @@ -2831,6 +2831,7 @@ void reiserfs_init_alloc_options(struct super_block *s); */ __le32 reiserfs_choose_packing(struct inode *dir); +void show_alloc_options(struct seq_file *seq, struct super_block *s); int reiserfs_init_bitmap_cache(struct super_block *sb); void reiserfs_free_bitmap_cache(struct super_block *sb); void reiserfs_cache_bitmap_metadata(struct super_block *sb, struct buffer_head *bh, struct reiserfs_bitmap_info *info); diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 2c803353f8ac..ed54a04c33bd 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -62,7 +62,6 @@ static int is_any_reiserfs_magic_string(struct reiserfs_super_block *rs) static int reiserfs_remount(struct super_block *s, int *flags, char *data); static int reiserfs_statfs(struct dentry *dentry, struct kstatfs *buf); -void show_alloc_options(struct seq_file *seq, struct super_block *s); static int reiserfs_sync_fs(struct super_block *s, int wait) { @@ -597,7 +596,7 @@ static void init_once(void *foo) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { reiserfs_inode_cachep = kmem_cache_create("reiser_inode_cache", sizeof(struct diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index c327d4ee1235..5625ca920f5e 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -295,7 +295,7 @@ int sysv_sync_inode(struct inode *inode) static void sysv_evict_inode(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (!inode->i_nlink) { inode->i_size = 0; sysv_truncate(inode); diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 5ded8490c0c6..48f943f7f5d5 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -351,7 +351,7 @@ static void ubifs_evict_inode(struct inode *inode) dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode); ubifs_assert(!atomic_read(&inode->i_count)); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (inode->i_nlink) goto done; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 982ce05c87ed..5d643706212f 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -146,8 +146,8 @@ void udf_evict_inode(struct inode *inode) want_delete = 1; udf_setsize(inode, 0); udf_update_inode(inode, IS_SYNC(inode)); - } else - truncate_inode_pages(&inode->i_data, 0); + } + truncate_inode_pages_final(&inode->i_data); invalidate_inode_buffers(inode); clear_inode(inode); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index c8ca96086784..61e8a9b021dd 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -885,7 +885,7 @@ void ufs_evict_inode(struct inode * inode) if (!inode->i_nlink && !is_bad_inode(inode)) want_delete = 1; - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); if (want_delete) { loff_t old_i_size; /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index d971f4932b5d..0ef599218991 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -996,7 +996,7 @@ xfs_fs_evict_inode( trace_xfs_evict_inode(ip); - truncate_inode_pages(&inode->i_data, 0); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); XFS_STATS_INC(vn_rele); XFS_STATS_INC(vn_remove); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 24819001f5c8..e488e9459a93 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -95,7 +95,7 @@ struct backing_dev_info { unsigned int max_ratio, max_prop_frac; struct bdi_writeback wb; /* default writeback info for this bdi */ - spinlock_t wb_lock; /* protects work_list */ + spinlock_t wb_lock; /* protects work_list & wb.dwork scheduling */ struct list_head work_list; diff --git a/include/linux/backlight.h b/include/linux/backlight.h index 5f9cd963213d..72647429adf6 100644 --- a/include/linux/backlight.h +++ b/include/linux/backlight.h @@ -9,6 +9,7 @@ #define _LINUX_BACKLIGHT_H #include <linux/device.h> +#include <linux/fb.h> #include <linux/mutex.h> #include <linux/notifier.h> @@ -104,6 +105,11 @@ struct backlight_device { struct list_head entry; struct device dev; + + /* Multiple framebuffers may share one backlight device */ + bool fb_bl_on[FB_MAX]; + + int use_count; }; static inline void backlight_update_status(struct backlight_device *bd) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 3fe661fe96d1..b19d3dc2e651 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -87,25 +87,26 @@ extern void rebuild_sched_domains(void); extern void cpuset_print_task_mems_allowed(struct task_struct *p); /* - * get_mems_allowed is required when making decisions involving mems_allowed - * such as during page allocation. mems_allowed can be updated in parallel - * and depending on the new value an operation can fail potentially causing - * process failure. A retry loop with get_mems_allowed and put_mems_allowed - * prevents these artificial failures. + * read_mems_allowed_begin is required when making decisions involving + * mems_allowed such as during page allocation. mems_allowed can be updated in + * parallel and depending on the new value an operation can fail potentially + * causing process failure. A retry loop with read_mems_allowed_begin and + * read_mems_allowed_retry prevents these artificial failures. */ -static inline unsigned int get_mems_allowed(void) +static inline unsigned int read_mems_allowed_begin(void) { return read_seqcount_begin(¤t->mems_allowed_seq); } /* - * If this returns false, the operation that took place after get_mems_allowed - * may have failed. It is up to the caller to retry the operation if + * If this returns true, the operation that took place after + * read_mems_allowed_begin may have failed artificially due to a concurrent + * update of mems_allowed. It is up to the caller to retry the operation if * appropriate. */ -static inline bool put_mems_allowed(unsigned int seq) +static inline bool read_mems_allowed_retry(unsigned int seq) { - return !read_seqcount_retry(¤t->mems_allowed_seq, seq); + return read_seqcount_retry(¤t->mems_allowed_seq, seq); } static inline void set_mems_allowed(nodemask_t nodemask) @@ -225,14 +226,14 @@ static inline void set_mems_allowed(nodemask_t nodemask) { } -static inline unsigned int get_mems_allowed(void) +static inline unsigned int read_mems_allowed_begin(void) { return 0; } -static inline bool put_mems_allowed(unsigned int seq) +static inline bool read_mems_allowed_retry(unsigned int seq) { - return true; + return false; } #endif /* !CONFIG_CPUSETS */ diff --git a/include/linux/cred.h b/include/linux/cred.h index 04421e825365..f61d6c8f5ef3 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -66,7 +66,7 @@ extern struct group_info *groups_alloc(int); extern struct group_info init_groups; extern void groups_free(struct group_info *); extern int set_current_groups(struct group_info *); -extern int set_groups(struct cred *, struct group_info *); +extern void set_groups(struct cred *, struct group_info *); extern int groups_search(const struct group_info *, kgid_t); /* access the groups "array" with this macro */ diff --git a/include/linux/decompress/inflate.h b/include/linux/decompress/inflate.h index 8c0aef1ba5f5..1d0aedef9822 100644 --- a/include/linux/decompress/inflate.h +++ b/include/linux/decompress/inflate.h @@ -1,5 +1,5 @@ -#ifndef INFLATE_H -#define INFLATE_H +#ifndef LINUX_DECOMPRESS_INFLATE_H +#define LINUX_DECOMPRESS_INFLATE_H int gunzip(unsigned char *inbuf, int len, int(*fill)(void*, unsigned int), diff --git a/include/linux/err.h b/include/linux/err.h index 15f92e072450..a729120644d5 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -2,12 +2,13 @@ #define _LINUX_ERR_H #include <linux/compiler.h> +#include <linux/types.h> #include <asm/errno.h> /* * Kernel pointers have redundant information, so we can use a - * scheme where we can return either an error code or a dentry + * scheme where we can return either an error code or a normal * pointer with the same return value. * * This should be a per-architecture thing, to allow different @@ -29,12 +30,12 @@ static inline long __must_check PTR_ERR(__force const void *ptr) return (long) ptr; } -static inline long __must_check IS_ERR(__force const void *ptr) +static inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } -static inline long __must_check IS_ERR_OR_NULL(__force const void *ptr) +static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return !ptr || IS_ERR_VALUE((unsigned long)ptr); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 6e765d28841b..3ca9420f627e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -419,6 +419,7 @@ struct address_space { struct mutex i_mmap_mutex; /* protect tree, count, list */ /* Protected by tree_lock together with the radix tree */ unsigned long nrpages; /* number of total pages */ + unsigned long nrshadows; /* number of shadow entries */ pgoff_t writeback_index;/* writeback starts here */ const struct address_space_operations *a_ops; /* methods */ unsigned long flags; /* error bits/gfp mask */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 64cf3ef50696..fc7718c6bd3e 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -178,7 +178,7 @@ struct fsnotify_group { struct fanotify_group_private_data { #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS /* allows a group to block waiting for a userspace response */ - struct mutex access_mutex; + spinlock_t access_lock; struct list_head access_list; wait_queue_head_t access_waitq; atomic_t bypass_perm; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 8c43cc469d78..5b337cf8fb86 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -6,6 +6,8 @@ #include <linux/fs.h> #include <linux/hugetlb_inline.h> #include <linux/cgroup.h> +#include <linux/list.h> +#include <linux/kref.h> struct ctl_table; struct user_struct; @@ -23,6 +25,14 @@ struct hugepage_subpool { long max_hpages, used_hpages; }; +struct resv_map { + struct kref refs; + spinlock_t lock; + struct list_head regions; +}; +extern struct resv_map *resv_map_alloc(void); +void resv_map_release(struct kref *ref); + extern spinlock_t hugetlb_lock; extern int hugetlb_max_hstate __read_mostly; #define for_each_hstate(h) \ diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h index 2a5e5548a1d2..5bb424659c04 100644 --- a/include/linux/kmemleak.h +++ b/include/linux/kmemleak.h @@ -30,8 +30,6 @@ extern void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) __ref; extern void kmemleak_free(const void *ptr) __ref; extern void kmemleak_free_part(const void *ptr, size_t size) __ref; extern void kmemleak_free_percpu(const void __percpu *ptr) __ref; -extern void kmemleak_padding(const void *ptr, unsigned long offset, - size_t size) __ref; extern void kmemleak_not_leak(const void *ptr) __ref; extern void kmemleak_ignore(const void *ptr) __ref; extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 926afb6f6b5f..f896a33e8341 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -119,6 +119,7 @@ struct kobj_type { }; struct kobj_uevent_env { + char *argv[3]; char *envp[UEVENT_NUM_ENVP]; int envp_idx; char buf[UEVENT_BUFFER_SIZE]; diff --git a/include/linux/list_lru.h b/include/linux/list_lru.h index 3ce541753c88..f3434533fbf8 100644 --- a/include/linux/list_lru.h +++ b/include/linux/list_lru.h @@ -13,6 +13,8 @@ /* list_lru_walk_cb has to always return one of those */ enum lru_status { LRU_REMOVED, /* item removed from list */ + LRU_REMOVED_RETRY, /* item removed, but lock has been + dropped and reacquired */ LRU_ROTATE, /* item referenced, give another pass */ LRU_SKIP, /* item cannot be locked, skip */ LRU_RETRY, /* item not freeable. May drop the lock @@ -32,7 +34,11 @@ struct list_lru { }; void list_lru_destroy(struct list_lru *lru); -int list_lru_init(struct list_lru *lru); +int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key); +static inline int list_lru_init(struct list_lru *lru) +{ + return list_lru_init_key(lru, NULL); +} /** * list_lru_add: add an element to the lru list's tail diff --git a/include/linux/mfd/pm8xxx/rtc.h b/include/linux/mfd/pm8xxx/rtc.h deleted file mode 100644 index 14f1983eaecc..000000000000 --- a/include/linux/mfd/pm8xxx/rtc.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * 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. - */ - -#ifndef __RTC_PM8XXX_H__ -#define __RTC_PM8XXX_H__ - -#define PM8XXX_RTC_DEV_NAME "rtc-pm8xxx" -/** - * struct pm8xxx_rtc_pdata - RTC driver platform data - * @rtc_write_enable: variable stating RTC write capability - */ -struct pm8xxx_rtc_platform_data { - bool rtc_write_enable; -}; - -#endif /* __RTC_PM8XXX_H__ */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 2eec61fe75c9..35300f390eb6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1041,6 +1041,14 @@ extern void show_free_areas(unsigned int flags); extern bool skip_free_areas_node(unsigned int flags, int nid); int shmem_zero_setup(struct vm_area_struct *); +#ifdef CONFIG_SHMEM +bool shmem_mapping(struct address_space *mapping); +#else +static inline bool shmem_mapping(struct address_space *mapping) +{ + return false; +} +#endif extern int can_do_mlock(void); extern int user_shm_lock(size_t, struct user_struct *); @@ -1658,10 +1666,8 @@ static inline int __early_pfn_to_nid(unsigned long pfn) #else /* please see mm/page_alloc.c */ extern int __meminit early_pfn_to_nid(unsigned long pfn); -#ifdef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID /* there is a per-arch backend function. */ extern int __meminit __early_pfn_to_nid(unsigned long pfn); -#endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */ #endif extern void set_dma_reserve(unsigned long new_dma_reserve); @@ -1826,6 +1832,7 @@ vm_unmapped_area(struct vm_unmapped_area_info *info) extern void truncate_inode_pages(struct address_space *, loff_t); extern void truncate_inode_pages_range(struct address_space *, loff_t lstart, loff_t lend); +extern void truncate_inode_pages_final(struct address_space *); /* generic vm_area_ops exported for stackable file systems */ extern int filemap_fault(struct vm_area_struct *, struct vm_fault *); diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9b61b9bf81ac..fac5509c18f0 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -142,6 +142,9 @@ enum zone_stat_item { NUMA_LOCAL, /* allocation from local node */ NUMA_OTHER, /* allocation from other node */ #endif + WORKINGSET_REFAULT, + WORKINGSET_ACTIVATE, + WORKINGSET_NODERECLAIM, NR_ANON_TRANSPARENT_HUGEPAGES, NR_FREE_CMA_PAGES, NR_VM_ZONE_STAT_ITEMS }; @@ -392,6 +395,9 @@ struct zone { spinlock_t lru_lock; struct lruvec lruvec; + /* Evictions & activations on the inactive file list */ + atomic_long_t inactive_age; + unsigned long pages_scanned; /* since last reclaim */ unsigned long flags; /* zone flags, see below */ diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 98755767c7b0..ff3fea3194c6 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -82,6 +82,8 @@ struct nilfs_inode { __le32 i_pad; }; +#define NILFS_MIN_INODE_SIZE 128 + /** * struct nilfs_super_root - structure of super root * @sr_sum: check sum @@ -482,6 +484,8 @@ struct nilfs_dat_entry { __le64 de_rsv; }; +#define NILFS_MIN_DAT_ENTRY_SIZE 32 + /** * struct nilfs_snapshot_list - snapshot list * @ssl_next: next checkpoint number on snapshot list @@ -520,6 +524,8 @@ struct nilfs_checkpoint { struct nilfs_inode cp_ifile_inode; }; +#define NILFS_MIN_CHECKPOINT_SIZE (64 + NILFS_MIN_INODE_SIZE) + /* checkpoint flags */ enum { NILFS_CHECKPOINT_SNAPSHOT, @@ -615,6 +621,8 @@ struct nilfs_segment_usage { __le32 su_flags; }; +#define NILFS_MIN_SEGMENT_USAGE_SIZE 16 + /* segment usage flag */ enum { NILFS_SEGMENT_USAGE_ACTIVE, @@ -710,6 +718,48 @@ static inline int nilfs_suinfo_clean(const struct nilfs_suinfo *si) } /* ioctl */ +/** + * nilfs_suinfo_update - segment usage information update + * @sup_segnum: segment number + * @sup_flags: flags for which fields are active in sup_sui + * @sup_reserved: reserved necessary for alignment + * @sup_sui: segment usage information + */ +struct nilfs_suinfo_update { + __u64 sup_segnum; + __u32 sup_flags; + __u32 sup_reserved; + struct nilfs_suinfo sup_sui; +}; + +enum { + NILFS_SUINFO_UPDATE_LASTMOD, + NILFS_SUINFO_UPDATE_NBLOCKS, + NILFS_SUINFO_UPDATE_FLAGS, + __NR_NILFS_SUINFO_UPDATE_FIELDS, +}; + +#define NILFS_SUINFO_UPDATE_FNS(flag, name) \ +static inline void \ +nilfs_suinfo_update_set_##name(struct nilfs_suinfo_update *sup) \ +{ \ + sup->sup_flags |= 1UL << NILFS_SUINFO_UPDATE_##flag; \ +} \ +static inline void \ +nilfs_suinfo_update_clear_##name(struct nilfs_suinfo_update *sup) \ +{ \ + sup->sup_flags &= ~(1UL << NILFS_SUINFO_UPDATE_##flag); \ +} \ +static inline int \ +nilfs_suinfo_update_##name(const struct nilfs_suinfo_update *sup) \ +{ \ + return !!(sup->sup_flags & (1UL << NILFS_SUINFO_UPDATE_##flag));\ +} + +NILFS_SUINFO_UPDATE_FNS(LASTMOD, lastmod) +NILFS_SUINFO_UPDATE_FNS(NBLOCKS, nblocks) +NILFS_SUINFO_UPDATE_FNS(FLAGS, flags) + enum { NILFS_CHECKPOINT, NILFS_SNAPSHOT, @@ -863,5 +913,7 @@ struct nilfs_bdesc { _IOW(NILFS_IOCTL_IDENT, 0x8B, __u64) #define NILFS_IOCTL_SET_ALLOC_RANGE \ _IOW(NILFS_IOCTL_IDENT, 0x8C, __u64[2]) +#define NILFS_IOCTL_SET_SUINFO \ + _IOW(NILFS_IOCTL_IDENT, 0x8D, struct nilfs_argv) #endif /* _LINUX_NILFS_FS_H */ diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 1710d1b060ba..45598f1e9aa3 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -25,6 +25,7 @@ enum mapping_flags { AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */ AS_UNEVICTABLE = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */ AS_BALLOON_MAP = __GFP_BITS_SHIFT + 4, /* balloon page special map */ + AS_EXITING = __GFP_BITS_SHIFT + 5, /* final truncate in progress */ }; static inline void mapping_set_error(struct address_space *mapping, int error) @@ -69,6 +70,16 @@ static inline int mapping_balloon(struct address_space *mapping) return mapping && test_bit(AS_BALLOON_MAP, &mapping->flags); } +static inline void mapping_set_exiting(struct address_space *mapping) +{ + set_bit(AS_EXITING, &mapping->flags); +} + +static inline int mapping_exiting(struct address_space *mapping) +{ + return test_bit(AS_EXITING, &mapping->flags); +} + static inline gfp_t mapping_gfp_mask(struct address_space * mapping) { return (__force gfp_t)mapping->flags & __GFP_BITS_MASK; @@ -243,12 +254,20 @@ static inline struct page *page_cache_alloc_readahead(struct address_space *x) typedef int filler_t(void *, struct page *); -extern struct page * find_get_page(struct address_space *mapping, - pgoff_t index); -extern struct page * find_lock_page(struct address_space *mapping, - pgoff_t index); -extern struct page * find_or_create_page(struct address_space *mapping, - pgoff_t index, gfp_t gfp_mask); +pgoff_t page_cache_next_hole(struct address_space *mapping, + pgoff_t index, unsigned long max_scan); +pgoff_t page_cache_prev_hole(struct address_space *mapping, + pgoff_t index, unsigned long max_scan); + +struct page *find_get_entry(struct address_space *mapping, pgoff_t offset); +struct page *find_get_page(struct address_space *mapping, pgoff_t offset); +struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset); +struct page *find_lock_page(struct address_space *mapping, pgoff_t offset); +struct page *find_or_create_page(struct address_space *mapping, pgoff_t index, + gfp_t gfp_mask); +unsigned find_get_entries(struct address_space *mapping, pgoff_t start, + unsigned int nr_entries, struct page **entries, + pgoff_t *indices); unsigned find_get_pages(struct address_space *mapping, pgoff_t start, unsigned int nr_pages, struct page **pages); unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start, @@ -270,8 +289,6 @@ static inline struct page *grab_cache_page(struct address_space *mapping, extern struct page * grab_cache_page_nowait(struct address_space *mapping, pgoff_t index); -extern struct page * read_cache_page_async(struct address_space *mapping, - pgoff_t index, filler_t *filler, void *data); extern struct page * read_cache_page(struct address_space *mapping, pgoff_t index, filler_t *filler, void *data); extern struct page * read_cache_page_gfp(struct address_space *mapping, @@ -279,14 +296,6 @@ extern struct page * read_cache_page_gfp(struct address_space *mapping, extern int read_cache_pages(struct address_space *mapping, struct list_head *pages, filler_t *filler, void *data); -static inline struct page *read_mapping_page_async( - struct address_space *mapping, - pgoff_t index, void *data) -{ - filler_t *filler = (filler_t *)mapping->a_ops->readpage; - return read_cache_page_async(mapping, index, filler, data); -} - static inline struct page *read_mapping_page(struct address_space *mapping, pgoff_t index, void *data) { @@ -539,7 +548,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, int add_to_page_cache_lru(struct page *page, struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); extern void delete_from_page_cache(struct page *page); -extern void __delete_from_page_cache(struct page *page); +extern void __delete_from_page_cache(struct page *page, void *shadow); int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask); /* diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h index e4dbfab37729..b45d391b4540 100644 --- a/include/linux/pagevec.h +++ b/include/linux/pagevec.h @@ -22,6 +22,11 @@ struct pagevec { void __pagevec_release(struct pagevec *pvec); void __pagevec_lru_add(struct pagevec *pvec); +unsigned pagevec_lookup_entries(struct pagevec *pvec, + struct address_space *mapping, + pgoff_t start, unsigned nr_entries, + pgoff_t *indices); +void pagevec_remove_exceptionals(struct pagevec *pvec); unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping, pgoff_t start, unsigned nr_pages); unsigned pagevec_lookup_tag(struct pagevec *pvec, diff --git a/include/linux/printk.h b/include/linux/printk.h index fa47e2708c01..8752f7595b27 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -24,13 +24,9 @@ static inline int printk_get_level(const char *buffer) static inline const char *printk_skip_level(const char *buffer) { - if (printk_get_level(buffer)) { - switch (buffer[1]) { - case '0' ... '7': - case 'd': /* KERN_DEFAULT */ - return buffer + 2; - } - } + if (printk_get_level(buffer)) + return buffer + 2; + return buffer; } @@ -124,9 +120,9 @@ asmlinkage __printf(1, 0) int vprintk(const char *fmt, va_list args); asmlinkage __printf(5, 6) __cold -asmlinkage int printk_emit(int facility, int level, - const char *dict, size_t dictlen, - const char *fmt, ...); +int printk_emit(int facility, int level, + const char *dict, size_t dictlen, + const char *fmt, ...); asmlinkage __printf(1, 2) __cold int printk(const char *fmt, ...); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 6965fe394c3b..1d3eee594cd6 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -46,6 +46,14 @@ void inode_reclaim_rsv_space(struct inode *inode, qsize_t number); void dquot_initialize(struct inode *inode); void dquot_drop(struct inode *inode); struct dquot *dqget(struct super_block *sb, struct kqid qid); +static inline struct dquot *dqgrab(struct dquot *dquot) +{ + /* Make sure someone else has active reference to dquot */ + WARN_ON_ONCE(!atomic_read(&dquot->dq_count)); + WARN_ON_ONCE(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)); + atomic_inc(&dquot->dq_count); + return dquot; +} void dqput(struct dquot *dquot); int dquot_scan_active(struct super_block *sb, int (*fn)(struct dquot *dquot, unsigned long priv), diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index 403940787be1..33170dbd9db4 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -60,6 +60,49 @@ static inline int radix_tree_is_indirect_ptr(void *ptr) #define RADIX_TREE_MAX_TAGS 3 +#ifdef __KERNEL__ +#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) +#else +#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ +#endif + +#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) +#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) + +#define RADIX_TREE_TAG_LONGS \ + ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) + +#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) +#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ + RADIX_TREE_MAP_SHIFT)) + +/* Height component in node->path */ +#define RADIX_TREE_HEIGHT_SHIFT (RADIX_TREE_MAX_PATH + 1) +#define RADIX_TREE_HEIGHT_MASK ((1UL << RADIX_TREE_HEIGHT_SHIFT) - 1) + +/* Internally used bits of node->count */ +#define RADIX_TREE_COUNT_SHIFT (RADIX_TREE_MAP_SHIFT + 1) +#define RADIX_TREE_COUNT_MASK ((1UL << RADIX_TREE_COUNT_SHIFT) - 1) + +struct radix_tree_node { + unsigned int path; /* Offset in parent & height from the bottom */ + unsigned int count; + union { + struct { + /* Used when ascending tree */ + struct radix_tree_node *parent; + /* For tree user */ + void *private_data; + }; + /* Used when freeing node */ + struct rcu_head rcu_head; + }; + /* For tree user */ + struct list_head private_list; + void __rcu *slots[RADIX_TREE_MAP_SIZE]; + unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; +}; + /* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ struct radix_tree_root { unsigned int height; @@ -101,6 +144,7 @@ do { \ * concurrently with other readers. * * The notable exceptions to this rule are the following functions: + * __radix_tree_lookup * radix_tree_lookup * radix_tree_lookup_slot * radix_tree_tag_get @@ -216,9 +260,16 @@ static inline void radix_tree_replace_slot(void **pslot, void *item) rcu_assign_pointer(*pslot, item); } +int __radix_tree_create(struct radix_tree_root *root, unsigned long index, + struct radix_tree_node **nodep, void ***slotp); int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); +void *__radix_tree_lookup(struct radix_tree_root *root, unsigned long index, + struct radix_tree_node **nodep, void ***slotp); void *radix_tree_lookup(struct radix_tree_root *, unsigned long); void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); +bool __radix_tree_delete_node(struct radix_tree_root *root, + struct radix_tree_node *node); +void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, @@ -226,10 +277,6 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, unsigned long *indices, unsigned long first_index, unsigned int max_items); -unsigned long radix_tree_next_hole(struct radix_tree_root *root, - unsigned long index, unsigned long max_scan); -unsigned long radix_tree_prev_hole(struct radix_tree_root *root, - unsigned long index, unsigned long max_scan); int radix_tree_preload(gfp_t gfp_mask); int radix_tree_maybe_preload(gfp_t gfp_mask); void radix_tree_init(void); diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 9d55438bc4ad..4d1771c2d29f 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -51,6 +51,7 @@ extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags); extern int shmem_zero_setup(struct vm_area_struct *); extern int shmem_lock(struct file *file, int lock, struct user_struct *user); +extern bool shmem_mapping(struct address_space *mapping); extern void shmem_unlock_mapping(struct address_space *mapping); extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); diff --git a/include/linux/swap.h b/include/linux/swap.h index 46ba0c6c219f..350711560753 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -260,6 +260,42 @@ struct swap_list_t { int next; /* swapfile to be used next */ }; +/* linux/mm/workingset.c */ +void *workingset_eviction(struct address_space *mapping, struct page *page); +bool workingset_refault(void *shadow); +void workingset_activation(struct page *page); +extern struct list_lru workingset_shadow_nodes; + +static inline unsigned int workingset_node_pages(struct radix_tree_node *node) +{ + return node->count & RADIX_TREE_COUNT_MASK; +} + +static inline void workingset_node_pages_inc(struct radix_tree_node *node) +{ + node->count++; +} + +static inline void workingset_node_pages_dec(struct radix_tree_node *node) +{ + node->count--; +} + +static inline unsigned int workingset_node_shadows(struct radix_tree_node *node) +{ + return node->count >> RADIX_TREE_COUNT_SHIFT; +} + +static inline void workingset_node_shadows_inc(struct radix_tree_node *node) +{ + node->count += 1U << RADIX_TREE_COUNT_SHIFT; +} + +static inline void workingset_node_shadows_dec(struct radix_tree_node *node) +{ + node->count -= 1U << RADIX_TREE_COUNT_SHIFT; +} + /* linux/mm/page_alloc.c */ extern unsigned long totalram_pages; extern unsigned long totalreserve_pages; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1e67b7a5968c..2aa8b749f13d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -200,6 +200,8 @@ extern struct trace_event_functions exit_syscall_print_funcs; } \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) +asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, + qid_t id, void __user *addr); asmlinkage long sys_time(time_t __user *tloc); asmlinkage long sys_stime(time_t __user *tptr); asmlinkage long sys_gettimeofday(struct timeval __user *tv, diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 3a712e2e7d76..486c3972c0be 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -37,6 +37,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, PGINODESTEAL, SLABS_SCANNED, KSWAPD_INODESTEAL, KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY, PAGEOUTRUN, ALLOCSTALL, PGROTATED, + DROP_PAGECACHE, DROP_SLAB, #ifdef CONFIG_NUMA_BALANCING NUMA_PTE_UPDATES, NUMA_HUGE_PTE_UPDATES, diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index 67ce70c8279b..ea4476157e00 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -187,8 +187,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp); #define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d) #define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d)) -extern void inc_zone_state(struct zone *, enum zone_stat_item); - #ifdef CONFIG_SMP void __mod_zone_page_state(struct zone *, enum zone_stat_item item, int); void __inc_zone_page_state(struct page *, enum zone_stat_item); @@ -230,18 +228,18 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item) atomic_long_inc(&vm_stat[item]); } -static inline void __inc_zone_page_state(struct page *page, - enum zone_stat_item item) -{ - __inc_zone_state(page_zone(page), item); -} - static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) { atomic_long_dec(&zone->vm_stat[item]); atomic_long_dec(&vm_stat[item]); } +static inline void __inc_zone_page_state(struct page *page, + enum zone_stat_item item) +{ + __inc_zone_state(page_zone(page), item); +} + static inline void __dec_zone_page_state(struct page *page, enum zone_stat_item item) { @@ -256,6 +254,9 @@ static inline void __dec_zone_page_state(struct page *page, #define dec_zone_page_state __dec_zone_page_state #define mod_zone_page_state __mod_zone_page_state +#define inc_zone_state __inc_zone_state +#define dec_zone_state __dec_zone_state + #define set_pgdat_percpu_threshold(pgdat, callback) { } static inline void refresh_cpu_vm_stats(int cpu) { } diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h index 335e8a7cad39..c140620dad92 100644 --- a/include/uapi/linux/libc-compat.h +++ b/include/uapi/linux/libc-compat.h @@ -85,6 +85,12 @@ #endif /* _NETINET_IN_H */ +/* Definitions for xattr.h */ +#if defined(_SYS_XATTR_H) +#define __UAPI_DEF_XATTR 0 +#else +#define __UAPI_DEF_XATTR 1 +#endif /* If we did not see any headers from any supported C libraries, * or we are being included in the kernel, then define everything @@ -98,6 +104,9 @@ #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 +/* Definitions for xattr.h */ +#define __UAPI_DEF_XATTR 1 + #endif /* __GLIBC__ */ #endif /* _UAPI_LIBC_COMPAT_H */ diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h index 40bbc04b6f81..c38355c1f3c9 100644 --- a/include/uapi/linux/xattr.h +++ b/include/uapi/linux/xattr.h @@ -7,11 +7,18 @@ Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> */ + +#include <linux/libc-compat.h> + #ifndef _UAPI_LINUX_XATTR_H #define _UAPI_LINUX_XATTR_H +#ifdef __UAPI_DEF_XATTR +#define __USE_KERNEL_XATTR_DEFS + #define XATTR_CREATE 0x1 /* set value, fail if attr already exists */ #define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */ +#endif /* Namespaces */ #define XATTR_OS2_PREFIX "os2." diff --git a/init/Kconfig b/init/Kconfig index 62b66acfdb30..8851c6417880 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -273,6 +273,16 @@ config FHANDLE get renamed. Enables open_by_handle_at(2) and name_to_handle_at(2) syscalls. +config USELIB + bool "uselib syscall" + default y + help + This option enables the uselib syscall, a system call used in the + dynamic linker from libc5 and earlier. glibc does not use this + system call. If you intend to run programs built on libc5 or + earlier, you may need to enable this syscall. Current systems + running glibc can safely disable this. + config AUDIT bool "Auditing support" depends on NET @@ -1291,6 +1301,16 @@ config UID16 help This enables the legacy 16-bit UID syscall wrappers. +config SYSFS_SYSCALL + bool "Sysfs syscall support" if EXPERT + default y + ---help--- + sys_sysfs is an obsolete system call no longer supported in libc. + Note that disabling this option is more secure but might break + compatibility with some systems. + + If unsure say Y here. + config SYSCTL_SYSCALL bool "Sysctl syscall support" if EXPERT depends on PROC_SYSCTL diff --git a/init/do_mounts.c b/init/do_mounts.c index 8e5addc45874..82f22885c87e 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -102,13 +102,13 @@ no_match: /** * devt_from_partuuid - looks up the dev_t of a partition by its UUID - * @uuid: char array containing ascii UUID + * @uuid_str: char array containing ascii UUID * * The function will return the first partition which contains a matching * UUID value in its partition_meta_info struct. This does not search * by filesystem UUIDs. * - * If @uuid is followed by a "/PARTNROFF=%d", then the number will be + * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be * extracted and used as an offset from the partition identified by the UUID. * * Returns the matching dev_t on success or 0 on failure. diff --git a/kernel/cpuset.c b/kernel/cpuset.c index e2dbb60004d4..3d54c418bd06 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -996,7 +996,7 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk, task_lock(tsk); /* * Determine if a loop is necessary if another thread is doing - * get_mems_allowed(). If at least one node remains unchanged and + * read_mems_allowed_begin(). If at least one node remains unchanged and * tsk does not have a mempolicy, then an empty nodemask will not be * possible when mems_allowed is larger than a word. */ diff --git a/kernel/groups.c b/kernel/groups.c index 90cf1c38c8ea..451698f86cfa 100644 --- a/kernel/groups.c +++ b/kernel/groups.c @@ -157,17 +157,13 @@ int groups_search(const struct group_info *group_info, kgid_t grp) * set_groups - Change a group subscription in a set of credentials * @new: The newly prepared set of credentials to alter * @group_info: The group list to install - * - * Validate a group subscription and, if valid, insert it into a set - * of credentials. */ -int set_groups(struct cred *new, struct group_info *group_info) +void set_groups(struct cred *new, struct group_info *group_info) { put_group_info(new->group_info); groups_sort(group_info); get_group_info(group_info); new->group_info = group_info; - return 0; } EXPORT_SYMBOL(set_groups); @@ -182,18 +178,12 @@ EXPORT_SYMBOL(set_groups); int set_current_groups(struct group_info *group_info) { struct cred *new; - int ret; new = prepare_creds(); if (!new) return -ENOMEM; - ret = set_groups(new, group_info); - if (ret < 0) { - abort_creds(new); - return ret; - } - + set_groups(new, group_info); return commit_creds(new); } diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 0b9c169d577f..06bb1417b063 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -246,5 +246,4 @@ static int __init hung_task_init(void) return 0; } - -module_init(hung_task_init); +subsys_initcall(hung_task_init); diff --git a/kernel/kexec.c b/kernel/kexec.c index 45601cf41bee..c0d261c7db7b 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1235,7 +1235,7 @@ static int __init crash_notes_memory_init(void) } return 0; } -module_init(crash_notes_memory_init) +subsys_initcall(crash_notes_memory_init); /* @@ -1629,7 +1629,7 @@ static int __init crash_save_vmcoreinfo_init(void) return 0; } -module_init(crash_save_vmcoreinfo_init) +subsys_initcall(crash_save_vmcoreinfo_init); /* * Move into place and start executing a preloaded standalone diff --git a/kernel/kthread.c b/kernel/kthread.c index b5ae3ee860a9..9a130ec06f7a 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -217,7 +217,7 @@ int tsk_fork_get_node(struct task_struct *tsk) if (tsk == kthreadd_task) return tsk->pref_node_fork; #endif - return numa_node_id(); + return NUMA_NO_NODE; } static void create_kthread(struct kthread_create_info *create) @@ -369,7 +369,7 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data), { struct task_struct *p; - p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt, + p = kthread_create_on_node(threadfn, data, cpu_to_mem(cpu), namefmt, cpu); if (IS_ERR(p)) return p; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 4dae9cbe9259..a45b50962295 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -319,7 +319,7 @@ static void log_store(int facility, int level, else free = log_first_idx - log_next_idx; - if (free > size + sizeof(struct printk_log)) + if (free >= size + sizeof(struct printk_log)) break; /* drop old messages until we have enough contiuous space */ @@ -327,7 +327,7 @@ static void log_store(int facility, int level, log_first_seq++; } - if (log_next_idx + size + sizeof(struct printk_log) >= log_buf_len) { + if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) { /* * This message + an additional empty header does not fit * at the end of the buffer. Add an empty header with len == 0 @@ -351,7 +351,7 @@ static void log_store(int facility, int level, else msg->ts_nsec = local_clock(); memset(log_dict(msg) + dict_len, 0, pad_len); - msg->len = sizeof(struct printk_log) + text_len + dict_len + pad_len; + msg->len = size; /* insert message */ log_next_idx += msg->len; @@ -1560,9 +1560,12 @@ asmlinkage int vprintk_emit(int facility, int level, level = kern_level - '0'; case 'd': /* KERN_DEFAULT */ lflags |= LOG_PREFIX; - case 'c': /* KERN_CONT */ - break; } + /* + * No need to check length here because vscnprintf + * put '\0' at the end of the string. Only valid and + * newly printed level is detected. + */ text_len -= end_of_header - text; text = (char *)end_of_header; } @@ -1880,6 +1883,7 @@ void suspend_console(void) console_lock(); console_suspended = 1; up(&console_sem); + mutex_release(&console_lock_dep_map, 1, _RET_IP_); } void resume_console(void) @@ -1887,6 +1891,7 @@ void resume_console(void) if (!console_suspend_enabled) return; down(&console_sem); + mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_); console_suspended = 0; console_unlock(); } diff --git a/kernel/profile.c b/kernel/profile.c index ebdd9c1a86b4..1b266dbe755a 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -604,5 +604,5 @@ int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */ hotcpu_notifier(profile_cpu_callback, 0); return 0; } -module_init(create_proc_profile); +subsys_initcall(create_proc_profile); #endif /* CONFIG_PROC_FS */ diff --git a/kernel/resource.c b/kernel/resource.c index 673061c06da1..8957d686e29b 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -511,7 +511,7 @@ static int find_resource(struct resource *root, struct resource *new, * @newsize: new size of the resource descriptor * @constraint: the size and alignment constraints to be met. */ -int reallocate_resource(struct resource *root, struct resource *old, +static int reallocate_resource(struct resource *root, struct resource *old, resource_size_t newsize, struct resource_constraint *constraint) { diff --git a/kernel/sched/stats.c b/kernel/sched/stats.c index da98af347e8b..a476bea17fbc 100644 --- a/kernel/sched/stats.c +++ b/kernel/sched/stats.c @@ -142,4 +142,4 @@ static int __init proc_schedstat_init(void) proc_create("schedstat", 0, NULL, &proc_schedstat_operations); return 0; } -module_init(proc_schedstat_init); +subsys_initcall(proc_schedstat_init); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 7078052284fd..bc8d1b74a6b9 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -146,11 +146,13 @@ cond_syscall(sys_io_destroy); cond_syscall(sys_io_submit); cond_syscall(sys_io_cancel); cond_syscall(sys_io_getevents); +cond_syscall(sys_sysfs); cond_syscall(sys_syslog); cond_syscall(sys_process_vm_readv); cond_syscall(sys_process_vm_writev); cond_syscall(compat_sys_process_vm_readv); cond_syscall(compat_sys_process_vm_writev); +cond_syscall(sys_uselib); /* arch-specific weak syscall entries */ cond_syscall(sys_pciconfig_read); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 09d2e2413605..5c14b547882e 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -123,7 +123,7 @@ static int __maybe_unused neg_one = -1; static int zero; static int __maybe_unused one = 1; static int __maybe_unused two = 2; -static int __maybe_unused three = 3; +static int __maybe_unused four = 4; static unsigned long one_ul = 1; static int one_hundred = 100; #ifdef CONFIG_PRINTK @@ -1264,7 +1264,7 @@ static struct ctl_table vm_table[] = { .mode = 0644, .proc_handler = drop_caches_sysctl_handler, .extra1 = &one, - .extra2 = &three, + .extra2 = &four, }, #ifdef CONFIG_COMPACTION { diff --git a/kernel/user.c b/kernel/user.c index c006131beb77..294fc6a94168 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -222,5 +222,4 @@ static int __init uid_cache_init(void) return 0; } - -module_init(uid_cache_init); +subsys_initcall(uid_cache_init); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index dd06439b9c84..0d8f6023fd8d 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -902,4 +902,4 @@ static __init int user_namespaces_init(void) user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC); return 0; } -module_init(user_namespaces_init); +subsys_initcall(user_namespaces_init); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 01c6f979486f..e90089fd78e0 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -158,14 +158,14 @@ void touch_all_softlockup_watchdogs(void) #ifdef CONFIG_HARDLOCKUP_DETECTOR void touch_nmi_watchdog(void) { - if (watchdog_user_enabled) { - unsigned cpu; - - for_each_present_cpu(cpu) { - if (per_cpu(watchdog_nmi_touch, cpu) != true) - per_cpu(watchdog_nmi_touch, cpu) = true; - } - } + /* + * Using __raw here because some code paths have + * preemption enabled. If preemption is enabled + * then interrupts should be enabled too, in which + * case we shouldn't have to worry about the watchdog + * going off. + */ + __raw_get_cpu_var(watchdog_nmi_touch) = true; touch_softlockup_watchdog(); } EXPORT_SYMBOL(touch_nmi_watchdog); diff --git a/lib/clz_ctz.c b/lib/clz_ctz.c index a8f8379eb49f..2e11e48446ab 100644 --- a/lib/clz_ctz.c +++ b/lib/clz_ctz.c @@ -6,6 +6,9 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * The functions in this file aren't called directly, but are required by + * GCC builtins such as __builtin_ctz, and therefore they can't be removed + * despite appearing unreferenced in kernel source. * * __c[lt]z[sd]i2 can be overridden by linking arch-specific versions. */ @@ -13,18 +16,22 @@ #include <linux/export.h> #include <linux/kernel.h> +int __weak __ctzsi2(int val); int __weak __ctzsi2(int val) { return __ffs(val); } EXPORT_SYMBOL(__ctzsi2); +int __weak __clzsi2(int val); int __weak __clzsi2(int val) { return 32 - fls(val); } EXPORT_SYMBOL(__clzsi2); +int __weak __clzdi2(long val); +int __weak __ctzdi2(long val); #if BITS_PER_LONG == 32 int __weak __clzdi2(long val) diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c index d619b28c456f..0edfd742a154 100644 --- a/lib/decompress_inflate.c +++ b/lib/decompress_inflate.c @@ -19,6 +19,7 @@ #include "zlib_inflate/inflate.h" #include "zlib_inflate/infutil.h" +#include <linux/decompress/inflate.h> #endif /* STATIC */ diff --git a/lib/devres.c b/lib/devres.c index 823533138fa0..48cb3c7bd7de 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -81,11 +81,13 @@ EXPORT_SYMBOL(devm_ioremap_nocache); void devm_iounmap(struct device *dev, void __iomem *addr) { WARN_ON(devres_destroy(dev, devm_ioremap_release, devm_ioremap_match, - (void *)addr)); + (__force void *)addr)); iounmap(addr); } EXPORT_SYMBOL(devm_iounmap); +#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err) + /** * devm_ioremap_resource() - check, request region, and ioremap resource * @dev: generic device to handle the resource for @@ -114,7 +116,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res) if (!res || resource_type(res) != IORESOURCE_MEM) { dev_err(dev, "invalid resource\n"); - return ERR_PTR(-EINVAL); + return IOMEM_ERR_PTR(-EINVAL); } size = resource_size(res); @@ -122,7 +124,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res) if (!devm_request_mem_region(dev, res->start, size, name)) { dev_err(dev, "can't request region for resource %pR\n", res); - return ERR_PTR(-EBUSY); + return IOMEM_ERR_PTR(-EBUSY); } if (res->flags & IORESOURCE_CACHEABLE) @@ -133,7 +135,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res) if (!dest_ptr) { dev_err(dev, "ioremap failed for resource %pR\n", res); devm_release_mem_region(dev, res->start, size); - dest_ptr = ERR_PTR(-ENOMEM); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); } return dest_ptr; @@ -224,7 +226,7 @@ void devm_ioport_unmap(struct device *dev, void __iomem *addr) { ioport_unmap(addr); WARN_ON(devres_destroy(dev, devm_ioport_map_release, - devm_ioport_map_match, (void *)addr)); + devm_ioport_map_match, (__force void *)addr)); } EXPORT_SYMBOL(devm_ioport_unmap); #endif /* CONFIG_HAS_IOPORT */ diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 5f72767ddd9b..4e3bd71bd949 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -124,6 +124,30 @@ static int kobj_usermode_filter(struct kobject *kobj) return 0; } +static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem) +{ + int len; + + len = strlcpy(&env->buf[env->buflen], subsystem, + sizeof(env->buf) - env->buflen); + if (len >= (sizeof(env->buf) - env->buflen)) { + WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n"); + return -ENOMEM; + } + + env->argv[0] = uevent_helper; + env->argv[1] = &env->buf[env->buflen]; + env->argv[2] = NULL; + + env->buflen += len + 1; + return 0; +} + +static void cleanup_uevent_env(struct subprocess_info *info) +{ + kfree(info->data); +} + /** * kobject_uevent_env - send an uevent with environmental data * @@ -301,11 +325,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { - char *argv [3]; + struct subprocess_info *info; - argv [0] = uevent_helper; - argv [1] = (char *)subsystem; - argv [2] = NULL; retval = add_uevent_var(env, "HOME=/"); if (retval) goto exit; @@ -313,9 +334,18 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); if (retval) goto exit; + retval = init_uevent_argv(env, subsystem); + if (retval) + goto exit; - retval = call_usermodehelper(argv[0], argv, - env->envp, UMH_WAIT_EXEC); + retval = -ENOMEM; + info = call_usermodehelper_setup(env->argv[0], env->argv, + env->envp, GFP_KERNEL, + NULL, cleanup_uevent_env, env); + if (info) { + retval = call_usermodehelper_exec(info, UMH_NO_WAIT); + env = NULL; /* freed by cleanup_uevent_env */ + } } exit: diff --git a/lib/radix-tree.c b/lib/radix-tree.c index bd4a8dfdf0b8..9599aa72d7a0 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -35,33 +35,6 @@ #include <linux/hardirq.h> /* in_interrupt() */ -#ifdef __KERNEL__ -#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) -#else -#define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ -#endif - -#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) -#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) - -#define RADIX_TREE_TAG_LONGS \ - ((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) / BITS_PER_LONG) - -struct radix_tree_node { - unsigned int height; /* Height from the bottom */ - unsigned int count; - union { - struct radix_tree_node *parent; /* Used when ascending tree */ - struct rcu_head rcu_head; /* Used when freeing node */ - }; - void __rcu *slots[RADIX_TREE_MAP_SIZE]; - unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS]; -}; - -#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) -#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ - RADIX_TREE_MAP_SHIFT)) - /* * The height_to_maxindex array needs to be one deeper than the maximum * path as height 0 holds only 1 entry. @@ -369,7 +342,8 @@ static int radix_tree_extend(struct radix_tree_root *root, unsigned long index) /* Increase the height. */ newheight = root->height+1; - node->height = newheight; + BUG_ON(newheight & ~RADIX_TREE_HEIGHT_MASK); + node->path = newheight; node->count = 1; node->parent = NULL; slot = root->rnode; @@ -387,23 +361,28 @@ out: } /** - * radix_tree_insert - insert into a radix tree + * __radix_tree_create - create a slot in a radix tree * @root: radix tree root * @index: index key - * @item: item to insert + * @nodep: returns node + * @slotp: returns slot * - * Insert an item into the radix tree at position @index. + * Create, if necessary, and return the node and slot for an item + * at position @index in the radix tree @root. + * + * Until there is more than one item in the tree, no nodes are + * allocated and @root->rnode is used as a direct slot instead of + * pointing to a node, in which case *@nodep will be NULL. + * + * Returns -ENOMEM, or 0 for success. */ -int radix_tree_insert(struct radix_tree_root *root, - unsigned long index, void *item) +int __radix_tree_create(struct radix_tree_root *root, unsigned long index, + struct radix_tree_node **nodep, void ***slotp) { struct radix_tree_node *node = NULL, *slot; - unsigned int height, shift; - int offset; + unsigned int height, shift, offset; int error; - BUG_ON(radix_tree_is_indirect_ptr(item)); - /* Make sure the tree is high enough. */ if (index > radix_tree_maxindex(root->height)) { error = radix_tree_extend(root, index); @@ -422,11 +401,12 @@ int radix_tree_insert(struct radix_tree_root *root, /* Have to add a child node. */ if (!(slot = radix_tree_node_alloc(root))) return -ENOMEM; - slot->height = height; + slot->path = height; slot->parent = node; if (node) { rcu_assign_pointer(node->slots[offset], slot); node->count++; + slot->path |= offset << RADIX_TREE_HEIGHT_SHIFT; } else rcu_assign_pointer(root->rnode, ptr_to_indirect(slot)); } @@ -439,16 +419,42 @@ int radix_tree_insert(struct radix_tree_root *root, height--; } - if (slot != NULL) + if (nodep) + *nodep = node; + if (slotp) + *slotp = node ? node->slots + offset : (void **)&root->rnode; + return 0; +} + +/** + * radix_tree_insert - insert into a radix tree + * @root: radix tree root + * @index: index key + * @item: item to insert + * + * Insert an item into the radix tree at position @index. + */ +int radix_tree_insert(struct radix_tree_root *root, + unsigned long index, void *item) +{ + struct radix_tree_node *node; + void **slot; + int error; + + BUG_ON(radix_tree_is_indirect_ptr(item)); + + error = __radix_tree_create(root, index, &node, &slot); + if (error) + return error; + if (*slot != NULL) return -EEXIST; + rcu_assign_pointer(*slot, item); if (node) { node->count++; - rcu_assign_pointer(node->slots[offset], item); - BUG_ON(tag_get(node, 0, offset)); - BUG_ON(tag_get(node, 1, offset)); + BUG_ON(tag_get(node, 0, index & RADIX_TREE_MAP_MASK)); + BUG_ON(tag_get(node, 1, index & RADIX_TREE_MAP_MASK)); } else { - rcu_assign_pointer(root->rnode, item); BUG_ON(root_tag_get(root, 0)); BUG_ON(root_tag_get(root, 1)); } @@ -457,15 +463,26 @@ int radix_tree_insert(struct radix_tree_root *root, } EXPORT_SYMBOL(radix_tree_insert); -/* - * is_slot == 1 : search for the slot. - * is_slot == 0 : search for the node. +/** + * __radix_tree_lookup - lookup an item in a radix tree + * @root: radix tree root + * @index: index key + * @nodep: returns node + * @slotp: returns slot + * + * Lookup and return the item at position @index in the radix + * tree @root. + * + * Until there is more than one item in the tree, no nodes are + * allocated and @root->rnode is used as a direct slot instead of + * pointing to a node, in which case *@nodep will be NULL. */ -static void *radix_tree_lookup_element(struct radix_tree_root *root, - unsigned long index, int is_slot) +void *__radix_tree_lookup(struct radix_tree_root *root, unsigned long index, + struct radix_tree_node **nodep, void ***slotp) { + struct radix_tree_node *node, *parent; unsigned int height, shift; - struct radix_tree_node *node, **slot; + void **slot; node = rcu_dereference_raw(root->rnode); if (node == NULL) @@ -474,19 +491,24 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root, if (!radix_tree_is_indirect_ptr(node)) { if (index > 0) return NULL; - return is_slot ? (void *)&root->rnode : node; + + if (nodep) + *nodep = NULL; + if (slotp) + *slotp = (void **)&root->rnode; + return node; } node = indirect_to_ptr(node); - height = node->height; + height = node->path & RADIX_TREE_HEIGHT_MASK; if (index > radix_tree_maxindex(height)) return NULL; shift = (height-1) * RADIX_TREE_MAP_SHIFT; do { - slot = (struct radix_tree_node **) - (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); + parent = node; + slot = node->slots + ((index >> shift) & RADIX_TREE_MAP_MASK); node = rcu_dereference_raw(*slot); if (node == NULL) return NULL; @@ -495,7 +517,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root, height--; } while (height > 0); - return is_slot ? (void *)slot : indirect_to_ptr(node); + if (nodep) + *nodep = parent; + if (slotp) + *slotp = slot; + return node; } /** @@ -513,7 +539,11 @@ static void *radix_tree_lookup_element(struct radix_tree_root *root, */ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) { - return (void **)radix_tree_lookup_element(root, index, 1); + void **slot; + + if (!__radix_tree_lookup(root, index, NULL, &slot)) + return NULL; + return slot; } EXPORT_SYMBOL(radix_tree_lookup_slot); @@ -531,7 +561,7 @@ EXPORT_SYMBOL(radix_tree_lookup_slot); */ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) { - return radix_tree_lookup_element(root, index, 0); + return __radix_tree_lookup(root, index, NULL, NULL); } EXPORT_SYMBOL(radix_tree_lookup); @@ -676,7 +706,7 @@ int radix_tree_tag_get(struct radix_tree_root *root, return (index == 0); node = indirect_to_ptr(node); - height = node->height; + height = node->path & RADIX_TREE_HEIGHT_MASK; if (index > radix_tree_maxindex(height)) return 0; @@ -713,7 +743,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root, { unsigned shift, tag = flags & RADIX_TREE_ITER_TAG_MASK; struct radix_tree_node *rnode, *node; - unsigned long index, offset; + unsigned long index, offset, height; if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag)) return NULL; @@ -744,7 +774,8 @@ void **radix_tree_next_chunk(struct radix_tree_root *root, return NULL; restart: - shift = (rnode->height - 1) * RADIX_TREE_MAP_SHIFT; + height = rnode->path & RADIX_TREE_HEIGHT_MASK; + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; offset = index >> shift; /* Index outside of the tree */ @@ -946,81 +977,6 @@ next: } EXPORT_SYMBOL(radix_tree_range_tag_if_tagged); - -/** - * radix_tree_next_hole - find the next hole (not-present entry) - * @root: tree root - * @index: index key - * @max_scan: maximum range to search - * - * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the lowest - * indexed hole. - * - * Returns: the index of the hole if found, otherwise returns an index - * outside of the set specified (in which case 'return - index >= max_scan' - * will be true). In rare cases of index wrap-around, 0 will be returned. - * - * radix_tree_next_hole may be called under rcu_read_lock. However, like - * radix_tree_gang_lookup, this will not atomically search a snapshot of - * the tree at a single point in time. For example, if a hole is created - * at index 5, then subsequently a hole is created at index 10, - * radix_tree_next_hole covering both indexes may return 10 if called - * under rcu_read_lock. - */ -unsigned long radix_tree_next_hole(struct radix_tree_root *root, - unsigned long index, unsigned long max_scan) -{ - unsigned long i; - - for (i = 0; i < max_scan; i++) { - if (!radix_tree_lookup(root, index)) - break; - index++; - if (index == 0) - break; - } - - return index; -} -EXPORT_SYMBOL(radix_tree_next_hole); - -/** - * radix_tree_prev_hole - find the prev hole (not-present entry) - * @root: tree root - * @index: index key - * @max_scan: maximum range to search - * - * Search backwards in the range [max(index-max_scan+1, 0), index] - * for the first hole. - * - * Returns: the index of the hole if found, otherwise returns an index - * outside of the set specified (in which case 'index - return >= max_scan' - * will be true). In rare cases of wrap-around, ULONG_MAX will be returned. - * - * radix_tree_next_hole may be called under rcu_read_lock. However, like - * radix_tree_gang_lookup, this will not atomically search a snapshot of - * the tree at a single point in time. For example, if a hole is created - * at index 10, then subsequently a hole is created at index 5, - * radix_tree_prev_hole covering both indexes may return 5 if called under - * rcu_read_lock. - */ -unsigned long radix_tree_prev_hole(struct radix_tree_root *root, - unsigned long index, unsigned long max_scan) -{ - unsigned long i; - - for (i = 0; i < max_scan; i++) { - if (!radix_tree_lookup(root, index)) - break; - index--; - if (index == ULONG_MAX) - break; - } - - return index; -} -EXPORT_SYMBOL(radix_tree_prev_hole); - /** * radix_tree_gang_lookup - perform multiple lookup on a radix tree * @root: radix tree root @@ -1189,7 +1145,7 @@ static unsigned long __locate(struct radix_tree_node *slot, void *item, unsigned int shift, height; unsigned long i; - height = slot->height; + height = slot->path & RADIX_TREE_HEIGHT_MASK; shift = (height-1) * RADIX_TREE_MAP_SHIFT; for ( ; height > 1; height--) { @@ -1252,7 +1208,8 @@ unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item) } node = indirect_to_ptr(node); - max_index = radix_tree_maxindex(node->height); + max_index = radix_tree_maxindex(node->path & + RADIX_TREE_HEIGHT_MASK); if (cur_index > max_index) { rcu_read_unlock(); break; @@ -1337,48 +1294,90 @@ static inline void radix_tree_shrink(struct radix_tree_root *root) } /** - * radix_tree_delete - delete an item from a radix tree + * __radix_tree_delete_node - try to free node after clearing a slot * @root: radix tree root * @index: index key + * @node: node containing @index * - * Remove the item at @index from the radix tree rooted at @root. + * After clearing the slot at @index in @node from radix tree + * rooted at @root, call this function to attempt freeing the + * node and shrinking the tree. * - * Returns the address of the deleted item, or NULL if it was not present. + * Returns %true if @node was freed, %false otherwise. */ -void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) +bool __radix_tree_delete_node(struct radix_tree_root *root, + struct radix_tree_node *node) { - struct radix_tree_node *node = NULL; - struct radix_tree_node *slot = NULL; - struct radix_tree_node *to_free; - unsigned int height, shift; + bool deleted = false; + + do { + struct radix_tree_node *parent; + + if (node->count) { + if (node == indirect_to_ptr(root->rnode)) { + radix_tree_shrink(root); + if (root->height == 0) + deleted = true; + } + return deleted; + } + + parent = node->parent; + if (parent) { + unsigned int offset; + + offset = node->path >> RADIX_TREE_HEIGHT_SHIFT; + parent->slots[offset] = NULL; + parent->count--; + } else { + root_tag_clear_all(root); + root->height = 0; + root->rnode = NULL; + } + + radix_tree_node_free(node); + deleted = true; + + node = parent; + } while (node); + + return deleted; +} + +/** + * radix_tree_delete_item - delete an item from a radix tree + * @root: radix tree root + * @index: index key + * @item: expected item + * + * Remove @item at @index from the radix tree rooted at @root. + * + * Returns the address of the deleted item, or NULL if it was not present + * or the entry at the given @index was not @item. + */ +void *radix_tree_delete_item(struct radix_tree_root *root, + unsigned long index, void *item) +{ + struct radix_tree_node *node; + unsigned int offset; + void **slot; + void *entry; int tag; - int uninitialized_var(offset); - height = root->height; - if (index > radix_tree_maxindex(height)) - goto out; + entry = __radix_tree_lookup(root, index, &node, &slot); + if (!entry) + return NULL; - slot = root->rnode; - if (height == 0) { + if (item && entry != item) + return NULL; + + if (!node) { root_tag_clear_all(root); root->rnode = NULL; - goto out; + return entry; } - slot = indirect_to_ptr(slot); - shift = height * RADIX_TREE_MAP_SHIFT; - do { - if (slot == NULL) - goto out; - - shift -= RADIX_TREE_MAP_SHIFT; - offset = (index >> shift) & RADIX_TREE_MAP_MASK; - node = slot; - slot = slot->slots[offset]; - } while (shift); - - if (slot == NULL) - goto out; + offset = index & RADIX_TREE_MAP_MASK; /* * Clear all tags associated with the item to be deleted. @@ -1389,40 +1388,27 @@ void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) radix_tree_tag_clear(root, index, tag); } - to_free = NULL; - /* Now free the nodes we do not need anymore */ - while (node) { - node->slots[offset] = NULL; - node->count--; - /* - * Queue the node for deferred freeing after the - * last reference to it disappears (set NULL, above). - */ - if (to_free) - radix_tree_node_free(to_free); - - if (node->count) { - if (node == indirect_to_ptr(root->rnode)) - radix_tree_shrink(root); - goto out; - } + node->slots[offset] = NULL; + node->count--; - /* Node with zero slots in use so free it */ - to_free = node; + __radix_tree_delete_node(root, node); - index >>= RADIX_TREE_MAP_SHIFT; - offset = index & RADIX_TREE_MAP_MASK; - node = node->parent; - } - - root_tag_clear_all(root); - root->height = 0; - root->rnode = NULL; - if (to_free) - radix_tree_node_free(to_free); + return entry; +} +EXPORT_SYMBOL(radix_tree_delete_item); -out: - return slot; +/** + * radix_tree_delete - delete an item from a radix tree + * @root: radix tree root + * @index: index key + * + * Remove the item at @index from the radix tree rooted at @root. + * + * Returns the address of the deleted item, or NULL if it was not present. + */ +void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) +{ + return radix_tree_delete_item(root, index, NULL); } EXPORT_SYMBOL(radix_tree_delete); @@ -1438,9 +1424,12 @@ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) EXPORT_SYMBOL(radix_tree_tagged); static void -radix_tree_node_ctor(void *node) +radix_tree_node_ctor(void *arg) { - memset(node, 0, sizeof(struct radix_tree_node)); + struct radix_tree_node *node = arg; + + memset(node, 0, sizeof(*node)); + INIT_LIST_HEAD(&node->private_list); } static __init unsigned long __maxindex(unsigned int height) diff --git a/lib/random32.c b/lib/random32.c index 614896778700..fa5da61ce7ad 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -1,37 +1,35 @@ /* - This is a maximally equidistributed combined Tausworthe generator - based on code from GNU Scientific Library 1.5 (30 Jun 2004) - - lfsr113 version: - - x_n = (s1_n ^ s2_n ^ s3_n ^ s4_n) - - s1_{n+1} = (((s1_n & 4294967294) << 18) ^ (((s1_n << 6) ^ s1_n) >> 13)) - s2_{n+1} = (((s2_n & 4294967288) << 2) ^ (((s2_n << 2) ^ s2_n) >> 27)) - s3_{n+1} = (((s3_n & 4294967280) << 7) ^ (((s3_n << 13) ^ s3_n) >> 21)) - s4_{n+1} = (((s4_n & 4294967168) << 13) ^ (((s4_n << 3) ^ s4_n) >> 12)) - - The period of this generator is about 2^113 (see erratum paper). - - From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe - Generators", Mathematics of Computation, 65, 213 (1996), 203--213: - http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps - ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps - - There is an erratum in the paper "Tables of Maximally - Equidistributed Combined LFSR Generators", Mathematics of - Computation, 68, 225 (1999), 261--269: - http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps - - ... the k_j most significant bits of z_j must be non- - zero, for each j. (Note: this restriction also applies to the - computer code given in [4], but was mistakenly not mentioned in - that paper.) - - This affects the seeding procedure by imposing the requirement - s1 > 1, s2 > 7, s3 > 15, s4 > 127. - -*/ + * This is a maximally equidistributed combined Tausworthe generator + * based on code from GNU Scientific Library 1.5 (30 Jun 2004) + * + * lfsr113 version: + * + * x_n = (s1_n ^ s2_n ^ s3_n ^ s4_n) + * + * s1_{n+1} = (((s1_n & 4294967294) << 18) ^ (((s1_n << 6) ^ s1_n) >> 13)) + * s2_{n+1} = (((s2_n & 4294967288) << 2) ^ (((s2_n << 2) ^ s2_n) >> 27)) + * s3_{n+1} = (((s3_n & 4294967280) << 7) ^ (((s3_n << 13) ^ s3_n) >> 21)) + * s4_{n+1} = (((s4_n & 4294967168) << 13) ^ (((s4_n << 3) ^ s4_n) >> 12)) + * + * The period of this generator is about 2^113 (see erratum paper). + * + * From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe + * Generators", Mathematics of Computation, 65, 213 (1996), 203--213: + * http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps + * ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps + * + * There is an erratum in the paper "Tables of Maximally Equidistributed + * Combined LFSR Generators", Mathematics of Computation, 68, 225 (1999), + * 261--269: http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme2.ps + * + * ... the k_j most significant bits of z_j must be non-zero, + * for each j. (Note: this restriction also applies to the + * computer code given in [4], but was mistakenly not mentioned + * in that paper.) + * + * This affects the seeding procedure by imposing the requirement + * s1 > 1, s2 > 7, s3 > 15, s4 > 127. + */ #include <linux/types.h> #include <linux/percpu.h> @@ -75,15 +73,17 @@ EXPORT_SYMBOL(prandom_u32_state); */ u32 prandom_u32(void) { - unsigned long r; struct rnd_state *state = &get_cpu_var(net_rand_state); - r = prandom_u32_state(state); + u32 res; + + res = prandom_u32_state(state); put_cpu_var(state); - return r; + + return res; } EXPORT_SYMBOL(prandom_u32); -/* +/** * prandom_bytes_state - get the requested number of pseudo-random bytes * * @state: pointer to state structure holding seeded state. @@ -204,6 +204,7 @@ static int __init prandom_init(void) prandom_seed_very_weak(state, (i + jiffies) ^ random_get_entropy()); prandom_warmup(state); } + return 0; } core_initcall(prandom_init); @@ -259,6 +260,7 @@ static void __prandom_reseed(bool late) if (latch && !late) goto out; + latch = true; for_each_possible_cpu(i) { diff --git a/lib/syscall.c b/lib/syscall.c index 58710eefeac8..e30e03932480 100644 --- a/lib/syscall.c +++ b/lib/syscall.c @@ -72,4 +72,3 @@ int task_current_syscall(struct task_struct *target, long *callno, return 0; } -EXPORT_SYMBOL_GPL(task_current_syscall); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 5e2cf6f342f8..0648291cdafe 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -364,7 +364,6 @@ enum format_type { FORMAT_TYPE_SHORT, FORMAT_TYPE_UINT, FORMAT_TYPE_INT, - FORMAT_TYPE_NRCHARS, FORMAT_TYPE_SIZE_T, FORMAT_TYPE_PTRDIFF }; @@ -1538,10 +1537,6 @@ qualifier: return fmt - start; /* skip alnum */ - case 'n': - spec->type = FORMAT_TYPE_NRCHARS; - return ++fmt - start; - case '%': spec->type = FORMAT_TYPE_PERCENT_CHAR; return ++fmt - start; @@ -1564,6 +1559,15 @@ qualifier: case 'u': break; + case 'n': + /* + * Since %n poses a greater security risk than utility, treat + * it as an invalid format specifier. Warn about its use so + * that new instances don't get added. + */ + WARN_ONCE(1, "Please remove ignored %%n in '%s'\n", fmt); + /* Fall-through */ + default: spec->type = FORMAT_TYPE_INVALID; return fmt - start; @@ -1737,20 +1741,6 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ++str; break; - case FORMAT_TYPE_NRCHARS: { - /* - * Since %n poses a greater security risk than - * utility, ignore %n and skip its argument. - */ - void *skip_arg; - - WARN_ONCE(1, "Please remove ignored %%n in '%s'\n", - old_fmt); - - skip_arg = va_arg(args, void *); - break; - } - default: switch (spec.type) { case FORMAT_TYPE_LONG_LONG: @@ -2025,19 +2015,6 @@ do { \ fmt++; break; - case FORMAT_TYPE_NRCHARS: { - /* skip %n 's argument */ - u8 qualifier = spec.qualifier; - void *skip_arg; - if (qualifier == 'l') - skip_arg = va_arg(args, long *); - else if (_tolower(qualifier) == 'z') - skip_arg = va_arg(args, size_t *); - else - skip_arg = va_arg(args, int *); - break; - } - default: switch (spec.type) { @@ -2196,10 +2173,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf) ++str; break; - case FORMAT_TYPE_NRCHARS: - /* skip */ - break; - default: { unsigned long long num; diff --git a/mm/Makefile b/mm/Makefile index 310c90a09264..cdd741519ee0 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -17,7 +17,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ util.o mmzone.o vmstat.o backing-dev.o \ mm_init.o mmu_context.o percpu.o slab_common.o \ compaction.o balloon_compaction.o \ - interval_tree.o list_lru.o $(mmu-y) + interval_tree.o list_lru.o workingset.o $(mmu-y) obj-y += init-mm.o diff --git a/mm/backing-dev.c b/mm/backing-dev.c index ce682f7a4f29..09d9591b7708 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -288,13 +288,19 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi) * Note, we wouldn't bother setting up the timer, but this function is on the * fast-path (used by '__mark_inode_dirty()'), so we save few context switches * by delaying the wake-up. + * + * We have to be careful not to postpone flush work if it is scheduled for + * earlier. Thus we use queue_delayed_work(). */ void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi) { unsigned long timeout; timeout = msecs_to_jiffies(dirty_writeback_interval * 10); - mod_delayed_work(bdi_wq, &bdi->wb.dwork, timeout); + spin_lock_bh(&bdi->wb_lock); + if (test_bit(BDI_registered, &bdi->state)) + queue_delayed_work(bdi_wq, &bdi->wb.dwork, timeout); + spin_unlock_bh(&bdi->wb_lock); } /* @@ -307,9 +313,6 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi) spin_unlock_bh(&bdi_lock); synchronize_rcu_expedited(); - - /* bdi_list is now unused, clear it to mark @bdi dying */ - INIT_LIST_HEAD(&bdi->bdi_list); } int bdi_register(struct backing_dev_info *bdi, struct device *parent, @@ -360,6 +363,11 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) */ bdi_remove_from_list(bdi); + /* Make sure nobody queues further work */ + spin_lock_bh(&bdi->wb_lock); + clear_bit(BDI_registered, &bdi->state); + spin_unlock_bh(&bdi->wb_lock); + /* * Drain work list and shutdown the delayed_work. At this point, * @bdi->bdi_list is empty telling bdi_Writeback_workfn() that @bdi diff --git a/mm/compaction.c b/mm/compaction.c index 918577595ea8..b6ab77160068 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -584,6 +584,15 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, continue; } + /* + * Migration will fail if an anonymous page is pinned in memory, + * so avoid taking lru_lock and isolating it unnecessarily in an + * admittedly racy check. + */ + if (!page_mapping(page) && + page_count(page) > page_mapcount(page)) + continue; + /* Check if it is ok to still hold the lock */ locked = compact_checklock_irqsave(&zone->lru_lock, &flags, locked, cc); @@ -1186,6 +1195,7 @@ static void compact_node(int nid) struct compact_control cc = { .order = -1, .sync = true, + .ignore_skip_hint = true, }; __compact_pgdat(NODE_DATA(nid), &cc); @@ -1225,7 +1235,7 @@ int sysctl_extfrag_handler(struct ctl_table *table, int write, } #if defined(CONFIG_SYSFS) && defined(CONFIG_NUMA) -ssize_t sysfs_compact_node(struct device *dev, +static ssize_t sysfs_compact_node(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/mm/filemap.c b/mm/filemap.c index 7a13f6ac5421..21781f1fe52b 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -107,12 +107,75 @@ * ->tasklist_lock (memory_failure, collect_procs_ao) */ +static void page_cache_tree_delete(struct address_space *mapping, + struct page *page, void *shadow) +{ + struct radix_tree_node *node; + unsigned long index; + unsigned int offset; + unsigned int tag; + void **slot; + + VM_BUG_ON(!PageLocked(page)); + + __radix_tree_lookup(&mapping->page_tree, page->index, &node, &slot); + + if (shadow) { + mapping->nrshadows++; + /* + * Make sure the nrshadows update is committed before + * the nrpages update so that final truncate racing + * with reclaim does not see both counters 0 at the + * same time and miss a shadow entry. + */ + smp_wmb(); + } + mapping->nrpages--; + + if (!node) { + /* Clear direct pointer tags in root node */ + mapping->page_tree.gfp_mask &= __GFP_BITS_MASK; + radix_tree_replace_slot(slot, shadow); + return; + } + + /* Clear tree tags for the removed page */ + index = page->index; + offset = index & RADIX_TREE_MAP_MASK; + for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { + if (test_bit(offset, node->tags[tag])) + radix_tree_tag_clear(&mapping->page_tree, index, tag); + } + + /* Delete page, swap shadow entry */ + radix_tree_replace_slot(slot, shadow); + workingset_node_pages_dec(node); + if (shadow) + workingset_node_shadows_inc(node); + else + if (__radix_tree_delete_node(&mapping->page_tree, node)) + return; + + /* + * Track node that only contains shadow entries. + * + * Avoid acquiring the list_lru lock if already tracked. The + * list_empty() test is safe as node->private_list is + * protected by mapping->tree_lock. + */ + if (!workingset_node_pages(node) && + list_empty(&node->private_list)) { + node->private_data = mapping; + list_lru_add(&workingset_shadow_nodes, &node->private_list); + } +} + /* * Delete a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage * is safe. The caller must hold the mapping's tree_lock. */ -void __delete_from_page_cache(struct page *page) +void __delete_from_page_cache(struct page *page, void *shadow) { struct address_space *mapping = page->mapping; @@ -127,10 +190,11 @@ void __delete_from_page_cache(struct page *page) else cleancache_invalidate_page(mapping, page); - radix_tree_delete(&mapping->page_tree, page->index); + page_cache_tree_delete(mapping, page, shadow); + page->mapping = NULL; /* Leave page->index set: truncation lookup relies upon it */ - mapping->nrpages--; + __dec_zone_page_state(page, NR_FILE_PAGES); if (PageSwapBacked(page)) __dec_zone_page_state(page, NR_SHMEM); @@ -166,7 +230,7 @@ void delete_from_page_cache(struct page *page) freepage = mapping->a_ops->freepage; spin_lock_irq(&mapping->tree_lock); - __delete_from_page_cache(page); + __delete_from_page_cache(page, NULL); spin_unlock_irq(&mapping->tree_lock); mem_cgroup_uncharge_cache_page(page); @@ -426,7 +490,7 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) new->index = offset; spin_lock_irq(&mapping->tree_lock); - __delete_from_page_cache(old); + __delete_from_page_cache(old, NULL); error = radix_tree_insert(&mapping->page_tree, offset, new); BUG_ON(error); mapping->nrpages++; @@ -446,18 +510,52 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask) } EXPORT_SYMBOL_GPL(replace_page_cache_page); -/** - * add_to_page_cache_locked - add a locked page to the pagecache - * @page: page to add - * @mapping: the page's address_space - * @offset: page index - * @gfp_mask: page allocation mode - * - * This function is used to add a page to the pagecache. It must be locked. - * This function does not add the page to the LRU. The caller must do that. - */ -int add_to_page_cache_locked(struct page *page, struct address_space *mapping, - pgoff_t offset, gfp_t gfp_mask) +static int page_cache_tree_insert(struct address_space *mapping, + struct page *page, void **shadowp) +{ + struct radix_tree_node *node; + void **slot; + int error; + + error = __radix_tree_create(&mapping->page_tree, page->index, + &node, &slot); + if (error) + return error; + if (*slot) { + void *p; + + p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock); + if (!radix_tree_exceptional_entry(p)) + return -EEXIST; + if (shadowp) + *shadowp = p; + mapping->nrshadows--; + if (node) + workingset_node_shadows_dec(node); + } + radix_tree_replace_slot(slot, page); + mapping->nrpages++; + if (node) { + workingset_node_pages_inc(node); + /* + * Don't track node that contains actual pages. + * + * Avoid acquiring the list_lru lock if already + * untracked. The list_empty() test is safe as + * node->private_list is protected by + * mapping->tree_lock. + */ + if (!list_empty(&node->private_list)) + list_lru_del(&workingset_shadow_nodes, + &node->private_list); + } + return 0; +} + +static int __add_to_page_cache_locked(struct page *page, + struct address_space *mapping, + pgoff_t offset, gfp_t gfp_mask, + void **shadowp) { int error; @@ -480,11 +578,10 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, page->index = offset; spin_lock_irq(&mapping->tree_lock); - error = radix_tree_insert(&mapping->page_tree, offset, page); + error = page_cache_tree_insert(mapping, page, shadowp); radix_tree_preload_end(); if (unlikely(error)) goto err_insert; - mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); spin_unlock_irq(&mapping->tree_lock); trace_mm_filemap_add_to_page_cache(page); @@ -497,16 +594,49 @@ err_insert: page_cache_release(page); return error; } + +/** + * add_to_page_cache_locked - add a locked page to the pagecache + * @page: page to add + * @mapping: the page's address_space + * @offset: page index + * @gfp_mask: page allocation mode + * + * This function is used to add a page to the pagecache. It must be locked. + * This function does not add the page to the LRU. The caller must do that. + */ +int add_to_page_cache_locked(struct page *page, struct address_space *mapping, + pgoff_t offset, gfp_t gfp_mask) +{ + return __add_to_page_cache_locked(page, mapping, offset, + gfp_mask, NULL); +} EXPORT_SYMBOL(add_to_page_cache_locked); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) { + void *shadow = NULL; int ret; - ret = add_to_page_cache(page, mapping, offset, gfp_mask); - if (ret == 0) - lru_cache_add_file(page); + __set_page_locked(page); + ret = __add_to_page_cache_locked(page, mapping, offset, + gfp_mask, &shadow); + if (unlikely(ret)) + __clear_page_locked(page); + else { + /* + * The page might have been evicted from cache only + * recently, in which case it should be activated like + * any other repeatedly accessed page. + */ + if (shadow && workingset_refault(shadow)) { + SetPageActive(page); + workingset_activation(page); + } else + ClearPageActive(page); + lru_cache_add(page); + } return ret; } EXPORT_SYMBOL_GPL(add_to_page_cache_lru); @@ -520,10 +650,10 @@ struct page *__page_cache_alloc(gfp_t gfp) if (cpuset_do_page_mem_spread()) { unsigned int cpuset_mems_cookie; do { - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); n = cpuset_mem_spread_node(); page = alloc_pages_exact_node(n, gfp, 0); - } while (!put_mems_allowed(cpuset_mems_cookie) && !page); + } while (!page && read_mems_allowed_retry(cpuset_mems_cookie)); return page; } @@ -686,14 +816,101 @@ int __lock_page_or_retry(struct page *page, struct mm_struct *mm, } /** - * find_get_page - find and get a page reference + * page_cache_next_hole - find the next hole (not-present entry) + * @mapping: mapping + * @index: index + * @max_scan: maximum range to search + * + * Search the set [index, min(index+max_scan-1, MAX_INDEX)] for the + * lowest indexed hole. + * + * Returns: the index of the hole if found, otherwise returns an index + * outside of the set specified (in which case 'return - index >= + * max_scan' will be true). In rare cases of index wrap-around, 0 will + * be returned. + * + * page_cache_next_hole may be called under rcu_read_lock. However, + * like radix_tree_gang_lookup, this will not atomically search a + * snapshot of the tree at a single point in time. For example, if a + * hole is created at index 5, then subsequently a hole is created at + * index 10, page_cache_next_hole covering both indexes may return 10 + * if called under rcu_read_lock. + */ +pgoff_t page_cache_next_hole(struct address_space *mapping, + pgoff_t index, unsigned long max_scan) +{ + unsigned long i; + + for (i = 0; i < max_scan; i++) { + struct page *page; + + page = radix_tree_lookup(&mapping->page_tree, index); + if (!page || radix_tree_exceptional_entry(page)) + break; + index++; + if (index == 0) + break; + } + + return index; +} +EXPORT_SYMBOL(page_cache_next_hole); + +/** + * page_cache_prev_hole - find the prev hole (not-present entry) + * @mapping: mapping + * @index: index + * @max_scan: maximum range to search + * + * Search backwards in the range [max(index-max_scan+1, 0), index] for + * the first hole. + * + * Returns: the index of the hole if found, otherwise returns an index + * outside of the set specified (in which case 'index - return >= + * max_scan' will be true). In rare cases of wrap-around, ULONG_MAX + * will be returned. + * + * page_cache_prev_hole may be called under rcu_read_lock. However, + * like radix_tree_gang_lookup, this will not atomically search a + * snapshot of the tree at a single point in time. For example, if a + * hole is created at index 10, then subsequently a hole is created at + * index 5, page_cache_prev_hole covering both indexes may return 5 if + * called under rcu_read_lock. + */ +pgoff_t page_cache_prev_hole(struct address_space *mapping, + pgoff_t index, unsigned long max_scan) +{ + unsigned long i; + + for (i = 0; i < max_scan; i++) { + struct page *page; + + page = radix_tree_lookup(&mapping->page_tree, index); + if (!page || radix_tree_exceptional_entry(page)) + break; + index--; + if (index == ULONG_MAX) + break; + } + + return index; +} +EXPORT_SYMBOL(page_cache_prev_hole); + +/** + * find_get_entry - find and get a page cache entry * @mapping: the address_space to search - * @offset: the page index + * @offset: the page cache index + * + * Looks up the page cache slot at @mapping & @offset. If there is a + * page cache page, it is returned with an increased refcount. + * + * If the slot holds a shadow entry of a previously evicted page, it + * is returned. * - * Is there a pagecache struct page at the given (mapping, offset) tuple? - * If yes, increment its refcount and return it; if no, return NULL. + * Otherwise, %NULL is returned. */ -struct page *find_get_page(struct address_space *mapping, pgoff_t offset) +struct page *find_get_entry(struct address_space *mapping, pgoff_t offset) { void **pagep; struct page *page; @@ -734,24 +951,50 @@ out: return page; } -EXPORT_SYMBOL(find_get_page); +EXPORT_SYMBOL(find_get_entry); /** - * find_lock_page - locate, pin and lock a pagecache page + * find_get_page - find and get a page reference * @mapping: the address_space to search * @offset: the page index * - * Locates the desired pagecache page, locks it, increments its reference - * count and returns its address. + * Looks up the page cache slot at @mapping & @offset. If there is a + * page cache page, it is returned with an increased refcount. * - * Returns zero if the page was not present. find_lock_page() may sleep. + * Otherwise, %NULL is returned. */ -struct page *find_lock_page(struct address_space *mapping, pgoff_t offset) +struct page *find_get_page(struct address_space *mapping, pgoff_t offset) +{ + struct page *page = find_get_entry(mapping, offset); + + if (radix_tree_exceptional_entry(page)) + page = NULL; + return page; +} +EXPORT_SYMBOL(find_get_page); + +/** + * find_lock_entry - locate, pin and lock a page cache entry + * @mapping: the address_space to search + * @offset: the page cache index + * + * Looks up the page cache slot at @mapping & @offset. If there is a + * page cache page, it is returned locked and with an increased + * refcount. + * + * If the slot holds a shadow entry of a previously evicted page, it + * is returned. + * + * Otherwise, %NULL is returned. + * + * find_lock_entry() may sleep. + */ +struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset) { struct page *page; repeat: - page = find_get_page(mapping, offset); + page = find_get_entry(mapping, offset); if (page && !radix_tree_exception(page)) { lock_page(page); /* Has the page been truncated? */ @@ -764,6 +1007,29 @@ repeat: } return page; } +EXPORT_SYMBOL(find_lock_entry); + +/** + * find_lock_page - locate, pin and lock a pagecache page + * @mapping: the address_space to search + * @offset: the page index + * + * Looks up the page cache slot at @mapping & @offset. If there is a + * page cache page, it is returned locked and with an increased + * refcount. + * + * Otherwise, %NULL is returned. + * + * find_lock_page() may sleep. + */ +struct page *find_lock_page(struct address_space *mapping, pgoff_t offset) +{ + struct page *page = find_lock_entry(mapping, offset); + + if (radix_tree_exceptional_entry(page)) + page = NULL; + return page; +} EXPORT_SYMBOL(find_lock_page); /** @@ -772,16 +1038,18 @@ EXPORT_SYMBOL(find_lock_page); * @index: the page's index into the mapping * @gfp_mask: page allocation mode * - * Locates a page in the pagecache. If the page is not present, a new page - * is allocated using @gfp_mask and is added to the pagecache and to the VM's - * LRU list. The returned page is locked and has its reference count - * incremented. + * Looks up the page cache slot at @mapping & @offset. If there is a + * page cache page, it is returned locked and with an increased + * refcount. * - * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic - * allocation! + * If the page is not present, a new page is allocated using @gfp_mask + * and added to the page cache and the VM's LRU list. The page is + * returned locked and with an increased refcount. * - * find_or_create_page() returns the desired page's address, or zero on - * memory exhaustion. + * On memory exhaustion, %NULL is returned. + * + * find_or_create_page() may sleep, even if @gfp_flags specifies an + * atomic allocation! */ struct page *find_or_create_page(struct address_space *mapping, pgoff_t index, gfp_t gfp_mask) @@ -814,6 +1082,76 @@ repeat: EXPORT_SYMBOL(find_or_create_page); /** + * find_get_entries - gang pagecache lookup + * @mapping: The address_space to search + * @start: The starting page cache index + * @nr_entries: The maximum number of entries + * @entries: Where the resulting entries are placed + * @indices: The cache indices corresponding to the entries in @entries + * + * find_get_entries() will search for and return a group of up to + * @nr_entries entries in the mapping. The entries are placed at + * @entries. find_get_entries() takes a reference against any actual + * pages it returns. + * + * The search returns a group of mapping-contiguous page cache entries + * with ascending indexes. There may be holes in the indices due to + * not-present pages. + * + * Any shadow entries of evicted pages are included in the returned + * array. + * + * find_get_entries() returns the number of pages and shadow entries + * which were found. + */ +unsigned find_get_entries(struct address_space *mapping, + pgoff_t start, unsigned int nr_entries, + struct page **entries, pgoff_t *indices) +{ + void **slot; + unsigned int ret = 0; + struct radix_tree_iter iter; + + if (!nr_entries) + return 0; + + rcu_read_lock(); +restart: + radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) { + struct page *page; +repeat: + page = radix_tree_deref_slot(slot); + if (unlikely(!page)) + continue; + if (radix_tree_exception(page)) { + if (radix_tree_deref_retry(page)) + goto restart; + /* + * Otherwise, we must be storing a swap entry + * here as an exceptional entry: so return it + * without attempting to raise page count. + */ + goto export; + } + if (!page_cache_get_speculative(page)) + goto repeat; + + /* Has the page moved? */ + if (unlikely(page != *slot)) { + page_cache_release(page); + goto repeat; + } +export: + indices[ret] = iter.index; + entries[ret] = page; + if (++ret == nr_entries) + break; + } + rcu_read_unlock(); + return ret; +} + +/** * find_get_pages - gang pagecache lookup * @mapping: The address_space to search * @start: The starting page index @@ -1795,6 +2133,18 @@ int generic_file_readonly_mmap(struct file * file, struct vm_area_struct * vma) EXPORT_SYMBOL(generic_file_mmap); EXPORT_SYMBOL(generic_file_readonly_mmap); +static struct page *wait_on_page_read(struct page *page) +{ + if (!IS_ERR(page)) { + wait_on_page_locked(page); + if (!PageUptodate(page)) { + page_cache_release(page); + page = ERR_PTR(-EIO); + } + } + return page; +} + static struct page *__read_cache_page(struct address_space *mapping, pgoff_t index, int (*filler)(void *, struct page *), @@ -1821,6 +2171,8 @@ repeat: if (err < 0) { page_cache_release(page); page = ERR_PTR(err); + } else { + page = wait_on_page_read(page); } } return page; @@ -1857,6 +2209,10 @@ retry: if (err < 0) { page_cache_release(page); return ERR_PTR(err); + } else { + page = wait_on_page_read(page); + if (IS_ERR(page)) + return page; } out: mark_page_accessed(page); @@ -1864,40 +2220,25 @@ out: } /** - * read_cache_page_async - read into page cache, fill it if needed + * read_cache_page - read into page cache, fill it if needed * @mapping: the page's address_space * @index: the page index * @filler: function to perform the read * @data: first arg to filler(data, page) function, often left as NULL * - * Same as read_cache_page, but don't wait for page to become unlocked - * after submitting it to the filler. - * * Read into the page cache. If a page already exists, and PageUptodate() is - * not set, try to fill the page but don't wait for it to become unlocked. + * not set, try to fill the page and wait for it to become unlocked. * * If the page does not get brought uptodate, return -EIO. */ -struct page *read_cache_page_async(struct address_space *mapping, +struct page *read_cache_page(struct address_space *mapping, pgoff_t index, int (*filler)(void *, struct page *), void *data) { return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping)); } -EXPORT_SYMBOL(read_cache_page_async); - -static struct page *wait_on_page_read(struct page *page) -{ - if (!IS_ERR(page)) { - wait_on_page_locked(page); - if (!PageUptodate(page)) { - page_cache_release(page); - page = ERR_PTR(-EIO); - } - } - return page; -} +EXPORT_SYMBOL(read_cache_page); /** * read_cache_page_gfp - read into page cache, using specified page allocation flags. @@ -1916,31 +2257,10 @@ struct page *read_cache_page_gfp(struct address_space *mapping, { filler_t *filler = (filler_t *)mapping->a_ops->readpage; - return wait_on_page_read(do_read_cache_page(mapping, index, filler, NULL, gfp)); + return do_read_cache_page(mapping, index, filler, NULL, gfp); } EXPORT_SYMBOL(read_cache_page_gfp); -/** - * read_cache_page - read into page cache, fill it if needed - * @mapping: the page's address_space - * @index: the page index - * @filler: function to perform the read - * @data: first arg to filler(data, page) function, often left as NULL - * - * Read into the page cache. If a page already exists, and PageUptodate() is - * not set, try to fill the page then wait for it to become unlocked. - * - * If the page does not get brought uptodate, return -EIO. - */ -struct page *read_cache_page(struct address_space *mapping, - pgoff_t index, - int (*filler)(void *, struct page *), - void *data) -{ - return wait_on_page_read(read_cache_page_async(mapping, index, filler, data)); -} -EXPORT_SYMBOL(read_cache_page); - static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1546655a2d78..6ac89e9f82ef 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -941,81 +941,6 @@ unlock: spin_unlock(ptl); } -static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm, - struct vm_area_struct *vma, unsigned long address, - pmd_t *pmd, pmd_t orig_pmd, unsigned long haddr) -{ - spinlock_t *ptl; - pgtable_t pgtable; - pmd_t _pmd; - struct page *page; - int i, ret = 0; - unsigned long mmun_start; /* For mmu_notifiers */ - unsigned long mmun_end; /* For mmu_notifiers */ - - page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); - if (!page) { - ret |= VM_FAULT_OOM; - goto out; - } - - if (mem_cgroup_newpage_charge(page, mm, GFP_KERNEL)) { - put_page(page); - ret |= VM_FAULT_OOM; - goto out; - } - - clear_user_highpage(page, address); - __SetPageUptodate(page); - - mmun_start = haddr; - mmun_end = haddr + HPAGE_PMD_SIZE; - mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); - - ptl = pmd_lock(mm, pmd); - if (unlikely(!pmd_same(*pmd, orig_pmd))) - goto out_free_page; - - pmdp_clear_flush(vma, haddr, pmd); - /* leave pmd empty until pte is filled */ - - pgtable = pgtable_trans_huge_withdraw(mm, pmd); - pmd_populate(mm, &_pmd, pgtable); - - for (i = 0; i < HPAGE_PMD_NR; i++, haddr += PAGE_SIZE) { - pte_t *pte, entry; - if (haddr == (address & PAGE_MASK)) { - entry = mk_pte(page, vma->vm_page_prot); - entry = maybe_mkwrite(pte_mkdirty(entry), vma); - page_add_new_anon_rmap(page, vma, haddr); - } else { - entry = pfn_pte(my_zero_pfn(haddr), vma->vm_page_prot); - entry = pte_mkspecial(entry); - } - pte = pte_offset_map(&_pmd, haddr); - VM_BUG_ON(!pte_none(*pte)); - set_pte_at(mm, haddr, pte, entry); - pte_unmap(pte); - } - smp_wmb(); /* make pte visible before pmd */ - pmd_populate(mm, pmd, pgtable); - spin_unlock(ptl); - put_huge_zero_page(); - inc_mm_counter(mm, MM_ANONPAGES); - - mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); - - ret |= VM_FAULT_WRITE; -out: - return ret; -out_free_page: - spin_unlock(ptl); - mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); - mem_cgroup_uncharge_page(page); - put_page(page); - goto out; -} - static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, @@ -1161,8 +1086,8 @@ alloc: if (unlikely(!new_page)) { if (!page) { - ret = do_huge_pmd_wp_zero_page_fallback(mm, vma, - address, pmd, orig_pmd, haddr); + split_huge_page_pmd(vma, address, pmd); + ret |= VM_FAULT_FALLBACK; } else { ret = do_huge_pmd_wp_page_fallback(mm, vma, address, pmd, orig_pmd, page, haddr); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index c01cb9fedb18..7c02b9dadfb0 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,6 +22,7 @@ #include <linux/swap.h> #include <linux/swapops.h> #include <linux/page-isolation.h> +#include <linux/jhash.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -53,6 +54,13 @@ static unsigned long __initdata default_hstate_size; */ DEFINE_SPINLOCK(hugetlb_lock); +/* + * Serializes faults on the same logical page. This is used to + * prevent spurious OOMs when the hugepage pool is fully utilized. + */ +static int num_fault_mutexes; +static struct mutex *htlb_fault_mutex_table ____cacheline_aligned_in_smp; + static inline void unlock_or_release_subpool(struct hugepage_subpool *spool) { bool free = (spool->count == 0) && (spool->used_hpages == 0); @@ -135,15 +143,8 @@ static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma) * Region tracking -- allows tracking of reservations and instantiated pages * across the pages in a mapping. * - * The region data structures are protected by a combination of the mmap_sem - * and the hugetlb_instantiation_mutex. To access or modify a region the caller - * must either hold the mmap_sem for write, or the mmap_sem for read and - * the hugetlb_instantiation_mutex: - * - * down_write(&mm->mmap_sem); - * or - * down_read(&mm->mmap_sem); - * mutex_lock(&hugetlb_instantiation_mutex); + * The region data structures are embedded into a resv_map and + * protected by a resv_map's lock */ struct file_region { struct list_head link; @@ -151,10 +152,12 @@ struct file_region { long to; }; -static long region_add(struct list_head *head, long f, long t) +static long region_add(struct resv_map *resv, long f, long t) { + struct list_head *head = &resv->regions; struct file_region *rg, *nrg, *trg; + spin_lock(&resv->lock); /* Locate the region we are either in or before. */ list_for_each_entry(rg, head, link) if (f <= rg->to) @@ -184,14 +187,18 @@ static long region_add(struct list_head *head, long f, long t) } nrg->from = f; nrg->to = t; + spin_unlock(&resv->lock); return 0; } -static long region_chg(struct list_head *head, long f, long t) +static long region_chg(struct resv_map *resv, long f, long t) { - struct file_region *rg, *nrg; + struct list_head *head = &resv->regions; + struct file_region *rg, *nrg = NULL; long chg = 0; +retry: + spin_lock(&resv->lock); /* Locate the region we are before or in. */ list_for_each_entry(rg, head, link) if (f <= rg->to) @@ -201,15 +208,21 @@ static long region_chg(struct list_head *head, long f, long t) * Subtle, allocate a new region at the position but make it zero * size such that we can guarantee to record the reservation. */ if (&rg->link == head || t < rg->from) { - nrg = kmalloc(sizeof(*nrg), GFP_KERNEL); - if (!nrg) - return -ENOMEM; - nrg->from = f; - nrg->to = f; - INIT_LIST_HEAD(&nrg->link); - list_add(&nrg->link, rg->link.prev); + if (!nrg) { + spin_unlock(&resv->lock); + nrg = kmalloc(sizeof(*nrg), GFP_KERNEL); + if (!nrg) + return -ENOMEM; + + nrg->from = f; + nrg->to = f; + INIT_LIST_HEAD(&nrg->link); + goto retry; + } - return t - f; + list_add(&nrg->link, rg->link.prev); + chg = t - f; + goto out_nrg; } /* Round our left edge to the current segment if it encloses us. */ @@ -222,7 +235,7 @@ static long region_chg(struct list_head *head, long f, long t) if (&rg->link == head) break; if (rg->from > t) - return chg; + goto out; /* We overlap with this area, if it extends further than * us then we must extend ourselves. Account for its @@ -233,20 +246,30 @@ static long region_chg(struct list_head *head, long f, long t) } chg -= rg->to - rg->from; } + +out: + spin_unlock(&resv->lock); + /* We already know we raced and no longer need the new region */ + kfree(nrg); + return chg; +out_nrg: + spin_unlock(&resv->lock); return chg; } -static long region_truncate(struct list_head *head, long end) +static long region_truncate(struct resv_map *resv, long end) { + struct list_head *head = &resv->regions; struct file_region *rg, *trg; long chg = 0; + spin_lock(&resv->lock); /* Locate the region we are either in or before. */ list_for_each_entry(rg, head, link) if (end <= rg->to) break; if (&rg->link == head) - return 0; + goto out; /* If we are in the middle of a region then adjust it. */ if (end > rg->from) { @@ -263,14 +286,19 @@ static long region_truncate(struct list_head *head, long end) list_del(&rg->link); kfree(rg); } + +out: + spin_unlock(&resv->lock); return chg; } -static long region_count(struct list_head *head, long f, long t) +static long region_count(struct resv_map *resv, long f, long t) { + struct list_head *head = &resv->regions; struct file_region *rg; long chg = 0; + spin_lock(&resv->lock); /* Locate each segment we overlap with, and count that overlap. */ list_for_each_entry(rg, head, link) { long seg_from; @@ -286,6 +314,7 @@ static long region_count(struct list_head *head, long f, long t) chg += seg_to - seg_from; } + spin_unlock(&resv->lock); return chg; } @@ -376,39 +405,46 @@ static void set_vma_private_data(struct vm_area_struct *vma, vma->vm_private_data = (void *)value; } -struct resv_map { - struct kref refs; - struct list_head regions; -}; - -static struct resv_map *resv_map_alloc(void) +struct resv_map *resv_map_alloc(void) { struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL); if (!resv_map) return NULL; kref_init(&resv_map->refs); + spin_lock_init(&resv_map->lock); INIT_LIST_HEAD(&resv_map->regions); return resv_map; } -static void resv_map_release(struct kref *ref) +void resv_map_release(struct kref *ref) { struct resv_map *resv_map = container_of(ref, struct resv_map, refs); /* Clear out any active regions before we release the map. */ - region_truncate(&resv_map->regions, 0); + region_truncate(resv_map, 0); kfree(resv_map); } +static inline struct resv_map *inode_resv_map(struct inode *inode) +{ + return inode->i_mapping->private_data; +} + static struct resv_map *vma_resv_map(struct vm_area_struct *vma) { VM_BUG_ON(!is_vm_hugetlb_page(vma)); - if (!(vma->vm_flags & VM_MAYSHARE)) + if (vma->vm_flags & VM_MAYSHARE) { + struct address_space *mapping = vma->vm_file->f_mapping; + struct inode *inode = mapping->host; + + return inode_resv_map(inode); + + } else { return (struct resv_map *)(get_vma_private_data(vma) & ~HPAGE_RESV_MASK); - return NULL; + } } static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map) @@ -540,7 +576,7 @@ static struct page *dequeue_huge_page_vma(struct hstate *h, goto err; retry_cpuset: - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); zonelist = huge_zonelist(vma, address, htlb_alloc_mask(h), &mpol, &nodemask); @@ -562,7 +598,7 @@ retry_cpuset: } mpol_cond_put(mpol); - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) + if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; return page; @@ -653,7 +689,8 @@ static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) put_page(page); /* free it into the hugepage allocator */ } -static void prep_compound_gigantic_page(struct page *page, unsigned long order) +static void __init prep_compound_gigantic_page(struct page *page, + unsigned long order) { int i; int nr_pages = 1 << order; @@ -1150,45 +1187,34 @@ static void return_unused_surplus_pages(struct hstate *h, static long vma_needs_reservation(struct hstate *h, struct vm_area_struct *vma, unsigned long addr) { - struct address_space *mapping = vma->vm_file->f_mapping; - struct inode *inode = mapping->host; - - if (vma->vm_flags & VM_MAYSHARE) { - pgoff_t idx = vma_hugecache_offset(h, vma, addr); - return region_chg(&inode->i_mapping->private_list, - idx, idx + 1); + struct resv_map *resv; + pgoff_t idx; + long chg; - } else if (!is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { + resv = vma_resv_map(vma); + if (!resv) return 1; - } else { - long err; - pgoff_t idx = vma_hugecache_offset(h, vma, addr); - struct resv_map *resv = vma_resv_map(vma); + idx = vma_hugecache_offset(h, vma, addr); + chg = region_chg(resv, idx, idx + 1); - err = region_chg(&resv->regions, idx, idx + 1); - if (err < 0) - return err; - return 0; - } + if (vma->vm_flags & VM_MAYSHARE) + return chg; + else + return chg < 0 ? chg : 0; } static void vma_commit_reservation(struct hstate *h, struct vm_area_struct *vma, unsigned long addr) { - struct address_space *mapping = vma->vm_file->f_mapping; - struct inode *inode = mapping->host; - - if (vma->vm_flags & VM_MAYSHARE) { - pgoff_t idx = vma_hugecache_offset(h, vma, addr); - region_add(&inode->i_mapping->private_list, idx, idx + 1); + struct resv_map *resv; + pgoff_t idx; - } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { - pgoff_t idx = vma_hugecache_offset(h, vma, addr); - struct resv_map *resv = vma_resv_map(vma); + resv = vma_resv_map(vma); + if (!resv) + return; - /* Mark this page used in the map. */ - region_add(&resv->regions, idx, idx + 1); - } + idx = vma_hugecache_offset(h, vma, addr); + region_add(resv, idx, idx + 1); } static struct page *alloc_huge_page(struct vm_area_struct *vma, @@ -1294,7 +1320,7 @@ found: return 1; } -static void prep_compound_huge_page(struct page *page, int order) +static void __init prep_compound_huge_page(struct page *page, int order) { if (unlikely(order > (MAX_ORDER - 1))) prep_compound_gigantic_page(page, order); @@ -1944,11 +1970,14 @@ static void __exit hugetlb_exit(void) } kobject_put(hugepages_kobj); + kfree(htlb_fault_mutex_table); } module_exit(hugetlb_exit); static int __init hugetlb_init(void) { + int i; + /* Some platform decide whether they support huge pages at boot * time. On these, such as powerpc, HPAGE_SHIFT is set to 0 when * there is no such support @@ -1973,6 +2002,17 @@ static int __init hugetlb_init(void) hugetlb_register_all_nodes(); hugetlb_cgroup_file_init(); +#ifdef CONFIG_SMP + num_fault_mutexes = roundup_pow_of_two(8 * num_possible_cpus()); +#else + num_fault_mutexes = 1; +#endif + htlb_fault_mutex_table = + kmalloc(sizeof(struct mutex) * num_fault_mutexes, GFP_KERNEL); + BUG_ON(!htlb_fault_mutex_table); + + for (i = 0; i < num_fault_mutexes; i++) + mutex_init(&htlb_fault_mutex_table[i]); return 0; } module_init(hugetlb_init); @@ -2251,41 +2291,30 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma) * after this open call completes. It is therefore safe to take a * new reference here without additional locking. */ - if (resv) + if (resv && is_vma_resv_set(vma, HPAGE_RESV_OWNER)) kref_get(&resv->refs); } -static void resv_map_put(struct vm_area_struct *vma) -{ - struct resv_map *resv = vma_resv_map(vma); - - if (!resv) - return; - kref_put(&resv->refs, resv_map_release); -} - static void hugetlb_vm_op_close(struct vm_area_struct *vma) { struct hstate *h = hstate_vma(vma); struct resv_map *resv = vma_resv_map(vma); struct hugepage_subpool *spool = subpool_vma(vma); - unsigned long reserve; - unsigned long start; - unsigned long end; + unsigned long reserve, start, end; - if (resv) { - start = vma_hugecache_offset(h, vma, vma->vm_start); - end = vma_hugecache_offset(h, vma, vma->vm_end); + if (!resv || !is_vma_resv_set(vma, HPAGE_RESV_OWNER)) + return; - reserve = (end - start) - - region_count(&resv->regions, start, end); + start = vma_hugecache_offset(h, vma, vma->vm_start); + end = vma_hugecache_offset(h, vma, vma->vm_end); - resv_map_put(vma); + reserve = (end - start) - region_count(resv, start, end); - if (reserve) { - hugetlb_acct_memory(h, -reserve); - hugepage_subpool_put_pages(spool, reserve); - } + kref_put(&resv->refs, resv_map_release); + + if (reserve) { + hugetlb_acct_memory(h, -reserve); + hugepage_subpool_put_pages(spool, reserve); } } @@ -2761,15 +2790,14 @@ static bool hugetlbfs_pagecache_present(struct hstate *h, } static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pte_t *ptep, unsigned int flags) + struct address_space *mapping, pgoff_t idx, + unsigned long address, pte_t *ptep, unsigned int flags) { struct hstate *h = hstate_vma(vma); int ret = VM_FAULT_SIGBUS; int anon_rmap = 0; - pgoff_t idx; unsigned long size; struct page *page; - struct address_space *mapping; pte_t new_pte; spinlock_t *ptl; @@ -2784,9 +2812,6 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, return ret; } - mapping = vma->vm_file->f_mapping; - idx = vma_hugecache_offset(h, vma, address); - /* * Use page lock to guard against racing truncation * before we get page_table_lock. @@ -2896,17 +2921,53 @@ backout_unlocked: goto out; } +#ifdef CONFIG_SMP +static u32 fault_mutex_hash(struct hstate *h, struct mm_struct *mm, + struct vm_area_struct *vma, + struct address_space *mapping, + pgoff_t idx, unsigned long address) +{ + unsigned long key[2]; + u32 hash; + + if (vma->vm_flags & VM_SHARED) { + key[0] = (unsigned long) mapping; + key[1] = idx; + } else { + key[0] = (unsigned long) mm; + key[1] = address >> huge_page_shift(h); + } + + hash = jhash2((u32 *)&key, sizeof(key)/sizeof(u32), 0); + + return hash & (num_fault_mutexes - 1); +} +#else +/* + * For uniprocesor systems we always use a single mutex, so just + * return 0 and avoid the hashing overhead. + */ +static u32 fault_mutex_hash(struct hstate *h, struct mm_struct *mm, + struct vm_area_struct *vma, + struct address_space *mapping, + pgoff_t idx, unsigned long address) +{ + return 0; +} +#endif + int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, unsigned int flags) { - pte_t *ptep; - pte_t entry; + pte_t *ptep, entry; spinlock_t *ptl; int ret; + u32 hash; + pgoff_t idx; struct page *page = NULL; struct page *pagecache_page = NULL; - static DEFINE_MUTEX(hugetlb_instantiation_mutex); struct hstate *h = hstate_vma(vma); + struct address_space *mapping; address &= huge_page_mask(h); @@ -2925,15 +2986,20 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (!ptep) return VM_FAULT_OOM; + mapping = vma->vm_file->f_mapping; + idx = vma_hugecache_offset(h, vma, address); + /* * Serialize hugepage allocation and instantiation, so that we don't * get spurious allocation failures if two CPUs race to instantiate * the same page in the page cache. */ - mutex_lock(&hugetlb_instantiation_mutex); + hash = fault_mutex_hash(h, mm, vma, mapping, idx, address); + mutex_lock(&htlb_fault_mutex_table[hash]); + entry = huge_ptep_get(ptep); if (huge_pte_none(entry)) { - ret = hugetlb_no_page(mm, vma, address, ptep, flags); + ret = hugetlb_no_page(mm, vma, mapping, idx, address, ptep, flags); goto out_mutex; } @@ -3002,8 +3068,7 @@ out_ptl: put_page(page); out_mutex: - mutex_unlock(&hugetlb_instantiation_mutex); - + mutex_unlock(&htlb_fault_mutex_table[hash]); return ret; } @@ -3161,6 +3226,7 @@ int hugetlb_reserve_pages(struct inode *inode, long ret, chg; struct hstate *h = hstate_inode(inode); struct hugepage_subpool *spool = subpool_inode(inode); + struct resv_map *resv_map; /* * Only apply hugepage reservation if asked. At fault time, an @@ -3176,10 +3242,13 @@ int hugetlb_reserve_pages(struct inode *inode, * to reserve the full area even if read-only as mprotect() may be * called to make the mapping read-write. Assume !vma is a shm mapping */ - if (!vma || vma->vm_flags & VM_MAYSHARE) - chg = region_chg(&inode->i_mapping->private_list, from, to); - else { - struct resv_map *resv_map = resv_map_alloc(); + if (!vma || vma->vm_flags & VM_MAYSHARE) { + resv_map = inode_resv_map(inode); + + chg = region_chg(resv_map, from, to); + + } else { + resv_map = resv_map_alloc(); if (!resv_map) return -ENOMEM; @@ -3222,20 +3291,23 @@ int hugetlb_reserve_pages(struct inode *inode, * else has to be done for private mappings here */ if (!vma || vma->vm_flags & VM_MAYSHARE) - region_add(&inode->i_mapping->private_list, from, to); + region_add(resv_map, from, to); return 0; out_err: - if (vma) - resv_map_put(vma); + if (vma && is_vma_resv_set(vma, HPAGE_RESV_OWNER)) + kref_put(&resv_map->refs, resv_map_release); return ret; } void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed) { struct hstate *h = hstate_inode(inode); - long chg = region_truncate(&inode->i_mapping->private_list, offset); + struct resv_map *resv_map = inode_resv_map(inode); + long chg = 0; struct hugepage_subpool *spool = subpool_inode(inode); + if (resv_map) + chg = region_truncate(resv_map, offset); spin_lock(&inode->i_lock); inode->i_blocks -= (blocks_per_huge_page(h) * freed); spin_unlock(&inode->i_lock); diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 31f01c5011e5..91d67eaee050 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -192,15 +192,15 @@ static struct kmem_cache *object_cache; static struct kmem_cache *scan_area_cache; /* set if tracing memory operations is enabled */ -static atomic_t kmemleak_enabled = ATOMIC_INIT(0); +static int kmemleak_enabled; /* set in the late_initcall if there were no errors */ -static atomic_t kmemleak_initialized = ATOMIC_INIT(0); +static int kmemleak_initialized; /* enables or disables early logging of the memory operations */ -static atomic_t kmemleak_early_log = ATOMIC_INIT(1); +static int kmemleak_early_log = 1; /* set if a kmemleak warning was issued */ -static atomic_t kmemleak_warning = ATOMIC_INIT(0); +static int kmemleak_warning; /* set if a fatal kmemleak error has occurred */ -static atomic_t kmemleak_error = ATOMIC_INIT(0); +static int kmemleak_error; /* minimum and maximum address that may be valid pointers */ static unsigned long min_addr = ULONG_MAX; @@ -218,7 +218,8 @@ static int kmemleak_stack_scan = 1; static DEFINE_MUTEX(scan_mutex); /* setting kmemleak=on, will set this var, skipping the disable */ static int kmemleak_skip_disable; - +/* If there are leaks that can be reported */ +static bool kmemleak_found_leaks; /* * Early object allocation/freeing logging. Kmemleak is initialized after the @@ -267,7 +268,7 @@ static void kmemleak_disable(void); #define kmemleak_warn(x...) do { \ pr_warning(x); \ dump_stack(); \ - atomic_set(&kmemleak_warning, 1); \ + kmemleak_warning = 1; \ } while (0) /* @@ -805,7 +806,7 @@ static void __init log_early(int op_type, const void *ptr, size_t size, unsigned long flags; struct early_log *log; - if (atomic_read(&kmemleak_error)) { + if (kmemleak_error) { /* kmemleak stopped recording, just count the requests */ crt_early_log++; return; @@ -840,7 +841,7 @@ static void early_alloc(struct early_log *log) unsigned long flags; int i; - if (!atomic_read(&kmemleak_enabled) || !log->ptr || IS_ERR(log->ptr)) + if (!kmemleak_enabled || !log->ptr || IS_ERR(log->ptr)) return; /* @@ -893,9 +894,9 @@ void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count, { pr_debug("%s(0x%p, %zu, %d)\n", __func__, ptr, size, min_count); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) create_object((unsigned long)ptr, size, min_count, gfp); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_ALLOC, ptr, size, min_count); } EXPORT_SYMBOL_GPL(kmemleak_alloc); @@ -919,11 +920,11 @@ void __ref kmemleak_alloc_percpu(const void __percpu *ptr, size_t size) * Percpu allocations are only scanned and not reported as leaks * (min_count is set to 0). */ - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) for_each_possible_cpu(cpu) create_object((unsigned long)per_cpu_ptr(ptr, cpu), size, 0, GFP_KERNEL); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_ALLOC_PERCPU, ptr, size, 0); } EXPORT_SYMBOL_GPL(kmemleak_alloc_percpu); @@ -939,9 +940,9 @@ void __ref kmemleak_free(const void *ptr) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) delete_object_full((unsigned long)ptr); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_FREE, ptr, 0, 0); } EXPORT_SYMBOL_GPL(kmemleak_free); @@ -959,9 +960,9 @@ void __ref kmemleak_free_part(const void *ptr, size_t size) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) delete_object_part((unsigned long)ptr, size); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_FREE_PART, ptr, size, 0); } EXPORT_SYMBOL_GPL(kmemleak_free_part); @@ -979,11 +980,11 @@ void __ref kmemleak_free_percpu(const void __percpu *ptr) pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) for_each_possible_cpu(cpu) delete_object_full((unsigned long)per_cpu_ptr(ptr, cpu)); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_FREE_PERCPU, ptr, 0, 0); } EXPORT_SYMBOL_GPL(kmemleak_free_percpu); @@ -999,9 +1000,9 @@ void __ref kmemleak_not_leak(const void *ptr) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) make_gray_object((unsigned long)ptr); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0); } EXPORT_SYMBOL(kmemleak_not_leak); @@ -1019,9 +1020,9 @@ void __ref kmemleak_ignore(const void *ptr) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) make_black_object((unsigned long)ptr); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_IGNORE, ptr, 0, 0); } EXPORT_SYMBOL(kmemleak_ignore); @@ -1041,9 +1042,9 @@ void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && size && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && size && !IS_ERR(ptr)) add_scan_area((unsigned long)ptr, size, gfp); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0); } EXPORT_SYMBOL(kmemleak_scan_area); @@ -1061,9 +1062,9 @@ void __ref kmemleak_no_scan(const void *ptr) { pr_debug("%s(0x%p)\n", __func__, ptr); - if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr)) + if (kmemleak_enabled && ptr && !IS_ERR(ptr)) object_no_scan((unsigned long)ptr); - else if (atomic_read(&kmemleak_early_log)) + else if (kmemleak_early_log) log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0); } EXPORT_SYMBOL(kmemleak_no_scan); @@ -1088,7 +1089,7 @@ static bool update_checksum(struct kmemleak_object *object) */ static int scan_should_stop(void) { - if (!atomic_read(&kmemleak_enabled)) + if (!kmemleak_enabled) return 1; /* @@ -1382,9 +1383,12 @@ static void kmemleak_scan(void) } rcu_read_unlock(); - if (new_leaks) + if (new_leaks) { + kmemleak_found_leaks = true; + pr_info("%d new suspected memory leaks (see " "/sys/kernel/debug/kmemleak)\n", new_leaks); + } } @@ -1545,11 +1549,6 @@ static int kmemleak_open(struct inode *inode, struct file *file) return seq_open(file, &kmemleak_seq_ops); } -static int kmemleak_release(struct inode *inode, struct file *file) -{ - return seq_release(inode, file); -} - static int dump_str_object_info(const char *str) { unsigned long flags; @@ -1592,8 +1591,12 @@ static void kmemleak_clear(void) spin_unlock_irqrestore(&object->lock, flags); } rcu_read_unlock(); + + kmemleak_found_leaks = false; } +static void __kmemleak_do_cleanup(void); + /* * File write operation to configure kmemleak at run-time. The following * commands can be written to the /sys/kernel/debug/kmemleak file: @@ -1606,7 +1609,8 @@ static void kmemleak_clear(void) * disable it) * scan - trigger a memory scan * clear - mark all current reported unreferenced kmemleak objects as - * grey to ignore printing them + * grey to ignore printing them, or free all kmemleak objects + * if kmemleak has been disabled. * dump=... - dump information about the object found at the given address */ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, @@ -1616,9 +1620,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, int buf_size; int ret; - if (!atomic_read(&kmemleak_enabled)) - return -EBUSY; - buf_size = min(size, (sizeof(buf) - 1)); if (strncpy_from_user(buf, user_buf, buf_size) < 0) return -EFAULT; @@ -1628,6 +1629,19 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, if (ret < 0) return ret; + if (strncmp(buf, "clear", 5) == 0) { + if (kmemleak_enabled) + kmemleak_clear(); + else + __kmemleak_do_cleanup(); + goto out; + } + + if (!kmemleak_enabled) { + ret = -EBUSY; + goto out; + } + if (strncmp(buf, "off", 3) == 0) kmemleak_disable(); else if (strncmp(buf, "stack=on", 8) == 0) @@ -1651,8 +1665,6 @@ static ssize_t kmemleak_write(struct file *file, const char __user *user_buf, } } else if (strncmp(buf, "scan", 4) == 0) kmemleak_scan(); - else if (strncmp(buf, "clear", 5) == 0) - kmemleak_clear(); else if (strncmp(buf, "dump=", 5) == 0) ret = dump_str_object_info(buf + 5); else @@ -1674,9 +1686,19 @@ static const struct file_operations kmemleak_fops = { .read = seq_read, .write = kmemleak_write, .llseek = seq_lseek, - .release = kmemleak_release, + .release = seq_release, }; +static void __kmemleak_do_cleanup(void) +{ + struct kmemleak_object *object; + + rcu_read_lock(); + list_for_each_entry_rcu(object, &object_list, object_list) + delete_object_full(object->pointer); + rcu_read_unlock(); +} + /* * Stop the memory scanning thread and free the kmemleak internal objects if * no previous scan thread (otherwise, kmemleak may still have some useful @@ -1684,18 +1706,14 @@ static const struct file_operations kmemleak_fops = { */ static void kmemleak_do_cleanup(struct work_struct *work) { - struct kmemleak_object *object; - bool cleanup = scan_thread == NULL; - mutex_lock(&scan_mutex); stop_scan_thread(); - if (cleanup) { - rcu_read_lock(); - list_for_each_entry_rcu(object, &object_list, object_list) - delete_object_full(object->pointer); - rcu_read_unlock(); - } + if (!kmemleak_found_leaks) + __kmemleak_do_cleanup(); + else + pr_info("Kmemleak disabled without freeing internal data. " + "Reclaim the memory with \"echo clear > /sys/kernel/debug/kmemleak\"\n"); mutex_unlock(&scan_mutex); } @@ -1708,14 +1726,14 @@ static DECLARE_WORK(cleanup_work, kmemleak_do_cleanup); static void kmemleak_disable(void) { /* atomically check whether it was already invoked */ - if (atomic_cmpxchg(&kmemleak_error, 0, 1)) + if (cmpxchg(&kmemleak_error, 0, 1)) return; /* stop any memory operation tracing */ - atomic_set(&kmemleak_enabled, 0); + kmemleak_enabled = 0; /* check whether it is too early for a kernel thread */ - if (atomic_read(&kmemleak_initialized)) + if (kmemleak_initialized) schedule_work(&cleanup_work); pr_info("Kernel memory leak detector disabled\n"); @@ -1757,9 +1775,10 @@ void __init kmemleak_init(void) int i; unsigned long flags; + kmemleak_early_log = 0; + #ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF if (!kmemleak_skip_disable) { - atomic_set(&kmemleak_early_log, 0); kmemleak_disable(); return; } @@ -1777,12 +1796,11 @@ void __init kmemleak_init(void) /* the kernel is still in UP mode, so disabling the IRQs is enough */ local_irq_save(flags); - atomic_set(&kmemleak_early_log, 0); - if (atomic_read(&kmemleak_error)) { + if (kmemleak_error) { local_irq_restore(flags); return; } else - atomic_set(&kmemleak_enabled, 1); + kmemleak_enabled = 1; local_irq_restore(flags); /* @@ -1826,9 +1844,9 @@ void __init kmemleak_init(void) log->op_type); } - if (atomic_read(&kmemleak_warning)) { + if (kmemleak_warning) { print_log_trace(log); - atomic_set(&kmemleak_warning, 0); + kmemleak_warning = 0; } } } @@ -1840,9 +1858,9 @@ static int __init kmemleak_late_init(void) { struct dentry *dentry; - atomic_set(&kmemleak_initialized, 1); + kmemleak_initialized = 1; - if (atomic_read(&kmemleak_error)) { + if (kmemleak_error) { /* * Some error occurred and kmemleak was disabled. There is a * small chance that kmemleak_disable() was called immediately diff --git a/mm/list_lru.c b/mm/list_lru.c index 72f9decb0104..f1a0db194173 100644 --- a/mm/list_lru.c +++ b/mm/list_lru.c @@ -87,11 +87,20 @@ restart: ret = isolate(item, &nlru->lock, cb_arg); switch (ret) { + case LRU_REMOVED_RETRY: + assert_spin_locked(&nlru->lock); case LRU_REMOVED: if (--nlru->nr_items == 0) node_clear(nid, lru->active_nodes); WARN_ON_ONCE(nlru->nr_items < 0); isolated++; + /* + * If the lru lock has been dropped, our list + * traversal is now invalid and so we have to + * restart from scratch. + */ + if (ret == LRU_REMOVED_RETRY) + goto restart; break; case LRU_ROTATE: list_move_tail(item, &nlru->list); @@ -103,6 +112,7 @@ restart: * The lru lock has been dropped, our list traversal is * now invalid and so we have to restart from scratch. */ + assert_spin_locked(&nlru->lock); goto restart; default: BUG(); @@ -114,7 +124,7 @@ restart: } EXPORT_SYMBOL_GPL(list_lru_walk_node); -int list_lru_init(struct list_lru *lru) +int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key) { int i; size_t size = sizeof(*lru->node) * nr_node_ids; @@ -126,12 +136,14 @@ int list_lru_init(struct list_lru *lru) nodes_clear(lru->active_nodes); for (i = 0; i < nr_node_ids; i++) { spin_lock_init(&lru->node[i].lock); + if (key) + lockdep_set_class(&lru->node[i].lock, key); INIT_LIST_HEAD(&lru->node[i].list); lru->node[i].nr_items = 0; } return 0; } -EXPORT_SYMBOL_GPL(list_lru_init); +EXPORT_SYMBOL_GPL(list_lru_init_key); void list_lru_destroy(struct list_lru *lru) { diff --git a/mm/memory.c b/mm/memory.c index 22dfa617bddb..90cea22001ef 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2587,6 +2587,38 @@ static inline void cow_user_page(struct page *dst, struct page *src, unsigned lo } /* + * Notify the address space that the page is about to become writable so that + * it can prohibit this or wait for the page to get into an appropriate state. + * + * We do this without the lock held, so that it can sleep if it needs to. + */ +static int do_page_mkwrite(struct vm_area_struct *vma, struct page *page, + unsigned long address) +{ + struct vm_fault vmf; + int ret; + + vmf.virtual_address = (void __user *)(address & PAGE_MASK); + vmf.pgoff = page->index; + vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; + vmf.page = page; + + ret = vma->vm_ops->page_mkwrite(vma, &vmf); + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) + return ret; + if (unlikely(!(ret & VM_FAULT_LOCKED))) { + lock_page(page); + if (!page->mapping) { + unlock_page(page); + return 0; /* retry */ + } + ret |= VM_FAULT_LOCKED; + } else + VM_BUG_ON_PAGE(!PageLocked(page), page); + return ret; +} + +/* * This routine handles present pages, when users try to write * to a shared page. It is done by copying the page to a new address * and decrementing the shared-page counter for the old page. @@ -2668,42 +2700,15 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, * get_user_pages(.write=1, .force=1). */ if (vma->vm_ops && vma->vm_ops->page_mkwrite) { - struct vm_fault vmf; int tmp; - - vmf.virtual_address = (void __user *)(address & - PAGE_MASK); - vmf.pgoff = old_page->index; - vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; - vmf.page = old_page; - - /* - * Notify the address space that the page is about to - * become writable so that it can prohibit this or wait - * for the page to get into an appropriate state. - * - * We do this without the lock held, so that it can - * sleep if it needs to. - */ page_cache_get(old_page); pte_unmap_unlock(page_table, ptl); - - tmp = vma->vm_ops->page_mkwrite(vma, &vmf); - if (unlikely(tmp & - (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { - ret = tmp; - goto unwritable_page; + tmp = do_page_mkwrite(vma, old_page, address); + if (unlikely(!tmp || (tmp & + (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) { + page_cache_release(old_page); + return tmp; } - if (unlikely(!(tmp & VM_FAULT_LOCKED))) { - lock_page(old_page); - if (!old_page->mapping) { - ret = 0; /* retry the fault */ - unlock_page(old_page); - goto unwritable_page; - } - } else - VM_BUG_ON_PAGE(!PageLocked(old_page), old_page); - /* * Since we dropped the lock we need to revalidate * the PTE as someone else may have changed it. If @@ -2748,7 +2753,7 @@ reuse: * bit after it clear all dirty ptes, but before a racing * do_wp_page installs a dirty pte. * - * __do_fault is protected similarly. + * do_shared_fault is protected similarly. */ if (!page_mkwrite) { wait_on_page_locked(dirty_page); @@ -2892,10 +2897,6 @@ oom: if (old_page) page_cache_release(old_page); return VM_FAULT_OOM; - -unwritable_page: - page_cache_release(old_page); - return ret; } static void unmap_mapping_range_vma(struct vm_area_struct *vma, @@ -3286,53 +3287,11 @@ oom: return VM_FAULT_OOM; } -/* - * __do_fault() tries to create a new page mapping. It aggressively - * tries to share with existing pages, but makes a separate copy if - * the FAULT_FLAG_WRITE is set in the flags parameter in order to avoid - * the next page fault. - * - * As this is called only for pages that do not currently exist, we - * do not need to flush old virtual caches or the TLB. - * - * We enter with non-exclusive mmap_sem (to exclude vma changes, - * but allow concurrent faults), and pte neither mapped nor locked. - * We return with mmap_sem still held, but pte unmapped and unlocked. - */ -static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pmd_t *pmd, - pgoff_t pgoff, unsigned int flags, pte_t orig_pte) +static int __do_fault(struct vm_area_struct *vma, unsigned long address, + pgoff_t pgoff, unsigned int flags, struct page **page) { - pte_t *page_table; - spinlock_t *ptl; - struct page *page; - struct page *cow_page; - pte_t entry; - int anon = 0; - struct page *dirty_page = NULL; struct vm_fault vmf; int ret; - int page_mkwrite = 0; - - /* - * If we do COW later, allocate page befor taking lock_page() - * on the file cache page. This will reduce lock holding time. - */ - if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) { - - if (unlikely(anon_vma_prepare(vma))) - return VM_FAULT_OOM; - - cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); - if (!cow_page) - return VM_FAULT_OOM; - - if (mem_cgroup_newpage_charge(cow_page, mm, GFP_KERNEL)) { - page_cache_release(cow_page); - return VM_FAULT_OOM; - } - } else - cow_page = NULL; vmf.virtual_address = (void __user *)(address & PAGE_MASK); vmf.pgoff = pgoff; @@ -3340,151 +3299,176 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, vmf.page = NULL; ret = vma->vm_ops->fault(vma, &vmf); - if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | - VM_FAULT_RETRY))) - goto uncharge_out; + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) + return ret; if (unlikely(PageHWPoison(vmf.page))) { if (ret & VM_FAULT_LOCKED) unlock_page(vmf.page); - ret = VM_FAULT_HWPOISON; page_cache_release(vmf.page); - goto uncharge_out; + return VM_FAULT_HWPOISON; } - /* - * For consistency in subsequent calls, make the faulted page always - * locked. - */ if (unlikely(!(ret & VM_FAULT_LOCKED))) lock_page(vmf.page); else VM_BUG_ON_PAGE(!PageLocked(vmf.page), vmf.page); - /* - * Should we do an early C-O-W break? - */ - page = vmf.page; - if (flags & FAULT_FLAG_WRITE) { - if (!(vma->vm_flags & VM_SHARED)) { - page = cow_page; - anon = 1; - copy_user_highpage(page, vmf.page, address, vma); - __SetPageUptodate(page); - } else { - /* - * If the page will be shareable, see if the backing - * address space wants to know that the page is about - * to become writable - */ - if (vma->vm_ops->page_mkwrite) { - int tmp; - - unlock_page(page); - vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; - tmp = vma->vm_ops->page_mkwrite(vma, &vmf); - if (unlikely(tmp & - (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { - ret = tmp; - goto unwritable_page; - } - if (unlikely(!(tmp & VM_FAULT_LOCKED))) { - lock_page(page); - if (!page->mapping) { - ret = 0; /* retry the fault */ - unlock_page(page); - goto unwritable_page; - } - } else - VM_BUG_ON_PAGE(!PageLocked(page), page); - page_mkwrite = 1; - } - } + *page = vmf.page; + return ret; +} +static void do_set_pte(struct vm_area_struct *vma, unsigned long address, + struct page *page, pte_t *pte, bool write, bool anon) +{ + pte_t entry; + + flush_icache_page(vma, page); + entry = mk_pte(page, vma->vm_page_prot); + if (write) + entry = maybe_mkwrite(pte_mkdirty(entry), vma); + else if (pte_file(*pte) && pte_file_soft_dirty(*pte)) + pte_mksoft_dirty(entry); + if (anon) { + inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES); + page_add_new_anon_rmap(page, vma, address); + } else { + inc_mm_counter_fast(vma->vm_mm, MM_FILEPAGES); + page_add_file_rmap(page); } + set_pte_at(vma->vm_mm, address, pte, entry); - page_table = pte_offset_map_lock(mm, pmd, address, &ptl); + /* no need to invalidate: a not-present page won't be cached */ + update_mmu_cache(vma, address, pte); +} - /* - * This silly early PAGE_DIRTY setting removes a race - * due to the bad i386 page protection. But it's valid - * for other architectures too. - * - * Note that if FAULT_FLAG_WRITE is set, we either now have - * an exclusive copy of the page, or this is a shared mapping, - * so we can make it writable and dirty to avoid having to - * handle that later. - */ - /* Only go through if we didn't race with anybody else... */ - if (likely(pte_same(*page_table, orig_pte))) { - flush_icache_page(vma, page); - entry = mk_pte(page, vma->vm_page_prot); - if (flags & FAULT_FLAG_WRITE) - entry = maybe_mkwrite(pte_mkdirty(entry), vma); - else if (pte_file(orig_pte) && pte_file_soft_dirty(orig_pte)) - pte_mksoft_dirty(entry); - if (anon) { - inc_mm_counter_fast(mm, MM_ANONPAGES); - page_add_new_anon_rmap(page, vma, address); - } else { - inc_mm_counter_fast(mm, MM_FILEPAGES); - page_add_file_rmap(page); - if (flags & FAULT_FLAG_WRITE) { - dirty_page = page; - get_page(dirty_page); - } - } - set_pte_at(mm, address, page_table, entry); +static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long address, pmd_t *pmd, + pgoff_t pgoff, unsigned int flags, pte_t orig_pte) +{ + struct page *fault_page; + spinlock_t *ptl; + pte_t *pte; + int ret; - /* no need to invalidate: a not-present page won't be cached */ - update_mmu_cache(vma, address, page_table); - } else { - if (cow_page) - mem_cgroup_uncharge_page(cow_page); - if (anon) - page_cache_release(page); - else - anon = 1; /* no anon but release faulted_page */ + ret = __do_fault(vma, address, pgoff, flags, &fault_page); + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) + return ret; + + pte = pte_offset_map_lock(mm, pmd, address, &ptl); + if (unlikely(!pte_same(*pte, orig_pte))) { + pte_unmap_unlock(pte, ptl); + unlock_page(fault_page); + page_cache_release(fault_page); + return ret; } + do_set_pte(vma, address, fault_page, pte, false, false); + pte_unmap_unlock(pte, ptl); + unlock_page(fault_page); + return ret; +} - pte_unmap_unlock(page_table, ptl); +static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long address, pmd_t *pmd, + pgoff_t pgoff, unsigned int flags, pte_t orig_pte) +{ + struct page *fault_page, *new_page; + spinlock_t *ptl; + pte_t *pte; + int ret; - if (dirty_page) { - struct address_space *mapping = page->mapping; - int dirtied = 0; + if (unlikely(anon_vma_prepare(vma))) + return VM_FAULT_OOM; - if (set_page_dirty(dirty_page)) - dirtied = 1; - unlock_page(dirty_page); - put_page(dirty_page); - if ((dirtied || page_mkwrite) && mapping) { - /* - * Some device drivers do not set page.mapping but still - * dirty their pages - */ - balance_dirty_pages_ratelimited(mapping); - } + new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); + if (!new_page) + return VM_FAULT_OOM; - /* file_update_time outside page_lock */ - if (vma->vm_file && !page_mkwrite) - file_update_time(vma->vm_file); - } else { - unlock_page(vmf.page); - if (anon) - page_cache_release(vmf.page); + if (mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL)) { + page_cache_release(new_page); + return VM_FAULT_OOM; } - return ret; + ret = __do_fault(vma, address, pgoff, flags, &fault_page); + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) + goto uncharge_out; -unwritable_page: - page_cache_release(page); + copy_user_highpage(new_page, fault_page, address, vma); + __SetPageUptodate(new_page); + + pte = pte_offset_map_lock(mm, pmd, address, &ptl); + if (unlikely(!pte_same(*pte, orig_pte))) { + pte_unmap_unlock(pte, ptl); + unlock_page(fault_page); + page_cache_release(fault_page); + goto uncharge_out; + } + do_set_pte(vma, address, new_page, pte, true, true); + pte_unmap_unlock(pte, ptl); + unlock_page(fault_page); + page_cache_release(fault_page); return ret; uncharge_out: - /* fs's fault handler get error */ - if (cow_page) { - mem_cgroup_uncharge_page(cow_page); - page_cache_release(cow_page); + mem_cgroup_uncharge_page(new_page); + page_cache_release(new_page); + return ret; +} + +static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long address, pmd_t *pmd, + pgoff_t pgoff, unsigned int flags, pte_t orig_pte) +{ + struct page *fault_page; + struct address_space *mapping; + spinlock_t *ptl; + pte_t *pte; + int dirtied = 0; + int ret, tmp; + + ret = __do_fault(vma, address, pgoff, flags, &fault_page); + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) + return ret; + + /* + * Check if the backing address space wants to know that the page is + * about to become writable + */ + if (vma->vm_ops->page_mkwrite) { + unlock_page(fault_page); + tmp = do_page_mkwrite(vma, fault_page, address); + if (unlikely(!tmp || + (tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) { + page_cache_release(fault_page); + return tmp; + } } + + pte = pte_offset_map_lock(mm, pmd, address, &ptl); + if (unlikely(!pte_same(*pte, orig_pte))) { + pte_unmap_unlock(pte, ptl); + unlock_page(fault_page); + page_cache_release(fault_page); + return ret; + } + do_set_pte(vma, address, fault_page, pte, true, false); + pte_unmap_unlock(pte, ptl); + + if (set_page_dirty(fault_page)) + dirtied = 1; + mapping = fault_page->mapping; + unlock_page(fault_page); + if ((dirtied || vma->vm_ops->page_mkwrite) && mapping) { + /* + * Some device drivers do not set page.mapping but still + * dirty their pages + */ + balance_dirty_pages_ratelimited(mapping); + } + + /* file_update_time outside page_lock */ + if (vma->vm_file && !vma->vm_ops->page_mkwrite) + file_update_time(vma->vm_file); + return ret; } @@ -3496,7 +3480,13 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; pte_unmap(page_table); - return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); + if (!(flags & FAULT_FLAG_WRITE)) + return do_read_fault(mm, vma, address, pmd, pgoff, flags, + orig_pte); + if (!(vma->vm_flags & VM_SHARED)) + return do_cow_fault(mm, vma, address, pmd, pgoff, flags, + orig_pte); + return do_shared_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); } /* @@ -3528,10 +3518,16 @@ static int do_nonlinear_fault(struct mm_struct *mm, struct vm_area_struct *vma, } pgoff = pte_to_pgoff(orig_pte); - return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); + if (!(flags & FAULT_FLAG_WRITE)) + return do_read_fault(mm, vma, address, pmd, pgoff, flags, + orig_pte); + if (!(vma->vm_flags & VM_SHARED)) + return do_cow_fault(mm, vma, address, pmd, pgoff, flags, + orig_pte); + return do_shared_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); } -int numa_migrate_prep(struct page *page, struct vm_area_struct *vma, +static int numa_migrate_prep(struct page *page, struct vm_area_struct *vma, unsigned long addr, int page_nid, int *flags) { @@ -3546,7 +3542,7 @@ int numa_migrate_prep(struct page *page, struct vm_area_struct *vma, return mpol_misplaced(page, vma, addr); } -int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, +static int do_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, pte_t pte, pte_t *ptep, pmd_t *pmd) { struct page *page = NULL; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 4755c8576942..e3ab02822799 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1899,7 +1899,7 @@ int node_random(const nodemask_t *maskp) * If the effective policy is 'BIND, returns a pointer to the mempolicy's * @nodemask for filtering the zonelist. * - * Must be protected by get_mems_allowed() + * Must be protected by read_mems_allowed_begin() */ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr, gfp_t gfp_flags, struct mempolicy **mpol, @@ -2063,7 +2063,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma, retry_cpuset: pol = get_vma_policy(current, vma, addr); - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); if (unlikely(pol->mode == MPOL_INTERLEAVE)) { unsigned nid; @@ -2071,7 +2071,7 @@ retry_cpuset: nid = interleave_nid(pol, vma, addr, PAGE_SHIFT + order); mpol_cond_put(pol); page = alloc_page_interleave(gfp, order, nid); - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) + if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; return page; @@ -2081,7 +2081,7 @@ retry_cpuset: policy_nodemask(gfp, pol)); if (unlikely(mpol_needs_cond_ref(pol))) __mpol_put(pol); - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) + if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; return page; } @@ -2115,7 +2115,7 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order) pol = &default_policy; retry_cpuset: - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); /* * No reference counting needed for current->mempolicy @@ -2128,7 +2128,7 @@ retry_cpuset: policy_zonelist(gfp, pol, numa_node_id()), policy_nodemask(gfp, pol)); - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) + if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; return page; diff --git a/mm/mincore.c b/mm/mincore.c index 101623378fbf..725c80961048 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -70,13 +70,21 @@ static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff) * any other file mapping (ie. marked !present and faulted in with * tmpfs's .fault). So swapped out tmpfs mappings are tested here. */ - page = find_get_page(mapping, pgoff); #ifdef CONFIG_SWAP - /* shmem/tmpfs may return swap: account for swapcache page too. */ - if (radix_tree_exceptional_entry(page)) { - swp_entry_t swap = radix_to_swp_entry(page); - page = find_get_page(swap_address_space(swap), swap.val); - } + if (shmem_mapping(mapping)) { + page = find_get_entry(mapping, pgoff); + /* + * shmem/tmpfs may return swap: account for swapcache + * page too. + */ + if (radix_tree_exceptional_entry(page)) { + swp_entry_t swp = radix_to_swp_entry(page); + page = find_get_page(swap_address_space(swp), swp.val); + } + } else + page = find_get_page(mapping, pgoff); +#else + page = find_get_page(mapping, pgoff); #endif if (page) { present = PageUptodate(page); diff --git a/mm/mmap.c b/mm/mmap.c index 81ba54ff96c7..ac1d6671b1ed 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -405,7 +405,7 @@ static void validate_mm_rb(struct rb_root *root, struct vm_area_struct *ignore) } } -void validate_mm(struct mm_struct *mm) +static void validate_mm(struct mm_struct *mm) { int bug = 0; int i = 0; diff --git a/mm/nobootmem.c b/mm/nobootmem.c index f73f2987a852..04a9d94333a5 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -334,7 +334,7 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, return ___alloc_bootmem_node_nopanic(pgdat, size, align, goal, 0); } -void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, +static void * __init ___alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3bac76ae4b30..979378deccbf 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2739,7 +2739,7 @@ __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, return NULL; retry_cpuset: - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); /* The preferred zone is used for statistics later */ first_zones_zonelist(zonelist, high_zoneidx, @@ -2777,7 +2777,7 @@ out: * the mask is being updated. If a page allocation is about to fail, * check if the cpuset changed during allocation and if so, retry. */ - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) + if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; memcg_kmem_commit_charge(page, memcg, order); @@ -3045,9 +3045,9 @@ bool skip_free_areas_node(unsigned int flags, int nid) goto out; do { - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); ret = !node_isset(nid, cpuset_current_mems_allowed); - } while (!put_mems_allowed(cpuset_mems_cookie)); + } while (read_mems_allowed_retry(cpuset_mems_cookie)); out: return ret; } diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index cfd162882c00..3708264d2833 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -175,7 +175,7 @@ static void free_page_cgroup(void *addr) } } -void __free_page_cgroup(unsigned long pfn) +static void __free_page_cgroup(unsigned long pfn) { struct mem_section *ms; struct page_cgroup *base; @@ -188,9 +188,9 @@ void __free_page_cgroup(unsigned long pfn) ms->page_cgroup = NULL; } -int __meminit online_page_cgroup(unsigned long start_pfn, - unsigned long nr_pages, - int nid) +static int __meminit online_page_cgroup(unsigned long start_pfn, + unsigned long nr_pages, + int nid) { unsigned long start, end, pfn; int fail = 0; @@ -223,8 +223,8 @@ int __meminit online_page_cgroup(unsigned long start_pfn, return -ENOMEM; } -int __meminit offline_page_cgroup(unsigned long start_pfn, - unsigned long nr_pages, int nid) +static int __meminit offline_page_cgroup(unsigned long start_pfn, + unsigned long nr_pages, int nid) { unsigned long start, end, pfn; diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c index 3c5cf68566ec..cb79065c19e5 100644 --- a/mm/process_vm_access.c +++ b/mm/process_vm_access.c @@ -412,7 +412,7 @@ SYSCALL_DEFINE6(process_vm_writev, pid_t, pid, #ifdef CONFIG_COMPAT -asmlinkage ssize_t +static ssize_t compat_process_vm_rw(compat_pid_t pid, const struct compat_iovec __user *lvec, unsigned long liovcnt, diff --git a/mm/readahead.c b/mm/readahead.c index 0de2360d65f3..29c5e1af5a0c 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -179,7 +179,7 @@ __do_page_cache_readahead(struct address_space *mapping, struct file *filp, rcu_read_lock(); page = radix_tree_lookup(&mapping->page_tree, page_offset); rcu_read_unlock(); - if (page) + if (page && !radix_tree_exceptional_entry(page)) continue; page = page_cache_alloc_readahead(mapping); @@ -233,14 +233,14 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp, return 0; } +#define MAX_READAHEAD ((512*4096)/PAGE_CACHE_SIZE) /* * Given a desired number of PAGE_CACHE_SIZE readahead pages, return a * sensible upper limit. */ unsigned long max_sane_readahead(unsigned long nr) { - return min(nr, (node_page_state(numa_node_id(), NR_INACTIVE_FILE) - + node_page_state(numa_node_id(), NR_FREE_PAGES)) / 2); + return min(nr, MAX_READAHEAD); } /* @@ -347,7 +347,7 @@ static pgoff_t count_history_pages(struct address_space *mapping, pgoff_t head; rcu_read_lock(); - head = radix_tree_prev_hole(&mapping->page_tree, offset - 1, max); + head = page_cache_prev_hole(mapping, offset - 1, max); rcu_read_unlock(); return offset - 1 - head; @@ -427,7 +427,7 @@ ondemand_readahead(struct address_space *mapping, pgoff_t start; rcu_read_lock(); - start = radix_tree_next_hole(&mapping->page_tree, offset+1,max); + start = page_cache_next_hole(mapping, offset + 1, max); rcu_read_unlock(); if (!start || start - offset > max) diff --git a/mm/shmem.c b/mm/shmem.c index 1f18c9d0d93e..a3ba988ec946 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -242,19 +242,17 @@ static int shmem_radix_tree_replace(struct address_space *mapping, pgoff_t index, void *expected, void *replacement) { void **pslot; - void *item = NULL; + void *item; VM_BUG_ON(!expected); + VM_BUG_ON(!replacement); pslot = radix_tree_lookup_slot(&mapping->page_tree, index); - if (pslot) - item = radix_tree_deref_slot_protected(pslot, - &mapping->tree_lock); + if (!pslot) + return -ENOENT; + item = radix_tree_deref_slot_protected(pslot, &mapping->tree_lock); if (item != expected) return -ENOENT; - if (replacement) - radix_tree_replace_slot(pslot, replacement); - else - radix_tree_delete(&mapping->page_tree, index); + radix_tree_replace_slot(pslot, replacement); return 0; } @@ -331,84 +329,20 @@ static void shmem_delete_from_page_cache(struct page *page, void *radswap) } /* - * Like find_get_pages, but collecting swap entries as well as pages. - */ -static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping, - pgoff_t start, unsigned int nr_pages, - struct page **pages, pgoff_t *indices) -{ - void **slot; - unsigned int ret = 0; - struct radix_tree_iter iter; - - if (!nr_pages) - return 0; - - rcu_read_lock(); -restart: - radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) { - struct page *page; -repeat: - page = radix_tree_deref_slot(slot); - if (unlikely(!page)) - continue; - if (radix_tree_exception(page)) { - if (radix_tree_deref_retry(page)) - goto restart; - /* - * Otherwise, we must be storing a swap entry - * here as an exceptional entry: so return it - * without attempting to raise page count. - */ - goto export; - } - if (!page_cache_get_speculative(page)) - goto repeat; - - /* Has the page moved? */ - if (unlikely(page != *slot)) { - page_cache_release(page); - goto repeat; - } -export: - indices[ret] = iter.index; - pages[ret] = page; - if (++ret == nr_pages) - break; - } - rcu_read_unlock(); - return ret; -} - -/* * Remove swap entry from radix tree, free the swap and its page cache. */ static int shmem_free_swap(struct address_space *mapping, pgoff_t index, void *radswap) { - int error; + void *old; spin_lock_irq(&mapping->tree_lock); - error = shmem_radix_tree_replace(mapping, index, radswap, NULL); + old = radix_tree_delete_item(&mapping->page_tree, index, radswap); spin_unlock_irq(&mapping->tree_lock); - if (!error) - free_swap_and_cache(radix_to_swp_entry(radswap)); - return error; -} - -/* - * Pagevec may contain swap entries, so shuffle up pages before releasing. - */ -static void shmem_deswap_pagevec(struct pagevec *pvec) -{ - int i, j; - - for (i = 0, j = 0; i < pagevec_count(pvec); i++) { - struct page *page = pvec->pages[i]; - if (!radix_tree_exceptional_entry(page)) - pvec->pages[j++] = page; - } - pvec->nr = j; + if (old != radswap) + return -ENOENT; + free_swap_and_cache(radix_to_swp_entry(radswap)); + return 0; } /* @@ -429,12 +363,12 @@ void shmem_unlock_mapping(struct address_space *mapping) * Avoid pagevec_lookup(): find_get_pages() returns 0 as if it * has finished, if it hits a row of PAGEVEC_SIZE swap entries. */ - pvec.nr = shmem_find_get_pages_and_swap(mapping, index, - PAGEVEC_SIZE, pvec.pages, indices); + pvec.nr = find_get_entries(mapping, index, + PAGEVEC_SIZE, pvec.pages, indices); if (!pvec.nr) break; index = indices[pvec.nr - 1] + 1; - shmem_deswap_pagevec(&pvec); + pagevec_remove_exceptionals(&pvec); check_move_unevictable_pages(pvec.pages, pvec.nr); pagevec_release(&pvec); cond_resched(); @@ -466,9 +400,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, pagevec_init(&pvec, 0); index = start; while (index < end) { - pvec.nr = shmem_find_get_pages_and_swap(mapping, index, - min(end - index, (pgoff_t)PAGEVEC_SIZE), - pvec.pages, indices); + pvec.nr = find_get_entries(mapping, index, + min(end - index, (pgoff_t)PAGEVEC_SIZE), + pvec.pages, indices); if (!pvec.nr) break; mem_cgroup_uncharge_start(); @@ -497,7 +431,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, } unlock_page(page); } - shmem_deswap_pagevec(&pvec); + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); @@ -535,9 +469,10 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, index = start; for ( ; ; ) { cond_resched(); - pvec.nr = shmem_find_get_pages_and_swap(mapping, index, + + pvec.nr = find_get_entries(mapping, index, min(end - index, (pgoff_t)PAGEVEC_SIZE), - pvec.pages, indices); + pvec.pages, indices); if (!pvec.nr) { if (index == start || unfalloc) break; @@ -545,7 +480,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, continue; } if ((index == start || unfalloc) && indices[0] >= end) { - shmem_deswap_pagevec(&pvec); + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); break; } @@ -574,7 +509,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, } unlock_page(page); } - shmem_deswap_pagevec(&pvec); + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); index++; @@ -1080,7 +1015,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, return -EFBIG; repeat: swap.val = 0; - page = find_lock_page(mapping, index); + page = find_lock_entry(mapping, index); if (radix_tree_exceptional_entry(page)) { swap = radix_to_swp_entry(page); page = NULL; @@ -1417,6 +1352,11 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode return inode; } +bool shmem_mapping(struct address_space *mapping) +{ + return mapping->backing_dev_info == &shmem_backing_dev_info; +} + #ifdef CONFIG_TMPFS static const struct inode_operations shmem_symlink_inode_operations; static const struct inode_operations shmem_short_symlink_operations; @@ -1729,7 +1669,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping, pagevec_init(&pvec, 0); pvec.nr = 1; /* start small: we may be there already */ while (!done) { - pvec.nr = shmem_find_get_pages_and_swap(mapping, index, + pvec.nr = find_get_entries(mapping, index, pvec.nr, pvec.pages, indices); if (!pvec.nr) { if (whence == SEEK_DATA) @@ -1756,7 +1696,7 @@ static pgoff_t shmem_seek_hole_data(struct address_space *mapping, break; } } - shmem_deswap_pagevec(&pvec); + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); pvec.nr = PAGEVEC_SIZE; cond_resched(); diff --git a/mm/slab.c b/mm/slab.c index b264214c77ea..9153c802e2fe 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3073,7 +3073,7 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags) local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK); retry_cpuset: - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); zonelist = node_zonelist(slab_node(), flags); retry: @@ -3131,7 +3131,7 @@ retry: } } - if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !obj)) + if (unlikely(!obj && read_mems_allowed_retry(cpuset_mems_cookie))) goto retry_cpuset; return obj; } diff --git a/mm/slub.c b/mm/slub.c index 25f14ad8f817..fe6d7be22ef0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1684,7 +1684,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags, return NULL; do { - cpuset_mems_cookie = get_mems_allowed(); + cpuset_mems_cookie = read_mems_allowed_begin(); zonelist = node_zonelist(slab_node(), flags); for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) { struct kmem_cache_node *n; @@ -1696,19 +1696,17 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags, object = get_partial_node(s, n, c, flags); if (object) { /* - * Return the object even if - * put_mems_allowed indicated that - * the cpuset mems_allowed was - * updated in parallel. It's a - * harmless race between the alloc - * and the cpuset update. + * Don't check read_mems_allowed_retry() + * here - if mems_allowed was updated in + * parallel, that was a harmless race + * between allocation and the cpuset + * update */ - put_mems_allowed(cpuset_mems_cookie); return object; } } } - } while (!put_mems_allowed(cpuset_mems_cookie)); + } while (read_mems_allowed_retry(cpuset_mems_cookie)); #endif return NULL; } @@ -3239,8 +3237,9 @@ int __kmem_cache_shutdown(struct kmem_cache *s) if (!rc) { /* - * We do the same lock strategy around sysfs_slab_add, see - * __kmem_cache_create. Because this is pretty much the last + * Since slab_attr_store may take the slab_mutex, we should + * release the lock while removing the sysfs entry in order to + * avoid a deadlock. Because this is pretty much the last * operation we do and the lock will be released shortly after * that in slab_common.c, we could just move sysfs_slab_remove * to a later point in common code. We should do that when we @@ -3780,10 +3779,7 @@ int __kmem_cache_create(struct kmem_cache *s, unsigned long flags) return 0; memcg_propagate_slab_attrs(s); - mutex_unlock(&slab_mutex); err = sysfs_slab_add(s); - mutex_lock(&slab_mutex); - if (err) kmem_cache_close(s); diff --git a/mm/swap.c b/mm/swap.c index 0092097b3f4c..9ce43ba4498b 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -574,6 +574,8 @@ void mark_page_accessed(struct page *page) else __lru_cache_activate_page(page); ClearPageReferenced(page); + if (page_is_file_cache(page)) + workingset_activation(page); } else if (!PageReferenced(page)) { SetPageReferenced(page); } @@ -948,6 +950,57 @@ void __pagevec_lru_add(struct pagevec *pvec) EXPORT_SYMBOL(__pagevec_lru_add); /** + * pagevec_lookup_entries - gang pagecache lookup + * @pvec: Where the resulting entries are placed + * @mapping: The address_space to search + * @start: The starting entry index + * @nr_entries: The maximum number of entries + * @indices: The cache indices corresponding to the entries in @pvec + * + * pagevec_lookup_entries() will search for and return a group of up + * to @nr_entries pages and shadow entries in the mapping. All + * entries are placed in @pvec. pagevec_lookup_entries() takes a + * reference against actual pages in @pvec. + * + * The search returns a group of mapping-contiguous entries with + * ascending indexes. There may be holes in the indices due to + * not-present entries. + * + * pagevec_lookup_entries() returns the number of entries which were + * found. + */ +unsigned pagevec_lookup_entries(struct pagevec *pvec, + struct address_space *mapping, + pgoff_t start, unsigned nr_pages, + pgoff_t *indices) +{ + pvec->nr = find_get_entries(mapping, start, nr_pages, + pvec->pages, indices); + return pagevec_count(pvec); +} + +/** + * pagevec_remove_exceptionals - pagevec exceptionals pruning + * @pvec: The pagevec to prune + * + * pagevec_lookup_entries() fills both pages and exceptional radix + * tree entries into the pagevec. This function prunes all + * exceptionals from @pvec without leaving holes, so that it can be + * passed on to page-only pagevec operations. + */ +void pagevec_remove_exceptionals(struct pagevec *pvec) +{ + int i, j; + + for (i = 0, j = 0; i < pagevec_count(pvec); i++) { + struct page *page = pvec->pages[i]; + if (!radix_tree_exceptional_entry(page)) + pvec->pages[j++] = page; + } + pvec->nr = j; +} + +/** * pagevec_lookup - gang pagecache lookup * @pvec: Where the resulting pages are placed * @mapping: The address_space to search diff --git a/mm/truncate.c b/mm/truncate.c index 353b683afd6e..e5cc39ab0751 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -22,6 +22,45 @@ #include <linux/cleancache.h> #include "internal.h" +static void clear_exceptional_entry(struct address_space *mapping, + pgoff_t index, void *entry) +{ + struct radix_tree_node *node; + void **slot; + + /* Handled by shmem itself */ + if (shmem_mapping(mapping)) + return; + + spin_lock_irq(&mapping->tree_lock); + /* + * Regular page slots are stabilized by the page lock even + * without the tree itself locked. These unlocked entries + * need verification under the tree lock. + */ + if (!__radix_tree_lookup(&mapping->page_tree, index, &node, &slot)) + goto unlock; + if (*slot != entry) + goto unlock; + radix_tree_replace_slot(slot, NULL); + mapping->nrshadows--; + if (!node) + goto unlock; + workingset_node_shadows_dec(node); + /* + * Don't track node without shadow entries. + * + * Avoid acquiring the list_lru lock if already untracked. + * The list_empty() test is safe as node->private_list is + * protected by mapping->tree_lock. + */ + if (!workingset_node_shadows(node) && + !list_empty(&node->private_list)) + list_lru_del(&workingset_shadow_nodes, &node->private_list); + __radix_tree_delete_node(&mapping->page_tree, node); +unlock: + spin_unlock_irq(&mapping->tree_lock); +} /** * do_invalidatepage - invalidate part or all of a page @@ -208,11 +247,12 @@ void truncate_inode_pages_range(struct address_space *mapping, unsigned int partial_start; /* inclusive */ unsigned int partial_end; /* exclusive */ struct pagevec pvec; + pgoff_t indices[PAGEVEC_SIZE]; pgoff_t index; int i; cleancache_invalidate_inode(mapping); - if (mapping->nrpages == 0) + if (mapping->nrpages == 0 && mapping->nrshadows == 0) return; /* Offsets within partial pages */ @@ -238,17 +278,23 @@ void truncate_inode_pages_range(struct address_space *mapping, pagevec_init(&pvec, 0); index = start; - while (index < end && pagevec_lookup(&pvec, mapping, index, - min(end - index, (pgoff_t)PAGEVEC_SIZE))) { + while (index < end && pagevec_lookup_entries(&pvec, mapping, index, + min(end - index, (pgoff_t)PAGEVEC_SIZE), + indices)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ - index = page->index; + index = indices[i]; if (index >= end) break; + if (radix_tree_exceptional_entry(page)) { + clear_exceptional_entry(mapping, index, page); + continue; + } + if (!trylock_page(page)) continue; WARN_ON(page->index != index); @@ -259,6 +305,7 @@ void truncate_inode_pages_range(struct address_space *mapping, truncate_inode_page(mapping, page); unlock_page(page); } + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); @@ -307,14 +354,16 @@ void truncate_inode_pages_range(struct address_space *mapping, index = start; for ( ; ; ) { cond_resched(); - if (!pagevec_lookup(&pvec, mapping, index, - min(end - index, (pgoff_t)PAGEVEC_SIZE))) { + if (!pagevec_lookup_entries(&pvec, mapping, index, + min(end - index, (pgoff_t)PAGEVEC_SIZE), + indices)) { if (index == start) break; index = start; continue; } - if (index == start && pvec.pages[0]->index >= end) { + if (index == start && indices[0] >= end) { + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); break; } @@ -323,16 +372,22 @@ void truncate_inode_pages_range(struct address_space *mapping, struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ - index = page->index; + index = indices[i]; if (index >= end) break; + if (radix_tree_exceptional_entry(page)) { + clear_exceptional_entry(mapping, index, page); + continue; + } + lock_page(page); WARN_ON(page->index != index); wait_on_page_writeback(page); truncate_inode_page(mapping, page); unlock_page(page); } + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); index++; @@ -360,6 +415,53 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart) EXPORT_SYMBOL(truncate_inode_pages); /** + * truncate_inode_pages_final - truncate *all* pages before inode dies + * @mapping: mapping to truncate + * + * Called under (and serialized by) inode->i_mutex. + * + * Filesystems have to use this in the .evict_inode path to inform the + * VM that this is the final truncate and the inode is going away. + */ +void truncate_inode_pages_final(struct address_space *mapping) +{ + unsigned long nrshadows; + unsigned long nrpages; + + /* + * Page reclaim can not participate in regular inode lifetime + * management (can't call iput()) and thus can race with the + * inode teardown. Tell it when the address space is exiting, + * so that it does not install eviction information after the + * final truncate has begun. + */ + mapping_set_exiting(mapping); + + /* + * When reclaim installs eviction entries, it increases + * nrshadows first, then decreases nrpages. Make sure we see + * this in the right order or we might miss an entry. + */ + nrpages = mapping->nrpages; + smp_rmb(); + nrshadows = mapping->nrshadows; + + if (nrpages || nrshadows) { + /* + * As truncation uses a lockless tree lookup, cycle + * the tree lock to make sure any ongoing tree + * modification that does not see AS_EXITING is + * completed before starting the final truncate. + */ + spin_lock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); + + truncate_inode_pages(mapping, 0); + } +} +EXPORT_SYMBOL(truncate_inode_pages_final); + +/** * invalidate_mapping_pages - Invalidate all the unlocked pages of one inode * @mapping: the address_space which holds the pages to invalidate * @start: the offset 'from' which to invalidate @@ -375,6 +477,7 @@ EXPORT_SYMBOL(truncate_inode_pages); unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end) { + pgoff_t indices[PAGEVEC_SIZE]; struct pagevec pvec; pgoff_t index = start; unsigned long ret; @@ -390,17 +493,23 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping, */ pagevec_init(&pvec, 0); - while (index <= end && pagevec_lookup(&pvec, mapping, index, - min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { + while (index <= end && pagevec_lookup_entries(&pvec, mapping, index, + min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1, + indices)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ - index = page->index; + index = indices[i]; if (index > end) break; + if (radix_tree_exceptional_entry(page)) { + clear_exceptional_entry(mapping, index, page); + continue; + } + if (!trylock_page(page)) continue; WARN_ON(page->index != index); @@ -414,6 +523,7 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping, deactivate_page(page); count += ret; } + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); @@ -444,7 +554,7 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) goto failed; BUG_ON(page_has_private(page)); - __delete_from_page_cache(page); + __delete_from_page_cache(page, NULL); spin_unlock_irq(&mapping->tree_lock); mem_cgroup_uncharge_cache_page(page); @@ -481,6 +591,7 @@ static int do_launder_page(struct address_space *mapping, struct page *page) int invalidate_inode_pages2_range(struct address_space *mapping, pgoff_t start, pgoff_t end) { + pgoff_t indices[PAGEVEC_SIZE]; struct pagevec pvec; pgoff_t index; int i; @@ -491,17 +602,23 @@ int invalidate_inode_pages2_range(struct address_space *mapping, cleancache_invalidate_inode(mapping); pagevec_init(&pvec, 0); index = start; - while (index <= end && pagevec_lookup(&pvec, mapping, index, - min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) { + while (index <= end && pagevec_lookup_entries(&pvec, mapping, index, + min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1, + indices)) { mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; /* We rely upon deletion not changing page->index */ - index = page->index; + index = indices[i]; if (index > end) break; + if (radix_tree_exceptional_entry(page)) { + clear_exceptional_entry(mapping, index, page); + continue; + } + lock_page(page); WARN_ON(page->index != index); if (page->mapping != mapping) { @@ -539,6 +656,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping, ret = ret2; unlock_page(page); } + pagevec_remove_exceptionals(&pvec); pagevec_release(&pvec); mem_cgroup_uncharge_end(); cond_resched(); diff --git a/mm/vmscan.c b/mm/vmscan.c index a9c74b409681..1f56a80a7c41 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -224,15 +224,15 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker, unsigned long freed = 0; unsigned long long delta; long total_scan; - long max_pass; + long freeable; long nr; long new_nr; int nid = shrinkctl->nid; long batch_size = shrinker->batch ? shrinker->batch : SHRINK_BATCH; - max_pass = shrinker->count_objects(shrinker, shrinkctl); - if (max_pass == 0) + freeable = shrinker->count_objects(shrinker, shrinkctl); + if (freeable == 0) return 0; /* @@ -244,14 +244,14 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker, total_scan = nr; delta = (4 * nr_pages_scanned) / shrinker->seeks; - delta *= max_pass; + delta *= freeable; do_div(delta, lru_pages + 1); total_scan += delta; if (total_scan < 0) { printk(KERN_ERR "shrink_slab: %pF negative objects to delete nr=%ld\n", shrinker->scan_objects, total_scan); - total_scan = max_pass; + total_scan = freeable; } /* @@ -260,26 +260,26 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker, * shrinkers to return -1 all the time. This results in a large * nr being built up so when a shrink that can do some work * comes along it empties the entire cache due to nr >>> - * max_pass. This is bad for sustaining a working set in + * freeable. This is bad for sustaining a working set in * memory. * * Hence only allow the shrinker to scan the entire cache when * a large delta change is calculated directly. */ - if (delta < max_pass / 4) - total_scan = min(total_scan, max_pass / 2); + if (delta < freeable / 4) + total_scan = min(total_scan, freeable / 2); /* * Avoid risking looping forever due to too large nr value: * never try to free more than twice the estimate number of * freeable entries. */ - if (total_scan > max_pass * 2) - total_scan = max_pass * 2; + if (total_scan > freeable * 2) + total_scan = freeable * 2; trace_mm_shrink_slab_start(shrinker, shrinkctl, nr, nr_pages_scanned, lru_pages, - max_pass, delta, total_scan); + freeable, delta, total_scan); /* * Normally, we should not scan less than batch_size objects in one @@ -292,12 +292,12 @@ shrink_slab_node(struct shrink_control *shrinkctl, struct shrinker *shrinker, * * We detect the "tight on memory" situations by looking at the total * number of objects we want to scan (total_scan). If it is greater - * than the total number of objects on slab (max_pass), we must be + * than the total number of objects on slab (freeable), we must be * scanning at high prio and therefore should try to reclaim as much as * possible. */ while (total_scan >= batch_size || - total_scan >= max_pass) { + total_scan >= freeable) { unsigned long ret; unsigned long nr_to_scan = min(batch_size, total_scan); @@ -523,7 +523,8 @@ static pageout_t pageout(struct page *page, struct address_space *mapping, * Same as remove_mapping, but if the page is removed from the mapping, it * gets returned with a refcount of 0. */ -static int __remove_mapping(struct address_space *mapping, struct page *page) +static int __remove_mapping(struct address_space *mapping, struct page *page, + bool reclaimed) { BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page)); @@ -569,10 +570,23 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) swapcache_free(swap, page); } else { void (*freepage)(struct page *); + void *shadow = NULL; freepage = mapping->a_ops->freepage; - - __delete_from_page_cache(page); + /* + * Remember a shadow entry for reclaimed file cache in + * order to detect refaults, thus thrashing, later on. + * + * But don't store shadows in an address space that is + * already exiting. This is not just an optizimation, + * inode reclaim needs to empty out the radix tree or + * the nodes are lost. Don't plant shadows behind its + * back. + */ + if (reclaimed && page_is_file_cache(page) && + !mapping_exiting(mapping)) + shadow = workingset_eviction(mapping, page); + __delete_from_page_cache(page, shadow); spin_unlock_irq(&mapping->tree_lock); mem_cgroup_uncharge_cache_page(page); @@ -595,7 +609,7 @@ cannot_free: */ int remove_mapping(struct address_space *mapping, struct page *page) { - if (__remove_mapping(mapping, page)) { + if (__remove_mapping(mapping, page, false)) { /* * Unfreezing the refcount with 1 rather than 2 effectively * drops the pagecache ref for us without requiring another @@ -1065,7 +1079,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, } } - if (!mapping || !__remove_mapping(mapping, page)) + if (!mapping || !__remove_mapping(mapping, page, true)) goto keep_locked; /* @@ -2297,7 +2311,12 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) struct zone *zone; unsigned long nr_soft_reclaimed; unsigned long nr_soft_scanned; + unsigned long lru_pages = 0; bool aborted_reclaim = false; + struct reclaim_state *reclaim_state = current->reclaim_state; + struct shrink_control shrink = { + .gfp_mask = sc->gfp_mask, + }; /* * If the number of buffer_heads in the machine exceeds the maximum @@ -2307,6 +2326,8 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) if (buffer_heads_over_limit) sc->gfp_mask |= __GFP_HIGHMEM; + nodes_clear(shrink.nodes_to_scan); + for_each_zone_zonelist_nodemask(zone, z, zonelist, gfp_zone(sc->gfp_mask), sc->nodemask) { if (!populated_zone(zone)) @@ -2318,6 +2339,10 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) if (global_reclaim(sc)) { if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) continue; + + lru_pages += zone_reclaimable_pages(zone); + node_set(zone_to_nid(zone), shrink.nodes_to_scan); + if (sc->priority != DEF_PRIORITY && !zone_reclaimable(zone)) continue; /* Let kswapd poll it */ @@ -2354,6 +2379,20 @@ static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc) shrink_zone(zone, sc); } + /* + * Don't shrink slabs when reclaiming memory from over limit cgroups + * but do shrink slab at least once when aborting reclaim for + * compaction to avoid unevenly scanning file/anon LRU pages over slab + * pages. + */ + if (global_reclaim(sc)) { + shrink_slab(&shrink, sc->nr_scanned, lru_pages); + if (reclaim_state) { + sc->nr_reclaimed += reclaim_state->reclaimed_slab; + reclaim_state->reclaimed_slab = 0; + } + } + return aborted_reclaim; } @@ -2394,13 +2433,9 @@ static bool all_unreclaimable(struct zonelist *zonelist, * else, the number of pages reclaimed */ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, - struct scan_control *sc, - struct shrink_control *shrink) + struct scan_control *sc) { unsigned long total_scanned = 0; - struct reclaim_state *reclaim_state = current->reclaim_state; - struct zoneref *z; - struct zone *zone; unsigned long writeback_threshold; bool aborted_reclaim; @@ -2415,32 +2450,6 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, sc->nr_scanned = 0; aborted_reclaim = shrink_zones(zonelist, sc); - /* - * Don't shrink slabs when reclaiming memory from over limit - * cgroups but do shrink slab at least once when aborting - * reclaim for compaction to avoid unevenly scanning file/anon - * LRU pages over slab pages. - */ - if (global_reclaim(sc)) { - unsigned long lru_pages = 0; - - nodes_clear(shrink->nodes_to_scan); - for_each_zone_zonelist(zone, z, zonelist, - gfp_zone(sc->gfp_mask)) { - if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) - continue; - - lru_pages += zone_reclaimable_pages(zone); - node_set(zone_to_nid(zone), - shrink->nodes_to_scan); - } - - shrink_slab(shrink, sc->nr_scanned, lru_pages); - if (reclaim_state) { - sc->nr_reclaimed += reclaim_state->reclaimed_slab; - reclaim_state->reclaimed_slab = 0; - } - } total_scanned += sc->nr_scanned; if (sc->nr_reclaimed >= sc->nr_to_reclaim) goto out; @@ -2602,9 +2611,6 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .target_mem_cgroup = NULL, .nodemask = nodemask, }; - struct shrink_control shrink = { - .gfp_mask = sc.gfp_mask, - }; /* * Do not enter reclaim if fatal signal was delivered while throttled. @@ -2618,7 +2624,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, sc.may_writepage, gfp_mask); - nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc); trace_mm_vmscan_direct_reclaim_end(nr_reclaimed); @@ -2685,9 +2691,6 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, .gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) | (GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK), }; - struct shrink_control shrink = { - .gfp_mask = sc.gfp_mask, - }; /* * Unlike direct reclaim via alloc_pages(), memcg's reclaim doesn't @@ -2702,7 +2705,7 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, sc.may_writepage, sc.gfp_mask); - nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc); trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed); @@ -3337,9 +3340,6 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim) .order = 0, .priority = DEF_PRIORITY, }; - struct shrink_control shrink = { - .gfp_mask = sc.gfp_mask, - }; struct zonelist *zonelist = node_zonelist(numa_node_id(), sc.gfp_mask); struct task_struct *p = current; unsigned long nr_reclaimed; @@ -3349,7 +3349,7 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim) reclaim_state.reclaimed_slab = 0; p->reclaim_state = &reclaim_state; - nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc); p->reclaim_state = NULL; lockdep_clear_current_reclaim_state(); diff --git a/mm/vmstat.c b/mm/vmstat.c index def5dd2fbe61..197b4c4a9587 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -770,6 +770,9 @@ const char * const vmstat_text[] = { "numa_local", "numa_other", #endif + "workingset_refault", + "workingset_activate", + "workingset_nodereclaim", "nr_anon_transparent_hugepages", "nr_free_cma", "nr_dirty_threshold", @@ -810,6 +813,9 @@ const char * const vmstat_text[] = { "pgrotated", + "drop_pagecache", + "drop_slab", + #ifdef CONFIG_NUMA_BALANCING "numa_pte_updates", "numa_huge_pte_updates", diff --git a/mm/workingset.c b/mm/workingset.c new file mode 100644 index 000000000000..f7216fa7da27 --- /dev/null +++ b/mm/workingset.c @@ -0,0 +1,414 @@ +/* + * Workingset detection + * + * Copyright (C) 2013 Red Hat, Inc., Johannes Weiner + */ + +#include <linux/memcontrol.h> +#include <linux/writeback.h> +#include <linux/pagemap.h> +#include <linux/atomic.h> +#include <linux/module.h> +#include <linux/swap.h> +#include <linux/fs.h> +#include <linux/mm.h> + +/* + * Double CLOCK lists + * + * Per zone, two clock lists are maintained for file pages: the + * inactive and the active list. Freshly faulted pages start out at + * the head of the inactive list and page reclaim scans pages from the + * tail. Pages that are accessed multiple times on the inactive list + * are promoted to the active list, to protect them from reclaim, + * whereas active pages are demoted to the inactive list when the + * active list grows too big. + * + * fault ------------------------+ + * | + * +--------------+ | +-------------+ + * reclaim <- | inactive | <-+-- demotion | active | <--+ + * +--------------+ +-------------+ | + * | | + * +-------------- promotion ------------------+ + * + * + * Access frequency and refault distance + * + * A workload is thrashing when its pages are frequently used but they + * are evicted from the inactive list every time before another access + * would have promoted them to the active list. + * + * In cases where the average access distance between thrashing pages + * is bigger than the size of memory there is nothing that can be + * done - the thrashing set could never fit into memory under any + * circumstance. + * + * However, the average access distance could be bigger than the + * inactive list, yet smaller than the size of memory. In this case, + * the set could fit into memory if it weren't for the currently + * active pages - which may be used more, hopefully less frequently: + * + * +-memory available to cache-+ + * | | + * +-inactive------+-active----+ + * a b | c d e f g h i | J K L M N | + * +---------------+-----------+ + * + * It is prohibitively expensive to accurately track access frequency + * of pages. But a reasonable approximation can be made to measure + * thrashing on the inactive list, after which refaulting pages can be + * activated optimistically to compete with the existing active pages. + * + * Approximating inactive page access frequency - Observations: + * + * 1. When a page is accessed for the first time, it is added to the + * head of the inactive list, slides every existing inactive page + * towards the tail by one slot, and pushes the current tail page + * out of memory. + * + * 2. When a page is accessed for the second time, it is promoted to + * the active list, shrinking the inactive list by one slot. This + * also slides all inactive pages that were faulted into the cache + * more recently than the activated page towards the tail of the + * inactive list. + * + * Thus: + * + * 1. The sum of evictions and activations between any two points in + * time indicate the minimum number of inactive pages accessed in + * between. + * + * 2. Moving one inactive page N page slots towards the tail of the + * list requires at least N inactive page accesses. + * + * Combining these: + * + * 1. When a page is finally evicted from memory, the number of + * inactive pages accessed while the page was in cache is at least + * the number of page slots on the inactive list. + * + * 2. In addition, measuring the sum of evictions and activations (E) + * at the time of a page's eviction, and comparing it to another + * reading (R) at the time the page faults back into memory tells + * the minimum number of accesses while the page was not cached. + * This is called the refault distance. + * + * Because the first access of the page was the fault and the second + * access the refault, we combine the in-cache distance with the + * out-of-cache distance to get the complete minimum access distance + * of this page: + * + * NR_inactive + (R - E) + * + * And knowing the minimum access distance of a page, we can easily + * tell if the page would be able to stay in cache assuming all page + * slots in the cache were available: + * + * NR_inactive + (R - E) <= NR_inactive + NR_active + * + * which can be further simplified to + * + * (R - E) <= NR_active + * + * Put into words, the refault distance (out-of-cache) can be seen as + * a deficit in inactive list space (in-cache). If the inactive list + * had (R - E) more page slots, the page would not have been evicted + * in between accesses, but activated instead. And on a full system, + * the only thing eating into inactive list space is active pages. + * + * + * Activating refaulting pages + * + * All that is known about the active list is that the pages have been + * accessed more than once in the past. This means that at any given + * time there is actually a good chance that pages on the active list + * are no longer in active use. + * + * So when a refault distance of (R - E) is observed and there are at + * least (R - E) active pages, the refaulting page is activated + * optimistically in the hope that (R - E) active pages are actually + * used less frequently than the refaulting page - or even not used at + * all anymore. + * + * If this is wrong and demotion kicks in, the pages which are truly + * used more frequently will be reactivated while the less frequently + * used once will be evicted from memory. + * + * But if this is right, the stale pages will be pushed out of memory + * and the used pages get to stay in cache. + * + * + * Implementation + * + * For each zone's file LRU lists, a counter for inactive evictions + * and activations is maintained (zone->inactive_age). + * + * On eviction, a snapshot of this counter (along with some bits to + * identify the zone) is stored in the now empty page cache radix tree + * slot of the evicted page. This is called a shadow entry. + * + * On cache misses for which there are shadow entries, an eligible + * refault distance will immediately activate the refaulting page. + */ + +static void *pack_shadow(unsigned long eviction, struct zone *zone) +{ + eviction = (eviction << NODES_SHIFT) | zone_to_nid(zone); + eviction = (eviction << ZONES_SHIFT) | zone_idx(zone); + eviction = (eviction << RADIX_TREE_EXCEPTIONAL_SHIFT); + + return (void *)(eviction | RADIX_TREE_EXCEPTIONAL_ENTRY); +} + +static void unpack_shadow(void *shadow, + struct zone **zone, + unsigned long *distance) +{ + unsigned long entry = (unsigned long)shadow; + unsigned long eviction; + unsigned long refault; + unsigned long mask; + int zid, nid; + + entry >>= RADIX_TREE_EXCEPTIONAL_SHIFT; + zid = entry & ((1UL << ZONES_SHIFT) - 1); + entry >>= ZONES_SHIFT; + nid = entry & ((1UL << NODES_SHIFT) - 1); + entry >>= NODES_SHIFT; + eviction = entry; + + *zone = NODE_DATA(nid)->node_zones + zid; + + refault = atomic_long_read(&(*zone)->inactive_age); + mask = ~0UL >> (NODES_SHIFT + ZONES_SHIFT + + RADIX_TREE_EXCEPTIONAL_SHIFT); + /* + * The unsigned subtraction here gives an accurate distance + * across inactive_age overflows in most cases. + * + * There is a special case: usually, shadow entries have a + * short lifetime and are either refaulted or reclaimed along + * with the inode before they get too old. But it is not + * impossible for the inactive_age to lap a shadow entry in + * the field, which can then can result in a false small + * refault distance, leading to a false activation should this + * old entry actually refault again. However, earlier kernels + * used to deactivate unconditionally with *every* reclaim + * invocation for the longest time, so the occasional + * inappropriate activation leading to pressure on the active + * list is not a problem. + */ + *distance = (refault - eviction) & mask; +} + +/** + * workingset_eviction - note the eviction of a page from memory + * @mapping: address space the page was backing + * @page: the page being evicted + * + * Returns a shadow entry to be stored in @mapping->page_tree in place + * of the evicted @page so that a later refault can be detected. + */ +void *workingset_eviction(struct address_space *mapping, struct page *page) +{ + struct zone *zone = page_zone(page); + unsigned long eviction; + + eviction = atomic_long_inc_return(&zone->inactive_age); + return pack_shadow(eviction, zone); +} + +/** + * workingset_refault - evaluate the refault of a previously evicted page + * @shadow: shadow entry of the evicted page + * + * Calculates and evaluates the refault distance of the previously + * evicted page in the context of the zone it was allocated in. + * + * Returns %true if the page should be activated, %false otherwise. + */ +bool workingset_refault(void *shadow) +{ + unsigned long refault_distance; + struct zone *zone; + + unpack_shadow(shadow, &zone, &refault_distance); + inc_zone_state(zone, WORKINGSET_REFAULT); + + if (refault_distance <= zone_page_state(zone, NR_ACTIVE_FILE)) { + inc_zone_state(zone, WORKINGSET_ACTIVATE); + return true; + } + return false; +} + +/** + * workingset_activation - note a page activation + * @page: page that is being activated + */ +void workingset_activation(struct page *page) +{ + atomic_long_inc(&page_zone(page)->inactive_age); +} + +/* + * Shadow entries reflect the share of the working set that does not + * fit into memory, so their number depends on the access pattern of + * the workload. In most cases, they will refault or get reclaimed + * along with the inode, but a (malicious) workload that streams + * through files with a total size several times that of available + * memory, while preventing the inodes from being reclaimed, can + * create excessive amounts of shadow nodes. To keep a lid on this, + * track shadow nodes and reclaim them when they grow way past the + * point where they would still be useful. + */ + +struct list_lru workingset_shadow_nodes; + +static unsigned long count_shadow_nodes(struct shrinker *shrinker, + struct shrink_control *sc) +{ + unsigned long shadow_nodes; + unsigned long max_nodes; + unsigned long pages; + + /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ + local_irq_disable(); + shadow_nodes = list_lru_count_node(&workingset_shadow_nodes, sc->nid); + local_irq_enable(); + + pages = node_present_pages(sc->nid); + /* + * Active cache pages are limited to 50% of memory, and shadow + * entries that represent a refault distance bigger than that + * do not have any effect. Limit the number of shadow nodes + * such that shadow entries do not exceed the number of active + * cache pages, assuming a worst-case node population density + * of 1/8th on average. + * + * On 64-bit with 7 radix_tree_nodes per page and 64 slots + * each, this will reclaim shadow entries when they consume + * ~2% of available memory: + * + * PAGE_SIZE / radix_tree_nodes / node_entries / PAGE_SIZE + */ + max_nodes = pages >> (1 + RADIX_TREE_MAP_SHIFT - 3); + + if (shadow_nodes <= max_nodes) + return 0; + + return shadow_nodes - max_nodes; +} + +static enum lru_status shadow_lru_isolate(struct list_head *item, + spinlock_t *lru_lock, + void *arg) +{ + struct address_space *mapping; + struct radix_tree_node *node; + unsigned int i; + int ret; + + /* + * Page cache insertions and deletions synchroneously maintain + * the shadow node LRU under the mapping->tree_lock and the + * lru_lock. Because the page cache tree is emptied before + * the inode can be destroyed, holding the lru_lock pins any + * address_space that has radix tree nodes on the LRU. + * + * We can then safely transition to the mapping->tree_lock to + * pin only the address_space of the particular node we want + * to reclaim, take the node off-LRU, and drop the lru_lock. + */ + + node = container_of(item, struct radix_tree_node, private_list); + mapping = node->private_data; + + /* Coming from the list, invert the lock order */ + if (!spin_trylock(&mapping->tree_lock)) { + spin_unlock(lru_lock); + ret = LRU_RETRY; + goto out; + } + + list_del_init(item); + spin_unlock(lru_lock); + + /* + * The nodes should only contain one or more shadow entries, + * no pages, so we expect to be able to remove them all and + * delete and free the empty node afterwards. + */ + + BUG_ON(!node->count); + BUG_ON(node->count & RADIX_TREE_COUNT_MASK); + + for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) { + if (node->slots[i]) { + BUG_ON(!radix_tree_exceptional_entry(node->slots[i])); + node->slots[i] = NULL; + BUG_ON(node->count < (1U << RADIX_TREE_COUNT_SHIFT)); + node->count -= 1U << RADIX_TREE_COUNT_SHIFT; + BUG_ON(!mapping->nrshadows); + mapping->nrshadows--; + } + } + BUG_ON(node->count); + inc_zone_state(page_zone(virt_to_page(node)), WORKINGSET_NODERECLAIM); + if (!__radix_tree_delete_node(&mapping->page_tree, node)) + BUG(); + + spin_unlock(&mapping->tree_lock); + ret = LRU_REMOVED_RETRY; +out: + local_irq_enable(); + cond_resched(); + local_irq_disable(); + spin_lock(lru_lock); + return ret; +} + +static unsigned long scan_shadow_nodes(struct shrinker *shrinker, + struct shrink_control *sc) +{ + unsigned long ret; + + /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ + local_irq_disable(); + ret = list_lru_walk_node(&workingset_shadow_nodes, sc->nid, + shadow_lru_isolate, NULL, &sc->nr_to_scan); + local_irq_enable(); + return ret; +} + +static struct shrinker workingset_shadow_shrinker = { + .count_objects = count_shadow_nodes, + .scan_objects = scan_shadow_nodes, + .seeks = DEFAULT_SEEKS, + .flags = SHRINKER_NUMA_AWARE, +}; + +/* + * Our list_lru->lock is IRQ-safe as it nests inside the IRQ-safe + * mapping->tree_lock. + */ +static struct lock_class_key shadow_nodes_key; + +static int __init workingset_init(void) +{ + int ret; + + ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key); + if (ret) + goto err; + ret = register_shrinker(&workingset_shadow_shrinker); + if (ret) + goto err_list_lru; + return 0; +err_list_lru: + list_lru_destroy(&workingset_shadow_nodes); +err: + return ret; +} +module_init(workingset_init); diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index 7203e66dcd6f..1b4e4b8f5e47 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -18,8 +18,8 @@ HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include bpf-direct-objs := bpf-direct.o # Try to match the kernel target. -ifndef CONFIG_64BIT ifndef CROSS_COMPILE +ifndef CONFIG_64BIT # s390 has -m31 flag to build 31 bit binaries ifndef CONFIG_S390 @@ -36,7 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG) HOSTLOADLIBES_bpf-fancy += $(MFLAG) HOSTLOADLIBES_dropper += $(MFLAG) endif -endif - -# Tell kbuild to always build the programs always := $(hostprogs-y) +else +# MIPS system calls are defined based on the -mabi that is passed +# to the toolchain which may or may not be a valid option +# for the host toolchain. So disable tests if target architecture +# is MIPS but the host isn't. +ifndef CONFIG_MIPS +always := $(hostprogs-y) +endif +endif diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 464dcef79b35..34eb2160489d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -281,7 +281,7 @@ our $Attribute = qr{ __weak }x; our $Modifier; -our $Inline = qr{inline|__always_inline|noinline}; +our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; @@ -289,13 +289,14 @@ our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; our $Binary = qr{(?i)0b[01]+$Int_type?}; our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; our $Int = qr{[0-9]+$Int_type?}; +our $Octal = qr{0[0-7]+$Int_type?}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|$Binary|$Hex|$Int}; +our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; -our $Compare = qr{<=|>=|==|!=|<|>}; +our $Compare = qr{<=|>=|==|!=|<|(?<!-)>}; our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| @@ -303,6 +304,8 @@ our $Operators = qr{ &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; +our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; + our $NonptrType; our $NonptrTypeWithAttr; our $Type; @@ -378,6 +381,22 @@ our @modifierList = ( qr{fastcall}, ); +our @mode_permission_funcs = ( + ["module_param", 3], + ["module_param_(?:array|named|string)", 4], + ["module_param_array_named", 5], + ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], + ["proc_create(?:_data|)", 2], + ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2], +); + +#Create a search pattern for all these functions to speed up a loop below +our $mode_perms_search = ""; +foreach my $entry (@mode_permission_funcs) { + $mode_perms_search .= '|' if ($mode_perms_search ne ""); + $mode_perms_search .= $entry->[0]; +} + our $allowed_asm_includes = qr{(?x: irq| memory @@ -412,7 +431,7 @@ sub build_types { (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)? (?:\s+$Inline|\s+$Modifier)* }x; - $Declare = qr{(?:$Storage\s+)?$Type}; + $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; } build_types(); @@ -423,15 +442,20 @@ our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Any use must be runtime checked with $^V our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; -our $LvalOrFunc = qr{($Lval)\s*($balanced_parens{0,1})\s*}; +our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant)}; sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); - $string =~ s@^\s*\(\s*@@g; - $string =~ s@\s*\)\s*$@@g; + + while ($string =~ /^\s*\(.*\)\s*$/) { + $string =~ s@^\s*\(\s*@@; + $string =~ s@\s*\)\s*$@@; + } + $string =~ s@\s+@ @g; + return $string; } @@ -1421,21 +1445,25 @@ sub possible { my $prefix = ''; sub show_type { - return defined $use_type{$_[0]} if (scalar keys %use_type > 0); + my ($type) = @_; + + return defined $use_type{$type} if (scalar keys %use_type > 0); - return !defined $ignore_type{$_[0]}; + return !defined $ignore_type{$type}; } sub report { - if (!show_type($_[1]) || - (defined $tst_only && $_[2] !~ /\Q$tst_only\E/)) { + my ($level, $type, $msg) = @_; + + if (!show_type($type) || + (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { return 0; } my $line; if ($show_types) { - $line = "$prefix$_[0]:$_[1]: $_[2]\n"; + $line = "$prefix$level:$type: $msg\n"; } else { - $line = "$prefix$_[0]: $_[2]\n"; + $line = "$prefix$level: $msg\n"; } $line = (split('\n', $line))[0] . "\n" if ($terse); @@ -1443,12 +1471,15 @@ sub report { return 1; } + sub report_dump { our @report; } sub ERROR { - if (report("ERROR", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if (report("ERROR", $type, $msg)) { our $clean = 0; our $cnt_error++; return 1; @@ -1456,7 +1487,9 @@ sub ERROR { return 0; } sub WARN { - if (report("WARNING", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if (report("WARNING", $type, $msg)) { our $clean = 0; our $cnt_warn++; return 1; @@ -1464,7 +1497,9 @@ sub WARN { return 0; } sub CHK { - if ($check && report("CHECK", $_[0], $_[1])) { + my ($type, $msg) = @_; + + if ($check && report("CHECK", $type, $msg)) { our $clean = 0; our $cnt_chk++; return 1; @@ -1574,7 +1609,7 @@ sub pos_last_openparen { } } - return $last_openparen + 1; + return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; } sub process { @@ -1891,6 +1926,12 @@ sub process { } } +# Check for unwanted Gerrit info + if ($in_commit_log && $line =~ /^\s*change-id:/i) { + ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("CORRUPTED_PATCH", @@ -2041,13 +2082,17 @@ sub process { } # check for DT compatible documentation - if (defined $root && $realfile =~ /\.dts/ && - $rawline =~ /^\+\s*compatible\s*=/) { + if (defined $root && + (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || + ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { + my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; + my $dt_path = $root . "/Documentation/devicetree/bindings/"; + my $vp_file = $dt_path . "vendor-prefixes.txt"; + foreach my $compat (@compats) { my $compat2 = $compat; - my $dt_path = $root . "/Documentation/devicetree/bindings/"; $compat2 =~ s/\,[a-z]*\-/\,<\.\*>\-/; `grep -Erq "$compat|$compat2" $dt_path`; if ( $? >> 8 ) { @@ -2055,14 +2100,12 @@ sub process { "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); } - my $vendor = $compat; - my $vendor_path = $dt_path . "vendor-prefixes.txt"; - next if (! -f $vendor_path); - $vendor =~ s/^([a-zA-Z0-9]+)\,.*/$1/; - `grep -Eq "$vendor" $vendor_path`; + next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; + my $vendor = $1; + `grep -Eq "^$vendor\\b" $vp_file`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", - "DT compatible string vendor \"$vendor\" appears un-documented -- check $vendor_path\n" . $herecurr); + "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); } } } @@ -2159,7 +2202,7 @@ sub process { # check multi-line statement indentation matches previous line if ($^V && $^V ge 5.10.0 && - $prevline =~ /^\+(\t*)(if \(|$Ident\().*(\&\&|\|\||,)\s*$/) { + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; @@ -2198,7 +2241,8 @@ sub process { if ($realfile =~ m@^(drivers/net/|net/)@ && $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $rawline =~ /^\+[ \t]*\*/) { + $rawline =~ /^\+[ \t]*\*/ && + $realline > 2) { WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } @@ -2221,6 +2265,21 @@ sub process { "networking block comments put the trailing */ on a separate line\n" . $herecurr); } +# check for missing blank lines after declarations + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevline =~ /^\+\s+$Declare\s+$Ident/ && + !($prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + $prevline =~ /(?:\{\s*|\\)$/) && #extended lines + $sline =~ /^\+\s+/ && #Not at char 1 + !($sline =~ /^\+\s+$Declare/ || + $sline =~ /^\+\s+$Ident\s+$Ident/ || #eg: typedef foo + $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(])/ || + $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) { + WARN("SPACING", + "networking uses a blank line after declarations\n" . $hereprev); + } + # check for spaces at the beginning of a line. # Exceptions: # 1) within comments @@ -2665,6 +2724,13 @@ sub process { $herecurr); } +# check for non-global char *foo[] = {"bar", ...} declarations. + if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { + WARN("STATIC_CONST_CHAR_ARRAY", + "char * array declaration might be better as static const\n" . + $herecurr); + } + # check for function declarations without arguments like "int foo()" if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { if (ERROR("FUNCTION_WITHOUT_ARGS", @@ -2799,7 +2865,7 @@ sub process { my $level2 = $level; $level2 = "dbg" if ($level eq "debug"); WARN("PREFER_PR_LEVEL", - "Prefer netdev_$level2(netdev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); } if ($line =~ /\bpr_warning\s*\(/) { @@ -2848,10 +2914,7 @@ sub process { # Function pointer declarations # check spacing between type, funcptr, and args # canonical declaration is "type (*funcptr)(args...)" -# -# the $Declare variable will capture all spaces after the type -# so check it for trailing missing spaces or multiple spaces - if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)$Ident(\s*)\)(\s*)\(/) { + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { my $declare = $1; my $pre_pointer_space = $2; my $post_pointer_space = $3; @@ -2859,16 +2922,30 @@ sub process { my $post_funcname_space = $5; my $pre_args_space = $6; - if ($declare !~ /\s$/) { +# the $Declare variable will capture all spaces after the type +# so check it for a missing trailing missing space but pointer return types +# don't need a space so don't warn for those. + my $post_declare_space = ""; + if ($declare =~ /(\s+)$/) { + $post_declare_space = $1; + $declare = rtrim($declare); + } + if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { WARN("SPACING", "missing space after return type\n" . $herecurr); + $post_declare_space = " "; } # unnecessary space "type (*funcptr)(args...)" - elsif ($declare =~ /\s{2,}$/) { - WARN("SPACING", - "Multiple spaces after return type\n" . $herecurr); - } +# This test is not currently implemented because these declarations are +# equivalent to +# int foo(int bar, ...) +# and this is form shouldn't/doesn't generate a checkpatch warning. +# +# elsif ($declare =~ /\s{2,}$/) { +# WARN("SPACING", +# "Multiple spaces after return type\n" . $herecurr); +# } # unnecessary space "type ( *funcptr)(args...)" if (defined $pre_pointer_space && @@ -2900,7 +2977,7 @@ sub process { if (show_type("SPACING") && $fix) { $fixed[$linenr - 1] =~ - s/^(.\s*$Declare)\(\s*\*\s*($Ident)\s*\)\s*\(/rtrim($1) . " " . "\(\*$2\)\("/ex; + s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; } } @@ -3061,10 +3138,13 @@ sub process { # // is a comment } elsif ($op eq '//') { + # : when part of a bitfield + } elsif ($opv eq ':B') { + # skip the bitfield test for now + # No spaces for: # -> - # : when part of a bitfield - } elsif ($op eq '->' || $opv eq ':B') { + } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { if (ERROR("SPACING", "spaces prohibited around that '$op' $at\n" . $hereptr)) { @@ -3334,14 +3414,17 @@ sub process { } } -# Return is not a function. +# return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; if ($^V && $^V ge 5.10.0 && - $stat =~ /^.\s*return\s*$balanced_parens\s*;\s*$/) { - ERROR("RETURN_PARENTHESES", - "return is not a function, parentheses are not required\n" . $herecurr); - + $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { + my $value = $1; + $value = deparenthesize($value); + if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { + ERROR("RETURN_PARENTHESES", + "return is not a function, parentheses are not required\n" . $herecurr); + } } elsif ($spacing !~ /\s+/) { ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); @@ -3910,12 +3993,30 @@ sub process { } } +# don't use __constant_<foo> functions outside of include/uapi/ + if ($realfile !~ m@^include/uapi/@ && + $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { + my $constant_func = $1; + my $func = $constant_func; + $func =~ s/^__constant_//; + if (WARN("CONSTANT_CONVERSION", + "$constant_func should be $func\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b$constant_func\b/$func/g; + } + } + # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + my $delay = $1; # ignore udelay's < 10, however - if (! ($1 < 10) ) { + if (! ($delay < 10) ) { CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); + } + if ($delay > 2000) { + WARN("LONG_UDELAY", + "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); } } @@ -3923,7 +4024,7 @@ sub process { if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); } } @@ -4149,7 +4250,7 @@ sub process { # check for naked sscanf if ($^V && $^V ge 5.10.0 && defined $stat && - $stat =~ /\bsscanf\b/ && + $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { @@ -4240,12 +4341,6 @@ sub process { "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); } -# check for GFP_NOWAIT use - if ($line =~ /\b__GFP_NOFAIL\b/) { - WARN("__GFP_NOFAIL", - "Use of __GFP_NOFAIL is deprecated, no new users should be added\n" . $herecurr); - } - # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { if (WARN("ONE_SEMICOLON", @@ -4457,6 +4552,34 @@ sub process { WARN("EXPORTED_WORLD_WRITABLE", "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } + +# Mode permission misuses where it seems decimal should be octal +# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop + if ($^V && $^V ge 5.10.0 && + $line =~ /$mode_perms_search/) { + foreach my $entry (@mode_permission_funcs) { + my $func = $entry->[0]; + my $arg_pos = $entry->[1]; + + my $skip_args = ""; + if ($arg_pos > 1) { + $arg_pos--; + $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; + } + my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]"; + if ($line =~ /$test/) { + my $val = $1; + $val = $6 if ($skip_args ne ""); + + if ($val !~ /^0$/ && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + length($val) ne 4)) { + ERROR("NON_OCTAL_PERMISSIONS", + "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr); + } + } + } + } } # If we have no input at all, then there is nothing to report on diff --git a/scripts/genksyms/keywords.gperf b/scripts/genksyms/keywords.gperf index 3e77a943e7b7..a9096d993172 100644 --- a/scripts/genksyms/keywords.gperf +++ b/scripts/genksyms/keywords.gperf @@ -23,6 +23,8 @@ __inline, INLINE_KEYW __inline__, INLINE_KEYW __signed, SIGNED_KEYW __signed__, SIGNED_KEYW +__typeof, TYPEOF_KEYW +__typeof__, TYPEOF_KEYW __volatile, VOLATILE_KEYW __volatile__, VOLATILE_KEYW # According to rth, c99 defines _Bool, __restrict, __restrict__, restrict. KAO @@ -51,9 +53,8 @@ signed, SIGNED_KEYW static, STATIC_KEYW struct, STRUCT_KEYW typedef, TYPEDEF_KEYW +typeof, TYPEOF_KEYW union, UNION_KEYW unsigned, UNSIGNED_KEYW void, VOID_KEYW volatile, VOLATILE_KEYW -typeof, TYPEOF_KEYW -__typeof__, TYPEOF_KEYW diff --git a/scripts/genksyms/keywords.hash.c_shipped b/scripts/genksyms/keywords.hash.c_shipped index 82062607e8c0..e9452482e198 100644 --- a/scripts/genksyms/keywords.hash.c_shipped +++ b/scripts/genksyms/keywords.hash.c_shipped @@ -34,7 +34,7 @@ struct resword; static const struct resword *is_reserved_word(register const char *str, register unsigned int len); #line 8 "scripts/genksyms/keywords.gperf" struct resword { const char *name; int token; }; -/* maximum key range = 64, duplicates = 0 */ +/* maximum key range = 98, duplicates = 0 */ #ifdef __GNUC__ __inline @@ -48,32 +48,32 @@ is_reserved_hash (register const char *str, register unsigned int len) { static const unsigned char asso_values[] = { - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 0, - 67, 67, 67, 67, 67, 67, 15, 67, 67, 67, - 0, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 0, 67, 0, 67, 5, - 25, 20, 15, 30, 67, 15, 67, 67, 10, 0, - 10, 40, 20, 67, 10, 5, 0, 10, 15, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67 + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 0, + 101, 101, 101, 101, 101, 101, 15, 101, 101, 101, + 0, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 0, 101, 0, 101, 5, + 25, 20, 55, 30, 101, 15, 101, 101, 10, 0, + 10, 40, 10, 101, 10, 5, 0, 10, 15, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101 }; return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]] + asso_values[(unsigned char)str[len - 1]]; } @@ -89,17 +89,17 @@ is_reserved_word (register const char *str, register unsigned int len) { enum { - TOTAL_KEYWORDS = 45, + TOTAL_KEYWORDS = 46, MIN_WORD_LENGTH = 3, MAX_WORD_LENGTH = 24, MIN_HASH_VALUE = 3, - MAX_HASH_VALUE = 66 + MAX_HASH_VALUE = 100 }; static const struct resword wordlist[] = { {""}, {""}, {""}, -#line 33 "scripts/genksyms/keywords.gperf" +#line 35 "scripts/genksyms/keywords.gperf" {"asm", ASM_KEYW}, {""}, #line 15 "scripts/genksyms/keywords.gperf" @@ -108,7 +108,7 @@ is_reserved_word (register const char *str, register unsigned int len) #line 16 "scripts/genksyms/keywords.gperf" {"__asm__", ASM_KEYW}, {""}, {""}, -#line 59 "scripts/genksyms/keywords.gperf" +#line 27 "scripts/genksyms/keywords.gperf" {"__typeof__", TYPEOF_KEYW}, {""}, #line 19 "scripts/genksyms/keywords.gperf" @@ -119,31 +119,31 @@ is_reserved_word (register const char *str, register unsigned int len) {"__const__", CONST_KEYW}, #line 25 "scripts/genksyms/keywords.gperf" {"__signed__", SIGNED_KEYW}, -#line 51 "scripts/genksyms/keywords.gperf" +#line 53 "scripts/genksyms/keywords.gperf" {"static", STATIC_KEYW}, {""}, -#line 46 "scripts/genksyms/keywords.gperf" +#line 48 "scripts/genksyms/keywords.gperf" {"int", INT_KEYW}, -#line 39 "scripts/genksyms/keywords.gperf" +#line 41 "scripts/genksyms/keywords.gperf" {"char", CHAR_KEYW}, -#line 40 "scripts/genksyms/keywords.gperf" +#line 42 "scripts/genksyms/keywords.gperf" {"const", CONST_KEYW}, -#line 52 "scripts/genksyms/keywords.gperf" +#line 54 "scripts/genksyms/keywords.gperf" {"struct", STRUCT_KEYW}, -#line 31 "scripts/genksyms/keywords.gperf" +#line 33 "scripts/genksyms/keywords.gperf" {"__restrict__", RESTRICT_KEYW}, -#line 32 "scripts/genksyms/keywords.gperf" +#line 34 "scripts/genksyms/keywords.gperf" {"restrict", RESTRICT_KEYW}, #line 12 "scripts/genksyms/keywords.gperf" {"EXPORT_SYMBOL_GPL_FUTURE", EXPORT_SYMBOL_KEYW}, #line 23 "scripts/genksyms/keywords.gperf" {"__inline__", INLINE_KEYW}, {""}, -#line 27 "scripts/genksyms/keywords.gperf" +#line 29 "scripts/genksyms/keywords.gperf" {"__volatile__", VOLATILE_KEYW}, #line 10 "scripts/genksyms/keywords.gperf" {"EXPORT_SYMBOL", EXPORT_SYMBOL_KEYW}, -#line 30 "scripts/genksyms/keywords.gperf" +#line 32 "scripts/genksyms/keywords.gperf" {"_restrict", RESTRICT_KEYW}, {""}, #line 17 "scripts/genksyms/keywords.gperf" @@ -152,56 +152,65 @@ is_reserved_word (register const char *str, register unsigned int len) {"EXPORT_SYMBOL_GPL", EXPORT_SYMBOL_KEYW}, #line 21 "scripts/genksyms/keywords.gperf" {"__extension__", EXTENSION_KEYW}, -#line 42 "scripts/genksyms/keywords.gperf" +#line 44 "scripts/genksyms/keywords.gperf" {"enum", ENUM_KEYW}, #line 13 "scripts/genksyms/keywords.gperf" {"EXPORT_UNUSED_SYMBOL", EXPORT_SYMBOL_KEYW}, -#line 43 "scripts/genksyms/keywords.gperf" +#line 45 "scripts/genksyms/keywords.gperf" {"extern", EXTERN_KEYW}, {""}, #line 24 "scripts/genksyms/keywords.gperf" {"__signed", SIGNED_KEYW}, #line 14 "scripts/genksyms/keywords.gperf" {"EXPORT_UNUSED_SYMBOL_GPL", EXPORT_SYMBOL_KEYW}, -#line 54 "scripts/genksyms/keywords.gperf" +#line 57 "scripts/genksyms/keywords.gperf" {"union", UNION_KEYW}, -#line 58 "scripts/genksyms/keywords.gperf" - {"typeof", TYPEOF_KEYW}, -#line 53 "scripts/genksyms/keywords.gperf" - {"typedef", TYPEDEF_KEYW}, + {""}, {""}, #line 22 "scripts/genksyms/keywords.gperf" {"__inline", INLINE_KEYW}, -#line 38 "scripts/genksyms/keywords.gperf" +#line 40 "scripts/genksyms/keywords.gperf" {"auto", AUTO_KEYW}, -#line 26 "scripts/genksyms/keywords.gperf" +#line 28 "scripts/genksyms/keywords.gperf" {"__volatile", VOLATILE_KEYW}, {""}, {""}, -#line 55 "scripts/genksyms/keywords.gperf" +#line 58 "scripts/genksyms/keywords.gperf" {"unsigned", UNSIGNED_KEYW}, {""}, -#line 49 "scripts/genksyms/keywords.gperf" +#line 51 "scripts/genksyms/keywords.gperf" {"short", SHORT_KEYW}, -#line 45 "scripts/genksyms/keywords.gperf" +#line 47 "scripts/genksyms/keywords.gperf" {"inline", INLINE_KEYW}, {""}, -#line 57 "scripts/genksyms/keywords.gperf" +#line 60 "scripts/genksyms/keywords.gperf" {"volatile", VOLATILE_KEYW}, -#line 47 "scripts/genksyms/keywords.gperf" +#line 49 "scripts/genksyms/keywords.gperf" {"long", LONG_KEYW}, -#line 29 "scripts/genksyms/keywords.gperf" +#line 31 "scripts/genksyms/keywords.gperf" {"_Bool", BOOL_KEYW}, {""}, {""}, -#line 48 "scripts/genksyms/keywords.gperf" +#line 50 "scripts/genksyms/keywords.gperf" {"register", REGISTER_KEYW}, -#line 56 "scripts/genksyms/keywords.gperf" +#line 59 "scripts/genksyms/keywords.gperf" {"void", VOID_KEYW}, -#line 44 "scripts/genksyms/keywords.gperf" - {"float", FLOAT_KEYW}, -#line 41 "scripts/genksyms/keywords.gperf" + {""}, +#line 43 "scripts/genksyms/keywords.gperf" {"double", DOUBLE_KEYW}, + {""}, +#line 26 "scripts/genksyms/keywords.gperf" + {"__typeof", TYPEOF_KEYW}, + {""}, {""}, +#line 52 "scripts/genksyms/keywords.gperf" + {"signed", SIGNED_KEYW}, {""}, {""}, {""}, {""}, -#line 50 "scripts/genksyms/keywords.gperf" - {"signed", SIGNED_KEYW} +#line 56 "scripts/genksyms/keywords.gperf" + {"typeof", TYPEOF_KEYW}, +#line 55 "scripts/genksyms/keywords.gperf" + {"typedef", TYPEDEF_KEYW}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 46 "scripts/genksyms/keywords.gperf" + {"float", FLOAT_KEYW} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l index f770071719cb..e583565f2011 100644 --- a/scripts/genksyms/lex.l +++ b/scripts/genksyms/lex.l @@ -129,8 +129,9 @@ int yylex(void) { static enum { - ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_BRACKET, ST_BRACE, - ST_EXPRESSION, ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4, + ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1, + ST_BRACKET, ST_BRACE, ST_EXPRESSION, + ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4, ST_TABLE_5, ST_TABLE_6 } lexstate = ST_NOTSTARTED; @@ -198,6 +199,10 @@ repeat: lexstate = ST_ASM; count = 0; goto repeat; + case TYPEOF_KEYW: + lexstate = ST_TYPEOF; + count = 0; + goto repeat; case STRUCT_KEYW: case UNION_KEYW: @@ -284,6 +289,48 @@ repeat: } break; + case ST_TYPEOF: + switch (token) + { + case '(': + if ( ++count == 1 ) + lexstate = ST_TYPEOF_1; + else + APP; + goto repeat; + case ')': + APP; + if (--count == 0) + { + lexstate = ST_NORMAL; + token = TYPEOF_PHRASE; + break; + } + goto repeat; + default: + APP; + goto repeat; + } + break; + + case ST_TYPEOF_1: + if (token == IDENT) + { + if (is_reserved_word(yytext, yyleng) + || find_symbol(yytext, SYM_TYPEDEF, 1)) + { + yyless(0); + unput('('); + lexstate = ST_NORMAL; + token = TYPEOF_KEYW; + break; + } + _APP("(", 1); + } + APP; + lexstate = ST_TYPEOF; + goto repeat; + case ST_BRACKET: APP; switch (token) diff --git a/scripts/genksyms/lex.lex.c_shipped b/scripts/genksyms/lex.lex.c_shipped index 0bf4157e6161..f82740a69b85 100644 --- a/scripts/genksyms/lex.lex.c_shipped +++ b/scripts/genksyms/lex.lex.c_shipped @@ -1938,8 +1938,9 @@ int yylex(void) { static enum { - ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_BRACKET, ST_BRACE, - ST_EXPRESSION, ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4, + ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1, + ST_BRACKET, ST_BRACE, ST_EXPRESSION, + ST_TABLE_1, ST_TABLE_2, ST_TABLE_3, ST_TABLE_4, ST_TABLE_5, ST_TABLE_6 } lexstate = ST_NOTSTARTED; @@ -2007,6 +2008,10 @@ repeat: lexstate = ST_ASM; count = 0; goto repeat; + case TYPEOF_KEYW: + lexstate = ST_TYPEOF; + count = 0; + goto repeat; case STRUCT_KEYW: case UNION_KEYW: @@ -2093,6 +2098,48 @@ repeat: } break; + case ST_TYPEOF: + switch (token) + { + case '(': + if ( ++count == 1 ) + lexstate = ST_TYPEOF_1; + else + APP; + goto repeat; + case ')': + APP; + if (--count == 0) + { + lexstate = ST_NORMAL; + token = TYPEOF_PHRASE; + break; + } + goto repeat; + default: + APP; + goto repeat; + } + break; + + case ST_TYPEOF_1: + if (token == IDENT) + { + if (is_reserved_word(yytext, yyleng) + || find_symbol(yytext, SYM_TYPEDEF, 1)) + { + yyless(0); + unput('('); + lexstate = ST_NORMAL; + token = TYPEOF_KEYW; + break; + } + _APP("(", 1); + } + APP; + lexstate = ST_TYPEOF; + goto repeat; + case ST_BRACKET: APP; switch (token) diff --git a/scripts/genksyms/parse.tab.c_shipped b/scripts/genksyms/parse.tab.c_shipped index ece53c79bb59..c9f0f0ce82ff 100644 --- a/scripts/genksyms/parse.tab.c_shipped +++ b/scripts/genksyms/parse.tab.c_shipped @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.5.1. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.5" +#define YYBISON_VERSION "2.5.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -117,6 +117,14 @@ static void record_compound(struct string_list **keyw, +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 1 @@ -171,18 +179,19 @@ static void record_compound(struct string_list **keyw, EXPORT_SYMBOL_KEYW = 284, ASM_PHRASE = 285, ATTRIBUTE_PHRASE = 286, - BRACE_PHRASE = 287, - BRACKET_PHRASE = 288, - EXPRESSION_PHRASE = 289, - CHAR = 290, - DOTS = 291, - IDENT = 292, - INT = 293, - REAL = 294, - STRING = 295, - TYPE = 296, - OTHER = 297, - FILENAME = 298 + TYPEOF_PHRASE = 287, + BRACE_PHRASE = 288, + BRACKET_PHRASE = 289, + EXPRESSION_PHRASE = 290, + CHAR = 291, + DOTS = 292, + IDENT = 293, + INT = 294, + REAL = 295, + STRING = 296, + TYPE = 297, + OTHER = 298, + FILENAME = 299 }; #endif @@ -304,6 +313,7 @@ YYID (yyi) # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif @@ -395,20 +405,20 @@ union yyalloc #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from FROM to TO. The source and destination do +/* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ while (YYID (0)) # endif # endif @@ -417,20 +427,20 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 532 +#define YYLAST 514 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 53 +#define YYNTOKENS 54 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 49 /* YYNRULES -- Number of rules. */ #define YYNRULES 132 /* YYNRULES -- Number of states. */ -#define YYNSTATES 188 +#define YYNSTATES 187 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 298 +#define YYMAXUTOK 299 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -442,15 +452,15 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 47, 49, 48, 2, 46, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 52, 44, - 2, 50, 2, 2, 2, 2, 2, 2, 2, 2, + 48, 49, 50, 2, 47, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 53, 45, + 2, 51, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 51, 2, 45, 2, 2, 2, 2, + 2, 2, 2, 52, 2, 46, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -467,7 +477,7 @@ static const yytype_uint8 yytranslate[] = 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43 + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 }; #if YYDEBUG @@ -478,78 +488,77 @@ static const yytype_uint16 yyprhs[] = 0, 0, 3, 5, 8, 9, 12, 13, 18, 19, 23, 25, 27, 29, 31, 34, 37, 41, 42, 44, 46, 50, 55, 56, 58, 60, 63, 65, 67, 69, - 71, 73, 75, 77, 79, 81, 87, 92, 95, 98, - 101, 105, 109, 113, 116, 119, 122, 124, 126, 128, - 130, 132, 134, 136, 138, 140, 142, 144, 147, 148, - 150, 152, 155, 157, 159, 161, 163, 166, 168, 170, - 175, 180, 183, 187, 191, 194, 196, 198, 200, 205, - 210, 213, 217, 221, 224, 226, 230, 231, 233, 235, - 239, 242, 245, 247, 248, 250, 252, 257, 262, 265, - 269, 273, 277, 278, 280, 283, 287, 291, 292, 294, - 296, 299, 303, 306, 307, 309, 311, 315, 318, 321, - 323, 326, 327, 330, 334, 339, 341, 345, 347, 351, - 354, 355, 357 + 71, 73, 75, 77, 79, 81, 86, 88, 91, 94, + 97, 101, 105, 109, 112, 115, 118, 120, 122, 124, + 126, 128, 130, 132, 134, 136, 138, 140, 143, 144, + 146, 148, 151, 153, 155, 157, 159, 162, 164, 166, + 171, 176, 179, 183, 187, 190, 192, 194, 196, 201, + 206, 209, 213, 217, 220, 222, 226, 227, 229, 231, + 235, 238, 241, 243, 244, 246, 248, 253, 258, 261, + 265, 269, 273, 274, 276, 279, 283, 287, 288, 290, + 292, 295, 299, 302, 303, 305, 307, 311, 314, 317, + 319, 322, 323, 326, 330, 335, 337, 341, 343, 347, + 350, 351, 353 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 54, 0, -1, 55, -1, 54, 55, -1, -1, 56, - 57, -1, -1, 12, 23, 58, 60, -1, -1, 23, - 59, 60, -1, 60, -1, 84, -1, 99, -1, 101, - -1, 1, 44, -1, 1, 45, -1, 64, 61, 44, - -1, -1, 62, -1, 63, -1, 62, 46, 63, -1, - 74, 100, 95, 85, -1, -1, 65, -1, 66, -1, - 65, 66, -1, 67, -1, 68, -1, 5, -1, 17, - -1, 21, -1, 11, -1, 14, -1, 69, -1, 73, - -1, 28, 47, 65, 48, 49, -1, 28, 47, 65, - 49, -1, 22, 37, -1, 24, 37, -1, 10, 37, - -1, 22, 37, 87, -1, 24, 37, 87, -1, 10, - 37, 96, -1, 10, 96, -1, 22, 87, -1, 24, - 87, -1, 7, -1, 19, -1, 15, -1, 16, -1, - 20, -1, 25, -1, 13, -1, 9, -1, 26, -1, - 6, -1, 41, -1, 48, 71, -1, -1, 72, -1, - 73, -1, 72, 73, -1, 8, -1, 27, -1, 31, - -1, 18, -1, 70, 74, -1, 75, -1, 37, -1, - 75, 47, 78, 49, -1, 75, 47, 1, 49, -1, - 75, 33, -1, 47, 74, 49, -1, 47, 1, 49, - -1, 70, 76, -1, 77, -1, 37, -1, 41, -1, - 77, 47, 78, 49, -1, 77, 47, 1, 49, -1, - 77, 33, -1, 47, 76, 49, -1, 47, 1, 49, - -1, 79, 36, -1, 79, -1, 80, 46, 36, -1, - -1, 80, -1, 81, -1, 80, 46, 81, -1, 65, - 82, -1, 70, 82, -1, 83, -1, -1, 37, -1, - 41, -1, 83, 47, 78, 49, -1, 83, 47, 1, - 49, -1, 83, 33, -1, 47, 82, 49, -1, 47, - 1, 49, -1, 64, 74, 32, -1, -1, 86, -1, - 50, 34, -1, 51, 88, 45, -1, 51, 1, 45, - -1, -1, 89, -1, 90, -1, 89, 90, -1, 64, - 91, 44, -1, 1, 44, -1, -1, 92, -1, 93, - -1, 92, 46, 93, -1, 76, 95, -1, 37, 94, - -1, 94, -1, 52, 34, -1, -1, 95, 31, -1, - 51, 97, 45, -1, 51, 97, 46, 45, -1, 98, - -1, 97, 46, 98, -1, 37, -1, 37, 50, 34, - -1, 30, 44, -1, -1, 30, -1, 29, 47, 37, - 49, 44, -1 + 55, 0, -1, 56, -1, 55, 56, -1, -1, 57, + 58, -1, -1, 12, 23, 59, 61, -1, -1, 23, + 60, 61, -1, 61, -1, 85, -1, 100, -1, 102, + -1, 1, 45, -1, 1, 46, -1, 65, 62, 45, + -1, -1, 63, -1, 64, -1, 63, 47, 64, -1, + 75, 101, 96, 86, -1, -1, 66, -1, 67, -1, + 66, 67, -1, 68, -1, 69, -1, 5, -1, 17, + -1, 21, -1, 11, -1, 14, -1, 70, -1, 74, + -1, 28, 48, 82, 49, -1, 32, -1, 22, 38, + -1, 24, 38, -1, 10, 38, -1, 22, 38, 88, + -1, 24, 38, 88, -1, 10, 38, 97, -1, 10, + 97, -1, 22, 88, -1, 24, 88, -1, 7, -1, + 19, -1, 15, -1, 16, -1, 20, -1, 25, -1, + 13, -1, 9, -1, 26, -1, 6, -1, 42, -1, + 50, 72, -1, -1, 73, -1, 74, -1, 73, 74, + -1, 8, -1, 27, -1, 31, -1, 18, -1, 71, + 75, -1, 76, -1, 38, -1, 76, 48, 79, 49, + -1, 76, 48, 1, 49, -1, 76, 34, -1, 48, + 75, 49, -1, 48, 1, 49, -1, 71, 77, -1, + 78, -1, 38, -1, 42, -1, 78, 48, 79, 49, + -1, 78, 48, 1, 49, -1, 78, 34, -1, 48, + 77, 49, -1, 48, 1, 49, -1, 80, 37, -1, + 80, -1, 81, 47, 37, -1, -1, 81, -1, 82, + -1, 81, 47, 82, -1, 66, 83, -1, 71, 83, + -1, 84, -1, -1, 38, -1, 42, -1, 84, 48, + 79, 49, -1, 84, 48, 1, 49, -1, 84, 34, + -1, 48, 83, 49, -1, 48, 1, 49, -1, 65, + 75, 33, -1, -1, 87, -1, 51, 35, -1, 52, + 89, 46, -1, 52, 1, 46, -1, -1, 90, -1, + 91, -1, 90, 91, -1, 65, 92, 45, -1, 1, + 45, -1, -1, 93, -1, 94, -1, 93, 47, 94, + -1, 77, 96, -1, 38, 95, -1, 95, -1, 53, + 35, -1, -1, 96, 31, -1, 52, 98, 46, -1, + 52, 98, 47, 46, -1, 99, -1, 98, 47, 99, + -1, 38, -1, 38, 51, 35, -1, 30, 45, -1, + -1, 30, -1, 29, 48, 38, 49, 45, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 123, 123, 124, 128, 128, 134, 134, 136, 136, - 138, 139, 140, 141, 142, 143, 147, 161, 162, 166, - 174, 187, 193, 194, 198, 199, 203, 209, 213, 214, - 215, 216, 217, 221, 222, 223, 224, 228, 230, 232, - 236, 238, 240, 245, 248, 249, 253, 254, 255, 256, - 257, 258, 259, 260, 261, 262, 263, 267, 272, 273, - 277, 278, 282, 282, 282, 283, 291, 292, 296, 305, - 307, 309, 311, 313, 320, 321, 325, 326, 327, 329, - 331, 333, 335, 340, 341, 342, 346, 347, 351, 352, - 357, 362, 364, 368, 369, 377, 381, 383, 385, 387, - 389, 394, 403, 404, 409, 414, 415, 419, 420, 424, - 425, 429, 431, 436, 437, 441, 442, 446, 447, 448, - 452, 456, 457, 461, 462, 466, 467, 470, 475, 483, - 487, 488, 492 + 0, 124, 124, 125, 129, 129, 135, 135, 137, 137, + 139, 140, 141, 142, 143, 144, 148, 162, 163, 167, + 175, 188, 194, 195, 199, 200, 204, 210, 214, 215, + 216, 217, 218, 222, 223, 224, 225, 229, 231, 233, + 237, 239, 241, 246, 249, 250, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 268, 273, 274, + 278, 279, 283, 283, 283, 284, 292, 293, 297, 306, + 308, 310, 312, 314, 321, 322, 326, 327, 328, 330, + 332, 334, 336, 341, 342, 343, 347, 348, 352, 353, + 358, 363, 365, 369, 370, 378, 382, 384, 386, 388, + 390, 395, 404, 405, 410, 415, 416, 420, 421, 425, + 426, 430, 432, 437, 438, 442, 443, 447, 448, 449, + 453, 457, 458, 462, 463, 467, 468, 471, 476, 484, + 488, 489, 493 }; #endif @@ -565,9 +574,9 @@ static const char *const yytname[] = "SHORT_KEYW", "SIGNED_KEYW", "STATIC_KEYW", "STRUCT_KEYW", "TYPEDEF_KEYW", "UNION_KEYW", "UNSIGNED_KEYW", "VOID_KEYW", "VOLATILE_KEYW", "TYPEOF_KEYW", "EXPORT_SYMBOL_KEYW", "ASM_PHRASE", - "ATTRIBUTE_PHRASE", "BRACE_PHRASE", "BRACKET_PHRASE", + "ATTRIBUTE_PHRASE", "TYPEOF_PHRASE", "BRACE_PHRASE", "BRACKET_PHRASE", "EXPRESSION_PHRASE", "CHAR", "DOTS", "IDENT", "INT", "REAL", "STRING", - "TYPE", "OTHER", "FILENAME", "';'", "'}'", "','", "'('", "'*'", "')'", + "TYPE", "OTHER", "FILENAME", "';'", "'}'", "','", "'('", "')'", "'*'", "'='", "'{'", "':'", "$accept", "declaration_seq", "declaration", "$@1", "declaration1", "$@2", "$@3", "simple_declaration", "init_declarator_list_opt", "init_declarator_list", "init_declarator", @@ -584,7 +593,7 @@ static const char *const yytname[] = "member_declarator_list_opt", "member_declarator_list", "member_declarator", "member_bitfield_declarator", "attribute_opt", "enum_body", "enumerator_list", "enumerator", "asm_definition", - "asm_phrase_opt", "export_definition", 0 + "asm_phrase_opt", "export_definition", YY_NULL }; #endif @@ -597,28 +606,28 @@ static const yytype_uint16 yytoknum[] = 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, - 295, 296, 297, 298, 59, 125, 44, 40, 42, 41, - 61, 123, 58 + 295, 296, 297, 298, 299, 59, 125, 44, 40, 41, + 42, 61, 123, 58 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 53, 54, 54, 56, 55, 58, 57, 59, 57, - 57, 57, 57, 57, 57, 57, 60, 61, 61, 62, - 62, 63, 64, 64, 65, 65, 66, 66, 67, 67, - 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 70, 71, 71, - 72, 72, 73, 73, 73, 73, 74, 74, 75, 75, - 75, 75, 75, 75, 76, 76, 77, 77, 77, 77, - 77, 77, 77, 78, 78, 78, 79, 79, 80, 80, - 81, 82, 82, 83, 83, 83, 83, 83, 83, 83, - 83, 84, 85, 85, 86, 87, 87, 88, 88, 89, - 89, 90, 90, 91, 91, 92, 92, 93, 93, 93, - 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, - 100, 100, 101 + 0, 54, 55, 55, 57, 56, 59, 58, 60, 58, + 58, 58, 58, 58, 58, 58, 61, 62, 62, 63, + 63, 64, 65, 65, 66, 66, 67, 67, 68, 68, + 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 71, 72, 72, + 73, 73, 74, 74, 74, 74, 75, 75, 76, 76, + 76, 76, 76, 76, 77, 77, 78, 78, 78, 78, + 78, 78, 78, 79, 79, 79, 80, 80, 81, 81, + 82, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 86, 86, 87, 88, 88, 89, 89, 90, + 90, 91, 91, 92, 92, 93, 93, 94, 94, 94, + 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, + 101, 101, 102 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -627,7 +636,7 @@ static const yytype_uint8 yyr2[] = 0, 2, 1, 2, 0, 2, 0, 4, 0, 3, 1, 1, 1, 1, 2, 2, 3, 0, 1, 1, 3, 4, 0, 1, 1, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 5, 4, 2, 2, 2, + 1, 1, 1, 1, 1, 4, 1, 2, 2, 2, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 4, @@ -648,68 +657,68 @@ static const yytype_uint8 yydefact[] = 4, 4, 2, 0, 1, 3, 0, 28, 55, 46, 62, 53, 0, 31, 0, 52, 32, 48, 49, 29, 65, 47, 50, 30, 0, 8, 0, 51, 54, 63, - 0, 0, 0, 64, 56, 5, 10, 17, 23, 24, - 26, 27, 33, 34, 11, 12, 13, 14, 15, 39, - 0, 43, 6, 37, 0, 44, 22, 38, 45, 0, - 0, 129, 68, 0, 58, 0, 18, 19, 0, 130, - 67, 25, 42, 127, 0, 125, 22, 40, 0, 113, - 0, 0, 109, 9, 17, 41, 0, 0, 0, 0, - 57, 59, 60, 16, 0, 66, 131, 101, 121, 71, - 0, 0, 123, 0, 7, 112, 106, 76, 77, 0, - 0, 0, 121, 75, 0, 114, 115, 119, 105, 0, - 110, 130, 0, 36, 0, 73, 72, 61, 20, 102, - 0, 93, 0, 84, 87, 88, 128, 124, 126, 118, - 0, 76, 0, 120, 74, 117, 80, 0, 111, 0, - 35, 132, 122, 0, 21, 103, 70, 94, 56, 0, - 93, 90, 92, 69, 83, 0, 82, 81, 0, 0, - 116, 104, 0, 95, 0, 91, 98, 0, 85, 89, - 79, 78, 100, 99, 0, 0, 97, 96 + 0, 0, 0, 64, 36, 56, 5, 10, 17, 23, + 24, 26, 27, 33, 34, 11, 12, 13, 14, 15, + 39, 0, 43, 6, 37, 0, 44, 22, 38, 45, + 0, 0, 129, 68, 0, 58, 0, 18, 19, 0, + 130, 67, 25, 42, 127, 0, 125, 22, 40, 0, + 113, 0, 0, 109, 9, 17, 41, 93, 0, 0, + 0, 0, 57, 59, 60, 16, 0, 66, 131, 101, + 121, 71, 0, 0, 123, 0, 7, 112, 106, 76, + 77, 0, 0, 0, 121, 75, 0, 114, 115, 119, + 105, 0, 110, 130, 94, 56, 0, 93, 90, 92, + 35, 0, 73, 72, 61, 20, 102, 0, 0, 84, + 87, 88, 128, 124, 126, 118, 0, 76, 0, 120, + 74, 117, 80, 0, 111, 0, 0, 95, 0, 91, + 98, 0, 132, 122, 0, 21, 103, 70, 69, 83, + 0, 82, 81, 0, 0, 116, 100, 99, 0, 0, + 104, 85, 89, 79, 78, 97, 96 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 2, 3, 35, 76, 56, 36, 65, 66, - 67, 79, 38, 39, 40, 41, 42, 68, 90, 91, - 43, 121, 70, 112, 113, 132, 133, 134, 135, 161, - 162, 44, 154, 155, 55, 80, 81, 82, 114, 115, - 116, 117, 129, 51, 74, 75, 45, 98, 46 + -1, 1, 2, 3, 36, 77, 57, 37, 66, 67, + 68, 80, 39, 40, 41, 42, 43, 69, 92, 93, + 44, 123, 71, 114, 115, 138, 139, 140, 141, 128, + 129, 45, 165, 166, 56, 81, 82, 83, 116, 117, + 118, 119, 136, 52, 75, 76, 46, 100, 47 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -135 +#define YYPACT_NINF -140 static const yytype_int16 yypact[] = { - -135, 20, -135, 321, -135, -135, 30, -135, -135, -135, - -135, -135, -28, -135, 2, -135, -135, -135, -135, -135, - -135, -135, -135, -135, -6, -135, 9, -135, -135, -135, - -5, 15, -17, -135, -135, -135, -135, 18, 491, -135, - -135, -135, -135, -135, -135, -135, -135, -135, -135, -22, - 31, -135, -135, 19, 106, -135, 491, 19, -135, 491, - 50, -135, -135, 11, -3, 51, 57, -135, 18, -14, - 14, -135, -135, 48, 46, -135, 491, -135, 33, 32, - 59, 154, -135, -135, 18, -135, 365, 56, 60, 61, - -135, -3, -135, -135, 18, -135, -135, -135, -135, -135, - 202, 74, -135, -23, -135, -135, -135, 77, -135, 16, - 101, 49, -135, 34, 92, 93, -135, -135, -135, 94, - -135, 110, 95, -135, 97, -135, -135, -135, -135, -20, - 96, 410, 99, 113, 100, -135, -135, -135, -135, -135, - 103, -135, 107, -135, -135, 111, -135, 239, -135, 32, - -135, -135, -135, 123, -135, -135, -135, -135, -135, 3, - 52, -135, 38, -135, -135, 454, -135, -135, 117, 128, - -135, -135, 134, -135, 135, -135, -135, 276, -135, -135, - -135, -135, -135, -135, 137, 138, -135, -135 + -140, 29, -140, 207, -140, -140, 40, -140, -140, -140, + -140, -140, -27, -140, 44, -140, -140, -140, -140, -140, + -140, -140, -140, -140, -22, -140, -18, -140, -140, -140, + -9, 22, 28, -140, -140, -140, -140, -140, 42, 472, + -140, -140, -140, -140, -140, -140, -140, -140, -140, -140, + 46, 43, -140, -140, 47, 107, -140, 472, 47, -140, + 472, 62, -140, -140, 16, -3, 57, 56, -140, 42, + 35, -11, -140, -140, 53, 48, -140, 472, -140, 51, + 21, 59, 157, -140, -140, 42, -140, 388, 58, 60, + 70, 81, -140, -3, -140, -140, 42, -140, -140, -140, + -140, -140, 253, 71, -140, -20, -140, -140, -140, 83, + -140, 5, 102, 34, -140, 12, 95, 94, -140, -140, + -140, 97, -140, 113, -140, -140, 2, 41, -140, 27, + -140, 99, -140, -140, -140, -140, -24, 98, 101, 109, + 104, -140, -140, -140, -140, -140, 105, -140, 110, -140, + -140, 117, -140, 298, -140, 21, 112, -140, 120, -140, + -140, 343, -140, -140, 121, -140, -140, -140, -140, -140, + 434, -140, -140, 131, 137, -140, -140, -140, 138, 141, + -140, -140, -140, -140, -140, -140, -140 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -135, -135, 187, -135, -135, -135, -135, -50, -135, -135, - 98, 0, -59, -37, -135, -135, -135, -77, -135, -135, - -54, -30, -135, -90, -135, -134, -135, -135, 24, -58, - -135, -135, -135, -135, -18, -135, -135, 109, -135, -135, - 44, 87, 84, 148, -135, 102, -135, -135, -135 + -140, -140, 190, -140, -140, -140, -140, -45, -140, -140, + 96, 1, -60, -31, -140, -140, -140, -78, -140, -140, + -55, -7, -140, -92, -140, -139, -140, -140, -59, -39, + -140, -140, -140, -140, -13, -140, -140, 111, -140, -140, + 39, 87, 84, 147, -140, 106, -140, -140, -140 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If @@ -718,149 +727,145 @@ static const yytype_int16 yypgoto[] = #define YYTABLE_NINF -109 static const yytype_int16 yytable[] = { - 86, 71, 111, 37, 172, 10, 83, 69, 58, 49, - 92, 152, 88, 169, 73, 20, 96, 140, 97, 142, - 4, 144, 137, 50, 29, 52, 104, 61, 33, 50, - 153, 53, 111, 89, 111, 77, -93, 127, 95, 85, - 157, 131, 59, 185, 173, 54, 57, 99, 62, 71, - 159, 64, -93, 141, 160, 62, 84, 108, 63, 64, - 54, 100, 60, 109, 64, 63, 64, 146, 73, 107, - 54, 176, 111, 108, 47, 48, 84, 105, 106, 109, - 64, 147, 160, 160, 110, 177, 141, 87, 131, 157, - 108, 102, 103, 173, 71, 93, 109, 64, 101, 159, - 64, 174, 175, 94, 118, 124, 131, 78, 136, 125, - 126, 7, 8, 9, 10, 11, 12, 13, 131, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 110, - 26, 27, 28, 29, 30, 143, 148, 33, 105, 149, - 96, 151, 152, -22, 150, 156, 165, 34, 163, 164, - -22, -107, 166, -22, -22, 119, 167, 171, -22, 7, - 8, 9, 10, 11, 12, 13, 180, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 181, 26, 27, - 28, 29, 30, 182, 183, 33, 186, 187, 5, 179, - 120, -22, 128, 170, 139, 34, 145, 72, -22, -108, - 0, -22, -22, 130, 0, 138, -22, 7, 8, 9, - 10, 11, 12, 13, 0, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 0, 26, 27, 28, 29, - 30, 0, 0, 33, 0, 0, 0, 0, -86, 0, - 168, 0, 0, 34, 7, 8, 9, 10, 11, 12, - 13, -86, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 0, 26, 27, 28, 29, 30, 0, 0, - 33, 0, 0, 0, 0, -86, 0, 184, 0, 0, - 34, 7, 8, 9, 10, 11, 12, 13, -86, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, - 26, 27, 28, 29, 30, 0, 0, 33, 0, 0, - 0, 0, -86, 0, 0, 0, 0, 34, 0, 0, - 0, 0, 6, 0, 0, -86, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 0, 0, 0, 0, 0, -22, 0, - 0, 0, 34, 0, 0, -22, 0, 0, -22, -22, - 7, 8, 9, 10, 11, 12, 13, 0, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 0, 26, - 27, 28, 29, 30, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, - 0, 0, 0, 122, 123, 7, 8, 9, 10, 11, - 12, 13, 0, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 0, 26, 27, 28, 29, 30, 0, - 0, 33, 0, 0, 0, 0, 0, 157, 0, 0, - 0, 158, 0, 0, 0, 0, 0, 159, 64, 7, + 87, 88, 113, 156, 38, 10, 146, 163, 72, 127, + 94, 50, 84, 59, 174, 20, 54, 90, 74, 148, + 58, 150, 179, 101, 29, 51, 143, 164, 33, 4, + 55, 70, 106, 113, 55, 113, -93, 102, 134, 60, + 124, 78, 87, 147, 157, 86, 152, 110, 127, 127, + 126, -93, 65, 111, 63, 65, 72, 91, 85, 109, + 153, 160, 97, 110, 64, 98, 65, 53, 99, 111, + 61, 65, 147, 62, 112, 161, 110, 113, 85, 124, + 63, 74, 111, 157, 65, 48, 49, 158, 159, 126, + 64, 65, 65, 87, 104, 105, 107, 108, 51, 55, + 89, 87, 95, 96, 103, 120, 142, 130, 79, 131, + 87, 182, 7, 8, 9, 10, 11, 12, 13, 132, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 133, 26, 27, 28, 29, 30, 112, 149, 33, 34, + 154, 155, 107, 98, 162, -22, 169, 167, 163, 35, + 168, 170, -22, -107, 171, -22, 180, -22, 121, 172, + -22, 176, 7, 8, 9, 10, 11, 12, 13, 177, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 183, 26, 27, 28, 29, 30, 184, 185, 33, 34, + 186, 5, 135, 122, 175, -22, 145, 73, 151, 35, + 0, 0, -22, -108, 0, -22, 0, -22, 6, 0, + -22, 144, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 0, 0, 0, 0, 0, -22, 0, 0, 0, 35, + 0, 0, -22, 0, 137, -22, 0, -22, 7, 8, + 9, 10, 11, 12, 13, 0, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 0, 26, 27, 28, + 29, 30, 0, 0, 33, 34, 0, 0, 0, 0, + -86, 0, 0, 0, 0, 35, 0, 0, 0, 173, + 0, 0, -86, 7, 8, 9, 10, 11, 12, 13, + 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 0, 26, 27, 28, 29, 30, 0, 0, 33, + 34, 0, 0, 0, 0, -86, 0, 0, 0, 0, + 35, 0, 0, 0, 178, 0, 0, -86, 7, 8, + 9, 10, 11, 12, 13, 0, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 0, 26, 27, 28, + 29, 30, 0, 0, 33, 34, 0, 0, 0, 0, + -86, 0, 0, 0, 0, 35, 0, 0, 0, 0, + 0, 0, -86, 7, 8, 9, 10, 11, 12, 13, + 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 0, 26, 27, 28, 29, 30, 0, 0, 33, + 34, 0, 0, 0, 0, 0, 124, 0, 0, 0, + 125, 0, 0, 0, 0, 0, 126, 0, 65, 7, 8, 9, 10, 11, 12, 13, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 26, 27, - 28, 29, 30, 0, 0, 33, 0, 0, 0, 0, - 178, 0, 0, 0, 0, 34, 7, 8, 9, 10, - 11, 12, 13, 0, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 0, 26, 27, 28, 29, 30, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 34 + 28, 29, 30, 0, 0, 33, 34, 0, 0, 0, + 0, 181, 0, 0, 0, 0, 35, 7, 8, 9, + 10, 11, 12, 13, 0, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 0, 26, 27, 28, 29, + 30, 0, 0, 33, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35 }; #define yypact_value_is_default(yystate) \ - ((yystate) == (-135)) + ((yystate) == (-140)) #define yytable_value_is_error(yytable_value) \ YYID (0) static const yytype_int16 yycheck[] = { - 59, 38, 79, 3, 1, 8, 56, 37, 26, 37, - 64, 31, 1, 147, 37, 18, 30, 1, 32, 109, - 0, 111, 45, 51, 27, 23, 76, 44, 31, 51, - 50, 37, 109, 63, 111, 53, 33, 91, 68, 57, - 37, 100, 47, 177, 41, 51, 37, 33, 37, 86, - 47, 48, 49, 37, 131, 37, 56, 41, 47, 48, - 51, 47, 47, 47, 48, 47, 48, 33, 37, 37, - 51, 33, 149, 41, 44, 45, 76, 44, 45, 47, - 48, 47, 159, 160, 52, 47, 37, 37, 147, 37, - 41, 45, 46, 41, 131, 44, 47, 48, 50, 47, - 48, 159, 160, 46, 45, 49, 165, 1, 34, 49, - 49, 5, 6, 7, 8, 9, 10, 11, 177, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 52, - 24, 25, 26, 27, 28, 34, 44, 31, 44, 46, - 30, 44, 31, 37, 49, 49, 46, 41, 49, 36, - 44, 45, 49, 47, 48, 1, 49, 34, 52, 5, - 6, 7, 8, 9, 10, 11, 49, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 49, 24, 25, - 26, 27, 28, 49, 49, 31, 49, 49, 1, 165, - 81, 37, 94, 149, 107, 41, 112, 49, 44, 45, - -1, 47, 48, 1, -1, 103, 52, 5, 6, 7, - 8, 9, 10, 11, -1, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, -1, 24, 25, 26, 27, - 28, -1, -1, 31, -1, -1, -1, -1, 36, -1, - 1, -1, -1, 41, 5, 6, 7, 8, 9, 10, - 11, 49, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, -1, 24, 25, 26, 27, 28, -1, -1, - 31, -1, -1, -1, -1, 36, -1, 1, -1, -1, - 41, 5, 6, 7, 8, 9, 10, 11, 49, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, -1, - 24, 25, 26, 27, 28, -1, -1, 31, -1, -1, - -1, -1, 36, -1, -1, -1, -1, 41, -1, -1, - -1, -1, 1, -1, -1, 49, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, -1, -1, -1, -1, -1, 37, -1, - -1, -1, 41, -1, -1, 44, -1, -1, 47, 48, - 5, 6, 7, 8, 9, 10, 11, -1, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, -1, 24, - 25, 26, 27, 28, -1, -1, 31, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 41, -1, -1, -1, - -1, -1, -1, 48, 49, 5, 6, 7, 8, 9, - 10, 11, -1, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, -1, 24, 25, 26, 27, 28, -1, - -1, 31, -1, -1, -1, -1, -1, 37, -1, -1, - -1, 41, -1, -1, -1, -1, -1, 47, 48, 5, + 60, 60, 80, 1, 3, 8, 1, 31, 39, 87, + 65, 38, 57, 26, 153, 18, 38, 1, 38, 111, + 38, 113, 161, 34, 27, 52, 46, 51, 31, 0, + 52, 38, 77, 111, 52, 113, 34, 48, 93, 48, + 38, 54, 102, 38, 42, 58, 34, 42, 126, 127, + 48, 49, 50, 48, 38, 50, 87, 64, 57, 38, + 48, 34, 69, 42, 48, 30, 50, 23, 33, 48, + 48, 50, 38, 45, 53, 48, 42, 155, 77, 38, + 38, 38, 48, 42, 50, 45, 46, 126, 127, 48, + 48, 50, 50, 153, 46, 47, 45, 46, 52, 52, + 38, 161, 45, 47, 51, 46, 35, 49, 1, 49, + 170, 170, 5, 6, 7, 8, 9, 10, 11, 49, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 49, 24, 25, 26, 27, 28, 53, 35, 31, 32, + 45, 47, 45, 30, 45, 38, 37, 49, 31, 42, + 49, 47, 45, 46, 49, 48, 35, 50, 1, 49, + 53, 49, 5, 6, 7, 8, 9, 10, 11, 49, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 49, 24, 25, 26, 27, 28, 49, 49, 31, 32, + 49, 1, 96, 82, 155, 38, 109, 50, 114, 42, + -1, -1, 45, 46, -1, 48, -1, 50, 1, -1, + 53, 105, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + -1, -1, -1, -1, -1, 38, -1, -1, -1, 42, + -1, -1, 45, -1, 1, 48, -1, 50, 5, 6, + 7, 8, 9, 10, 11, -1, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, -1, 24, 25, 26, + 27, 28, -1, -1, 31, 32, -1, -1, -1, -1, + 37, -1, -1, -1, -1, 42, -1, -1, -1, 1, + -1, -1, 49, 5, 6, 7, 8, 9, 10, 11, + -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, -1, 24, 25, 26, 27, 28, -1, -1, 31, + 32, -1, -1, -1, -1, 37, -1, -1, -1, -1, + 42, -1, -1, -1, 1, -1, -1, 49, 5, 6, + 7, 8, 9, 10, 11, -1, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, -1, 24, 25, 26, + 27, 28, -1, -1, 31, 32, -1, -1, -1, -1, + 37, -1, -1, -1, -1, 42, -1, -1, -1, -1, + -1, -1, 49, 5, 6, 7, 8, 9, 10, 11, + -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, -1, 24, 25, 26, 27, 28, -1, -1, 31, + 32, -1, -1, -1, -1, -1, 38, -1, -1, -1, + 42, -1, -1, -1, -1, -1, 48, -1, 50, 5, 6, 7, 8, 9, 10, 11, -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, -1, 24, 25, - 26, 27, 28, -1, -1, 31, -1, -1, -1, -1, - 36, -1, -1, -1, -1, 41, 5, 6, 7, 8, - 9, 10, 11, -1, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, -1, 24, 25, 26, 27, 28, - -1, -1, 31, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 41 + 26, 27, 28, -1, -1, 31, 32, -1, -1, -1, + -1, 37, -1, -1, -1, -1, 42, 5, 6, 7, + 8, 9, 10, 11, -1, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, -1, 24, 25, 26, 27, + 28, -1, -1, 31, 32, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 42 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 54, 55, 56, 0, 55, 1, 5, 6, 7, + 0, 55, 56, 57, 0, 56, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 41, 57, 60, 64, 65, 66, - 67, 68, 69, 73, 84, 99, 101, 44, 45, 37, - 51, 96, 23, 37, 51, 87, 59, 37, 87, 47, - 47, 44, 37, 47, 48, 61, 62, 63, 70, 74, - 75, 66, 96, 37, 97, 98, 58, 87, 1, 64, - 88, 89, 90, 60, 64, 87, 65, 37, 1, 74, - 71, 72, 73, 44, 46, 74, 30, 32, 100, 33, - 47, 50, 45, 46, 60, 44, 45, 37, 41, 47, - 52, 70, 76, 77, 91, 92, 93, 94, 45, 1, - 90, 74, 48, 49, 49, 49, 49, 73, 63, 95, - 1, 65, 78, 79, 80, 81, 34, 45, 98, 94, - 1, 37, 76, 34, 76, 95, 33, 47, 44, 46, - 49, 44, 31, 50, 85, 86, 49, 37, 41, 47, - 70, 82, 83, 49, 36, 46, 49, 49, 1, 78, - 93, 34, 1, 41, 82, 82, 33, 47, 36, 81, - 49, 49, 49, 49, 1, 78, 49, 49 + 28, 29, 30, 31, 32, 42, 58, 61, 65, 66, + 67, 68, 69, 70, 74, 85, 100, 102, 45, 46, + 38, 52, 97, 23, 38, 52, 88, 60, 38, 88, + 48, 48, 45, 38, 48, 50, 62, 63, 64, 71, + 75, 76, 67, 97, 38, 98, 99, 59, 88, 1, + 65, 89, 90, 91, 61, 65, 88, 66, 82, 38, + 1, 75, 72, 73, 74, 45, 47, 75, 30, 33, + 101, 34, 48, 51, 46, 47, 61, 45, 46, 38, + 42, 48, 53, 71, 77, 78, 92, 93, 94, 95, + 46, 1, 91, 75, 38, 42, 48, 71, 83, 84, + 49, 49, 49, 49, 74, 64, 96, 1, 79, 80, + 81, 82, 35, 46, 99, 95, 1, 38, 77, 35, + 77, 96, 34, 48, 45, 47, 1, 42, 83, 83, + 34, 48, 45, 31, 51, 86, 87, 49, 49, 37, + 47, 49, 49, 1, 79, 94, 49, 49, 1, 79, + 35, 37, 82, 49, 49, 49, 49 }; #define yyerrok (yyerrstatus = 0) @@ -890,17 +895,18 @@ static const yytype_uint8 yystos[] = #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ @@ -995,6 +1001,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) YYSTYPE const * const yyvaluep; #endif { + FILE *yyo = yyoutput; + YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT @@ -1246,12 +1254,12 @@ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ - const char *yyformat = 0; + const char *yyformat = YY_NULL; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per @@ -1311,7 +1319,7 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, break; } yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; @@ -1463,7 +1471,7 @@ yyparse () `yyss': related to states. `yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow + Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ @@ -2346,7 +2354,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ diff --git a/scripts/genksyms/parse.tab.h_shipped b/scripts/genksyms/parse.tab.h_shipped index 93240a3cdecc..a4737dec4532 100644 --- a/scripts/genksyms/parse.tab.h_shipped +++ b/scripts/genksyms/parse.tab.h_shipped @@ -1,8 +1,8 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.5.1. */ /* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -66,18 +66,19 @@ EXPORT_SYMBOL_KEYW = 284, ASM_PHRASE = 285, ATTRIBUTE_PHRASE = 286, - BRACE_PHRASE = 287, - BRACKET_PHRASE = 288, - EXPRESSION_PHRASE = 289, - CHAR = 290, - DOTS = 291, - IDENT = 292, - INT = 293, - REAL = 294, - STRING = 295, - TYPE = 296, - OTHER = 297, - FILENAME = 298 + TYPEOF_PHRASE = 287, + BRACE_PHRASE = 288, + BRACKET_PHRASE = 289, + EXPRESSION_PHRASE = 290, + CHAR = 291, + DOTS = 292, + IDENT = 293, + INT = 294, + REAL = 295, + STRING = 296, + TYPE = 297, + OTHER = 298, + FILENAME = 299 }; #endif diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y index 23c39998ad86..b9f4cf202302 100644 --- a/scripts/genksyms/parse.y +++ b/scripts/genksyms/parse.y @@ -103,6 +103,7 @@ static void record_compound(struct string_list **keyw, %token ASM_PHRASE %token ATTRIBUTE_PHRASE +%token TYPEOF_PHRASE %token BRACE_PHRASE %token BRACKET_PHRASE %token EXPRESSION_PHRASE @@ -220,8 +221,8 @@ storage_class_specifier: type_specifier: simple_type_specifier | cvar_qualifier - | TYPEOF_KEYW '(' decl_specifier_seq '*' ')' - | TYPEOF_KEYW '(' decl_specifier_seq ')' + | TYPEOF_KEYW '(' parameter_declaration ')' + | TYPEOF_PHRASE /* References to s/u/e's defined elsewhere. Rearrange things so that it is easier to expand the definition fully later. */ |