summaryrefslogtreecommitdiff
path: root/packages/linux
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2008-12-07 18:29:41 +0100
committerKoen Kooi <koen@openembedded.org>2008-12-07 18:29:41 +0100
commit0b13ccb9a40b8184f5b5653ffad71af81593ef92 (patch)
treee5e30d461504ee046ffbfb93cfb303ef807b6a3f /packages/linux
parent978a924e444517061b79c9349b157f80d403647a (diff)
linux-omap 2.6.27: add openvz patch and enable it in the defconfig
* see http://openvz.linuxguru.co.uk/openvz-is-running-on-an-arm-gumstix-overo/39/
Diffstat (limited to 'packages/linux')
-rw-r--r--packages/linux/linux-omap-2.6.27/beagleboard/defconfig51
-rw-r--r--packages/linux/linux-omap-2.6.27/omap3evm/defconfig56
-rw-r--r--packages/linux/linux-omap-2.6.27/openvz/0001-arm-introduce-MAP_EXECPRIO-define.patch25
-rw-r--r--packages/linux/linux-omap-2.6.27/openvz/0002-arm-export-arm-version-of-show_mem.patch35
-rw-r--r--packages/linux/linux-omap-2.6.27/openvz/0003-arm-wire-OpenVZ-syscalls.patch45
-rw-r--r--packages/linux/linux-omap-2.6.27/openvz/0004-arm-add-openvz-and-bc-Kconfigs.patch25
-rw-r--r--packages/linux/linux-omap-2.6.27/openvz/openvz-2.6.27.diff82318
-rw-r--r--packages/linux/linux-omap_2.6.27.bb7
8 files changed, 82546 insertions, 16 deletions
diff --git a/packages/linux/linux-omap-2.6.27/beagleboard/defconfig b/packages/linux/linux-omap-2.6.27/beagleboard/defconfig
index 1344060446..ed2acaafff 100644
--- a/packages/linux/linux-omap-2.6.27/beagleboard/defconfig
+++ b/packages/linux/linux-omap-2.6.27/beagleboard/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.27-omap1
-# Sun Oct 26 10:27:59 2008
+# Sun Dec 7 16:18:46 2008
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -25,6 +25,7 @@ CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_SUPPORTS_AOUT=y
CONFIG_ZONE_DMA=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_OPROFILE_ARMV7=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
@@ -47,16 +48,25 @@ CONFIG_BSD_PROCESS_ACCT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_CGROUPS is not set
-CONFIG_GROUP_SCHED=y
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_DEVICE=y
+CONFIG_GROUP_SCHED=n
CONFIG_FAIR_GROUP_SCHED=y
# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_USER_SCHED=y
+# CONFIG_USER_SCHED is not set
# CONFIG_CGROUP_SCHED is not set
+CONFIG_VZ_FAIRSCHED=y
+# CONFIG_CGROUP_CPUACCT is not set
+# CONFIG_RESOURCE_COUNTERS is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
-# CONFIG_NAMESPACES is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
@@ -353,6 +363,7 @@ CONFIG_NET=y
#
# Networking options
#
+CONFIG_NET_NS=y
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
@@ -1687,8 +1698,13 @@ CONFIG_INOTIFY_USER=y
CONFIG_QUOTA=y
# CONFIG_QUOTA_NETLINK_INTERFACE is not set
CONFIG_PRINT_QUOTA_WARNING=y
+CONFIG_QUOTA_COMPAT=y
# CONFIG_QFMT_V1 is not set
CONFIG_QFMT_V2=y
+CONFIG_SIM_FS=m
+CONFIG_VZ_QUOTA=m
+# CONFIG_VZ_QUOTA_UNLOAD is not set
+CONFIG_VZ_QUOTA_UGID=y
CONFIG_QUOTACTL=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
@@ -1854,6 +1870,7 @@ CONFIG_MAGIC_SYSRQ=y
# CONFIG_UNUSED_SYMBOLS is not set
# CONFIG_DEBUG_FS is not set
# CONFIG_HEADERS_CHECK is not set
+CONFIG_SYSRQ_DEBUG=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
@@ -1905,7 +1922,6 @@ CONFIG_HAVE_ARCH_KGDB=y
# Security options
#
# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_XOR_BLOCKS=m
CONFIG_ASYNC_CORE=m
@@ -2016,3 +2032,26 @@ CONFIG_PLIST=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
+
+#
+# OpenVZ
+#
+CONFIG_VE=y
+CONFIG_VE_CALLS=m
+CONFIG_VZ_GENCALLS=y
+CONFIG_VE_NETDEV=m
+CONFIG_VE_ETHDEV=m
+CONFIG_VZ_DEV=m
+CONFIG_VZ_WDOG=m
+CONFIG_VZ_CHECKPOINT=n
+
+#
+# User resources
+#
+CONFIG_BEANCOUNTERS=y
+CONFIG_BC_RSS_ACCOUNTING=y
+CONFIG_BC_IO_ACCOUNTING=y
+CONFIG_BC_IO_SCHED=y
+CONFIG_BC_SWAP_ACCOUNTING=y
+CONFIG_BC_PROC=y
+# CONFIG_BC_DEBUG is not set
diff --git a/packages/linux/linux-omap-2.6.27/omap3evm/defconfig b/packages/linux/linux-omap-2.6.27/omap3evm/defconfig
index f4431728cd..64597db3d2 100644
--- a/packages/linux/linux-omap-2.6.27/omap3evm/defconfig
+++ b/packages/linux/linux-omap-2.6.27/omap3evm/defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.27-omap1
-# Sun Oct 26 14:48:42 2008
+# Sun Dec 7 18:11:56 2008
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -25,6 +25,7 @@ CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_SUPPORTS_AOUT=y
CONFIG_ZONE_DMA=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_OPROFILE_ARMV7=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
@@ -48,16 +49,25 @@ CONFIG_BSD_PROCESS_ACCT=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_CGROUPS is not set
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_DEVICE=y
CONFIG_GROUP_SCHED=y
CONFIG_FAIR_GROUP_SCHED=y
# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_USER_SCHED=y
+# CONFIG_USER_SCHED is not set
# CONFIG_CGROUP_SCHED is not set
+CONFIG_VZ_FAIRSCHED=y
+# CONFIG_CGROUP_CPUACCT is not set
+# CONFIG_RESOURCE_COUNTERS is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
-# CONFIG_NAMESPACES is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
@@ -82,8 +92,9 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
# CONFIG_SLOB is not set
CONFIG_PROFILING=y
# CONFIG_MARKERS is not set
@@ -100,7 +111,6 @@ CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_CLK=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
-CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
@@ -354,6 +364,7 @@ CONFIG_NET=y
#
# Networking options
#
+CONFIG_NET_NS=y
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
@@ -1738,8 +1749,13 @@ CONFIG_INOTIFY_USER=y
CONFIG_QUOTA=y
# CONFIG_QUOTA_NETLINK_INTERFACE is not set
CONFIG_PRINT_QUOTA_WARNING=y
+CONFIG_QUOTA_COMPAT=y
# CONFIG_QFMT_V1 is not set
CONFIG_QFMT_V2=y
+CONFIG_SIM_FS=m
+CONFIG_VZ_QUOTA=m
+# CONFIG_VZ_QUOTA_UNLOAD is not set
+CONFIG_VZ_QUOTA_UGID=y
CONFIG_QUOTACTL=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
@@ -1907,6 +1923,7 @@ CONFIG_MAGIC_SYSRQ=y
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
+CONFIG_SYSRQ_DEBUG=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
@@ -1916,7 +1933,6 @@ CONFIG_SCHED_DEBUG=y
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_DEBUG_SLAB is not set
CONFIG_DEBUG_PREEMPT=y
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
@@ -1964,7 +1980,6 @@ CONFIG_HAVE_ARCH_KGDB=y
# Security options
#
# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_XOR_BLOCKS=m
CONFIG_ASYNC_CORE=m
@@ -2075,3 +2090,26 @@ CONFIG_PLIST=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
+
+#
+# OpenVZ
+#
+CONFIG_VE=y
+CONFIG_VE_CALLS=m
+CONFIG_VZ_GENCALLS=y
+CONFIG_VE_NETDEV=m
+CONFIG_VE_ETHDEV=m
+CONFIG_VZ_DEV=m
+CONFIG_VZ_WDOG=m
+# CONFIG_VZ_CHECKPOINT is not set
+
+#
+# User resources
+#
+CONFIG_BEANCOUNTERS=y
+CONFIG_BC_RSS_ACCOUNTING=y
+CONFIG_BC_IO_ACCOUNTING=y
+CONFIG_BC_IO_SCHED=y
+CONFIG_BC_SWAP_ACCOUNTING=y
+CONFIG_BC_PROC=y
+# CONFIG_BC_DEBUG is not set
diff --git a/packages/linux/linux-omap-2.6.27/openvz/0001-arm-introduce-MAP_EXECPRIO-define.patch b/packages/linux/linux-omap-2.6.27/openvz/0001-arm-introduce-MAP_EXECPRIO-define.patch
new file mode 100644
index 0000000000..773f04ee89
--- /dev/null
+++ b/packages/linux/linux-omap-2.6.27/openvz/0001-arm-introduce-MAP_EXECPRIO-define.patch
@@ -0,0 +1,25 @@
+From da5282caf3967d200781969350b0fdd5366662db Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kir@openvz.org>
+Date: Fri, 24 Oct 2008 18:00:26 +0400
+Subject: [PATCH] arm: introduce MAP_EXECPRIO define
+
+Signed-off-by: Kir Kolyshkin <kir@openvz.org>
+---
+ arch/arm/include/asm/mman.h | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/include/asm/mman.h b/arch/arm/include/asm/mman.h
+index 54570d2..10b4b4d 100644
+--- a/arch/arm/include/asm/mman.h
++++ b/arch/arm/include/asm/mman.h
+@@ -10,6 +10,7 @@
+ #define MAP_NORESERVE 0x4000 /* don't check for reservations */
+ #define MAP_POPULATE 0x8000 /* populate (prefault) page tables */
+ #define MAP_NONBLOCK 0x10000 /* do not block on IO */
++#define MAP_EXECPRIO 0x40000 /* soft ubc charge */
+
+ #define MCL_CURRENT 1 /* lock all current mappings */
+ #define MCL_FUTURE 2 /* lock all future mappings */
+--
+1.5.5.1
+
diff --git a/packages/linux/linux-omap-2.6.27/openvz/0002-arm-export-arm-version-of-show_mem.patch b/packages/linux/linux-omap-2.6.27/openvz/0002-arm-export-arm-version-of-show_mem.patch
new file mode 100644
index 0000000000..cf0e4222f8
--- /dev/null
+++ b/packages/linux/linux-omap-2.6.27/openvz/0002-arm-export-arm-version-of-show_mem.patch
@@ -0,0 +1,35 @@
+From e7b3c23fd50bf2d377384af721b8a6077ea4400a Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kir@openvz.org>
+Date: Fri, 24 Oct 2008 18:01:40 +0400
+Subject: [PATCH] arm: export arm version of show_mem()
+
+Needed for vzwdog module.
+
+Signed-off-by: Kir Kolyshkin <kir@openvz.org>
+---
+ arch/arm/mm/init.c | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
+index 30a69d6..c9332cc 100644
+--- a/arch/arm/mm/init.c
++++ b/arch/arm/mm/init.c
+@@ -15,6 +15,7 @@
+ #include <linux/mman.h>
+ #include <linux/nodemask.h>
+ #include <linux/initrd.h>
++#include <linux/module.h>
+
+ #include <asm/mach-types.h>
+ #include <asm/setup.h>
+@@ -86,6 +87,7 @@ void show_mem(void)
+ printk("%d pages shared\n", shared);
+ printk("%d pages swap cached\n", cached);
+ }
++EXPORT_SYMBOL_GPL(show_mem);
+
+ /*
+ * FIXME: We really want to avoid allocating the bootmap bitmap
+--
+1.5.5.1
+
diff --git a/packages/linux/linux-omap-2.6.27/openvz/0003-arm-wire-OpenVZ-syscalls.patch b/packages/linux/linux-omap-2.6.27/openvz/0003-arm-wire-OpenVZ-syscalls.patch
new file mode 100644
index 0000000000..b6dbd1cc81
--- /dev/null
+++ b/packages/linux/linux-omap-2.6.27/openvz/0003-arm-wire-OpenVZ-syscalls.patch
@@ -0,0 +1,45 @@
+From ce4ad508dc8ec3f8ee8cc604ce2b36dec641cdd0 Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kir@openvz.org>
+Date: Fri, 24 Oct 2008 18:03:54 +0400
+Subject: [PATCH] arm: wire OpenVZ syscalls
+
+Leave some safety gap for future syscall expansion; hope it's enough.
+
+Signed-off-by: Kir Kolyshkin <kir@openvz.org>
+---
+ arch/arm/kernel/calls.S | 19 +++++++++++++++++++
+ 1 files changed, 19 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
+index 09a061c..dd1c00b 100644
+--- a/arch/arm/kernel/calls.S
++++ b/arch/arm/kernel/calls.S
+@@ -370,6 +370,25 @@
+ CALL(sys_dup3)
+ CALL(sys_pipe2)
+ /* 360 */ CALL(sys_inotify_init1)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++/* 365 */ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++ CALL(sys_ni_syscall)
++/* 370 */ CALL(sys_fairsched_vcpus) /* OpenVZ */
++ CALL(sys_fairsched_mknod)
++ CALL(sys_fairsched_rmnod)
++ CALL(sys_fairsched_chwt)
++ CALL(sys_fairsched_mvpr)
++/* 375 */ CALL(sys_fairsched_rate)
++ CALL(sys_getluid)
++ CALL(sys_setluid)
++ CALL(sys_setublimit)
++ CALL(sys_ubstat)
+ #ifndef syscalls_counted
+ .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
+ #define syscalls_counted
+--
+1.5.5.1
+
diff --git a/packages/linux/linux-omap-2.6.27/openvz/0004-arm-add-openvz-and-bc-Kconfigs.patch b/packages/linux/linux-omap-2.6.27/openvz/0004-arm-add-openvz-and-bc-Kconfigs.patch
new file mode 100644
index 0000000000..a2cdee3433
--- /dev/null
+++ b/packages/linux/linux-omap-2.6.27/openvz/0004-arm-add-openvz-and-bc-Kconfigs.patch
@@ -0,0 +1,25 @@
+From a63021d3d909d30e7683193b4076e223b5acaa9b Mon Sep 17 00:00:00 2001
+From: Kir Kolyshkin <kir@openvz.org>
+Date: Fri, 24 Oct 2008 18:05:47 +0400
+Subject: [PATCH] arm: add openvz and bc Kconfigs
+
+Signed-off-by: Kir Kolyshkin <kir@openvz.org>
+---
+ arch/arm/Kconfig | 4 ++++
+ 1 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index 70dba16..e354013 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1245,3 +1245,7 @@ source "security/Kconfig"
+ source "crypto/Kconfig"
+
+ source "lib/Kconfig"
++
++source "kernel/Kconfig.openvz"
++
++source "kernel/bc/Kconfig"
+--
+1.5.5.1
+
diff --git a/packages/linux/linux-omap-2.6.27/openvz/openvz-2.6.27.diff b/packages/linux/linux-omap-2.6.27/openvz/openvz-2.6.27.diff
new file mode 100644
index 0000000000..a24aab9a03
--- /dev/null
+++ b/packages/linux/linux-omap-2.6.27/openvz/openvz-2.6.27.diff
@@ -0,0 +1,82318 @@
+diff --git a/COPYING.SWsoft b/COPYING.SWsoft
+new file mode 100644
+index 0000000..059256d
+--- /dev/null
++++ b/COPYING.SWsoft
+@@ -0,0 +1,350 @@
++
++Nothing in this license should be construed as a grant by SWsoft of any rights
++beyond the rights specified in the GNU General Public License, and nothing in
++this license should be construed as a waiver by SWsoft of its patent, copyright
++and/or trademark rights, beyond the waiver required by the GNU General Public
++License. This license is expressly inapplicable to any product that is not
++within the scope of the GNU General Public License
++
++----------------------------------------
++
++ GNU GENERAL PUBLIC LICENSE
++ Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++ Preamble
++
++ The licenses for most software are designed to take away your
++freedom to share and change it. By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users. This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it. (Some other Free Software Foundation software is covered by
++the GNU Library General Public License instead.) You can apply it to
++your programs, too.
++
++ When we speak of free software, we are referring to freedom, not
++price. Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++ To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++ For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have. You must make sure that they, too, receive or can get the
++source code. And you must show them these terms so they know their
++rights.
++
++ We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++ Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software. If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++ Finally, any free program is threatened constantly by software
++patents. We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary. To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++ The precise terms and conditions for copying, distribution and
++modification follow.
++
++ GNU GENERAL PUBLIC LICENSE
++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++ 0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License. The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language. (Hereinafter, translation is included without limitation in
++the term "modification".) Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope. The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++ 1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++ 2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++ a) You must cause the modified files to carry prominent notices
++ stating that you changed the files and the date of any change.
++
++ b) You must cause any work that you distribute or publish, that in
++ whole or in part contains or is derived from the Program or any
++ part thereof, to be licensed as a whole at no charge to all third
++ parties under the terms of this License.
++
++ c) If the modified program normally reads commands interactively
++ when run, you must cause it, when started running for such
++ interactive use in the most ordinary way, to print or display an
++ announcement including an appropriate copyright notice and a
++ notice that there is no warranty (or else, saying that you provide
++ a warranty) and that users may redistribute the program under
++ these conditions, and telling the user how to view a copy of this
++ License. (Exception: if the Program itself is interactive but
++ does not normally print such an announcement, your work based on
++ the Program is not required to print an announcement.)
++
++These requirements apply to the modified work as a whole. If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works. But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++ 3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++ a) Accompany it with the complete corresponding machine-readable
++ source code, which must be distributed under the terms of Sections
++ 1 and 2 above on a medium customarily used for software interchange; or,
++
++ b) Accompany it with a written offer, valid for at least three
++ years, to give any third party, for a charge no more than your
++ cost of physically performing source distribution, a complete
++ machine-readable copy of the corresponding source code, to be
++ distributed under the terms of Sections 1 and 2 above on a medium
++ customarily used for software interchange; or,
++
++ c) Accompany it with the information you received as to the offer
++ to distribute corresponding source code. (This alternative is
++ allowed only for noncommercial distribution and only if you
++ received the program in object code or executable form with such
++ an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it. For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable. However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++ 4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License. Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++ 5. You are not required to accept this License, since you have not
++signed it. However, nothing else grants you permission to modify or
++distribute the Program or its derivative works. These actions are
++prohibited by law if you do not accept this License. Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++ 6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions. You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++ 7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License. If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all. For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices. Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++ 8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded. In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++ 9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time. Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number. If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation. If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++ 10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission. For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this. Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++ NO WARRANTY
++
++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++ END OF TERMS AND CONDITIONS
++
++ How to Apply These Terms to Your New Programs
++
++ If you develop a new program, and you want it to be of the greatest
++possible use to the public, the best way to achieve this is to make it
++free software which everyone can redistribute and change under these terms.
++
++ To do so, attach the following notices to the program. It is safest
++to attach them to the start of each source file to most effectively
++convey the exclusion of warranty; and each file should have at least
++the "copyright" line and a pointer to where the full notice is found.
++
++ <one line to give the program's name and a brief idea of what it does.>
++ Copyright (C) <year> <name of author>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++
++Also add information on how to contact you by electronic and paper mail.
++
++If the program is interactive, make it output a short notice like this
++when it starts in an interactive mode:
++
++ Gnomovision version 69, Copyright (C) year name of author
++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
++ This is free software, and you are welcome to redistribute it
++ under certain conditions; type `show c' for details.
++
++The hypothetical commands `show w' and `show c' should show the appropriate
++parts of the General Public License. Of course, the commands you use may
++be called something other than `show w' and `show c'; they could even be
++mouse-clicks or menu items--whatever suits your program.
++
++You should also get your employer (if you work as a programmer) or your
++school, if any, to sign a "copyright disclaimer" for the program, if
++necessary. Here is a sample; alter the names:
++
++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
++ `Gnomovision' (which makes passes at compilers) written by James Hacker.
++
++ <signature of Ty Coon>, 1 April 1989
++ Ty Coon, President of Vice
++
++This General Public License does not permit incorporating your program into
++proprietary programs. If your program is a subroutine library, you may
++consider it more useful to permit linking proprietary applications with the
++library. If this is what you want to do, use the GNU Library General
++Public License instead of this License.
+diff --git a/Makefile b/Makefile
+index 16e3fbb..fcffc7e 100644
+--- a/Makefile
++++ b/Makefile
+@@ -2,6 +2,7 @@ VERSION = 2
+ PATCHLEVEL = 6
+ SUBLEVEL = 27
+ EXTRAVERSION =
++VZVERSION = 037test001
+ NAME = Rotary Wombat
+
+ # *DOCUMENTATION*
+@@ -347,7 +348,7 @@ KBUILD_AFLAGS := -D__ASSEMBLY__
+ KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
+ KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+
+-export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
++export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION VZVERSION
+ export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
+ export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE
+ export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
+@@ -1002,7 +1003,8 @@ define filechk_utsrelease.h
+ echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
+ exit 1; \
+ fi; \
+- (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
++ (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \
++ echo \#define VZVERSION \"$(VZVERSION)\";)
+ endef
+
+ define filechk_version.h
+diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
+index e9842f6..643f220 100644
+--- a/arch/arm/kernel/smp.c
++++ b/arch/arm/kernel/smp.c
+@@ -191,7 +191,7 @@ int __cpuexit __cpu_disable(void)
+ local_flush_tlb_all();
+
+ read_lock(&tasklist_lock);
+- for_each_process(p) {
++ for_each_process_all(p) {
+ if (p->mm)
+ cpu_clear(cpu, p->mm->cpu_vm_mask);
+ }
+diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
+index 48e496f..8a2572b 100644
+--- a/arch/ia64/Kconfig
++++ b/arch/ia64/Kconfig
+@@ -612,6 +612,7 @@ source "arch/ia64/kvm/Kconfig"
+
+ source "lib/Kconfig"
+
++source "kernel/bc/Kconfig"
+ #
+ # Use the generic interrupt handling code in kernel/irq/:
+ #
+@@ -639,6 +640,8 @@ source "arch/ia64/hp/sim/Kconfig"
+
+ source "arch/ia64/Kconfig.debug"
+
++source "kernel/Kconfig.openvz"
++
+ source "security/Kconfig"
+
+ source "crypto/Kconfig"
+diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c
+index 4f0c30c..067cb28 100644
+--- a/arch/ia64/ia32/binfmt_elf32.c
++++ b/arch/ia64/ia32/binfmt_elf32.c
+@@ -17,6 +17,8 @@
+ #include <asm/param.h>
+ #include <asm/signal.h>
+
++#include <bc/vmpages.h>
++
+ #include "ia32priv.h"
+ #include "elfcore32.h"
+
+@@ -132,6 +134,12 @@ ia64_elf32_init (struct pt_regs *regs)
+ up_write(&current->mm->mmap_sem);
+ }
+
++ if (ub_memory_charge(current->mm, PAGE_ALIGN(IA32_LDT_ENTRIES *
++ IA32_LDT_ENTRY_SIZE),
++ VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE,
++ NULL, UB_SOFT))
++ goto skip;
++
+ /*
+ * Install LDT as anonymous memory. This gives us all-zero segment descriptors
+ * until a task modifies them via modify_ldt().
+@@ -152,7 +160,12 @@ ia64_elf32_init (struct pt_regs *regs)
+ }
+ }
+ up_write(&current->mm->mmap_sem);
+- }
++ } else
++ ub_memory_uncharge(current->mm, PAGE_ALIGN(IA32_LDT_ENTRIES *
++ IA32_LDT_ENTRY_SIZE),
++ VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE, NULL);
++
++skip:
+
+ ia64_psr(regs)->ac = 0; /* turn off alignment checking */
+ regs->loadrs = 0;
+diff --git a/arch/ia64/include/asm/pgalloc.h b/arch/ia64/include/asm/pgalloc.h
+index b9ac1a6..9504729 100644
+--- a/arch/ia64/include/asm/pgalloc.h
++++ b/arch/ia64/include/asm/pgalloc.h
+@@ -20,11 +20,13 @@
+ #include <linux/threads.h>
+ #include <linux/quicklist.h>
+
++#include <bc/kmem.h>
++
+ #include <asm/mmu_context.h>
+
+ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
+ {
+- return quicklist_alloc(0, GFP_KERNEL, NULL);
++ return quicklist_alloc(0, GFP_KERNEL_UBC|__GFP_SOFT_UBC, NULL);
+ }
+
+ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+@@ -41,7 +43,7 @@ pgd_populate(struct mm_struct *mm, pgd_t * pgd_entry, pud_t * pud)
+
+ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+- return quicklist_alloc(0, GFP_KERNEL, NULL);
++ return quicklist_alloc(0, GFP_KERNEL_UBC|__GFP_SOFT_UBC, NULL);
+ }
+
+ static inline void pud_free(struct mm_struct *mm, pud_t *pud)
+@@ -59,7 +61,7 @@ pud_populate(struct mm_struct *mm, pud_t * pud_entry, pmd_t * pmd)
+
+ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+- return quicklist_alloc(0, GFP_KERNEL, NULL);
++ return quicklist_alloc(0, GFP_KERNEL_UBC|__GFP_SOFT_UBC, NULL);
+ }
+
+ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+@@ -87,7 +89,7 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr)
+ struct page *page;
+ void *pg;
+
+- pg = quicklist_alloc(0, GFP_KERNEL, NULL);
++ pg = quicklist_alloc(0, GFP_KERNEL_UBC|__GFP_SOFT_UBC, NULL);
+ if (!pg)
+ return NULL;
+ page = virt_to_page(pg);
+diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h
+index f88fa05..695c23f 100644
+--- a/arch/ia64/include/asm/processor.h
++++ b/arch/ia64/include/asm/processor.h
+@@ -361,7 +361,7 @@ struct thread_struct {
+ regs->loadrs = 0; \
+ regs->r8 = get_dumpable(current->mm); /* set "don't zap registers" flag */ \
+ regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \
+- if (unlikely(!get_dumpable(current->mm))) { \
++ if (unlikely(!get_dumpable(current->mm) || !current->mm->vps_dumpable)) { \
+ /* \
+ * Zap scratch regs to avoid leaking bits between processes with different \
+ * uid/privileges. \
+diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h
+index d535833..5b5eb9c 100644
+--- a/arch/ia64/include/asm/unistd.h
++++ b/arch/ia64/include/asm/unistd.h
+@@ -308,6 +308,16 @@
+ #define __NR_dup3 1316
+ #define __NR_pipe2 1317
+ #define __NR_inotify_init1 1318
++#define __NR_fairsched_vcpus 1499
++#define __NR_fairsched_mknod 1500
++#define __NR_fairsched_rmnod 1501
++#define __NR_fairsched_chwt 1502
++#define __NR_fairsched_mvpr 1503
++#define __NR_fairsched_rate 1504
++#define __NR_getluid 1505
++#define __NR_setluid 1506
++#define __NR_setublimit 1507
++#define __NR_ubstat 1508
+
+ #ifdef __KERNEL__
+
+diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
+index 0dd6c14..d96ff73 100644
+--- a/arch/ia64/kernel/entry.S
++++ b/arch/ia64/kernel/entry.S
+@@ -510,6 +510,74 @@ GLOBAL_ENTRY(clone)
+ br.ret.sptk.many rp
+ END(clone)
+
++GLOBAL_ENTRY(ia64_ret_from_resume)
++ PT_REGS_UNWIND_INFO(0)
++{ /*
++ * Some versions of gas generate bad unwind info if the first instruction of a
++ * procedure doesn't go into the first slot of a bundle. This is a workaround.
++ */
++ nop.m 0
++ nop.i 0
++ /*
++ * We need to call schedule_tail() to complete the scheduling process.
++ * Called by ia64_switch_to() after do_fork()->copy_thread(). r8 contains the
++ * address of the previously executing task.
++ */
++ br.call.sptk.many rp=ia64_invoke_schedule_tail
++}
++ br.call.sptk.many rp=ia64_invoke_resume
++ ;;
++ adds sp=256,sp
++ ;;
++ /* Return from interrupt, we are all right. */
++(pNonSys) br ia64_leave_kernel
++ ;;
++ /* Tricky part follows. We must restore correct syscall
++ * register frame before doing normal syscall exit job.
++ * It would the most natural to keep sw->ar_pfs correct,
++ * then we would be here with correct register frame.
++ * Unfortunately, IA64 has a feature. Registers were in backstore
++ * after context switch, and the first br.ret does _NOT_ fetch
++ * output registers.
++ * It is quite natural: look, if caller has output regs in his
++ * frame, they should be consumed. If callee does not have (enough of)
++ * input/local registers (1 in this case), the situation is unusual.
++ * Practical evidence: they are filled with something random crap.
++ * The only case, when this is essential in mainstream kernel
++ * is sys_clone(). The result is that new process gets some kernel
++ * information in its register frame. Which is a security problem, btw.
++ *
++ * So, we set sw->ar_pfs to pretend the whole frame is of local
++ * regs. And we have to repartition the frame it manually, using
++ * information from pt->cr_ifs (the register is invalid in this
++ * case, but it holds correct pfm).
++ */
++ adds r3=PT(CR_IFS)+16,sp
++ ;;
++ ld8 r2=[r3],-(PT(CR_IFS)-PT(R8))
++ ;;
++ extr.u r2=r2,0,37
++ mov r8=ar.ec
++ ;;
++ extr.u r8=r8,0,5
++ ;;
++ shl r8=r8,52
++ ;;
++ or r2=r2,r8
++ ;;
++ mov ar.pfs=r2
++ ;;
++ movl r2=ia64_leave_syscall
++ ;;
++ mov rp=r2
++ /* Plus, we should fetch r8 and r10 from pt_regs. Something else? */
++ ld8 r8=[r3],PT(R10)-PT(R8)
++ ;;
++ ld8 r10=[r3]
++ ;;
++ br.ret.sptk.many rp
++END(ia64_ret_from_resume)
++
+ /*
+ * Invoke a system call, but do some tracing before and after the call.
+ * We MUST preserve the current register frame throughout this routine
+@@ -1264,6 +1332,34 @@ GLOBAL_ENTRY(ia64_invoke_schedule_tail)
+ br.ret.sptk.many rp
+ END(ia64_invoke_schedule_tail)
+
++GLOBAL_ENTRY(ia64_invoke_resume)
++ alloc loc1=ar.pfs,0,3,1,0
++ mov loc0=rp
++ adds out0=16,sp
++ ;;
++ ld8 r8=[out0]
++ ;;
++ cmp.eq p6,p0=r8,r0
++ ;;
++(p6) br.cond.sptk 1f
++ ;;
++ mov loc2=gp
++ ;;
++ ld8 r10=[r8],8
++ ;;
++ ld8 gp=[r8]
++ ;;
++ mov b7=r10
++ ;;
++ br.call.sptk.many rp=b7
++ ;;
++ mov gp=loc2
++1:
++ mov ar.pfs=loc1
++ mov rp=loc0
++ br.ret.sptk.many rp
++END(ia64_invoke_resume)
++
+ /*
+ * Setup stack and call do_notify_resume_user(), keeping interrupts
+ * disabled.
+@@ -1698,5 +1794,18 @@ sys_call_table:
+ data8 sys_pipe2
+ data8 sys_inotify_init1
+
++.rept 1499-1313
++ data8 sys_ni_syscall
++.endr
++ data8 sys_fairsched_vcpus
++ data8 sys_fairsched_mknod // 1500
++ data8 sys_fairsched_rmnod
++ data8 sys_fairsched_chwt
++ data8 sys_fairsched_mvpr
++ data8 sys_fairsched_rate
++ data8 sys_getluid // 1505
++ data8 sys_setluid
++ data8 sys_setublimit
++ data8 sys_ubstat
+ .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
+ #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
+diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S
+index c1625c7..634b102 100644
+--- a/arch/ia64/kernel/fsys.S
++++ b/arch/ia64/kernel/fsys.S
+@@ -90,53 +90,6 @@ ENTRY(fsys_getpid)
+ FSYS_RETURN
+ END(fsys_getpid)
+
+-ENTRY(fsys_getppid)
+- .prologue
+- .altrp b6
+- .body
+- add r17=IA64_TASK_GROUP_LEADER_OFFSET,r16
+- ;;
+- ld8 r17=[r17] // r17 = current->group_leader
+- add r9=TI_FLAGS+IA64_TASK_SIZE,r16
+- ;;
+-
+- ld4 r9=[r9]
+- add r17=IA64_TASK_REAL_PARENT_OFFSET,r17 // r17 = &current->group_leader->real_parent
+- ;;
+- and r9=TIF_ALLWORK_MASK,r9
+-
+-1: ld8 r18=[r17] // r18 = current->group_leader->real_parent
+- ;;
+- cmp.ne p8,p0=0,r9
+- add r8=IA64_TASK_TGID_OFFSET,r18 // r8 = &current->group_leader->real_parent->tgid
+- ;;
+-
+- /*
+- * The .acq is needed to ensure that the read of tgid has returned its data before
+- * we re-check "real_parent".
+- */
+- ld4.acq r8=[r8] // r8 = current->group_leader->real_parent->tgid
+-#ifdef CONFIG_SMP
+- /*
+- * Re-read current->group_leader->real_parent.
+- */
+- ld8 r19=[r17] // r19 = current->group_leader->real_parent
+-(p8) br.spnt.many fsys_fallback_syscall
+- ;;
+- cmp.ne p6,p0=r18,r19 // did real_parent change?
+- mov r19=0 // i must not leak kernel bits...
+-(p6) br.cond.spnt.few 1b // yes -> redo the read of tgid and the check
+- ;;
+- mov r17=0 // i must not leak kernel bits...
+- mov r18=0 // i must not leak kernel bits...
+-#else
+- mov r17=0 // i must not leak kernel bits...
+- mov r18=0 // i must not leak kernel bits...
+- mov r19=0 // i must not leak kernel bits...
+-#endif
+- FSYS_RETURN
+-END(fsys_getppid)
+-
+ ENTRY(fsys_set_tid_address)
+ .prologue
+ .altrp b6
+@@ -767,7 +720,7 @@ fsyscall_table:
+ data8 0 // chown
+ data8 0 // lseek // 1040
+ data8 fsys_getpid // getpid
+- data8 fsys_getppid // getppid
++ data8 0 // getppid
+ data8 0 // mount
+ data8 0 // umount
+ data8 0 // setuid // 1045
+diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
+index 66e491d..4308d48 100644
+--- a/arch/ia64/kernel/head.S
++++ b/arch/ia64/kernel/head.S
+@@ -1097,7 +1097,7 @@ GLOBAL_ENTRY(start_kernel_thread)
+ mov out1 = r11;;
+ br.call.sptk.many rp = kernel_thread_helper;;
+ mov out0 = r8
+- br.call.sptk.many rp = sys_exit;;
++ br.call.sptk.many rp = do_exit;;
+ 1: br.sptk.few 1b // not reached
+ END(start_kernel_thread)
+
+diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
+index 6da1f20..24950d6 100644
+--- a/arch/ia64/kernel/ia64_ksyms.c
++++ b/arch/ia64/kernel/ia64_ksyms.c
+@@ -75,6 +75,8 @@ EXPORT_SYMBOL(xor_ia64_4);
+ EXPORT_SYMBOL(xor_ia64_5);
+ #endif
+
++EXPORT_SYMBOL(empty_zero_page);
++
+ #include <asm/pal.h>
+ EXPORT_SYMBOL(ia64_pal_call_phys_stacked);
+ EXPORT_SYMBOL(ia64_pal_call_phys_static);
+diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c
+index 7dd96c1..d849ed0 100644
+--- a/arch/ia64/kernel/mca.c
++++ b/arch/ia64/kernel/mca.c
+@@ -1608,10 +1608,10 @@ default_monarch_init_process(struct notifier_block *self, unsigned long val, voi
+ }
+ printk("\n\n");
+ if (read_trylock(&tasklist_lock)) {
+- do_each_thread (g, t) {
++ do_each_thread_all (g, t) {
+ printk("\nBacktrace of pid %d (%s)\n", t->pid, t->comm);
+ show_stack(t, NULL);
+- } while_each_thread (g, t);
++ } while_each_thread_all (g, t);
+ read_unlock(&tasklist_lock);
+ }
+ /* FIXME: This will not restore zapped printk locks. */
+diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
+index fc8f350..057bbb3 100644
+--- a/arch/ia64/kernel/perfmon.c
++++ b/arch/ia64/kernel/perfmon.c
+@@ -4176,12 +4176,12 @@ pfm_check_task_exist(pfm_context_t *ctx)
+
+ read_lock(&tasklist_lock);
+
+- do_each_thread (g, t) {
++ do_each_thread_ve (g, t) {
+ if (t->thread.pfm_context == ctx) {
+ ret = 0;
+ goto out;
+ }
+- } while_each_thread (g, t);
++ } while_each_thread_ve (g, t);
+ out:
+ read_unlock(&tasklist_lock);
+
+diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
+index 3ab8373..af4e88a 100644
+--- a/arch/ia64/kernel/process.c
++++ b/arch/ia64/kernel/process.c
+@@ -28,6 +28,7 @@
+ #include <linux/delay.h>
+ #include <linux/kdebug.h>
+ #include <linux/utsname.h>
++#include <linux/sysctl.h>
+
+ #include <asm/cpu.h>
+ #include <asm/delay.h>
+@@ -391,6 +392,9 @@ ia64_load_extra (struct task_struct *task)
+ #endif
+ }
+
++extern char ia64_ret_from_resume;
++EXPORT_SYMBOL(ia64_ret_from_resume);
++
+ /*
+ * Copy the state of an ia-64 thread.
+ *
+@@ -464,7 +468,6 @@ copy_thread (int nr, unsigned long clone_flags,
+ child_ptregs->r12 = user_stack_base + user_stack_size - 16;
+ child_ptregs->ar_bspstore = user_stack_base;
+ child_ptregs->ar_rnat = 0;
+- child_ptregs->loadrs = 0;
+ }
+ } else {
+ /*
+@@ -676,16 +679,25 @@ out:
+ return error;
+ }
+
++extern void start_kernel_thread (void);
++EXPORT_SYMBOL(start_kernel_thread);
++
+ pid_t
+ kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
+ {
+- extern void start_kernel_thread (void);
+ unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
+ struct {
+ struct switch_stack sw;
+ struct pt_regs pt;
+ } regs;
+
++ /* Don't allow kernel_thread() inside VE */
++ if (!ve_allow_kthreads && !ve_is_super(get_exec_env())) {
++ printk("kernel_thread call inside container\n");
++ dump_stack();
++ return -EPERM;
++ }
++
+ memset(&regs, 0, sizeof(regs));
+ regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
+ regs.pt.r1 = helper_fptr[1]; /* set GP */
+diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
+index 2a9943b..e44debf 100644
+--- a/arch/ia64/kernel/ptrace.c
++++ b/arch/ia64/kernel/ptrace.c
+@@ -10,6 +10,7 @@
+ * Derived from the x86 and Alpha versions.
+ */
+ #include <linux/kernel.h>
++#include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/mm.h>
+@@ -105,6 +106,8 @@ ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat)
+
+ # undef GET_BITS
+ }
++EXPORT_SYMBOL(ia64_get_scratch_nat_bits);
++EXPORT_SYMBOL(__ia64_save_fpu);
+
+ /*
+ * Set the NaT bits for the scratch registers according to NAT and
+@@ -461,6 +464,7 @@ ia64_peek (struct task_struct *child, struct switch_stack *child_stack,
+ *val = ret;
+ return 0;
+ }
++EXPORT_SYMBOL(ia64_peek);
+
+ long
+ ia64_poke (struct task_struct *child, struct switch_stack *child_stack,
+@@ -525,6 +529,7 @@ ia64_get_user_rbs_end (struct task_struct *child, struct pt_regs *pt,
+ *cfmp = cfm;
+ return (unsigned long) ia64_rse_skip_regs(bspstore, ndirty);
+ }
++EXPORT_SYMBOL(ia64_get_user_rbs_end);
+
+ /*
+ * Synchronize (i.e, write) the RSE backing store living in kernel
+@@ -820,20 +825,20 @@ access_nat_bits (struct task_struct *child, struct pt_regs *pt,
+ if (write_access) {
+ nat_bits = *data;
+ scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits);
+- if (unw_set_ar(info, UNW_AR_UNAT, scratch_unat) < 0) {
+- dprintk("ptrace: failed to set ar.unat\n");
+- return -1;
+- }
++ if (info->pri_unat_loc)
++ *info->pri_unat_loc = scratch_unat;
++ else
++ info->sw->caller_unat = scratch_unat;
+ for (regnum = 4; regnum <= 7; ++regnum) {
+ unw_get_gr(info, regnum, &dummy, &nat);
+ unw_set_gr(info, regnum, dummy,
+ (nat_bits >> regnum) & 1);
+ }
+ } else {
+- if (unw_get_ar(info, UNW_AR_UNAT, &scratch_unat) < 0) {
+- dprintk("ptrace: failed to read ar.unat\n");
+- return -1;
+- }
++ if (info->pri_unat_loc)
++ scratch_unat = *info->pri_unat_loc;
++ else
++ scratch_unat = info->sw->caller_unat;
+ nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat);
+ for (regnum = 4; regnum <= 7; ++regnum) {
+ unw_get_gr(info, regnum, &dummy, &nat);
+diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
+index 19c5a78..cc6c4e6 100644
+--- a/arch/ia64/kernel/signal.c
++++ b/arch/ia64/kernel/signal.c
+@@ -14,6 +14,7 @@
+ #include <linux/sched.h>
+ #include <linux/signal.h>
+ #include <linux/smp.h>
++#include <linux/freezer.h>
+ #include <linux/stddef.h>
+ #include <linux/tty.h>
+ #include <linux/binfmts.h>
+@@ -464,6 +465,12 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
+ if (!user_mode(&scr->pt))
+ return;
+
++ if (try_to_freeze() && !signal_pending(current)) {
++ if ((long) scr->pt.r10 != -1)
++ restart = 0;
++ goto no_signal;
++ }
++
+ if (current_thread_info()->status & TS_RESTORE_SIGMASK)
+ oldset = &current->saved_sigmask;
+ else
+@@ -519,8 +526,10 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
+ if (IS_IA32_PROCESS(&scr->pt)) {
+ scr->pt.r8 = scr->pt.r1;
+ scr->pt.cr_iip -= 2;
+- } else
++ } else {
+ ia64_decrement_ip(&scr->pt);
++ scr->pt.r10 = 0;
++ }
+ restart = 0; /* don't restart twice if handle_signal() fails... */
+ }
+ }
+@@ -542,6 +551,7 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
+ }
+
+ /* Did we come from a system call? */
++no_signal:
+ if (restart) {
+ /* Restart the system call - no handlers present */
+ if (errno == ERESTARTNOHAND || errno == ERESTARTSYS || errno == ERESTARTNOINTR
+@@ -561,6 +571,7 @@ ia64_do_signal (struct sigscratch *scr, long in_syscall)
+ ia64_decrement_ip(&scr->pt);
+ if (errno == ERESTART_RESTARTBLOCK)
+ scr->pt.r15 = __NR_restart_syscall;
++ scr->pt.r10 = 0;
+ }
+ }
+ }
+diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
+index bcbb6d8..40c8320 100644
+--- a/arch/ia64/kernel/sys_ia64.c
++++ b/arch/ia64/kernel/sys_ia64.c
+@@ -204,7 +204,7 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un
+
+ /* Careful about overflows.. */
+ len = PAGE_ALIGN(len);
+- if (!len || len > TASK_SIZE) {
++ if (len > TASK_SIZE) {
+ addr = -EINVAL;
+ goto out;
+ }
+diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
+index 65c10a4..9f0cdde 100644
+--- a/arch/ia64/kernel/time.c
++++ b/arch/ia64/kernel/time.c
+@@ -41,6 +41,8 @@ struct fsyscall_gtod_data_t fsyscall_gtod_data = {
+ struct itc_jitter_data_t itc_jitter_data;
+
+ volatile int time_keeper_id = 0; /* smp_processor_id() of time-keeper */
++unsigned int cpu_khz; /* TSC clocks / usec, not used here */
++EXPORT_SYMBOL(cpu_khz);
+
+ #ifdef CONFIG_IA64_DEBUG_IRQ
+
+@@ -358,6 +360,8 @@ ia64_init_itm (void)
+ /* avoid softlock up message when cpu is unplug and plugged again. */
+ touch_softlockup_watchdog();
+
++ cpu_khz = local_cpu_data->proc_freq / 1000;
++
+ /* Setup the CPU local timer tick */
+ ia64_cpu_local_tick();
+
+diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c
+index ff0e7c1..7288a9f 100644
+--- a/arch/ia64/kernel/unaligned.c
++++ b/arch/ia64/kernel/unaligned.c
+@@ -1291,7 +1291,7 @@ within_logging_rate_limit (void)
+ {
+ static unsigned long count, last_time;
+
+- if (time_after(jiffies, last_time + 5 * HZ))
++ if (time_after(jiffies, last_time + 60 * HZ))
+ count = 0;
+ if (count < 5) {
+ last_time = jiffies;
+diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c
+index 23088be..da13815 100644
+--- a/arch/ia64/mm/fault.c
++++ b/arch/ia64/mm/fault.c
+@@ -148,7 +148,6 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
+ if ((vma->vm_flags & mask) != mask)
+ goto bad_area;
+
+- survive:
+ /*
+ * If for any reason at all we couldn't handle the fault, make
+ * sure we exit gracefully rather than endlessly redo the
+@@ -276,13 +275,13 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
+
+ out_of_memory:
+ up_read(&mm->mmap_sem);
+- if (is_global_init(current)) {
+- yield();
+- down_read(&mm->mmap_sem);
+- goto survive;
++ if (user_mode(regs)) {
++ /*
++ * 0-order allocation always success if something really
++ * fatal not happen: beancounter overdraft or OOM.
++ */
++ force_sig(SIGKILL, current);
++ return;
+ }
+- printk(KERN_CRIT "VM: killing process %s\n", current->comm);
+- if (user_mode(regs))
+- do_group_exit(SIGKILL);
+ goto no_context;
+ }
+diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
+index 200100e..226b5cc 100644
+--- a/arch/ia64/mm/init.c
++++ b/arch/ia64/mm/init.c
+@@ -37,6 +37,8 @@
+ #include <asm/unistd.h>
+ #include <asm/mca.h>
+
++#include <bc/vmpages.h>
++
+ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+ extern void ia64_tlb_init (void);
+@@ -111,6 +113,10 @@ ia64_init_addr_space (void)
+
+ ia64_set_rbs_bot();
+
++ if (ub_memory_charge(current->mm, PAGE_SIZE, VM_DATA_DEFAULT_FLAGS,
++ NULL, UB_SOFT))
++ goto skip;
++
+ /*
+ * If we're out of memory and kmem_cache_alloc() returns NULL, we simply ignore
+ * the problem. When the process attempts to write to the register backing store
+@@ -127,11 +133,16 @@ ia64_init_addr_space (void)
+ if (insert_vm_struct(current->mm, vma)) {
+ up_write(&current->mm->mmap_sem);
+ kmem_cache_free(vm_area_cachep, vma);
++ ub_memory_uncharge(current->mm, PAGE_SIZE,
++ VM_DATA_DEFAULT_FLAGS, NULL);
+ return;
+ }
+ up_write(&current->mm->mmap_sem);
+- }
++ } else
++ ub_memory_uncharge(current->mm, PAGE_SIZE,
++ VM_DATA_DEFAULT_FLAGS, NULL);
+
++skip:
+ /* map NaT-page at address zero to speed up speculative dereferencing of NULL: */
+ if (!(current->personality & MMAP_PAGE_ZERO)) {
+ vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
+diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
+index 587da5e..a9d6b81 100644
+--- a/arch/powerpc/Kconfig
++++ b/arch/powerpc/Kconfig
+@@ -833,8 +833,12 @@ source "arch/powerpc/sysdev/qe_lib/Kconfig"
+
+ source "lib/Kconfig"
+
++source "kernel/bc/Kconfig"
++
+ source "arch/powerpc/Kconfig.debug"
+
++source "kernel/Kconfig.openvz"
++
+ source "security/Kconfig"
+
+ config KEYS_COMPAT
+diff --git a/arch/powerpc/include/asm/pgalloc-64.h b/arch/powerpc/include/asm/pgalloc-64.h
+index 812a1d8..c0f7a7f 100644
+--- a/arch/powerpc/include/asm/pgalloc-64.h
++++ b/arch/powerpc/include/asm/pgalloc-64.h
+@@ -26,7 +26,8 @@ extern struct kmem_cache *pgtable_cache[];
+
+ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
+ {
+- return kmem_cache_alloc(pgtable_cache[PGD_CACHE_NUM], GFP_KERNEL);
++ return kmem_cache_alloc(pgtable_cache[PGD_CACHE_NUM],
++ GFP_KERNEL_UBC | __GFP_SOFT_UBC);
+ }
+
+ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+@@ -42,7 +43,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+ return kmem_cache_alloc(pgtable_cache[PUD_CACHE_NUM],
+- GFP_KERNEL|__GFP_REPEAT);
++ GFP_KERNEL_UBC|__GFP_SOFT_UBC|__GFP_REPEAT);
+ }
+
+ static inline void pud_free(struct mm_struct *mm, pud_t *pud)
+@@ -88,10 +89,15 @@ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+ kmem_cache_free(pgtable_cache[PMD_CACHE_NUM], pmd);
+ }
+
++static inline pte_t *do_pte_alloc(gfp_t flags)
++{
++ return (pte_t *)__get_free_page(flags);
++}
++
+ static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
+ unsigned long address)
+ {
+- return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
++ return do_pte_alloc(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
+ }
+
+ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
+@@ -100,7 +106,7 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
+ struct page *page;
+ pte_t *pte;
+
+- pte = pte_alloc_one_kernel(mm, address);
++ pte = do_pte_alloc(GFP_KERNEL_UBC | __GFP_REPEAT | __GFP_ZERO);
+ if (!pte)
+ return NULL;
+ page = virt_to_page(pte);
+diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
+index f6cc7a4..34fc004 100644
+--- a/arch/powerpc/include/asm/systbl.h
++++ b/arch/powerpc/include/asm/systbl.h
+@@ -322,3 +322,19 @@ SYSCALL_SPU(epoll_create1)
+ SYSCALL_SPU(dup3)
+ SYSCALL_SPU(pipe2)
+ SYSCALL(inotify_init1)
++SYS_SKIP(319, 400)
++SYSCALL(ni_syscall)
++SYS_SKIP_END()
++SYSCALL(fairsched_mknod) /* 400 */
++SYSCALL(fairsched_rmnod)
++SYSCALL(fairsched_chwt)
++SYSCALL(fairsched_mvpr)
++SYSCALL(fairsched_rate)
++SYSCALL(fairsched_vcpus)
++SYS_SKIP(406, 410)
++SYSCALL(ni_syscall)
++SYS_SKIP_END()
++SYSCALL(getluid) /* 410 */
++SYSCALL(setluid)
++SYSCALL(setublimit)
++SYSCALL(ubstat)
+diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
+index e07d0c7..3fea592 100644
+--- a/arch/powerpc/include/asm/unistd.h
++++ b/arch/powerpc/include/asm/unistd.h
+@@ -342,9 +342,14 @@
+ #define __NR_pipe2 317
+ #define __NR_inotify_init1 318
+
++#define __NR_getluid 410
++#define __NR_setluid 411
++#define __NR_setublimit 412
++#define __NR_ubstat 413
++
+ #ifdef __KERNEL__
+
+-#define __NR_syscalls 319
++#define __NR_syscalls 414
+
+ #define __NR__exit __NR_exit
+ #define NR_syscalls __NR_syscalls
+diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
+index 7a6dfbc..28c26b4 100644
+--- a/arch/powerpc/kernel/misc_32.S
++++ b/arch/powerpc/kernel/misc_32.S
+@@ -835,7 +835,7 @@ _GLOBAL(abs)
+ * Create a kernel thread
+ * kernel_thread(fn, arg, flags)
+ */
+-_GLOBAL(kernel_thread)
++_GLOBAL(ppc_kernel_thread)
+ stwu r1,-16(r1)
+ stw r30,8(r1)
+ stw r31,12(r1)
+diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
+index 4dd70cf..2e10116 100644
+--- a/arch/powerpc/kernel/misc_64.S
++++ b/arch/powerpc/kernel/misc_64.S
+@@ -415,7 +415,7 @@ _GLOBAL(scom970_write)
+ * Create a kernel thread
+ * kernel_thread(fn, arg, flags)
+ */
+-_GLOBAL(kernel_thread)
++_GLOBAL(ppc_kernel_thread)
+ std r29,-24(r1)
+ std r30,-16(r1)
+ stdu r1,-STACK_FRAME_OVERHEAD(r1)
+diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
+index 957bded..ca7410c 100644
+--- a/arch/powerpc/kernel/process.c
++++ b/arch/powerpc/kernel/process.c
+@@ -50,6 +50,8 @@
+ #include <linux/kprobes.h>
+ #include <linux/kdebug.h>
+
++#include <linux/utsrelease.h>
++
+ extern unsigned long _get_SP(void);
+
+ #ifndef CONFIG_SMP
+@@ -501,8 +503,9 @@ void show_regs(struct pt_regs * regs)
+
+ printk("NIP: "REG" LR: "REG" CTR: "REG"\n",
+ regs->nip, regs->link, regs->ctr);
+- printk("REGS: %p TRAP: %04lx %s (%s)\n",
+- regs, regs->trap, print_tainted(), init_utsname()->release);
++ printk("REGS: %p TRAP: %04lx %s (%s %s)\n",
++ regs, regs->trap, print_tainted(), init_utsname()->release,
++ VZVERSION);
+ printk("MSR: "REG" ", regs->msr);
+ printbits(regs->msr, msr_bits);
+ printk(" CR: %08lx XER: %08lx\n", regs->ccr, regs->xer);
+@@ -1057,6 +1060,20 @@ void dump_stack(void)
+ }
+ EXPORT_SYMBOL(dump_stack);
+
++long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
++{
++ extern long ppc_kernel_thread(int (*fn)(void *), void *arg,
++ unsigned long flags);
++
++ if (!ve_is_super(get_exec_env())) {
++ printk("kernel_thread call inside container\n");
++ dump_stack();
++ return -EPERM;
++ }
++
++ return ppc_kernel_thread(fn, arg, flags);
++}
++
+ #ifdef CONFIG_PPC64
+ void ppc64_runlatch_on(void)
+ {
+diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S
+index 93219c3..a9e16bb 100644
+--- a/arch/powerpc/kernel/systbl.S
++++ b/arch/powerpc/kernel/systbl.S
+@@ -43,5 +43,9 @@
+ .p2align 3
+ #endif
+
++#define SYS_SKIP(from, to) .rept to - from \
++ SYSCALL(sys_ni_syscall) \
++ .endr
++
+ _GLOBAL(sys_call_table)
+ #include <asm/systbl.h>
+diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
+index 565b7a2..8400dec 100644
+--- a/arch/powerpc/mm/fault.c
++++ b/arch/powerpc/mm/fault.c
+@@ -309,7 +309,6 @@ good_area:
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+- survive:
+ ret = handle_mm_fault(mm, vma, address, is_write);
+ if (unlikely(ret & VM_FAULT_ERROR)) {
+ if (ret & VM_FAULT_OOM)
+@@ -349,14 +348,12 @@ bad_area_nosemaphore:
+ */
+ out_of_memory:
+ up_read(&mm->mmap_sem);
+- if (is_global_init(current)) {
+- yield();
+- down_read(&mm->mmap_sem);
+- goto survive;
+- }
+- printk("VM: killing process %s\n", current->comm);
+ if (user_mode(regs))
+- do_group_exit(SIGKILL);
++ /*
++ * 0-order allocation always success if something really
++ * fatal not happen: beancounter overdraft or OOM. Den
++ */
++ force_sig(SIGKILL, current);
+ return SIGKILL;
+
+ do_sigbus:
+diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
+index 036fe2f..807473a 100644
+--- a/arch/powerpc/mm/init_64.c
++++ b/arch/powerpc/mm/init_64.c
+@@ -168,8 +168,8 @@ struct kmem_cache *pgtable_cache[ARRAY_SIZE(pgtable_cache_size)];
+
+ void pgtable_cache_init(void)
+ {
+- pgtable_cache[0] = kmem_cache_create(pgtable_cache_name[0], PGD_TABLE_SIZE, PGD_TABLE_SIZE, SLAB_PANIC, pgd_ctor);
+- pgtable_cache[1] = kmem_cache_create(pgtable_cache_name[1], PMD_TABLE_SIZE, PMD_TABLE_SIZE, SLAB_PANIC, pmd_ctor);
++ pgtable_cache[0] = kmem_cache_create(pgtable_cache_name[0], PGD_TABLE_SIZE, PGD_TABLE_SIZE, SLAB_PANIC|SLAB_UBC|SLAB_NO_CHARGE, pgd_ctor);
++ pgtable_cache[1] = kmem_cache_create(pgtable_cache_name[1], PMD_TABLE_SIZE, PMD_TABLE_SIZE, SLAB_PANIC|SLAB_UBC|SLAB_NO_CHARGE, pmd_ctor);
+ }
+
+ #ifdef CONFIG_SPARSEMEM_VMEMMAP
+diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
+index 2001abd..ea128b6 100644
+--- a/arch/powerpc/mm/pgtable_32.c
++++ b/arch/powerpc/mm/pgtable_32.c
+@@ -83,7 +83,8 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
+ {
+ pgd_t *ret;
+
+- ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGDIR_ORDER);
++ ret = (pgd_t *)__get_free_pages(GFP_KERNEL_UBC | __GFP_SOFT_UBC |
++ __GFP_ZERO, PGDIR_ORDER);
+ return ret;
+ }
+
+@@ -117,6 +118,7 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
+ #else
+ gfp_t flags = GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO;
+ #endif
++ flags |= (__GFP_UBC | __GFP_SOFT_UBC);
+
+ ptepage = alloc_pages(flags, 0);
+ if (!ptepage)
+diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c
+index 19f6bfd..4f23f43 100644
+--- a/arch/powerpc/platforms/cell/spu_callbacks.c
++++ b/arch/powerpc/platforms/cell/spu_callbacks.c
+@@ -46,6 +46,8 @@ static void *spu_syscall_table[] = {
+ #define PPC_SYS_SPU(func) ppc_##func,
+ #define SYSX_SPU(f, f3264, f32) f,
+
++#define SYS_SKIP(from, to) [from ... to] = sys_ni_syscall,
++
+ #include <asm/systbl.h>
+ };
+
+diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
+index 8d41908..2e2f811 100644
+--- a/arch/s390/Kconfig
++++ b/arch/s390/Kconfig
+@@ -587,6 +587,8 @@ source "fs/Kconfig"
+
+ source "arch/s390/Kconfig.debug"
+
++source "kernel/Kconfig.openvz"
++
+ source "security/Kconfig"
+
+ source "crypto/Kconfig"
+diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
+index 00b9b4d..6194a6a 100644
+--- a/arch/s390/kernel/smp.c
++++ b/arch/s390/kernel/smp.c
+@@ -573,8 +573,19 @@ out:
+ */
+ int __cpuinit start_secondary(void *cpuvoid)
+ {
+- /* Setup the cpu */
+- cpu_init();
++ /* Setup the cpu */
++ cpu_init();
++
++#ifdef CONFIG_VE
++ /* TSC reset. kill whatever might rely on old values */
++ VE_TASK_INFO(current)->wakeup_stamp = 0;
++ /*
++ * Cosmetic: sleep_time won't be changed afterwards for the idle
++ * thread; keep it 0 rather than -cycles.
++ */
++ VE_TASK_INFO(idle)->sleep_time = 0;
++#endif
++
+ preempt_disable();
+ /* Enable TOD clock interrupts on the secondary cpu. */
+ init_cpu_timer();
+@@ -831,6 +842,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
+ for_each_possible_cpu(cpu)
+ if (cpu != smp_processor_id())
+ smp_create_idle(cpu);
++
++#ifdef CONFIG_VE
++ /* TSC reset. kill whatever might rely on old values */
++ VE_TASK_INFO(current)->wakeup_stamp = 0;
++#endif
+ }
+
+ void __init smp_prepare_boot_cpu(void)
+diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c
+index b9dbd2d..d0a8c54 100644
+--- a/arch/sh/kernel/process_64.c
++++ b/arch/sh/kernel/process_64.c
+@@ -670,7 +670,7 @@ asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void
+ int len=0;
+ struct task_struct *p;
+ read_lock(&tasklist_lock);
+- for_each_process(p) {
++ for_each_process_ve(p) {
+ int pid = p->pid;
+
+ if (!pid)
+diff --git a/arch/sparc/include/asm/pgalloc_64.h b/arch/sparc/include/asm/pgalloc_64.h
+index 5bdfa2c..dd89e73 100644
+--- a/arch/sparc/include/asm/pgalloc_64.h
++++ b/arch/sparc/include/asm/pgalloc_64.h
+@@ -16,7 +16,7 @@
+
+ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
+ {
+- return quicklist_alloc(0, GFP_KERNEL, NULL);
++ return quicklist_alloc(0, GFP_KERNEL_UBC, NULL);
+ }
+
+ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+@@ -28,7 +28,7 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+
+ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+- return quicklist_alloc(0, GFP_KERNEL, NULL);
++ return quicklist_alloc(0, GFP_KERNEL_UBC|__GFP_REPEAT, NULL);
+ }
+
+ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+@@ -48,7 +48,7 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
+ struct page *page;
+ void *pg;
+
+- pg = quicklist_alloc(0, GFP_KERNEL, NULL);
++ pg = quicklist_alloc(0, GFP_KERNEL_UBC, NULL);
+ if (!pg)
+ return NULL;
+ page = virt_to_page(pg);
+diff --git a/arch/sparc/include/asm/thread_info_64.h b/arch/sparc/include/asm/thread_info_64.h
+index c0a737d..5ef8b1d 100644
+--- a/arch/sparc/include/asm/thread_info_64.h
++++ b/arch/sparc/include/asm/thread_info_64.h
+@@ -163,14 +163,14 @@ register struct thread_info *current_thread_info_reg asm("g6");
+ struct thread_info *ret; \
+ \
+ ret = (struct thread_info *) \
+- __get_free_pages(GFP_KERNEL, __THREAD_INFO_ORDER); \
++ __get_free_pages(GFP_KERNEL_UBC, __THREAD_INFO_ORDER); \
+ if (ret) \
+ memset(ret, 0, PAGE_SIZE<<__THREAD_INFO_ORDER); \
+ ret; \
+ })
+ #else
+ #define alloc_thread_info(tsk) \
+- ((struct thread_info *)__get_free_pages(GFP_KERNEL, __THREAD_INFO_ORDER))
++ ((struct thread_info *)__get_free_pages(GFP_KERNEL_UBC, __THREAD_INFO_ORDER))
+ #endif
+
+ #define free_thread_info(ti) \
+@@ -237,6 +237,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
+ #define TIF_ABI_PENDING 12
+ #define TIF_MEMDIE 13
+ #define TIF_POLLING_NRFLAG 14
++#define TIF_FREEZE 15 /* Freeze request (atomic PF_FREEZE) */
+
+ #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
+ #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
+diff --git a/arch/sparc/include/asm/unistd_64.h b/arch/sparc/include/asm/unistd_64.h
+index c5cc0e0..8f9d76c 100644
+--- a/arch/sparc/include/asm/unistd_64.h
++++ b/arch/sparc/include/asm/unistd_64.h
+@@ -340,8 +340,12 @@
+ #define __NR_dup3 320
+ #define __NR_pipe2 321
+ #define __NR_inotify_init1 322
++#define __NR_getluid 510
++#define __NR_setluid 511
++#define __NR_setublimit 512
++#define __NR_ubstat 513
+
+-#define NR_SYSCALLS 323
++#define NR_SYSCALLS 514
+
+ #ifdef __KERNEL__
+ #define __ARCH_WANT_IPC_PARSE_VERSION
+diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
+index 36b4b7a..065c1fd 100644
+--- a/arch/sparc64/Kconfig
++++ b/arch/sparc64/Kconfig
+@@ -403,8 +403,12 @@ source "fs/Kconfig"
+
+ source "arch/sparc64/Kconfig.debug"
+
++source "kernel/Kconfig.openvz"
++
+ source "security/Kconfig"
+
+ source "crypto/Kconfig"
+
+ source "lib/Kconfig"
++
++source "kernel/bc/Kconfig"
+diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
+index 15f4178..c07a634 100644
+--- a/arch/sparc64/kernel/process.c
++++ b/arch/sparc64/kernel/process.c
+@@ -688,6 +688,13 @@ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+ {
+ long retval;
+
++ /* Don't allow kernel_thread() inside VE */
++ if (!ve_is_super(get_exec_env())) {
++ printk("kernel_thread call inside container\n");
++ dump_stack();
++ return -EPERM;
++ }
++
+ /* If the parent runs before fn(arg) is called by the child,
+ * the input registers of this function can be clobbered.
+ * So we stash 'fn' and 'arg' into global registers which
+diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
+index 0fdbf3b..756adce 100644
+--- a/arch/sparc64/kernel/systbls.S
++++ b/arch/sparc64/kernel/systbls.S
+@@ -84,6 +84,24 @@ sys_call_table32:
+ .word compat_sys_timerfd_settime, compat_sys_timerfd_gettime, compat_sys_signalfd4, sys_eventfd2, sys_epoll_create1
+ /*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1
+
++ .rept 500-323
++ .word sys_nis_syscall
++ .endr
++ .word sys_fairsched_mknod /* 500 */
++ .word sys_fairsched_rmnod
++ .word sys_fairsched_chwt
++ .word sys_fairsched_mvpr
++ .word sys_fairsched_rate
++ .word sys_nis_syscall /* 505 */
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_getluid /* 510 */
++ .word sys_setluid
++ .word compat_sys_setublimit
++ .word compat_sys_ubstat
++
+ #endif /* CONFIG_COMPAT */
+
+ /* Now the 64-bit native Linux syscall table. */
+@@ -157,3 +175,20 @@ sys_call_table:
+ /*310*/ .word sys_utimensat, sys_signalfd, sys_timerfd_create, sys_eventfd, sys_fallocate
+ .word sys_timerfd_settime, sys_timerfd_gettime, sys_signalfd4, sys_eventfd2, sys_epoll_create1
+ /*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1
++ .rept 500-323
++ .word sys_nis_syscall
++ .endr
++ .word sys_fairsched_mknod /* 500 */
++ .word sys_fairsched_rmnod
++ .word sys_fairsched_chwt
++ .word sys_fairsched_mvpr
++ .word sys_fairsched_rate
++ .word sys_nis_syscall /* 505 */
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_nis_syscall
++ .word sys_getluid /* 510 */
++ .word sys_setluid
++ .word sys_setublimit
++ .word sys_ubstat
+diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
+index c824df1..1a7916e 100644
+--- a/arch/sparc64/kernel/traps.c
++++ b/arch/sparc64/kernel/traps.c
+@@ -2187,6 +2187,10 @@ void die_if_kernel(char *str, struct pt_regs *regs)
+ " \\__U_/\n");
+
+ printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
++ printk("VE:EXCVE %d:%d, CPU %d, VCPU %d:%d\n",
++ VEID(VE_TASK_INFO(current)->owner_env), VEID(get_exec_env()),
++ smp_processor_id(),
++ task_vsched_id(current), task_cpu(current));
+ notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV);
+ __asm__ __volatile__("flushw");
+ show_regs(regs);
+diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
+index ed92864..518d26d 100644
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -1777,6 +1777,7 @@ config SYSVIPC_COMPAT
+
+ endmenu
+
++source "kernel/Kconfig.openvz"
+
+ source "net/Kconfig"
+
+@@ -1795,3 +1796,5 @@ source "crypto/Kconfig"
+ source "arch/x86/kvm/Kconfig"
+
+ source "lib/Kconfig"
++
++source "kernel/bc/Kconfig"
+diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
+index ffc1bb4..0c8651c 100644
+--- a/arch/x86/ia32/ia32entry.S
++++ b/arch/x86/ia32/ia32entry.S
+@@ -612,7 +612,7 @@ ia32_sys_call_table:
+ .quad stub32_iopl /* 110 */
+ .quad sys_vhangup
+ .quad quiet_ni_syscall /* old "idle" system call */
+- .quad sys32_vm86_warning /* vm86old */
++ .quad quiet_ni_syscall /* vm86old */
+ .quad compat_sys_wait4
+ .quad sys_swapoff /* 115 */
+ .quad compat_sys_sysinfo
+@@ -665,7 +665,7 @@ ia32_sys_call_table:
+ .quad sys_mremap
+ .quad sys_setresuid16
+ .quad sys_getresuid16 /* 165 */
+- .quad sys32_vm86_warning /* vm86 */
++ .quad quiet_ni_syscall /* vm86 */
+ .quad quiet_ni_syscall /* query_module */
+ .quad sys_poll
+ .quad compat_sys_nfsservctl
+diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
+index d3c6408..3b2163f 100644
+--- a/arch/x86/ia32/sys_ia32.c
++++ b/arch/x86/ia32/sys_ia32.c
+@@ -817,20 +817,6 @@ long sys32_fadvise64_64(int fd, __u32 offset_low, __u32 offset_high,
+ advice);
+ }
+
+-long sys32_vm86_warning(void)
+-{
+- struct task_struct *me = current;
+- static char lastcomm[sizeof(me->comm)];
+-
+- if (strncmp(lastcomm, me->comm, sizeof(lastcomm))) {
+- compat_printk(KERN_INFO
+- "%s: vm86 mode not supported on 64 bit kernel\n",
+- me->comm);
+- strncpy(lastcomm, me->comm, sizeof(lastcomm));
+- }
+- return -ENOSYS;
+-}
+-
+ long sys32_lookup_dcookie(u32 addr_low, u32 addr_high,
+ char __user *buf, size_t len)
+ {
+diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
+index 109792b..37f57b0 100644
+--- a/arch/x86/kernel/entry_32.S
++++ b/arch/x86/kernel/entry_32.S
+@@ -225,6 +225,7 @@ ENTRY(ret_from_fork)
+ GET_THREAD_INFO(%ebp)
+ popl %eax
+ CFI_ADJUST_CFA_OFFSET -4
++ret_from_fork_tail:
+ pushl $0x0202 # Reset kernel eflags
+ CFI_ADJUST_CFA_OFFSET 4
+ popfl
+@@ -233,6 +234,25 @@ ENTRY(ret_from_fork)
+ CFI_ENDPROC
+ END(ret_from_fork)
+
++ENTRY(i386_ret_from_resume)
++ CFI_STARTPROC
++ pushl %eax
++ CFI_ADJUST_CFA_OFFSET 4
++ call schedule_tail
++ GET_THREAD_INFO(%ebp)
++ popl %eax
++ CFI_ADJUST_CFA_OFFSET -4
++ movl (%esp),%eax
++ testl %eax,%eax
++ jz 1f
++ pushl %esp
++ call *%eax
++ addl $4,%esp
++1:
++ addl $256,%esp
++ jmp ret_from_fork_tail
++ CFI_ENDPROC
++
+ /*
+ * Return to user mode is not as complex as all this looks,
+ * but we want the default path for a system call return to
+diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
+index 89434d4..f422ac6 100644
+--- a/arch/x86/kernel/entry_64.S
++++ b/arch/x86/kernel/entry_64.S
+@@ -279,7 +279,12 @@ ENTRY(ret_from_fork)
+ popf # reset kernel eflags
+ CFI_ADJUST_CFA_OFFSET -4
+ call schedule_tail
++ret_from_fork_tail:
+ GET_THREAD_INFO(%rcx)
++ btr $TIF_RESUME,TI_flags(%rcx)
++ jc x86_64_ret_from_resume
++
++ret_from_fork_check:
+ testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%rcx)
+ jnz rff_trace
+ rff_action:
+@@ -295,6 +300,19 @@ rff_trace:
+ call syscall_trace_leave
+ GET_THREAD_INFO(%rcx)
+ jmp rff_action
++
++x86_64_ret_from_resume:
++ movq (%rsp),%rax
++ testq %rax,%rax
++ jz 1f
++ movq %rsp,%rdi
++ call *%rax
++1:
++ addq $256,%rsp
++ cmpq $0,ORIG_RAX(%rsp)
++ jge ret_from_fork_tail
++ RESTORE_REST
++ jmp int_ret_from_sys_call
+ CFI_ENDPROC
+ END(ret_from_fork)
+
+@@ -1155,7 +1173,7 @@ ENTRY(kernel_thread)
+ xorl %r9d,%r9d
+
+ # clone now
+- call do_fork
++ call do_fork_kthread
+ movq %rax,RAX(%rsp)
+ xorl %edi,%edi
+
+diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
+index eb9ddd8..ee119de 100644
+--- a/arch/x86/kernel/i387.c
++++ b/arch/x86/kernel/i387.c
+@@ -144,6 +144,7 @@ int init_fpu(struct task_struct *tsk)
+ set_stopped_child_used_math(tsk);
+ return 0;
+ }
++EXPORT_SYMBOL(init_fpu);
+
+ int fpregs_active(struct task_struct *target, const struct user_regset *regset)
+ {
+diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
+index b68e21f..c0d3285 100644
+--- a/arch/x86/kernel/ldt.c
++++ b/arch/x86/kernel/ldt.c
+@@ -12,6 +12,8 @@
+ #include <linux/mm.h>
+ #include <linux/smp.h>
+ #include <linux/vmalloc.h>
++#include <linux/module.h>
++#include <bc/kmem.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -38,9 +40,9 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
+ mincount = (mincount + (PAGE_SIZE / LDT_ENTRY_SIZE - 1)) &
+ (~(PAGE_SIZE / LDT_ENTRY_SIZE - 1));
+ if (mincount * LDT_ENTRY_SIZE > PAGE_SIZE)
+- newldt = vmalloc(mincount * LDT_ENTRY_SIZE);
++ newldt = ub_vmalloc(mincount * LDT_ENTRY_SIZE);
+ else
+- newldt = (void *)__get_free_page(GFP_KERNEL);
++ newldt = (void *)__get_free_page(GFP_KERNEL_UBC);
+
+ if (!newldt)
+ return -ENOMEM;
+@@ -110,6 +112,7 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+ }
+ return retval;
+ }
++EXPORT_SYMBOL_GPL(init_new_context);
+
+ /*
+ * No need to lock the MM as we are the last user
+diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
+index abb78a2..2f01bd2 100644
+--- a/arch/x86/kernel/nmi.c
++++ b/arch/x86/kernel/nmi.c
+@@ -378,6 +378,21 @@ void touch_nmi_watchdog(void)
+ }
+ EXPORT_SYMBOL(touch_nmi_watchdog);
+
++void smp_show_regs(struct pt_regs *regs, void *info)
++{
++ static DEFINE_SPINLOCK(show_regs_lock);
++
++ if (regs == NULL)
++ return;
++
++ spin_lock(&show_regs_lock);
++ bust_spinlocks(1);
++ printk("----------- IPI show regs -----------");
++ show_regs(regs);
++ bust_spinlocks(0);
++ spin_unlock(&show_regs_lock);
++}
++
+ notrace __kprobes int
+ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
+ {
+@@ -423,10 +438,10 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
+ if (!touched && __get_cpu_var(last_irq_sum) == sum) {
+ /*
+ * Ayiee, looks like this CPU is stuck ...
+- * wait a few IRQs (5 seconds) before doing the oops ...
++ * wait a few IRQs (30 seconds) before doing the oops ...
+ */
+ local_inc(&__get_cpu_var(alert_counter));
+- if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz)
++ if (local_read(&__get_cpu_var(alert_counter)) == 30 * nmi_hz)
+ /*
+ * die_nmi will return ONLY if NOTIFY_STOP happens..
+ */
+diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
+index 31f40b2..f269e6b 100644
+--- a/arch/x86/kernel/process_32.c
++++ b/arch/x86/kernel/process_32.c
+@@ -37,6 +37,8 @@
+ #include <linux/tick.h>
+ #include <linux/percpu.h>
+ #include <linux/prctl.h>
++#include <linux/sysctl.h>
++#include <linux/utsrelease.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/pgtable.h>
+@@ -58,6 +60,9 @@
+ #include <asm/idle.h>
+
+ asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
++EXPORT_SYMBOL(ret_from_fork);
++asmlinkage void i386_ret_from_resume(void) __asm__("i386_ret_from_resume");
++EXPORT_SYMBOL_GPL(i386_ret_from_resume);
+
+ DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
+ EXPORT_PER_CPU_SYMBOL(current_task);
+@@ -173,16 +178,17 @@ void __show_registers(struct pt_regs *regs, int all)
+ }
+
+ printk("\n");
+- printk("Pid: %d, comm: %s %s (%s %.*s)\n",
++ printk("Pid: %d, comm: %s %s (%s %.*s %s)\n",
+ task_pid_nr(current), current->comm,
+ print_tainted(), init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+- init_utsname()->version);
++ init_utsname()->version, VZVERSION);
+
+ printk("EIP: %04x:[<%08lx>] EFLAGS: %08lx CPU: %d\n",
+ (u16)regs->cs, regs->ip, regs->flags,
+ smp_processor_id());
+- print_symbol("EIP is at %s\n", regs->ip);
++ if (decode_call_traces)
++ print_symbol("EIP is at %s\n", regs->ip);
+
+ printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
+ regs->ax, regs->bx, regs->cx, regs->dx);
+@@ -218,6 +224,8 @@ void show_regs(struct pt_regs *regs)
+ {
+ __show_registers(regs, 1);
+ show_trace(NULL, regs, &regs->sp, regs->bp);
++ if (!decode_call_traces)
++ printk(" EIP: [<%08lx>]\n", regs->ip);
+ }
+
+ /*
+@@ -226,6 +234,7 @@ void show_regs(struct pt_regs *regs)
+ * the "args".
+ */
+ extern void kernel_thread_helper(void);
++EXPORT_SYMBOL(kernel_thread_helper);
+
+ /*
+ * Create a kernel thread
+@@ -234,6 +243,13 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+ {
+ struct pt_regs regs;
+
++ /* Don't allow kernel_thread() inside VE */
++ if (!ve_allow_kthreads && !ve_is_super(get_exec_env())) {
++ printk("kernel_thread call inside container\n");
++ dump_stack();
++ return -EPERM;
++ }
++
+ memset(&regs, 0, sizeof(regs));
+
+ regs.bx = (unsigned long) fn;
+diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
+index e12e0e4..df9a2c1 100644
+--- a/arch/x86/kernel/process_64.c
++++ b/arch/x86/kernel/process_64.c
+@@ -26,8 +26,10 @@
+ #include <linux/smp.h>
+ #include <linux/slab.h>
+ #include <linux/user.h>
++#include <linux/sysctl.h>
+ #include <linux/interrupt.h>
+ #include <linux/utsname.h>
++#include <linux/utsrelease.h>
+ #include <linux/delay.h>
+ #include <linux/module.h>
+ #include <linux/ptrace.h>
+@@ -52,8 +54,6 @@
+ #include <asm/ia32.h>
+ #include <asm/idle.h>
+
+-asmlinkage extern void ret_from_fork(void);
+-
+ unsigned long kernel_thread_flags = CLONE_VM | CLONE_UNTRACED;
+
+ static ATOMIC_NOTIFIER_HEAD(idle_notifier);
+@@ -162,13 +162,14 @@ void __show_regs(struct pt_regs * regs)
+
+ printk("\n");
+ print_modules();
+- printk("Pid: %d, comm: %.20s %s %s %.*s\n",
++ printk("Pid: %d, comm: %.20s %s %s %.*s %s\n",
+ current->pid, current->comm, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+- init_utsname()->version);
++ init_utsname()->version, VZVERSION);
+ printk("RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->ip);
+- printk_address(regs->ip, 1);
++ if (decode_call_traces)
++ printk_address(regs->ip, 1);
+ printk("RSP: %04lx:%016lx EFLAGS: %08lx\n", regs->ss, regs->sp,
+ regs->flags);
+ printk("RAX: %016lx RBX: %016lx RCX: %016lx\n",
+@@ -216,7 +217,9 @@ void show_regs(struct pt_regs *regs)
+ {
+ printk("CPU %d:", smp_processor_id());
+ __show_regs(regs);
+- show_trace(NULL, regs, (void *)(regs + 1), regs->bp);
++ show_trace(NULL, regs, &regs->sp, regs->bp);
++ if (!decode_call_traces)
++ printk(" EIP: [<%08lx>]\n", regs->ip);
+ }
+
+ /*
+@@ -857,3 +860,20 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
+ unsigned long range_end = mm->brk + 0x02000000;
+ return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+ }
++
++long do_fork_kthread(unsigned long clone_flags,
++ unsigned long stack_start,
++ struct pt_regs *regs,
++ unsigned long stack_size,
++ int __user *parent_tidptr,
++ int __user *child_tidptr)
++{
++ if (ve_allow_kthreads || ve_is_super(get_exec_env()))
++ return do_fork(clone_flags, stack_start, regs, stack_size,
++ parent_tidptr, child_tidptr);
++
++ /* Don't allow kernel_thread() inside VE */
++ printk("kernel_thread call inside container\n");
++ dump_stack();
++ return -EPERM;
++}
+diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
+index 6fb5bcd..9636847 100644
+--- a/arch/x86/kernel/signal_32.c
++++ b/arch/x86/kernel/signal_32.c
+@@ -20,6 +20,7 @@
+ #include <linux/elf.h>
+ #include <linux/smp.h>
+ #include <linux/mm.h>
++#include <linux/freezer.h>
+
+ #include <asm/processor.h>
+ #include <asm/ucontext.h>
+@@ -593,6 +594,9 @@ static void do_signal(struct pt_regs *regs)
+ if (!user_mode(regs))
+ return;
+
++ if (try_to_freeze() && !signal_pending(current))
++ goto no_signal;
++
+ if (current_thread_info()->status & TS_RESTORE_SIGMASK)
+ oldset = &current->saved_sigmask;
+ else
+@@ -622,6 +626,7 @@ static void do_signal(struct pt_regs *regs)
+ return;
+ }
+
++no_signal:
+ /* Did we come from a system call? */
+ if ((long)regs->orig_ax >= 0) {
+ /* Restart the system call - no handlers present */
+diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
+index ca316b5..aab9142 100644
+--- a/arch/x86/kernel/signal_64.c
++++ b/arch/x86/kernel/signal_64.c
+@@ -19,6 +19,7 @@
+ #include <linux/stddef.h>
+ #include <linux/personality.h>
+ #include <linux/compiler.h>
++#include <linux/freezer.h>
+ #include <asm/processor.h>
+ #include <asm/ucontext.h>
+ #include <asm/uaccess.h>
+@@ -489,6 +490,9 @@ static void do_signal(struct pt_regs *regs)
+ if (!user_mode(regs))
+ return;
+
++ if (try_to_freeze() && !signal_pending(current))
++ goto no_signal;
++
+ if (current_thread_info()->status & TS_RESTORE_SIGMASK)
+ oldset = &current->saved_sigmask;
+ else
+@@ -517,6 +521,7 @@ static void do_signal(struct pt_regs *regs)
+ return;
+ }
+
++no_signal:
+ /* Did we come from a system call? */
+ if (current_syscall(regs) >= 0) {
+ /* Restart the system call - no handlers present */
+diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
+index 361b7a4..a4cd6cc 100644
+--- a/arch/x86/kernel/smp.c
++++ b/arch/x86/kernel/smp.c
+@@ -22,6 +22,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/cpu.h>
+
++#include <linux/nmi.h>
+ #include <asm/mtrr.h>
+ #include <asm/tlbflush.h>
+ #include <asm/mmu_context.h>
+@@ -140,6 +141,89 @@ void native_send_call_func_ipi(cpumask_t mask)
+ send_IPI_mask(mask, CALL_FUNCTION_VECTOR);
+ }
+
++static DEFINE_SPINLOCK(nmi_call_lock);
++static struct nmi_call_data_struct {
++ smp_nmi_function func;
++ void *info;
++ atomic_t started;
++ atomic_t finished;
++ cpumask_t cpus_called;
++ int wait;
++} *nmi_call_data;
++
++static int smp_nmi_callback(struct pt_regs *regs, int cpu)
++{
++ smp_nmi_function func;
++ void *info;
++ int wait;
++
++ func = nmi_call_data->func;
++ info = nmi_call_data->info;
++ wait = nmi_call_data->wait;
++ ack_APIC_irq();
++ /* prevent from calling func() multiple times */
++ if (cpu_test_and_set(cpu, nmi_call_data->cpus_called))
++ return 0;
++ /*
++ * notify initiating CPU that I've grabbed the data and am
++ * about to execute the function
++ */
++ mb();
++ atomic_inc(&nmi_call_data->started);
++ /* at this point the nmi_call_data structure is out of scope */
++ irq_enter();
++ func(regs, info);
++ irq_exit();
++ if (wait)
++ atomic_inc(&nmi_call_data->finished);
++
++ return 1;
++}
++
++/*
++ * This function tries to call func(regs, info) on each cpu.
++ * Func must be fast and non-blocking.
++ * May be called with disabled interrupts and from any context.
++ */
++int smp_nmi_call_function(smp_nmi_function func, void *info, int wait)
++{
++ struct nmi_call_data_struct data;
++ int cpus;
++
++ cpus = num_online_cpus() - 1;
++ if (!cpus)
++ return 0;
++
++ data.func = func;
++ data.info = info;
++ data.wait = wait;
++ atomic_set(&data.started, 0);
++ atomic_set(&data.finished, 0);
++ cpus_clear(data.cpus_called);
++ /* prevent this cpu from calling func if NMI happens */
++ cpu_set(smp_processor_id(), data.cpus_called);
++
++ if (!spin_trylock(&nmi_call_lock))
++ return -1;
++
++ nmi_call_data = &data;
++ set_nmi_ipi_callback(smp_nmi_callback);
++ mb();
++
++ /* Send a message to all other CPUs and wait for them to respond */
++ send_IPI_allbutself(APIC_DM_NMI);
++ while (atomic_read(&data.started) != cpus)
++ barrier();
++
++ unset_nmi_ipi_callback();
++ if (wait)
++ while (atomic_read(&data.finished) != cpus)
++ barrier();
++ spin_unlock(&nmi_call_lock);
++
++ return 0;
++}
++
+ static void stop_this_cpu(void *dummy)
+ {
+ local_irq_disable();
+diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
+index 7985c5b..b806c31 100644
+--- a/arch/x86/kernel/smpboot.c
++++ b/arch/x86/kernel/smpboot.c
+@@ -870,6 +870,12 @@ do_rest:
+ initial_code = (unsigned long)start_secondary;
+ stack_start.sp = (void *) c_idle.idle->thread.sp;
+
++#ifdef CONFIG_VE
++ /* Cosmetic: sleep_time won't be changed afterwards for the idle
++ * thread; keep it 0 rather than -cycles. */
++ VE_TASK_INFO(c_idle.idle)->sleep_time = 0;
++#endif
++
+ /* start_ip had better be page-aligned! */
+ start_ip = setup_trampoline();
+
+diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
+index d44395f..5493e66 100644
+--- a/arch/x86/kernel/syscall_table_32.S
++++ b/arch/x86/kernel/syscall_table_32.S
+@@ -332,3 +332,22 @@ ENTRY(sys_call_table)
+ .long sys_dup3 /* 330 */
+ .long sys_pipe2
+ .long sys_inotify_init1
++ .rept 500-(.-sys_call_table)/4
++ .long sys_ni_syscall
++ .endr
++ .long sys_fairsched_mknod /* 500 */
++ .long sys_fairsched_rmnod
++ .long sys_fairsched_chwt
++ .long sys_fairsched_mvpr
++ .long sys_fairsched_rate
++ .long sys_fairsched_vcpus /* 505 */
++ .long sys_ni_syscall
++ .long sys_ni_syscall
++ .long sys_ni_syscall
++ .long sys_ni_syscall
++ .long sys_getluid /* 510 */
++ .long sys_setluid
++ .long sys_setublimit
++ .long sys_ubstat
++ .long sys_ni_syscall
++ .long sys_ni_syscall
+diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c
+index fec1ece..75f4b4e 100644
+--- a/arch/x86/kernel/tlb_32.c
++++ b/arch/x86/kernel/tlb_32.c
+@@ -204,6 +204,8 @@ void flush_tlb_mm(struct mm_struct *mm)
+ preempt_enable();
+ }
+
++EXPORT_SYMBOL(flush_tlb_mm);
++
+ void flush_tlb_page(struct vm_area_struct *vma, unsigned long va)
+ {
+ struct mm_struct *mm = vma->vm_mm;
+diff --git a/arch/x86/kernel/tlb_64.c b/arch/x86/kernel/tlb_64.c
+index dcbf7a1..d8ab2ef 100644
+--- a/arch/x86/kernel/tlb_64.c
++++ b/arch/x86/kernel/tlb_64.c
+@@ -242,6 +242,8 @@ void flush_tlb_mm(struct mm_struct *mm)
+ preempt_enable();
+ }
+
++EXPORT_SYMBOL(flush_tlb_mm);
++
+ void flush_tlb_page(struct vm_area_struct *vma, unsigned long va)
+ {
+ struct mm_struct *mm = vma->vm_mm;
+diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps_32.c
+index 03df8e4..7f2e838 100644
+--- a/arch/x86/kernel/traps_32.c
++++ b/arch/x86/kernel/traps_32.c
+@@ -203,6 +203,8 @@ print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
+ {
+ printk(data);
+ print_symbol(msg, symbol);
++ if (decode_call_traces)
++ print_symbol("%s\n", symbol);
+ printk("\n");
+ }
+
+@@ -240,7 +242,10 @@ show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp, char *log_lvl)
+ {
+ dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
+- printk("%s =======================\n", log_lvl);
++ if (decode_call_traces)
++ printk("%s =======================\n", log_lvl);
++ else
++ printk("%s =<ctx>=", log_lvl);
+ }
+
+ void show_trace(struct task_struct *task, struct pt_regs *regs,
+@@ -271,9 +276,14 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ printk("\n%s ", log_lvl);
+ printk("%08lx ", *stack++);
+ }
+- printk("\n%sCall Trace:\n", log_lvl);
++ if (decode_call_traces)
++ printk("\n%s Call Trace:\n", log_lvl);
++ else
++ printk("\n%s Call Trace: ", log_lvl);
+
+ show_trace_log_lvl(task, regs, sp, bp, log_lvl);
++ if (!decode_call_traces)
++ printk("\n");
+ }
+
+ void show_stack(struct task_struct *task, unsigned long *sp)
+@@ -302,6 +312,8 @@ void dump_stack(void)
+ init_utsname()->version);
+
+ show_trace(current, NULL, &stack, bp);
++ if (!decode_call_traces)
++ printk("\n");
+ }
+
+ EXPORT_SYMBOL(dump_stack);
+@@ -313,8 +325,9 @@ void show_registers(struct pt_regs *regs)
+ print_modules();
+ __show_registers(regs, 0);
+
+- printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)",
++ printk(KERN_EMERG "Process %.*s (pid: %d, veid: %d, ti=%p task=%p task.ti=%p)",
+ TASK_COMM_LEN, current->comm, task_pid_nr(current),
++ VEID(current->ve_task_info.owner_env),
+ current_thread_info(), current, task_thread_info(current));
+ /*
+ * When in-kernel, we also print out the stack and code at the
+@@ -739,6 +752,21 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
+ printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
+ }
+
++/*
++ * Voyager doesn't implement these
++ */
++void __attribute__((weak)) smp_show_regs(struct pt_regs *regs, void *info)
++{
++}
++
++#ifdef CONFIG_SMP
++int __attribute__((weak))
++smp_nmi_call_function(smp_nmi_function func, void *info, int wait)
++{
++ return 0;
++}
++#endif
++
+ static DEFINE_SPINLOCK(nmi_print_lock);
+
+ void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
+@@ -756,6 +784,10 @@ void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
+ printk(" on CPU%d, ip %08lx, registers:\n",
+ smp_processor_id(), regs->ip);
+ show_registers(regs);
++ smp_nmi_call_function(smp_show_regs, NULL, 1);
++ bust_spinlocks(1);
++ if (!decode_call_traces)
++ show_registers(regs);
+ if (do_panic)
+ panic("Non maskable interrupt");
+ console_silent();
+@@ -774,6 +806,13 @@ void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
+ do_exit(SIGSEGV);
+ }
+
++static int dummy_nmi_callback(struct pt_regs *regs, int cpu)
++{
++ return 0;
++}
++
++static nmi_callback_t nmi_ipi_callback = dummy_nmi_callback;
++
+ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
+ {
+ unsigned char reason = 0;
+@@ -829,12 +868,24 @@ notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code)
+
+ ++nmi_count(cpu);
+
+- if (!ignore_nmis)
+- default_do_nmi(regs);
++ if (!ignore_nmis) {
++ if (!nmi_ipi_callback(regs, cpu))
++ default_do_nmi(regs);
++ }
+
+ nmi_exit();
+ }
+
++void set_nmi_ipi_callback(nmi_callback_t callback)
++{
++ nmi_ipi_callback = callback;
++}
++
++void unset_nmi_ipi_callback(void)
++{
++ nmi_ipi_callback = dummy_nmi_callback;
++}
++
+ void stop_nmi(void)
+ {
+ acpi_nmi_disable();
+diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c
+index 513caac..67846bf 100644
+--- a/arch/x86/kernel/traps_64.c
++++ b/arch/x86/kernel/traps_64.c
+@@ -381,7 +381,7 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ if (((long) stack & (THREAD_SIZE-1)) == 0)
+ break;
+ }
+- if (i && ((i % 4) == 0))
++ if (i && ((i % 4) == 0) && decode_call_traces)
+ printk("\n");
+ printk(" %016lx", *stack++);
+ touch_nmi_watchdog();
+@@ -425,10 +425,12 @@ void show_registers(struct pt_regs *regs)
+ struct task_struct *cur = cpu_pda(cpu)->pcurrent;
+
+ sp = regs->sp;
+- printk("CPU %d ", cpu);
++ printk("CPU: %d ", cpu);
+ __show_regs(regs);
+- printk("Process %s (pid: %d, threadinfo %p, task %p)\n",
+- cur->comm, cur->pid, task_thread_info(cur), cur);
++ printk("Process %s (pid: %d, veid=%d, threadinfo %p, task %p)\n",
++ cur->comm, cur->pid,
++ VEID(VE_TASK_INFO(current)->owner_env),
++ task_thread_info(cur), cur);
+
+ /*
+ * When in-kernel, we also print out the stack and code at the
+@@ -830,6 +832,13 @@ asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs)
+ io_check_error(reason, regs);
+ }
+
++static int dummy_nmi_callback(struct pt_regs *regs, int cpu)
++{
++ return 0;
++}
++
++static nmi_callback_t nmi_ipi_callback = dummy_nmi_callback;
++
+ asmlinkage notrace __kprobes void
+ do_nmi(struct pt_regs *regs, long error_code)
+ {
+@@ -837,12 +846,24 @@ do_nmi(struct pt_regs *regs, long error_code)
+
+ add_pda(__nmi_count, 1);
+
+- if (!ignore_nmis)
+- default_do_nmi(regs);
++ if (!ignore_nmis) {
++ if (!nmi_ipi_callback(regs, smp_processor_id()))
++ default_do_nmi(regs);
++ }
+
+ nmi_exit();
+ }
+
++void set_nmi_ipi_callback(nmi_callback_t callback)
++{
++ nmi_ipi_callback = callback;
++}
++
++void unset_nmi_ipi_callback(void)
++{
++ nmi_ipi_callback = dummy_nmi_callback;
++}
++
+ void stop_nmi(void)
+ {
+ acpi_nmi_disable();
+diff --git a/arch/x86/kernel/tsc_sync.c b/arch/x86/kernel/tsc_sync.c
+index 9ffb01c..f523122 100644
+--- a/arch/x86/kernel/tsc_sync.c
++++ b/arch/x86/kernel/tsc_sync.c
+@@ -140,6 +140,10 @@ void __cpuinit check_tsc_sync_source(int cpu)
+ printk(" passed.\n");
+ }
+
++#ifdef CONFIG_VE
++ /* TSC reset. kill whatever might rely on old values */
++ VE_TASK_INFO(current)->wakeup_stamp = 0;
++#endif
+ /*
+ * Reset it - just in case we boot another CPU later:
+ */
+diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
+index b545f37..949ee93 100644
+--- a/arch/x86/kernel/x8664_ksyms_64.c
++++ b/arch/x86/kernel/x8664_ksyms_64.c
+@@ -3,6 +3,7 @@
+
+ #include <linux/module.h>
+ #include <linux/smp.h>
++#include <linux/syscalls.h>
+
+ #include <net/checksum.h>
+
+@@ -17,6 +18,7 @@
+ EXPORT_SYMBOL(mcount);
+ #endif
+
++EXPORT_SYMBOL(kernel_execve);
+ EXPORT_SYMBOL(kernel_thread);
+
+ EXPORT_SYMBOL(__get_user_1);
+diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
+index 455f3fe..1f51c50 100644
+--- a/arch/x86/mm/fault.c
++++ b/arch/x86/mm/fault.c
+@@ -405,7 +405,8 @@ static void show_fault_oops(struct pt_regs *regs, unsigned long error_code,
+ printk(KERN_CONT "paging request");
+ printk(KERN_CONT " at %p\n", (void *) address);
+ printk(KERN_ALERT "IP:");
+- printk_address(regs->ip, 1);
++ if (decode_call_traces)
++ printk_address(regs->ip, 1);
+ dump_pagetable(address);
+ }
+
+@@ -571,7 +572,7 @@ static int vmalloc_fault(unsigned long address)
+ #endif
+ }
+
+-int show_unhandled_signals = 1;
++int show_unhandled_signals = 0;
+
+ /*
+ * This routine handles page faults. It determines the address,
+@@ -678,7 +679,6 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
+ */
+ if (user_mode_vm(regs))
+ error_code |= PF_USER;
+-again:
+ #endif
+ /* When running in the kernel we expect faults to occur only to
+ * addresses in user space. All other faults represent errors in the
+@@ -744,7 +744,6 @@ good_area:
+ }
+
+ #ifdef CONFIG_X86_32
+-survive:
+ #endif
+ /*
+ * If for any reason at all we couldn't handle the fault,
+@@ -804,7 +803,7 @@ bad_area_nosemaphore:
+
+ if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
+ printk_ratelimit()) {
+- printk(
++ ve_printk(VE_LOG,
+ "%s%s[%d]: segfault at %lx ip %p sp %p error %lx",
+ task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
+ tsk->comm, task_pid_nr(tsk), address,
+@@ -878,19 +877,14 @@ no_context:
+ */
+ out_of_memory:
+ up_read(&mm->mmap_sem);
+- if (is_global_init(tsk)) {
+- yield();
+-#ifdef CONFIG_X86_32
+- down_read(&mm->mmap_sem);
+- goto survive;
+-#else
+- goto again;
+-#endif
++ if (error_code & PF_USER) {
++ /*
++ * 0-order allocation always success if something really
++ * fatal not happen: beancounter overdraft or OOM.
++ */
++ force_sig(SIGKILL, tsk);
++ return;
+ }
+-
+- printk("VM: killing process %s\n", tsk->comm);
+- if (error_code & PF_USER)
+- do_group_exit(SIGKILL);
+ goto no_context;
+
+ do_sigbus:
+diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
+index 8f307d9..996b1ce 100644
+--- a/arch/x86/mm/hugetlbpage.c
++++ b/arch/x86/mm/hugetlbpage.c
+@@ -12,6 +12,7 @@
+ #include <linux/slab.h>
+ #include <linux/err.h>
+ #include <linux/sysctl.h>
++#include <linux/module.h>
+ #include <asm/mman.h>
+ #include <asm/tlb.h>
+ #include <asm/tlbflush.h>
+@@ -226,6 +227,7 @@ int pud_huge(pud_t pud)
+ {
+ return !!(pud_val(pud) & _PAGE_PSE);
+ }
++EXPORT_SYMBOL(pmd_huge);
+
+ struct page *
+ follow_huge_pmd(struct mm_struct *mm, unsigned long address,
+diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
+index d503027..c45bb24 100644
+--- a/arch/x86/mm/pgtable.c
++++ b/arch/x86/mm/pgtable.c
+@@ -14,9 +14,9 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
+ struct page *pte;
+
+ #ifdef CONFIG_HIGHPTE
+- pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT|__GFP_ZERO, 0);
++ pte = alloc_pages(GFP_KERNEL_UBC|__GFP_HIGHMEM|__GFP_REPEAT|__GFP_ZERO, 0);
+ #else
+- pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
++ pte = alloc_pages(GFP_KERNEL_UBC|__GFP_REPEAT|__GFP_ZERO, 0);
+ #endif
+ if (pte)
+ pgtable_page_ctor(pte);
+@@ -230,7 +230,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
+ pmd_t *pmds[PREALLOCATED_PMDS];
+ unsigned long flags;
+
+- pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
++ pgd = (pgd_t *)__get_free_page(GFP_KERNEL_UBC | __GFP_ZERO);
+
+ if (pgd == NULL)
+ goto out;
+diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
+index 513f330..b964987 100644
+--- a/arch/x86/vdso/vdso32-setup.c
++++ b/arch/x86/vdso/vdso32-setup.c
+@@ -17,6 +17,8 @@
+ #include <linux/err.h>
+ #include <linux/module.h>
+
++#include <bc/vmpages.h>
++
+ #include <asm/cpufeature.h>
+ #include <asm/msr.h>
+ #include <asm/pgtable.h>
+@@ -37,6 +39,8 @@ enum {
+ #else
+ #define VDSO_DEFAULT VDSO_ENABLED
+ #endif
++#undef VDSO_DEFAULT
++#define VDSO_DEFAULT VDSO_DISABLED
+
+ #ifdef CONFIG_X86_64
+ #define vdso_enabled sysctl_vsyscall32
+@@ -193,7 +197,8 @@ static __init void relocate_vdso(Elf32_Ehdr *ehdr)
+ }
+ }
+
+-static struct page *vdso32_pages[1];
++struct page *vdso32_pages[1];
++EXPORT_SYMBOL_GPL(vdso32_pages);
+
+ #ifdef CONFIG_X86_64
+
+@@ -309,16 +314,30 @@ int __init sysenter_setup(void)
+ return 0;
+ }
+
++EXPORT_SYMBOL_GPL(VDSO32_SYSENTER_RETURN);
++EXPORT_SYMBOL_GPL(VDSO32_PRELINK);
++
+ /* Setup a VMA at program startup for the vsyscall page */
+-int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
++int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack,
++ unsigned long map_address)
+ {
+ struct mm_struct *mm = current->mm;
+- unsigned long addr;
++ unsigned long addr = map_address;
+ int ret = 0;
+ bool compat;
++ unsigned long flags;
+
+- if (vdso_enabled == VDSO_DISABLED)
++ if (vdso_enabled == VDSO_DISABLED && map_address == 0) {
++ current->mm->context.vdso = NULL;
+ return 0;
++ }
++
++ flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC | VM_MAYWRITE |
++ mm->def_flags;
++
++ ret = -ENOMEM;
++ if (ub_memory_charge(mm, PAGE_SIZE, flags, NULL, UB_SOFT))
++ goto err_charge;
+
+ down_write(&mm->mmap_sem);
+
+@@ -328,17 +347,16 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
+
+ map_compat_vdso(compat);
+
+- if (compat)
+- addr = VDSO_HIGH_BASE;
+- else {
+- addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
++ if (!compat || map_address) {
++ addr = get_unmapped_area(NULL, addr, PAGE_SIZE, 0, 0);
+ if (IS_ERR_VALUE(addr)) {
+ ret = addr;
+ goto up_fail;
+ }
+- }
++ } else
++ addr = VDSO_HIGH_BASE;
+
+- if (compat_uses_vma || !compat) {
++ if (compat_uses_vma || !compat || map_address) {
+ /*
+ * MAYWRITE to allow gdb to COW and set breakpoints
+ *
+@@ -364,9 +382,13 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
+
+ up_fail:
+ up_write(&mm->mmap_sem);
++ if (ret < 0)
++ ub_memory_uncharge(mm, PAGE_SIZE, flags, NULL);
++err_charge:
+
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(arch_setup_additional_pages);
+
+ #ifdef CONFIG_X86_64
+
+diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
+index 257ba4a..36cd3c0 100644
+--- a/arch/x86/vdso/vma.c
++++ b/arch/x86/vdso/vma.c
+@@ -4,6 +4,7 @@
+ * Subject to the GPL, v.2
+ */
+ #include <linux/mm.h>
++#include <linux/module.h>
+ #include <linux/err.h>
+ #include <linux/sched.h>
+ #include <linux/init.h>
+@@ -98,17 +99,23 @@ static unsigned long vdso_addr(unsigned long start, unsigned len)
+
+ /* Setup a VMA at program startup for the vsyscall page.
+ Not called for compat tasks */
+-int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
++int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack,
++ unsigned long map_address)
+ {
+ struct mm_struct *mm = current->mm;
+ unsigned long addr;
+ int ret;
+
+- if (!vdso_enabled)
++ if (!vdso_enabled && map_address == 0) {
++ current->mm->context.vdso = NULL;
+ return 0;
++ }
+
+ down_write(&mm->mmap_sem);
+- addr = vdso_addr(mm->start_stack, vdso_size);
++ if (map_address)
++ addr = map_address;
++ else
++ addr = vdso_addr(mm->start_stack, vdso_size);
+ addr = get_unmapped_area(NULL, addr, vdso_size, 0, 0);
+ if (IS_ERR_VALUE(addr)) {
+ ret = addr;
+@@ -128,6 +135,7 @@ up_fail:
+ up_write(&mm->mmap_sem);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(arch_setup_additional_pages);
+
+ static __init int vdso_setup(char *s)
+ {
+diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
+index 1e2aff8..277481e 100644
+--- a/block/cfq-iosched.c
++++ b/block/cfq-iosched.c
+@@ -12,6 +12,11 @@
+ #include <linux/rbtree.h>
+ #include <linux/ioprio.h>
+ #include <linux/blktrace_api.h>
++#include <linux/cfq-iosched.h>
++#include <bc/beancounter.h>
++#include <bc/io_prio.h>
++#include <bc/io_acct.h>
++#include <bc/hash.h>
+
+ /*
+ * tunables
+@@ -27,6 +32,7 @@ static const int cfq_slice_sync = HZ / 10;
+ static int cfq_slice_async = HZ / 25;
+ static const int cfq_slice_async_rq = 2;
+ static int cfq_slice_idle = HZ / 125;
++static int cfq_ub_slice = HZ / 2;
+
+ /*
+ * offset from end of service tree
+@@ -44,14 +50,12 @@ static int cfq_slice_idle = HZ / 125;
+ ((struct cfq_io_context *) (rq)->elevator_private)
+ #define RQ_CFQQ(rq) (struct cfq_queue *) ((rq)->elevator_private2)
+
+-static struct kmem_cache *cfq_pool;
+ static struct kmem_cache *cfq_ioc_pool;
+
+ static DEFINE_PER_CPU(unsigned long, ioc_count);
+ static struct completion *ioc_gone;
+ static DEFINE_SPINLOCK(ioc_gone_lock);
+
+-#define CFQ_PRIO_LISTS IOPRIO_BE_NR
+ #define cfq_class_idle(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE)
+ #define cfq_class_rt(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_RT)
+
+@@ -60,106 +64,6 @@ static DEFINE_SPINLOCK(ioc_gone_lock);
+
+ #define sample_valid(samples) ((samples) > 80)
+
+-/*
+- * Most of our rbtree usage is for sorting with min extraction, so
+- * if we cache the leftmost node we don't have to walk down the tree
+- * to find it. Idea borrowed from Ingo Molnars CFS scheduler. We should
+- * move this into the elevator for the rq sorting as well.
+- */
+-struct cfq_rb_root {
+- struct rb_root rb;
+- struct rb_node *left;
+-};
+-#define CFQ_RB_ROOT (struct cfq_rb_root) { RB_ROOT, NULL, }
+-
+-/*
+- * Per block device queue structure
+- */
+-struct cfq_data {
+- struct request_queue *queue;
+-
+- /*
+- * rr list of queues with requests and the count of them
+- */
+- struct cfq_rb_root service_tree;
+- unsigned int busy_queues;
+-
+- int rq_in_driver;
+- int sync_flight;
+- int hw_tag;
+-
+- /*
+- * idle window management
+- */
+- struct timer_list idle_slice_timer;
+- struct work_struct unplug_work;
+-
+- struct cfq_queue *active_queue;
+- struct cfq_io_context *active_cic;
+-
+- /*
+- * async queue for each priority case
+- */
+- struct cfq_queue *async_cfqq[2][IOPRIO_BE_NR];
+- struct cfq_queue *async_idle_cfqq;
+-
+- sector_t last_position;
+- unsigned long last_end_request;
+-
+- /*
+- * tunables, see top of file
+- */
+- unsigned int cfq_quantum;
+- unsigned int cfq_fifo_expire[2];
+- unsigned int cfq_back_penalty;
+- unsigned int cfq_back_max;
+- unsigned int cfq_slice[2];
+- unsigned int cfq_slice_async_rq;
+- unsigned int cfq_slice_idle;
+-
+- struct list_head cic_list;
+-};
+-
+-/*
+- * Per process-grouping structure
+- */
+-struct cfq_queue {
+- /* reference count */
+- atomic_t ref;
+- /* various state flags, see below */
+- unsigned int flags;
+- /* parent cfq_data */
+- struct cfq_data *cfqd;
+- /* service_tree member */
+- struct rb_node rb_node;
+- /* service_tree key */
+- unsigned long rb_key;
+- /* sorted list of pending requests */
+- struct rb_root sort_list;
+- /* if fifo isn't expired, next request to serve */
+- struct request *next_rq;
+- /* requests queued in sort_list */
+- int queued[2];
+- /* currently allocated requests */
+- int allocated[2];
+- /* fifo list of requests in sort_list */
+- struct list_head fifo;
+-
+- unsigned long slice_end;
+- long slice_resid;
+-
+- /* pending metadata requests */
+- int meta_pending;
+- /* number of requests that are on the dispatch list or inside driver */
+- int dispatched;
+-
+- /* io prio of this group */
+- unsigned short ioprio, org_ioprio;
+- unsigned short ioprio_class, org_ioprio_class;
+-
+- pid_t pid;
+-};
+-
+ enum cfqq_state_flags {
+ CFQ_CFQQ_FLAG_on_rr = 0, /* on round-robin busy list */
+ CFQ_CFQQ_FLAG_wait_request, /* waiting for a request */
+@@ -209,6 +113,67 @@ CFQ_CFQQ_FNS(sync);
+ static void cfq_dispatch_insert(struct request_queue *, struct request *);
+ static struct cfq_queue *cfq_get_queue(struct cfq_data *, int,
+ struct io_context *, gfp_t);
++static void cfq_put_queue(struct cfq_queue *cfqq);
++
++static void __cfq_put_async_queues(struct cfq_bc_data *cfq_bc)
++{
++ int i;
++
++ for (i = 0; i < CFQ_PRIO_LISTS; i++) {
++ if (cfq_bc->async_cfqq[0][i]) {
++ cfq_put_queue(cfq_bc->async_cfqq[0][i]);
++ cfq_bc->async_cfqq[0][i] = NULL;
++ }
++ if (cfq_bc->async_cfqq[1][i]) {
++ cfq_put_queue(cfq_bc->async_cfqq[1][i]);
++ cfq_bc->async_cfqq[1][i] = NULL;
++ }
++ }
++ if (cfq_bc->async_idle_cfqq) {
++ cfq_put_queue(cfq_bc->async_idle_cfqq);
++ cfq_bc->async_idle_cfqq = NULL;
++ }
++}
++
++#ifdef CONFIG_BC_IO_SCHED
++static inline struct ub_iopriv *cfqq_ub_iopriv(struct cfq_data *cfqd, int sync)
++{
++ int mode;
++
++ mode = sync ? cfqd->virt_mode : cfqd->write_virt_mode;
++ return mode ? &get_io_ub()->iopriv : &get_ub0()->iopriv;
++}
++
++static inline void cfq_put_async_queues(struct cfq_data *cfqd)
++{
++ struct user_beancounter *ub;
++ struct cfq_bc_data *cfq_bc;
++
++ rcu_read_lock();
++ for_each_beancounter(ub) {
++ write_lock(&ub->iopriv.cfq_bc_list_lock);
++ cfq_bc = __find_cfq_bc(&ub->iopriv, cfqd);
++ if (!cfq_bc) {
++ write_unlock(&ub->iopriv.cfq_bc_list_lock);
++ continue;
++ }
++ __cfq_put_async_queues(cfq_bc);
++ write_unlock(&ub->iopriv.cfq_bc_list_lock);
++ }
++ rcu_read_unlock();
++}
++#else
++static inline struct ub_iopriv *cfqq_ub_iopriv(struct cfq_data *cfqd, int sync)
++{
++ return NULL;
++}
++
++static inline void cfq_put_async_queues(struct cfq_data *cfqd)
++{
++ __cfq_put_async_queues(&cfqd->cfq_bc);
++}
++#endif
++
+ static struct cfq_io_context *cfq_cic_lookup(struct cfq_data *,
+ struct io_context *);
+
+@@ -298,6 +263,11 @@ static inline int cfq_slice_used(struct cfq_queue *cfqq)
+ return 1;
+ }
+
++static inline struct user_beancounter *ub_by_iopriv(struct ub_iopriv *iopriv)
++{
++ return container_of(iopriv, struct user_beancounter, iopriv);
++}
++
+ /*
+ * Lifted from AS - choose which of rq1 and rq2 that is best served now.
+ * We choose the request that is closest to the head right now. Distance
+@@ -461,6 +431,7 @@ static unsigned long cfq_slice_offset(struct cfq_data *cfqd,
+ static void cfq_service_tree_add(struct cfq_data *cfqd,
+ struct cfq_queue *cfqq, int add_front)
+ {
++ struct cfq_bc_data *cfq_bc = cfqq->cfq_bc;
+ struct rb_node **p, *parent;
+ struct cfq_queue *__cfqq;
+ unsigned long rb_key;
+@@ -468,7 +439,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd,
+
+ if (cfq_class_idle(cfqq)) {
+ rb_key = CFQ_IDLE_DELAY;
+- parent = rb_last(&cfqd->service_tree.rb);
++ parent = rb_last(&cfq_bc->service_tree.rb);
+ if (parent && parent != &cfqq->rb_node) {
+ __cfqq = rb_entry(parent, struct cfq_queue, rb_node);
+ rb_key += __cfqq->rb_key;
+@@ -488,12 +459,12 @@ static void cfq_service_tree_add(struct cfq_data *cfqd,
+ if (rb_key == cfqq->rb_key)
+ return;
+
+- cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree);
++ cfq_rb_erase(&cfqq->rb_node, &cfq_bc->service_tree);
+ }
+
+ left = 1;
+ parent = NULL;
+- p = &cfqd->service_tree.rb.rb_node;
++ p = &cfq_bc->service_tree.rb.rb_node;
+ while (*p) {
+ struct rb_node **n;
+
+@@ -525,11 +496,11 @@ static void cfq_service_tree_add(struct cfq_data *cfqd,
+ }
+
+ if (left)
+- cfqd->service_tree.left = &cfqq->rb_node;
++ cfq_bc->service_tree.left = &cfqq->rb_node;
+
+ cfqq->rb_key = rb_key;
+ rb_link_node(&cfqq->rb_node, parent, p);
+- rb_insert_color(&cfqq->rb_node, &cfqd->service_tree.rb);
++ rb_insert_color(&cfqq->rb_node, &cfq_bc->service_tree.rb);
+ }
+
+ /*
+@@ -554,6 +525,7 @@ static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+ BUG_ON(cfq_cfqq_on_rr(cfqq));
+ cfq_mark_cfqq_on_rr(cfqq);
+ cfqd->busy_queues++;
++ bc_inc_rqnum(cfqq);
+
+ cfq_resort_rr_list(cfqd, cfqq);
+ }
+@@ -564,15 +536,20 @@ static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+ */
+ static void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+ {
++ struct cfq_bc_data *cfq_bc;
++
+ cfq_log_cfqq(cfqd, cfqq, "del_from_rr");
+ BUG_ON(!cfq_cfqq_on_rr(cfqq));
+ cfq_clear_cfqq_on_rr(cfqq);
+
++ cfq_bc = cfqq->cfq_bc;
++
+ if (!RB_EMPTY_NODE(&cfqq->rb_node))
+- cfq_rb_erase(&cfqq->rb_node, &cfqd->service_tree);
++ cfq_rb_erase(&cfqq->rb_node, &cfq_bc->service_tree);
+
+ BUG_ON(!cfqd->busy_queues);
+ cfqd->busy_queues--;
++ bc_dec_rqnum(cfqq);
+ }
+
+ /*
+@@ -692,8 +669,7 @@ static void cfq_remove_request(struct request *rq)
+ }
+ }
+
+-static int cfq_merge(struct request_queue *q, struct request **req,
+- struct bio *bio)
++static int cfq_merge(struct request_queue *q, struct request **req, struct bio *bio)
+ {
+ struct cfq_data *cfqd = q->elevator->elevator_data;
+ struct request *__rq;
+@@ -822,10 +798,16 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, int timed_out)
+ */
+ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
+ {
+- if (RB_EMPTY_ROOT(&cfqd->service_tree.rb))
++ struct cfq_bc_data *cfq_bc;
++
++ cfq_bc = cfqd->active_cfq_bc;
++ if (!cfq_bc)
+ return NULL;
+
+- return cfq_rb_first(&cfqd->service_tree);
++ if (RB_EMPTY_ROOT(&cfq_bc->service_tree.rb))
++ return NULL;
++
++ return cfq_rb_first(&cfq_bc->service_tree);
+ }
+
+ /*
+@@ -833,9 +815,17 @@ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd)
+ */
+ static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd)
+ {
+- struct cfq_queue *cfqq;
++ struct cfq_queue *cfqq = NULL;
++ struct cfq_bc_data *cfq_bc;
++
++ bc_schedule_active(cfqd);
++
++ cfq_bc = cfqd->active_cfq_bc;
++ if (!cfq_bc)
++ goto out;
+
+ cfqq = cfq_get_next_queue(cfqd);
++out:
+ __cfq_set_active_queue(cfqd, cfqq);
+ return cfqq;
+ }
+@@ -935,6 +925,7 @@ static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)
+
+ cfq_remove_request(rq);
+ cfqq->dispatched++;
++ cfqq->cfq_bc->on_dispatch++;
+ elv_dispatch_sort(q, rq);
+
+ if (cfq_cfqq_sync(cfqq))
+@@ -993,7 +984,7 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
+ /*
+ * The active queue has run out of time, expire it and select new.
+ */
+- if (cfq_slice_used(cfqq))
++ if (cfq_slice_used(cfqq) || bc_expired(cfqd))
+ goto expire;
+
+ /*
+@@ -1092,14 +1083,33 @@ static int __cfq_forced_dispatch_cfqq(struct cfq_queue *cfqq)
+ * Drain our current requests. Used for barriers and when switching
+ * io schedulers on-the-fly.
+ */
+-static int cfq_forced_dispatch(struct cfq_data *cfqd)
++static int __cfq_forced_dispatch(struct cfq_bc_data *cfq_bc)
+ {
+ struct cfq_queue *cfqq;
+ int dispatched = 0;
+
+- while ((cfqq = cfq_rb_first(&cfqd->service_tree)) != NULL)
++ while ((cfqq = cfq_rb_first(&cfq_bc->service_tree)) != NULL)
+ dispatched += __cfq_forced_dispatch_cfqq(cfqq);
+
++ return dispatched;
++}
++
++static int cfq_forced_dispatch(struct cfq_data *cfqd)
++{
++ struct cfq_bc_data *cfq_bc;
++ struct cfq_bc_data *cfq_bc_tmp;
++ int dispatched;
++
++ dispatched = 0;
++ /*
++ * We use here _safe iterating, because
++ * __cfq_forced_dispatch() produces list_del() implicitly
++ */
++ list_for_each_entry_safe(cfq_bc, cfq_bc_tmp,
++ &cfqd->act_cfq_bc_head, act_cfq_bc_list) {
++ dispatched += __cfq_forced_dispatch(cfq_bc);
++ }
++
+ cfq_slice_expired(cfqd, 0);
+
+ BUG_ON(cfqd->busy_queues);
+@@ -1289,6 +1299,10 @@ static void __cfq_exit_single_io_context(struct cfq_data *cfqd,
+ if (ioc->ioc_data == cic)
+ rcu_assign_pointer(ioc->ioc_data, NULL);
+
++ /*
++ * cic->cfqq[ASYNC] is always NULL and the put of async queues
++ * happens on appropriate bc death or device unplug
++ */
+ if (cic->cfqq[ASYNC]) {
+ cfq_exit_cfqq(cfqd, cic->cfqq[ASYNC]);
+ cic->cfqq[ASYNC] = NULL;
+@@ -1397,6 +1411,10 @@ static void changed_ioprio(struct io_context *ioc, struct cfq_io_context *cic)
+
+ spin_lock_irqsave(cfqd->queue->queue_lock, flags);
+
++ /*
++ * cic->cfqq[ASYNC] is always NULL, ioprio change
++ * for async queues happens automatically
++ */
+ cfqq = cic->cfqq[ASYNC];
+ if (cfqq) {
+ struct cfq_queue *new_cfqq;
+@@ -1426,8 +1444,11 @@ cfq_find_alloc_queue(struct cfq_data *cfqd, int is_sync,
+ {
+ struct cfq_queue *cfqq, *new_cfqq = NULL;
+ struct cfq_io_context *cic;
++ struct ub_iopriv *iopriv;
++ struct cfq_bc_data *cfq_bc = NULL;
+
+ retry:
++ iopriv = cfqq_ub_iopriv(cfqd, is_sync);
+ cic = cfq_cic_lookup(cfqd, ioc);
+ /* cic always exists here */
+ cfqq = cic_to_cfqq(cic, is_sync);
+@@ -1445,18 +1466,32 @@ retry:
+ */
+ spin_unlock_irq(cfqd->queue->queue_lock);
+ new_cfqq = kmem_cache_alloc_node(cfq_pool,
+- gfp_mask | __GFP_NOFAIL | __GFP_ZERO,
++ gfp_mask|__GFP_NOFAIL|__GFP_ZERO,
+ cfqd->queue->node);
++ if (new_cfqq) {
++ cfq_bc = bc_findcreate_cfq_bc(iopriv,
++ cfqd, gfp_mask);
++ if (!cfq_bc) {
++ kmem_cache_free(cfq_pool, new_cfqq);
++ new_cfqq = NULL;
++ }
++ }
+ spin_lock_irq(cfqd->queue->queue_lock);
+ goto retry;
+ } else {
+ cfqq = kmem_cache_alloc_node(cfq_pool,
+- gfp_mask | __GFP_ZERO,
+- cfqd->queue->node);
++ gfp_mask|__GFP_ZERO, cfqd->queue->node);
+ if (!cfqq)
+ goto out;
++ cfq_bc = bc_findcreate_cfq_bc(iopriv, cfqd, gfp_mask);
++ if (!cfq_bc) {
++ kmem_cache_free(cfq_pool, cfqq);
++ cfqq = NULL;
++ goto out;
++ }
+ }
+
++ cfqq->cfq_bc = cfq_bc;
+ RB_CLEAR_NODE(&cfqq->rb_node);
+ INIT_LIST_HEAD(&cfqq->fifo);
+
+@@ -1486,15 +1521,15 @@ out:
+ }
+
+ static struct cfq_queue **
+-cfq_async_queue_prio(struct cfq_data *cfqd, int ioprio_class, int ioprio)
++cfq_async_queue_prio(struct cfq_bc_data *cfq_bc, int ioprio_class, int ioprio)
+ {
+ switch (ioprio_class) {
+ case IOPRIO_CLASS_RT:
+- return &cfqd->async_cfqq[0][ioprio];
++ return &cfq_bc->async_cfqq[0][ioprio];
+ case IOPRIO_CLASS_BE:
+- return &cfqd->async_cfqq[1][ioprio];
++ return &cfq_bc->async_cfqq[1][ioprio];
+ case IOPRIO_CLASS_IDLE:
+- return &cfqd->async_idle_cfqq;
++ return &cfq_bc->async_idle_cfqq;
+ default:
+ BUG();
+ }
+@@ -1508,9 +1543,16 @@ cfq_get_queue(struct cfq_data *cfqd, int is_sync, struct io_context *ioc,
+ const int ioprio_class = task_ioprio_class(ioc);
+ struct cfq_queue **async_cfqq = NULL;
+ struct cfq_queue *cfqq = NULL;
++ struct cfq_bc_data *cfq_bc;
++ struct ub_iopriv *iopriv;
++
++ iopriv = cfqq_ub_iopriv(cfqd, is_sync);
+
+ if (!is_sync) {
+- async_cfqq = cfq_async_queue_prio(cfqd, ioprio_class, ioprio);
++ cfq_bc = bc_findcreate_cfq_bc(iopriv, cfqd, gfp_mask);
++ if (!cfq_bc)
++ return NULL;
++ async_cfqq = cfq_async_queue_prio(cfq_bc, ioprio_class, ioprio);
+ cfqq = *async_cfqq;
+ }
+
+@@ -1894,6 +1936,7 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
+ WARN_ON(!cfqq->dispatched);
+ cfqd->rq_in_driver--;
+ cfqq->dispatched--;
++ cfqq->cfq_bc->on_dispatch--;
+
+ if (cfq_cfqq_sync(cfqq))
+ cfqd->sync_flight--;
+@@ -2006,6 +2049,7 @@ static void cfq_put_request(struct request *rq)
+ rq->elevator_private = NULL;
+ rq->elevator_private2 = NULL;
+
++ put_beancounter(ub_by_iopriv(cfqq->cfq_bc->ub_iopriv));
+ cfq_put_queue(cfqq);
+ }
+ }
+@@ -2022,14 +2066,19 @@ cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
+ const int is_sync = rq_is_sync(rq);
+ struct cfq_queue *cfqq;
+ unsigned long flags;
++ struct ub_iopriv *iopriv;
++ struct cfq_bc_data *cfq_bc = NULL;
+
+ might_sleep_if(gfp_mask & __GFP_WAIT);
+
+ cic = cfq_get_io_context(cfqd, gfp_mask);
++ iopriv = cfqq_ub_iopriv(cfqd, is_sync);
++ if (!is_sync)
++ cfq_bc = bc_findcreate_cfq_bc(iopriv, cfqd, gfp_mask);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+
+- if (!cic)
++ if (!cic || (!is_sync && cfq_bc == NULL))
+ goto queue_fail;
+
+ cfqq = cic_to_cfqq(cic, is_sync);
+@@ -2050,6 +2099,7 @@ cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
+
+ rq->elevator_private = cic;
+ rq->elevator_private2 = cfqq;
++ get_beancounter(ub_by_iopriv(cfqq->cfq_bc->ub_iopriv));
+ return 0;
+
+ queue_fail:
+@@ -2127,21 +2177,6 @@ static void cfq_shutdown_timer_wq(struct cfq_data *cfqd)
+ kblockd_flush_work(&cfqd->unplug_work);
+ }
+
+-static void cfq_put_async_queues(struct cfq_data *cfqd)
+-{
+- int i;
+-
+- for (i = 0; i < IOPRIO_BE_NR; i++) {
+- if (cfqd->async_cfqq[0][i])
+- cfq_put_queue(cfqd->async_cfqq[0][i]);
+- if (cfqd->async_cfqq[1][i])
+- cfq_put_queue(cfqd->async_cfqq[1][i]);
+- }
+-
+- if (cfqd->async_idle_cfqq)
+- cfq_put_queue(cfqd->async_idle_cfqq);
+-}
+-
+ static void cfq_exit_queue(elevator_t *e)
+ {
+ struct cfq_data *cfqd = e->elevator_data;
+@@ -2168,6 +2203,8 @@ static void cfq_exit_queue(elevator_t *e)
+
+ cfq_shutdown_timer_wq(cfqd);
+
++ bc_cfq_exit_queue(cfqd);
++
+ kfree(cfqd);
+ }
+
+@@ -2175,11 +2212,19 @@ static void *cfq_init_queue(struct request_queue *q)
+ {
+ struct cfq_data *cfqd;
+
+- cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node);
++ cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL|__GFP_ZERO, q->node);
+ if (!cfqd)
+ return NULL;
+
+- cfqd->service_tree = CFQ_RB_ROOT;
++ INIT_LIST_HEAD(&cfqd->act_cfq_bc_head);
++#ifndef CONFIG_BC_IO_SCHED
++ cfq_init_cfq_bc(&cfqd->cfq_bc);
++ /*
++ * Adding ub0 to active list in order to serve force dispatching
++ * case uniformally. Note, that nobody removes ub0 from this list.
++ */
++ list_add_tail(&cfqd->cfq_bc.act_cfq_bc_list, &cfqd->act_cfq_bc_head);
++#endif
+ INIT_LIST_HEAD(&cfqd->cic_list);
+
+ cfqd->queue = q;
+@@ -2200,6 +2245,9 @@ static void *cfq_init_queue(struct request_queue *q)
+ cfqd->cfq_slice[1] = cfq_slice_sync;
+ cfqd->cfq_slice_async_rq = cfq_slice_async_rq;
+ cfqd->cfq_slice_idle = cfq_slice_idle;
++ cfqd->cfq_ub_slice = cfq_ub_slice;
++ cfqd->virt_mode = 1;
++ cfqd->write_virt_mode = 1;
+
+ return cfqd;
+ }
+@@ -2268,6 +2316,9 @@ SHOW_FUNCTION(cfq_slice_idle_show, cfqd->cfq_slice_idle, 1);
+ SHOW_FUNCTION(cfq_slice_sync_show, cfqd->cfq_slice[1], 1);
+ SHOW_FUNCTION(cfq_slice_async_show, cfqd->cfq_slice[0], 1);
+ SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0);
++SHOW_FUNCTION(cfq_ub_slice_show, cfqd->cfq_ub_slice, 1);
++SHOW_FUNCTION(cfq_virt_mode_show, cfqd->virt_mode, 0);
++SHOW_FUNCTION(cfq_write_virt_mode_show, cfqd->write_virt_mode, 0);
+ #undef SHOW_FUNCTION
+
+ #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
+@@ -2299,6 +2350,9 @@ STORE_FUNCTION(cfq_slice_sync_store, &cfqd->cfq_slice[1], 1, UINT_MAX, 1);
+ STORE_FUNCTION(cfq_slice_async_store, &cfqd->cfq_slice[0], 1, UINT_MAX, 1);
+ STORE_FUNCTION(cfq_slice_async_rq_store, &cfqd->cfq_slice_async_rq, 1,
+ UINT_MAX, 0);
++STORE_FUNCTION(cfq_ub_slice_store, &cfqd->cfq_ub_slice, 1, UINT_MAX, 1);
++STORE_FUNCTION(cfq_virt_mode_store, &cfqd->virt_mode, 0, 1, 0);
++STORE_FUNCTION(cfq_write_virt_mode_store, &cfqd->write_virt_mode, 0, 1, 0);
+ #undef STORE_FUNCTION
+
+ #define CFQ_ATTR(name) \
+@@ -2314,6 +2368,9 @@ static struct elv_fs_entry cfq_attrs[] = {
+ CFQ_ATTR(slice_async),
+ CFQ_ATTR(slice_async_rq),
+ CFQ_ATTR(slice_idle),
++ CFQ_ATTR(ub_slice),
++ CFQ_ATTR(virt_mode),
++ CFQ_ATTR(write_virt_mode),
+ __ATTR_NULL
+ };
+
+@@ -2337,6 +2394,7 @@ static struct elevator_type iosched_cfq = {
+ .elevator_init_fn = cfq_init_queue,
+ .elevator_exit_fn = cfq_exit_queue,
+ .trim = cfq_free_io_context,
++ .put_queue = cfq_put_queue,
+ },
+ .elevator_attrs = cfq_attrs,
+ .elevator_name = "cfq",
+diff --git a/block/elevator.c b/block/elevator.c
+index ed6f8f3..c0aad1b 100644
+--- a/block/elevator.c
++++ b/block/elevator.c
+@@ -40,6 +40,9 @@
+ static DEFINE_SPINLOCK(elv_list_lock);
+ static LIST_HEAD(elv_list);
+
++struct kmem_cache *cfq_pool;
++EXPORT_SYMBOL_GPL(cfq_pool);
++
+ /*
+ * Merge hash stuff.
+ */
+@@ -1034,12 +1037,12 @@ void elv_unregister(struct elevator_type *e)
+ */
+ if (e->ops.trim) {
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ task_lock(p);
+ if (p->io_context)
+ e->ops.trim(p->io_context);
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ read_unlock(&tasklist_lock);
+ }
+
+diff --git a/block/genhd.c b/block/genhd.c
+index e0ce23a..3156ed4 100644
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -407,7 +407,7 @@ static int __init genhd_device_init(void)
+ {
+ int error;
+
+- block_class.dev_kobj = sysfs_dev_block_kobj;
++ block_class.dev_kobj = ve_sysfs_dev_block_kobj;
+ error = class_register(&block_class);
+ if (unlikely(error))
+ return error;
+@@ -563,6 +563,7 @@ static void disk_release(struct device *dev)
+ struct class block_class = {
+ .name = "block",
+ };
++EXPORT_SYMBOL(block_class);
+
+ static struct device_type disk_type = {
+ .name = "disk",
+diff --git a/drivers/base/base.h b/drivers/base/base.h
+index 31dc0cd..a56363c 100644
+--- a/drivers/base/base.h
++++ b/drivers/base/base.h
+@@ -95,7 +95,12 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
+
+ extern int devres_release_all(struct device *dev);
+
++#ifndef CONFIG_VE
+ extern struct kset *devices_kset;
++#define ve_devices_kset devices_kset
++#else
++#define ve_devices_kset (get_exec_env()->devices_kset)
++#endif
+
+ #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
+ extern void module_add_driver(struct module *mod, struct device_driver *drv);
+diff --git a/drivers/base/class.c b/drivers/base/class.c
+index cc5e28c..a6f0802 100644
+--- a/drivers/base/class.c
++++ b/drivers/base/class.c
+@@ -19,6 +19,8 @@
+ #include <linux/slab.h>
+ #include <linux/genhd.h>
+ #include <linux/mutex.h>
++#include <linux/sched.h>
++#include <linux/ve.h>
+ #include "base.h"
+
+ #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
+@@ -72,8 +74,14 @@ static struct kobj_type class_ktype = {
+ };
+
+ /* Hotplug events for classes go to the class class_subsys */
+-static struct kset *class_kset;
++struct kset *class_kset;
++EXPORT_SYMBOL_GPL(class_kset);
+
++#ifndef CONFIG_VE
++#define visible_class_kset class_kset
++#else
++#define visible_class_kset (get_exec_env()->class_kset)
++#endif
+
+ int class_create_file(struct class *cls, const struct class_attribute *attr)
+ {
+@@ -157,14 +165,14 @@ int __class_register(struct class *cls, struct lock_class_key *key)
+
+ /* set the default /sys/dev directory for devices of this class */
+ if (!cls->dev_kobj)
+- cls->dev_kobj = sysfs_dev_char_kobj;
++ cls->dev_kobj = ve_sysfs_dev_char_kobj;
+
+ #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
+ /* let the block class directory show up in the root of sysfs */
+ if (cls != &block_class)
+- cp->class_subsys.kobj.kset = class_kset;
++ cp->class_subsys.kobj.kset = visible_class_kset;
+ #else
+- cp->class_subsys.kobj.kset = class_kset;
++ cp->class_subsys.kobj.kset = visible_class_kset;
+ #endif
+ cp->class_subsys.kobj.ktype = &class_ktype;
+ cp->class = cls;
+@@ -418,13 +426,20 @@ void class_interface_unregister(struct class_interface *class_intf)
+ class_put(parent);
+ }
+
+-int __init classes_init(void)
++int classes_init(void)
+ {
+- class_kset = kset_create_and_add("class", NULL, NULL);
+- if (!class_kset)
++ visible_class_kset = kset_create_and_add("class", NULL, NULL);
++ if (!visible_class_kset)
+ return -ENOMEM;
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(classes_init);
++
++void classes_fini(void)
++{
++ kset_unregister(visible_class_kset);
++}
++EXPORT_SYMBOL_GPL(classes_fini);
+
+ EXPORT_SYMBOL_GPL(class_create_file);
+ EXPORT_SYMBOL_GPL(class_remove_file);
+diff --git a/drivers/base/core.c b/drivers/base/core.c
+index d021c98..29d6e7d 100644
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -22,15 +22,22 @@
+ #include <linux/kallsyms.h>
+ #include <linux/semaphore.h>
+ #include <linux/mutex.h>
++#include <linux/sched.h>
++#include <linux/ve.h>
+
+ #include "base.h"
+ #include "power/power.h"
+
+ int (*platform_notify)(struct device *dev) = NULL;
+ int (*platform_notify_remove)(struct device *dev) = NULL;
++#ifndef CONFIG_VE
+ static struct kobject *dev_kobj;
++#define ve_dev_kobj dev_kobj
+ struct kobject *sysfs_dev_char_kobj;
+ struct kobject *sysfs_dev_block_kobj;
++#else
++#define ve_dev_kobj (get_exec_env()->dev_kobj)
++#endif
+
+ #ifdef CONFIG_BLOCK
+ static inline int device_is_not_partition(struct device *dev)
+@@ -419,8 +426,9 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
+ static struct device_attribute devt_attr =
+ __ATTR(dev, S_IRUGO, show_dev, NULL);
+
+-/* kset to create /sys/devices/ */
++#ifndef CONFIG_VE
+ struct kset *devices_kset;
++#endif
+
+ /**
+ * device_create_file - create sysfs attribute file for device.
+@@ -531,7 +539,7 @@ static void klist_children_put(struct klist_node *n)
+ */
+ void device_initialize(struct device *dev)
+ {
+- dev->kobj.kset = devices_kset;
++ dev->kobj.kset = ve_devices_kset;
+ kobject_init(&dev->kobj, &device_ktype);
+ klist_init(&dev->klist_children, klist_children_get,
+ klist_children_put);
+@@ -569,7 +577,7 @@ static struct kobject *virtual_device_parent(struct device *dev)
+
+ if (!virtual_dir)
+ virtual_dir = kobject_create_and_add("virtual",
+- &devices_kset->kobj);
++ &ve_devices_kset->kobj);
+
+ return virtual_dir;
+ }
+@@ -799,7 +807,7 @@ static struct kobject *device_to_dev_kobj(struct device *dev)
+ if (dev->class)
+ kobj = dev->class->dev_kobj;
+ else
+- kobj = sysfs_dev_char_kobj;
++ kobj = ve_sysfs_dev_char_kobj;
+
+ return kobj;
+ }
+@@ -1142,31 +1150,43 @@ struct device *device_find_child(struct device *parent, void *data,
+ return child;
+ }
+
+-int __init devices_init(void)
++int devices_init(void)
+ {
+- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
+- if (!devices_kset)
+- return -ENOMEM;
+- dev_kobj = kobject_create_and_add("dev", NULL);
+- if (!dev_kobj)
++ ve_devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
++ if (!ve_devices_kset)
++ goto dev_kset_err;
++ ve_dev_kobj = kobject_create_and_add("dev", NULL);
++ if (!ve_dev_kobj)
+ goto dev_kobj_err;
+- sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
+- if (!sysfs_dev_block_kobj)
++ ve_sysfs_dev_block_kobj = kobject_create_and_add("block", ve_dev_kobj);
++ if (!ve_sysfs_dev_block_kobj)
+ goto block_kobj_err;
+- sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
+- if (!sysfs_dev_char_kobj)
++ ve_sysfs_dev_char_kobj = kobject_create_and_add("char", ve_dev_kobj);
++ if (!ve_sysfs_dev_char_kobj)
+ goto char_kobj_err;
+
+ return 0;
+
+ char_kobj_err:
+- kobject_put(sysfs_dev_block_kobj);
++ kobject_put(ve_sysfs_dev_block_kobj);
+ block_kobj_err:
+- kobject_put(dev_kobj);
++ kobject_put(ve_dev_kobj);
+ dev_kobj_err:
+- kset_unregister(devices_kset);
++ kset_unregister(ve_devices_kset);
++dev_kset_err:
+ return -ENOMEM;
+ }
++EXPORT_SYMBOL_GPL(devices_init);
++
++void devices_fini(void)
++{
++ kobject_put(ve_sysfs_dev_char_kobj);
++ kobject_put(ve_sysfs_dev_block_kobj);
++ kobject_put(ve_dev_kobj);
++ kset_unregister(ve_devices_kset);
++}
++EXPORT_SYMBOL_GPL(devices_fini);
++
+
+ EXPORT_SYMBOL_GPL(device_for_each_child);
+ EXPORT_SYMBOL_GPL(device_find_child);
+@@ -1493,7 +1513,12 @@ void device_shutdown(void)
+ {
+ struct device *dev, *devn;
+
+- list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
++ if (!ve_is_super(get_exec_env())) {
++ printk("BUG: device_shutdown call from inside VE\n");
++ return;
++ }
++
++ list_for_each_entry_safe_reverse(dev, devn, &ve_devices_kset->list,
+ kobj.entry) {
+ if (dev->bus && dev->bus->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+@@ -1503,7 +1528,8 @@ void device_shutdown(void)
+ dev->driver->shutdown(dev);
+ }
+ }
+- kobject_put(sysfs_dev_char_kobj);
+- kobject_put(sysfs_dev_block_kobj);
+- kobject_put(dev_kobj);
++
++ kobject_put(ve_sysfs_dev_char_kobj);
++ kobject_put(ve_sysfs_dev_block_kobj);
++ kobject_put(ve_dev_kobj);
+ }
+diff --git a/drivers/base/sys.c b/drivers/base/sys.c
+index 75dd6e2..6c4dee7 100644
+--- a/drivers/base/sys.c
++++ b/drivers/base/sys.c
+@@ -20,6 +20,8 @@
+ #include <linux/slab.h>
+ #include <linux/string.h>
+ #include <linux/pm.h>
++#include <linux/sched.h>
++#include <linux/ve.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
+
+@@ -467,7 +469,7 @@ int sysdev_resume(void)
+
+ int __init system_bus_init(void)
+ {
+- system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
++ system_kset = kset_create_and_add("system", NULL, &ve_devices_kset->kobj);
+ if (!system_kset)
+ return -ENOMEM;
+ return 0;
+diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
+index 7b3a212..da591f4 100644
+--- a/drivers/char/keyboard.c
++++ b/drivers/char/keyboard.c
+@@ -162,6 +162,7 @@ unsigned char kbd_sysrq_xlate[KEY_MAX + 1] =
+ static int sysrq_down;
+ static int sysrq_alt_use;
+ #endif
++int sysrq_key_scancode = KEY_SYSRQ;
+ static int sysrq_alt;
+
+ /*
+@@ -1067,6 +1068,9 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
+ {
+ int code;
+
++ if (keycode == sysrq_key_scancode && sysrq_alt)
++ goto sysrq;
++
+ switch (keycode) {
+ case KEY_PAUSE:
+ put_queue(vc, 0xe1);
+@@ -1085,6 +1089,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
+ break;
+
+ case KEY_SYSRQ:
++sysrq:
+ /*
+ * Real AT keyboards (that's what we're trying
+ * to emulate here emit 0xe0 0x2a 0xe0 0x37 when
+@@ -1181,7 +1186,8 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
+ printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", keycode);
+
+ #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
+- if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) {
++ if ((keycode == sysrq_key_scancode || keycode == KEY_SYSRQ) &&
++ (sysrq_down || (down == 1 && sysrq_alt))) {
+ if (!sysrq_down) {
+ sysrq_down = down;
+ sysrq_alt_use = sysrq_alt;
+diff --git a/drivers/char/pty.c b/drivers/char/pty.c
+index 76b2793..b8c76ba 100644
+--- a/drivers/char/pty.c
++++ b/drivers/char/pty.c
+@@ -29,16 +29,22 @@
+ #include <linux/bitops.h>
+ #include <linux/devpts_fs.h>
+
++#include <bc/misc.h>
++
+ /* These are global because they are accessed in tty_io.c */
+ #ifdef CONFIG_UNIX98_PTYS
+ struct tty_driver *ptm_driver;
+-static struct tty_driver *pts_driver;
++struct tty_driver *pts_driver;
++EXPORT_SYMBOL(ptm_driver);
++EXPORT_SYMBOL(pts_driver);
+ #endif
+
+ static void pty_close(struct tty_struct * tty, struct file * filp)
+ {
+ if (!tty)
+ return;
++
++ ub_pty_uncharge(tty);
+ if (tty->driver->subtype == PTY_TYPE_MASTER) {
+ if (tty->count > 1)
+ printk("master pty_close: count = %d!!\n", tty->count);
+@@ -58,8 +64,12 @@ static void pty_close(struct tty_struct * tty, struct file * filp)
+ if (tty->driver->subtype == PTY_TYPE_MASTER) {
+ set_bit(TTY_OTHER_CLOSED, &tty->flags);
+ #ifdef CONFIG_UNIX98_PTYS
+- if (tty->driver == ptm_driver)
++ if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) {
++ struct ve_struct *old_env;
++ old_env = set_exec_env(tty->owner_env);
+ devpts_pty_kill(tty->index);
++ (void)set_exec_env(old_env);
++ }
+ #endif
+ tty_vhangup(tty->link);
+ }
+@@ -212,6 +222,10 @@ static int pty_open(struct tty_struct *tty, struct file * filp)
+ if (tty->link->count != 1)
+ goto out;
+
++ retval = -ENOMEM;
++ if (ub_pty_charge(tty))
++ goto out;
++
+ clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
+ set_bit(TTY_THROTTLED, &tty->flags);
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+@@ -239,7 +253,9 @@ static const struct tty_operations pty_ops = {
+
+ /* Traditional BSD devices */
+ #ifdef CONFIG_LEGACY_PTYS
+-static struct tty_driver *pty_driver, *pty_slave_driver;
++struct tty_driver *pty_driver, *pty_slave_driver;
++EXPORT_SYMBOL(pty_driver);
++EXPORT_SYMBOL(pty_slave_driver);
+
+ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+@@ -452,6 +468,9 @@ static void __init unix98_pty_init(void)
+
+ pty_table[1].data = &ptm_driver->refcount;
+ register_sysctl_table(pty_root_table);
++#ifdef CONFIG_VE
++ get_ve0()->ptm_driver = ptm_driver;
++#endif
+ }
+ #else
+ static inline void unix98_pty_init(void) { }
+diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
+index 8fdfe9c..111b7a6 100644
+--- a/drivers/char/sysrq.c
++++ b/drivers/char/sysrq.c
+@@ -36,6 +36,8 @@
+ #include <linux/kexec.h>
+ #include <linux/irq.h>
+ #include <linux/hrtimer.h>
++#include <linux/kallsyms.h>
++#include <linux/slab.h>
+ #include <linux/oom.h>
+
+ #include <asm/ptrace.h>
+@@ -241,9 +243,16 @@ static struct sysrq_key_op sysrq_showallcpus_op = {
+ static void sysrq_handle_showregs(int key, struct tty_struct *tty)
+ {
+ struct pt_regs *regs = get_irq_regs();
++
++ bust_spinlocks(1);
+ if (regs)
+ show_regs(regs);
++ bust_spinlocks(0);
++#if defined(__i386__) || defined(__x86_64__)
++ smp_nmi_call_function(smp_show_regs, NULL, 1);
++#endif
+ }
++
+ static struct sysrq_key_op sysrq_showregs_op = {
+ .handler = sysrq_handle_showregs,
+ .help_msg = "showPc",
+@@ -277,6 +286,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = {
+ static void sysrq_handle_showmem(int key, struct tty_struct *tty)
+ {
+ show_mem();
++ show_slab_info();
+ }
+ static struct sysrq_key_op sysrq_showmem_op = {
+ .handler = sysrq_handle_showmem,
+@@ -292,7 +302,7 @@ static void send_sig_all(int sig)
+ {
+ struct task_struct *p;
+
+- for_each_process(p) {
++ for_each_process_all(p) {
+ if (p->mm && !is_global_init(p))
+ /* Not swapper, init nor kernel thread */
+ force_sig(sig, p);
+@@ -354,7 +364,267 @@ static struct sysrq_key_op sysrq_unrt_op = {
+ /* Key Operations table and lock */
+ static DEFINE_SPINLOCK(sysrq_key_table_lock);
+
+-static struct sysrq_key_op *sysrq_key_table[36] = {
++#define SYSRQ_KEY_TABLE_LENGTH 37
++static struct sysrq_key_op **sysrq_key_table;
++static struct sysrq_key_op *sysrq_default_key_table[];
++
++#ifdef CONFIG_SYSRQ_DEBUG
++#define SYSRQ_NAMELEN_MAX 64
++#define SYSRQ_DUMP_LINES 32
++
++static struct sysrq_key_op *sysrq_debug_key_table[];
++static struct sysrq_key_op *sysrq_input_key_table[];
++static unsigned long *dump_address;
++static int orig_console_loglevel;
++static void (*sysrq_input_return)(char *) = NULL;
++
++static void dump_mem(void)
++{
++ unsigned long value[4];
++ mm_segment_t old_fs;
++ int line, err;
++
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = 0;
++
++ for (line = 0; line < SYSRQ_DUMP_LINES; line++) {
++ err |= __get_user(value[0], dump_address++);
++ err |= __get_user(value[1], dump_address++);
++ err |= __get_user(value[2], dump_address++);
++ err |= __get_user(value[3], dump_address++);
++ if (err) {
++ printk("Invalid address %p\n", dump_address - 4);
++ break;
++ }
++#if BITS_PER_LONG == 32
++ printk("0x%p: %08lx %08lx %08lx %08lx\n",
++ dump_address - 4,
++ value[0], value[1], value[2], value[3]);
++#else
++ printk("0x%p: %016lx %016lx %016lx %016lx\n",
++ dump_address - 4,
++ value[0], value[1], value[2], value[3]);
++#endif
++ }
++ set_fs(old_fs);
++}
++
++static void write_mem(unsigned long val)
++{
++ mm_segment_t old_fs;
++ unsigned long old_val;
++
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ if (__get_user(old_val, dump_address)) {
++ printk("Invalid address %p\n", dump_address);
++ goto out;
++ }
++
++#if BITS_PER_LONG == 32
++ printk("Changing [%p] from %08lx to %08lx\n",
++ dump_address, old_val, val);
++#else
++ printk("Changing [%p] from %016lx to %016lx\n",
++ dump_address, old_val, val);
++#endif
++ __put_user(val, dump_address);
++out:
++ set_fs(old_fs);
++}
++
++static void handle_read(int key, struct tty_struct *tty)
++{
++ static int pos;
++ static int upper_case;
++ static char str[SYSRQ_NAMELEN_MAX];
++
++ if (key == 0) {
++ /* actually 0 is not shift only... */
++ upper_case = 1;
++ return;
++ }
++
++ if (key == 0x0d || pos == SYSRQ_NAMELEN_MAX - 1) {
++ /* enter */
++ sysrq_key_table = sysrq_debug_key_table;
++ str[pos] = '\0';
++ pos = upper_case = 0;
++ printk("\n");
++ if (sysrq_input_return == NULL)
++ printk("No return handler!!!\n");
++ else
++ sysrq_input_return(str);
++ return;
++ };
++
++ /* check for alowed symbols */
++ if (key == '-') {
++ if (upper_case)
++ key = '_';
++ goto correct;
++ };
++ if (key >= 'a' && key <= 'z') {
++ if (upper_case)
++ key = key - 'a' + 'A';
++ goto correct;
++ };
++ if (key >= '0' && key <= '9')
++ goto correct;
++
++ upper_case = 0;
++ return;
++
++correct:
++ str[pos] = key;
++ printk("%c", (char)key);
++ pos++;
++ upper_case = 0;
++}
++
++static struct sysrq_key_op input_read = {
++ .handler = handle_read,
++ .help_msg = "",
++ .action_msg = NULL,
++};
++
++static struct sysrq_key_op *sysrq_input_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
++ [0 ... SYSRQ_KEY_TABLE_LENGTH - 1] = &input_read,
++};
++
++static void return_dump_mem(char *str)
++{
++ unsigned long address;
++ char *end;
++
++ address = simple_strtoul(str, &end, 0);
++ if (*end != '\0') {
++ printk("Bad address [%s]\n", str);
++ return;
++ }
++
++ dump_address = (unsigned long *)address;
++ dump_mem();
++}
++
++static void handle_dump_mem(int key, struct tty_struct *tty)
++{
++ sysrq_input_return = return_dump_mem;
++ sysrq_key_table = sysrq_input_key_table;
++}
++
++static struct sysrq_key_op debug_dump_mem = {
++ .handler = handle_dump_mem,
++ .help_msg = "Dump",
++ .action_msg = "Enter address:",
++};
++
++static void return_resolve(char *str)
++{
++ unsigned long address;
++
++ address = kallsyms_lookup_name(str);
++ printk("%s : %lx\n", str, address);
++ if (address) {
++ dump_address = (unsigned long *)address;
++ printk("Now you can dump it via X\n");
++ }
++}
++
++static void handle_resolve(int key, struct tty_struct *tty)
++{
++ sysrq_input_return = return_resolve;
++ sysrq_key_table = sysrq_input_key_table;
++}
++
++static struct sysrq_key_op debug_resolve = {
++ .handler = handle_resolve,
++ .help_msg = "Resolve",
++ .action_msg = "Enter symbol name:",
++};
++
++static void return_write_mem(char *str)
++{
++ unsigned long address;
++ unsigned long value;
++ char *end;
++
++ address = simple_strtoul(str, &end, 0);
++ if (*end != '-') {
++ printk("Bad address in %s\n", str);
++ return;
++ }
++ value = simple_strtoul(end + 1, &end, 0);
++ if (*end != '\0') {
++ printk("Bad value in %s\n", str);
++ return;
++ }
++
++ dump_address = (unsigned long *)address;
++ write_mem(value);
++}
++
++static void handle_write_mem(int key, struct tty_struct *tty)
++{
++ sysrq_input_return = return_write_mem;
++ sysrq_key_table = sysrq_input_key_table;
++}
++
++static struct sysrq_key_op debug_write_mem = {
++ .handler = handle_write_mem,
++ .help_msg = "Writemem",
++ .action_msg = "Enter address-value:",
++};
++
++static void handle_next(int key, struct tty_struct *tty)
++{
++ dump_mem();
++}
++
++static struct sysrq_key_op debug_next = {
++ .handler = handle_next,
++ .help_msg = "neXt",
++ .action_msg = "continuing",
++};
++
++static void handle_quit(int key, struct tty_struct *tty)
++{
++ sysrq_key_table = sysrq_default_key_table;
++ console_loglevel = orig_console_loglevel;
++}
++
++static struct sysrq_key_op debug_quit = {
++ .handler = handle_quit,
++ .help_msg = "Quit",
++ .action_msg = "Tnahk you for using debugger",
++};
++
++static struct sysrq_key_op *sysrq_debug_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
++ [13] = &debug_dump_mem, /* d */
++ [26] = &debug_quit, /* q */
++ [27] = &debug_resolve, /* r */
++ [32] = &debug_write_mem, /* w */
++ [33] = &debug_next, /* x */
++};
++
++static void sysrq_handle_debug(int key, struct tty_struct *tty)
++{
++ orig_console_loglevel = console_loglevel;
++ console_loglevel = 8;
++ sysrq_key_table = sysrq_debug_key_table;
++ printk("Welcome sysrq debugging mode\n"
++ "Press H for help\n");
++}
++
++static struct sysrq_key_op sysrq_debug_op = {
++ .handler = sysrq_handle_debug,
++ .help_msg = "debuG",
++ .action_msg = "Select desired action",
++};
++#endif
++
++static struct sysrq_key_op *sysrq_default_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
+ &sysrq_loglevel_op, /* 0 */
+ &sysrq_loglevel_op, /* 1 */
+ &sysrq_loglevel_op, /* 2 */
+@@ -377,7 +647,11 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
+ &sysrq_term_op, /* e */
+ &sysrq_moom_op, /* f */
+ /* g: May be registered by ppc for kgdb */
++#ifdef CONFIG_SYSRQ_DEBUG
++ &sysrq_debug_op, /* g */
++#else
+ NULL, /* g */
++#endif
+ NULL, /* h */
+ &sysrq_kill_op, /* i */
+ NULL, /* j */
+@@ -404,9 +678,12 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
+ NULL, /* x */
+ /* y: May be registered on sparc64 for global register dump */
+ NULL, /* y */
+- NULL /* z */
++ NULL, /* z */
++ NULL, /* for debugger */
+ };
+
++static struct sysrq_key_op **sysrq_key_table = sysrq_default_key_table;
++
+ /* key2index calculation, -1 on invalid index */
+ static int sysrq_key_table_key2index(int key)
+ {
+@@ -416,6 +693,10 @@ static int sysrq_key_table_key2index(int key)
+ retval = key - '0';
+ else if ((key >= 'a') && (key <= 'z'))
+ retval = key + 10 - 'a';
++#ifdef CONFIG_SYSRQ_DEBUG
++ else if (key == 0 || key == 0x0d || key == '-')
++ retval = SYSRQ_KEY_TABLE_LENGTH - 1;
++#endif
+ else
+ retval = -1;
+ return retval;
+@@ -457,7 +738,6 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
+ spin_lock_irqsave(&sysrq_key_table_lock, flags);
+ orig_log_level = console_loglevel;
+ console_loglevel = 7;
+- printk(KERN_INFO "SysRq : ");
+
+ op_p = __sysrq_get_key_op(key);
+ if (op_p) {
+@@ -466,16 +746,17 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask)
+ * should not) and is the invoked operation enabled?
+ */
+ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
+- printk("%s\n", op_p->action_msg);
++ if (op_p->action_msg)
++ printk("%s\n", op_p->action_msg);
+ console_loglevel = orig_log_level;
+ op_p->handler(key, tty);
+ } else {
+ printk("This sysrq operation is disabled.\n");
+ }
+ } else {
+- printk("HELP : ");
++ printk("SysRq HELP : ");
+ /* Only print the help msg once per handler */
+- for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) {
++ for (i = 0; i < SYSRQ_KEY_TABLE_LENGTH; i++) {
+ if (sysrq_key_table[i]) {
+ int j;
+
+diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
+index e4dce87..186e878 100644
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -96,6 +96,8 @@
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
+ #include <linux/seq_file.h>
++#include <linux/nsproxy.h>
++#include <linux/ve.h>
+
+ #include <linux/uaccess.h>
+ #include <asm/system.h>
+@@ -106,6 +108,7 @@
+
+ #include <linux/kmod.h>
+ #include <linux/nsproxy.h>
++#include <bc/kmem.h>
+
+ #undef TTY_DEBUG_HANGUP
+
+@@ -130,6 +133,7 @@ EXPORT_SYMBOL(tty_std_termios);
+ into this file */
+
+ LIST_HEAD(tty_drivers); /* linked list of tty drivers */
++EXPORT_SYMBOL(tty_drivers);
+
+ /* Mutex to protect creating and releasing a tty. This is shared with
+ vt.c for deeply disgusting hack reasons */
+@@ -137,7 +141,11 @@ DEFINE_MUTEX(tty_mutex);
+ EXPORT_SYMBOL(tty_mutex);
+
+ #ifdef CONFIG_UNIX98_PTYS
++#ifdef CONFIG_VE
++#define ptm_driver (get_exec_env()->ptm_driver)
++#else
+ extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
++#endif
+ static int ptmx_open(struct inode *, struct file *);
+ #endif
+
+@@ -173,7 +181,7 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+
+ static struct tty_struct *alloc_tty_struct(void)
+ {
+- return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
++ return kzalloc(sizeof(struct tty_struct), GFP_KERNEL_UBC);
+ }
+
+ static void tty_buffer_free_all(struct tty_struct *);
+@@ -675,9 +683,29 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
+ if (device < base || device >= base + p->num)
+ continue;
+ *index = device - base;
+- return p;
++#ifdef CONFIG_VE
++ if (in_interrupt())
++ goto found;
++ if (p->major!=PTY_MASTER_MAJOR && p->major!=PTY_SLAVE_MAJOR
++#ifdef CONFIG_UNIX98_PTYS
++ && (p->major<UNIX98_PTY_MASTER_MAJOR ||
++ p->major>UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT-1) &&
++ (p->major<UNIX98_PTY_SLAVE_MAJOR ||
++ p->major>UNIX98_PTY_SLAVE_MAJOR+UNIX98_PTY_MAJOR_COUNT-1)
++#endif
++ )
++ goto found;
++ if (ve_is_super(p->owner_env) && ve_is_super(get_exec_env()))
++ goto found;
++ if (!ve_accessible_strict(p->owner_env, get_exec_env()))
++ continue;
++#endif
++ goto found;
+ }
+ return NULL;
++
++found:
++ return p;
+ }
+
+ #ifdef CONFIG_CONSOLE_POLL
+@@ -1632,13 +1660,21 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
+ */
+
+ static int init_dev(struct tty_driver *driver, int idx,
+- struct tty_struct **ret_tty)
++ struct tty_struct *i_tty, struct tty_struct **ret_tty)
+ {
+ struct tty_struct *tty, *o_tty;
+ struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
+ struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
++ struct ve_struct * owner;
+ int retval = 0;
+
++ owner = driver->owner_env;
++
++ if (i_tty) {
++ tty = i_tty;
++ goto fast_track;
++ }
++
+ /* check whether we're reopening an existing tty */
+ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+ tty = devpts_get_tty(idx);
+@@ -1688,6 +1724,7 @@ static int init_dev(struct tty_driver *driver, int idx,
+ tty->ops = driver->ops;
+ tty->index = idx;
+ tty_line_name(driver, idx, tty->name);
++ tty->owner_env = owner;
+
+ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+ tp_loc = &tty->termios;
+@@ -1698,14 +1735,14 @@ static int init_dev(struct tty_driver *driver, int idx,
+ }
+
+ if (!*tp_loc) {
+- tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
++ tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL_UBC);
+ if (!tp)
+ goto free_mem_out;
+ *tp = driver->init_termios;
+ }
+
+ if (!*ltp_loc) {
+- ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
++ ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL_UBC);
+ if (!ltp)
+ goto free_mem_out;
+ }
+@@ -1719,6 +1756,7 @@ static int init_dev(struct tty_driver *driver, int idx,
+ o_tty->ops = driver->ops;
+ o_tty->index = idx;
+ tty_line_name(driver->other, idx, o_tty->name);
++ o_tty->owner_env = owner;
+
+ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
+ o_tp_loc = &o_tty->termios;
+@@ -1729,14 +1767,14 @@ static int init_dev(struct tty_driver *driver, int idx,
+ }
+
+ if (!*o_tp_loc) {
+- o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
++ o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL_UBC);
+ if (!o_tp)
+ goto free_mem_out;
+ *o_tp = driver->other->init_termios;
+ }
+
+ if (!*o_ltp_loc) {
+- o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
++ o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL_UBC);
+ if (!o_ltp)
+ goto free_mem_out;
+ }
+@@ -1752,6 +1790,10 @@ static int init_dev(struct tty_driver *driver, int idx,
+ *o_ltp_loc = o_ltp;
+ o_tty->termios = *o_tp_loc;
+ o_tty->termios_locked = *o_ltp_loc;
++#ifdef CONFIG_VE
++ if (driver->other->refcount == 0)
++ (void)get_ve(owner);
++#endif
+ driver->other->refcount++;
+ if (driver->subtype == PTY_TYPE_MASTER)
+ o_tty->count++;
+@@ -1775,6 +1817,10 @@ static int init_dev(struct tty_driver *driver, int idx,
+ *ltp_loc = ltp;
+ tty->termios = *tp_loc;
+ tty->termios_locked = *ltp_loc;
++#ifdef CONFIG_VE
++ if (driver->refcount == 0)
++ (void)get_ve(owner);
++#endif
+ /* Compatibility until drivers always set this */
+ tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+ tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+@@ -1888,7 +1934,8 @@ static void release_one_tty(struct tty_struct *tty, int idx)
+
+ tty->magic = 0;
+ tty->driver->refcount--;
+-
++ if (tty->driver->refcount == 0)
++ put_ve(tty->owner_env);
+ file_list_lock();
+ list_del_init(&tty->tty_files);
+ file_list_unlock();
+@@ -2171,7 +2218,7 @@ static void release_dev(struct file *filp)
+
+ static int __tty_open(struct inode *inode, struct file *filp)
+ {
+- struct tty_struct *tty;
++ struct tty_struct *tty, *c_tty;
+ int noctty, retval;
+ struct tty_driver *driver;
+ int index;
+@@ -2184,6 +2231,7 @@ retry_open:
+ noctty = filp->f_flags & O_NOCTTY;
+ index = -1;
+ retval = 0;
++ c_tty = NULL;
+
+ mutex_lock(&tty_mutex);
+
+@@ -2195,6 +2243,7 @@ retry_open:
+ }
+ driver = tty->driver;
+ index = tty->index;
++ c_tty = tty;
+ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
+ /* noctty = 1; */
+ goto got_driver;
+@@ -2202,6 +2251,12 @@ retry_open:
+ #ifdef CONFIG_VT
+ if (device == MKDEV(TTY_MAJOR, 0)) {
+ extern struct tty_driver *console_driver;
++#ifdef CONFIG_VE
++ if (!ve_is_super(get_exec_env())) {
++ mutex_unlock(&tty_mutex);
++ return -ENODEV;
++ }
++#endif
+ driver = console_driver;
+ index = fg_console;
+ noctty = 1;
+@@ -2209,6 +2264,12 @@ retry_open:
+ }
+ #endif
+ if (device == MKDEV(TTYAUX_MAJOR, 1)) {
++#ifdef CONFIG_VE
++ if (!ve_is_super(get_exec_env())) {
++ mutex_unlock(&tty_mutex);
++ return -ENODEV;
++ }
++#endif
+ driver = console_device(&index);
+ if (driver) {
+ /* Don't let /dev/console block */
+@@ -2226,7 +2287,7 @@ retry_open:
+ return -ENODEV;
+ }
+ got_driver:
+- retval = init_dev(driver, index, &tty);
++ retval = init_dev(driver, index, c_tty, &tty);
+ mutex_unlock(&tty_mutex);
+ if (retval)
+ return retval;
+@@ -2323,7 +2384,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
+ return index;
+
+ mutex_lock(&tty_mutex);
+- retval = init_dev(ptm_driver, index, &tty);
++ retval = init_dev(ptm_driver, index, NULL, &tty);
+ mutex_unlock(&tty_mutex);
+
+ if (retval)
+@@ -2589,6 +2650,8 @@ static int tioccons(struct file *file)
+ {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
++ if (!ve_is_super(get_exec_env()))
++ return -EACCES;
+ if (file->f_op->write == redirected_tty_write) {
+ struct file *f;
+ spin_lock(&redirect_lock);
+@@ -3160,7 +3223,7 @@ void __do_SAK(struct tty_struct *tty)
+ /* Now kill any processes that happen to have the
+ * tty open.
+ */
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ if (p->signal->tty == tty) {
+ printk(KERN_NOTICE "SAK: killed process %d"
+ " (%s): task_session_nr(p)==tty->session\n",
+@@ -3192,7 +3255,7 @@ void __do_SAK(struct tty_struct *tty)
+ spin_unlock(&p->files->file_lock);
+ }
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ read_unlock(&tasklist_lock);
+ #endif
+ }
+@@ -3527,6 +3590,7 @@ int tty_register_driver(struct tty_driver *driver)
+ }
+
+ mutex_lock(&tty_mutex);
++ driver->owner_env = get_exec_env();
+ list_add(&driver->tty_drivers, &tty_drivers);
+ mutex_unlock(&tty_mutex);
+
+@@ -3725,3 +3789,43 @@ static int __init tty_init(void)
+ return 0;
+ }
+ module_init(tty_init);
++
++#ifdef CONFIG_UNIX98_PTYS
++int init_ve_tty_class(void)
++{
++ struct class * ve_tty_class;
++ struct device * ve_ptmx_dev_class;
++
++ ve_tty_class = class_create(THIS_MODULE, "tty");
++ if (IS_ERR(ve_tty_class))
++ return -ENOMEM;
++
++ ve_ptmx_dev_class = device_create(ve_tty_class, NULL,
++ MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
++ if (IS_ERR(ve_ptmx_dev_class)) {
++ class_destroy(ve_tty_class);
++ return PTR_ERR(ve_ptmx_dev_class);
++ }
++
++ get_exec_env()->tty_class = ve_tty_class;
++ return 0;
++}
++
++void fini_ve_tty_class(void)
++{
++ struct class *ve_tty_class = get_exec_env()->tty_class;
++
++ device_destroy(ve_tty_class, MKDEV(TTYAUX_MAJOR, 2));
++ class_destroy(ve_tty_class);
++}
++#else
++int init_ve_tty_class(void)
++{
++ return 0;
++}
++void fini_ve_tty_class(void)
++{
++}
++#endif
++EXPORT_SYMBOL(init_ve_tty_class);
++EXPORT_SYMBOL(fini_ve_tty_class);
+diff --git a/drivers/net/Makefile b/drivers/net/Makefile
+index 7629c90..6e9042c 100644
+--- a/drivers/net/Makefile
++++ b/drivers/net/Makefile
+@@ -27,6 +27,10 @@ gianfar_driver-objs := gianfar.o \
+ obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
+ ucc_geth_driver-objs := ucc_geth.o ucc_geth_mii.o ucc_geth_ethtool.o
+
++obj-$(CONFIG_VE_NETDEV) += vznetdev.o
++vznetdev-objs := open_vznet.o venet_core.o
++obj-$(CONFIG_VE_ETHDEV) += vzethdev.o
++
+ #
+ # link order important here
+ #
+diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
+index 3b43bfd..b59c38a 100644
+--- a/drivers/net/loopback.c
++++ b/drivers/net/loopback.c
+@@ -72,6 +72,12 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+ struct pcpu_lstats *pcpu_lstats, *lb_stats;
+
++#ifdef CONFIG_VE
++ if (unlikely(get_exec_env()->disable_net)) {
++ kfree_skb(skb);
++ return 0;
++ }
++#endif
+ skb_orphan(skb);
+
+ skb->protocol = eth_type_trans(skb,dev);
+@@ -173,7 +179,8 @@ static void loopback_setup(struct net_device *dev)
+ | NETIF_F_NO_CSUM
+ | NETIF_F_HIGHDMA
+ | NETIF_F_LLTX
+- | NETIF_F_NETNS_LOCAL;
++ | NETIF_F_NETNS_LOCAL
++ | NETIF_F_VIRTUAL;
+ dev->ethtool_ops = &loopback_ethtool_ops;
+ dev->header_ops = &eth_header_ops;
+ dev->init = loopback_dev_init;
+diff --git a/drivers/net/open_vznet.c b/drivers/net/open_vznet.c
+new file mode 100644
+index 0000000..79bf640
+--- /dev/null
++++ b/drivers/net/open_vznet.c
+@@ -0,0 +1,244 @@
++/*
++ * open_vznet.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++/*
++ * Virtual Networking device used to change VE ownership on packets
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/seq_file.h>
++
++#include <linux/inet.h>
++#include <net/ip.h>
++#include <linux/skbuff.h>
++#include <linux/venet.h>
++
++void veip_stop(struct ve_struct *ve)
++{
++ struct list_head *p, *tmp;
++
++ write_lock_irq(&veip_hash_lock);
++ if (ve->veip == NULL)
++ goto unlock;
++ list_for_each_safe(p, tmp, &ve->veip->ip_lh) {
++ struct ip_entry_struct *ptr;
++ ptr = list_entry(p, struct ip_entry_struct, ve_list);
++ ptr->active_env = NULL;
++ list_del(&ptr->ve_list);
++ list_del(&ptr->ip_hash);
++ kfree(ptr);
++ }
++ veip_put(ve->veip);
++ ve->veip = NULL;
++ if (!ve_is_super(ve))
++ module_put(THIS_MODULE);
++unlock:
++ write_unlock_irq(&veip_hash_lock);
++}
++
++int veip_start(struct ve_struct *ve)
++{
++ int err, get;
++
++ err = 0;
++ write_lock_irq(&veip_hash_lock);
++ get = ve->veip == NULL;
++ ve->veip = veip_findcreate(ve->veid);
++ if (ve->veip == NULL)
++ err = -ENOMEM;
++ write_unlock_irq(&veip_hash_lock);
++ if (err == 0 && get && !ve_is_super(ve))
++ __module_get(THIS_MODULE);
++ return err;
++}
++
++int veip_entry_add(struct ve_struct *ve, struct ve_addr_struct *addr)
++{
++ struct ip_entry_struct *entry, *found;
++ int err;
++
++ entry = kzalloc(sizeof(struct ip_entry_struct), GFP_KERNEL);
++ if (entry == NULL)
++ return -ENOMEM;
++
++ if (ve->veip == NULL) {
++ /* This can happen if we load venet AFTER ve was started */
++ err = veip_start(ve);
++ if (err < 0)
++ goto out;
++ }
++
++ write_lock_irq(&veip_hash_lock);
++ err = -EADDRINUSE;
++ found = venet_entry_lookup(addr);
++ if (found != NULL)
++ goto out_unlock;
++
++ entry->active_env = ve;
++ entry->addr = *addr;
++ ip_entry_hash(entry, ve->veip);
++
++ err = 0;
++ entry = NULL;
++out_unlock:
++ write_unlock_irq(&veip_hash_lock);
++out:
++ if (entry != NULL)
++ kfree(entry);
++ return err;
++}
++
++int veip_entry_del(envid_t veid, struct ve_addr_struct *addr)
++{
++ struct ip_entry_struct *found;
++ int err;
++
++ err = -EADDRNOTAVAIL;
++ write_lock_irq(&veip_hash_lock);
++ found = venet_entry_lookup(addr);
++ if (found == NULL)
++ goto out;
++ if (found->active_env->veid != veid)
++ goto out;
++
++ err = 0;
++ found->active_env = NULL;
++
++ list_del(&found->ip_hash);
++ list_del(&found->ve_list);
++ kfree(found);
++out:
++ write_unlock_irq(&veip_hash_lock);
++ return err;
++}
++
++static int skb_extract_addr(struct sk_buff *skb,
++ struct ve_addr_struct *addr, int dir)
++{
++ switch (skb->protocol) {
++ case __constant_htons(ETH_P_IP):
++ addr->family = AF_INET;
++ addr->key[0] = 0;
++ addr->key[1] = 0;
++ addr->key[2] = 0;
++ addr->key[3] = (dir ? ip_hdr(skb)->daddr : ip_hdr(skb)->saddr);
++ return 0;
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ case __constant_htons(ETH_P_IPV6):
++ addr->family = AF_INET6;
++ memcpy(&addr->key, dir ?
++ ipv6_hdr(skb)->daddr.s6_addr32 :
++ ipv6_hdr(skb)->saddr.s6_addr32,
++ sizeof(addr->key));
++ return 0;
++#endif
++ }
++
++ return -EAFNOSUPPORT;
++}
++
++static struct ve_struct *venet_find_ve(struct sk_buff *skb, int dir)
++{
++ struct ip_entry_struct *entry;
++ struct ve_addr_struct addr;
++
++ if (skb_extract_addr(skb, &addr, dir) < 0)
++ return NULL;
++
++ entry = venet_entry_lookup(&addr);
++ if (entry == NULL)
++ return NULL;
++
++ return entry->active_env;
++}
++
++int venet_change_skb_owner(struct sk_buff *skb)
++{
++ struct ve_struct *ve, *ve_old;
++
++ ve_old = skb->owner_env;
++
++ read_lock(&veip_hash_lock);
++ if (!ve_is_super(ve_old)) {
++ /* from VE to host */
++ ve = venet_find_ve(skb, 0);
++ if (ve == NULL)
++ goto out_drop;
++ if (!ve_accessible_strict(ve, ve_old))
++ goto out_source;
++ skb->owner_env = get_ve0();
++ } else {
++ /* from host to VE */
++ ve = venet_find_ve(skb, 1);
++ if (ve == NULL)
++ goto out_drop;
++ skb->owner_env = ve;
++ }
++ read_unlock(&veip_hash_lock);
++
++ return 0;
++
++out_drop:
++ read_unlock(&veip_hash_lock);
++ return -ESRCH;
++
++out_source:
++ read_unlock(&veip_hash_lock);
++ if (net_ratelimit() && skb->protocol == __constant_htons(ETH_P_IP)) {
++ printk(KERN_WARNING "Dropped packet, source wrong "
++ "veid=%u src-IP=%u.%u.%u.%u "
++ "dst-IP=%u.%u.%u.%u\n",
++ skb->owner_env->veid,
++ NIPQUAD(ip_hdr(skb)->saddr),
++ NIPQUAD(ip_hdr(skb)->daddr));
++ }
++ return -EACCES;
++}
++
++#ifdef CONFIG_PROC_FS
++int veip_seq_show(struct seq_file *m, void *v)
++{
++ struct list_head *p;
++ struct ip_entry_struct *entry;
++ char s[40];
++
++ p = (struct list_head *)v;
++ if (p == ip_entry_hash_table) {
++ seq_puts(m, "Version: 2.5\n");
++ return 0;
++ }
++ entry = list_entry(p, struct ip_entry_struct, ip_hash);
++ veaddr_print(s, sizeof(s), &entry->addr);
++ seq_printf(m, "%39s %10u\n", s, 0);
++ return 0;
++}
++#endif
++
++__exit void veip_cleanup(void)
++{
++ int i;
++
++ write_lock_irq(&veip_hash_lock);
++ for (i = 0; i < VEIP_HASH_SZ; i++)
++ while (!list_empty(ip_entry_hash_table + i)) {
++ struct ip_entry_struct *entry;
++
++ entry = list_first_entry(ip_entry_hash_table + i,
++ struct ip_entry_struct, ip_hash);
++ list_del(&entry->ip_hash);
++ kfree(entry);
++ }
++ write_unlock_irq(&veip_hash_lock);
++}
++
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Virtuozzo Virtual Network Device");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/net/tun.c b/drivers/net/tun.c
+index 6daea0c..bebd95e 100644
+--- a/drivers/net/tun.c
++++ b/drivers/net/tun.c
+@@ -80,33 +80,6 @@ static int debug;
+ #define DBG1( a... )
+ #endif
+
+-#define FLT_EXACT_COUNT 8
+-struct tap_filter {
+- unsigned int count; /* Number of addrs. Zero means disabled */
+- u32 mask[2]; /* Mask of the hashed addrs */
+- unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN];
+-};
+-
+-struct tun_struct {
+- struct list_head list;
+- unsigned int flags;
+- int attached;
+- uid_t owner;
+- gid_t group;
+-
+- wait_queue_head_t read_wait;
+- struct sk_buff_head readq;
+-
+- struct net_device *dev;
+- struct fasync_struct *fasync;
+-
+- struct tap_filter txflt;
+-
+-#ifdef TUN_DEBUG
+- int debug;
+-#endif
+-};
+-
+ /* TAP filterting */
+ static void addr_hash_set(u32 *mask, const u8 *addr)
+ {
+@@ -213,19 +186,18 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)
+
+ /* Network device part of the driver */
+
+-static unsigned int tun_net_id;
+-struct tun_net {
+- struct list_head dev_list;
+-};
++unsigned int tun_net_id;
++EXPORT_SYMBOL(tun_net_id);
+
+ static const struct ethtool_ops tun_ethtool_ops;
+
+ /* Net device open. */
+-static int tun_net_open(struct net_device *dev)
++int tun_net_open(struct net_device *dev)
+ {
+ netif_start_queue(dev);
+ return 0;
+ }
++EXPORT_SYMBOL(tun_net_open);
+
+ /* Net device close. */
+ static int tun_net_close(struct net_device *dev)
+@@ -306,7 +278,7 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu)
+ }
+
+ /* Initialize net device. */
+-static void tun_net_init(struct net_device *dev)
++void tun_net_init(struct net_device *dev)
+ {
+ struct tun_struct *tun = netdev_priv(dev);
+
+@@ -336,6 +308,7 @@ static void tun_net_init(struct net_device *dev)
+ break;
+ }
+ }
++EXPORT_SYMBOL(tun_net_init);
+
+ /* Character device part */
+
+@@ -666,7 +639,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
+ return ret;
+ }
+
+-static void tun_setup(struct net_device *dev)
++void tun_setup(struct net_device *dev)
+ {
+ struct tun_struct *tun = netdev_priv(dev);
+
+@@ -683,6 +656,7 @@ static void tun_setup(struct net_device *dev)
+ dev->destructor = free_netdev;
+ dev->features |= NETIF_F_NETNS_LOCAL;
+ }
++EXPORT_SYMBOL(tun_setup);
+
+ static struct tun_struct *tun_get_by_name(struct tun_net *tn, const char *name)
+ {
+@@ -715,7 +689,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
+ current->euid != tun->owner) ||
+ (tun->group != -1 &&
+ current->egid != tun->group)) &&
+- !capable(CAP_NET_ADMIN))
++ !capable(CAP_NET_ADMIN) &&
++ !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+ }
+ else if (__dev_get_by_name(net, ifr->ifr_name))
+@@ -790,6 +765,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
+ file->private_data = tun;
+ tun->attached = 1;
+ get_net(dev_net(tun->dev));
++ tun->bind_file = file;
+
+ /* Make sure persistent devices do not get stuck in
+ * xoff state.
+@@ -1053,13 +1029,14 @@ out:
+ return ret;
+ }
+
+-static int tun_chr_open(struct inode *inode, struct file * file)
++int tun_chr_open(struct inode *inode, struct file * file)
+ {
+ cycle_kernel_lock();
+ DBG1(KERN_INFO "tunX: tun_chr_open\n");
+ file->private_data = NULL;
+ return 0;
+ }
++EXPORT_SYMBOL(tun_chr_open);
+
+ static int tun_chr_close(struct inode *inode, struct file *file)
+ {
+diff --git a/drivers/net/venet_core.c b/drivers/net/venet_core.c
+new file mode 100644
+index 0000000..8e605b6
+--- /dev/null
++++ b/drivers/net/venet_core.c
+@@ -0,0 +1,768 @@
++/*
++ * venet_core.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++/*
++ * Common part for Virtuozzo virtual network devices
++ */
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/fs.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/socket.h>
++#include <linux/errno.h>
++#include <linux/fcntl.h>
++#include <linux/in.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/nsproxy.h>
++#include <linux/tcp.h>
++#include <linux/proc_fs.h>
++#include <linux/seq_file.h>
++#include <net/addrconf.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/unistd.h>
++
++#include <linux/inet.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <net/ip.h>
++#include <linux/skbuff.h>
++#include <net/sock.h>
++#include <linux/if_ether.h> /* For the statistics structure. */
++#include <linux/if_arp.h> /* For ARPHRD_ETHER */
++#include <linux/ethtool.h>
++#include <linux/venet.h>
++#include <linux/ve_proto.h>
++#include <linux/vzctl.h>
++#include <linux/vzctl_venet.h>
++
++struct list_head ip_entry_hash_table[VEIP_HASH_SZ];
++rwlock_t veip_hash_lock = RW_LOCK_UNLOCKED;
++LIST_HEAD(veip_lh);
++
++#define ip_entry_hash_function(ip) (ntohl(ip) & (VEIP_HASH_SZ - 1))
++
++void ip_entry_hash(struct ip_entry_struct *entry, struct veip_struct *veip)
++{
++ list_add(&entry->ip_hash,
++ ip_entry_hash_table +
++ ip_entry_hash_function(entry->addr.key[3]));
++ list_add(&entry->ve_list, &veip->ip_lh);
++}
++
++void veip_put(struct veip_struct *veip)
++{
++ if (!list_empty(&veip->ip_lh))
++ return;
++ if (!list_empty(&veip->src_lh))
++ return;
++ if (!list_empty(&veip->dst_lh))
++ return;
++
++ list_del(&veip->list);
++ kfree(veip);
++}
++
++struct ip_entry_struct *venet_entry_lookup(struct ve_addr_struct *addr)
++{
++ struct ip_entry_struct *entry;
++
++ list_for_each_entry (entry, ip_entry_hash_table +
++ ip_entry_hash_function(addr->key[3]), ip_hash)
++ if (memcmp(&entry->addr, addr, sizeof(*addr)) == 0)
++ return entry;
++ return NULL;
++}
++
++struct veip_struct *veip_find(envid_t veid)
++{
++ struct veip_struct *ptr;
++
++ list_for_each_entry(ptr, &veip_lh, list) {
++ if (ptr->veid != veid)
++ continue;
++ return ptr;
++ }
++ return NULL;
++}
++
++struct veip_struct *veip_findcreate(envid_t veid)
++{
++ struct veip_struct *ptr;
++
++ ptr = veip_find(veid);
++ if (ptr != NULL)
++ return ptr;
++
++ ptr = kmalloc(sizeof(struct veip_struct), GFP_ATOMIC);
++ if (ptr == NULL)
++ return NULL;
++ memset(ptr, 0, sizeof(struct veip_struct));
++ INIT_LIST_HEAD(&ptr->ip_lh);
++ INIT_LIST_HEAD(&ptr->src_lh);
++ INIT_LIST_HEAD(&ptr->dst_lh);
++ ptr->veid = veid;
++ list_add(&ptr->list, &veip_lh);
++ return ptr;
++}
++
++static int convert_sockaddr(struct sockaddr *addr, int addrlen,
++ struct ve_addr_struct *veaddr)
++{
++ int err;
++
++ switch (addr->sa_family) {
++ case AF_INET: {
++ struct sockaddr_in *sin;
++
++ err = -EINVAL;
++ if (addrlen != sizeof(struct sockaddr_in))
++ break;
++
++ err = 0;
++ sin = (struct sockaddr_in *)addr;
++ veaddr->family = AF_INET;
++ veaddr->key[0] = 0;
++ veaddr->key[1] = 0;
++ veaddr->key[2] = 0;
++ veaddr->key[3] = sin->sin_addr.s_addr;
++ break;
++ }
++ case AF_INET6: {
++ struct sockaddr_in6 *sin;
++
++ err = -EINVAL;
++ if (addrlen != sizeof(struct sockaddr_in6))
++ break;
++
++ err = 0;
++ sin = (struct sockaddr_in6 *)addr;
++ veaddr->family = AF_INET6;
++ memcpy(veaddr->key, &sin->sin6_addr, sizeof(veaddr->key));
++ break;
++ }
++ default:
++ err = -EAFNOSUPPORT;
++ }
++ return err;
++}
++
++int sockaddr_to_veaddr(struct sockaddr __user *uaddr, int addrlen,
++ struct ve_addr_struct *veaddr)
++{
++ int err;
++ char addr[MAX_SOCK_ADDR];
++
++ err = move_addr_to_kernel(uaddr, addrlen, (struct sockaddr *)&addr);
++ if (err < 0)
++ goto out;
++
++ err = convert_sockaddr((struct sockaddr *)&addr, addrlen, veaddr);
++out:
++ return err;
++}
++
++void veaddr_print(char *str, int len, struct ve_addr_struct *a)
++{
++ if (a->family == AF_INET)
++ snprintf(str, len, "%u.%u.%u.%u", NIPQUAD(a->key[3]));
++ else
++ snprintf(str, len, "%x:%x:%x:%x:%x:%x:%x:%x",
++ ntohl(a->key[0])>>16, ntohl(a->key[0])&0xFFFF,
++ ntohl(a->key[1])>>16, ntohl(a->key[1])&0xFFFF,
++ ntohl(a->key[2])>>16, ntohl(a->key[2])&0xFFFF,
++ ntohl(a->key[3])>>16, ntohl(a->key[3])&0xFFFF
++ );
++}
++
++/*
++ * Device functions
++ */
++
++static int venet_open(struct net_device *dev)
++{
++ if (!ve_is_super(get_exec_env()) && !try_module_get(THIS_MODULE))
++ return -EBUSY;
++ return 0;
++}
++
++static int venet_close(struct net_device *master)
++{
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++ return 0;
++}
++
++static void venet_destructor(struct net_device *dev)
++{
++ struct venet_stats *stats = (struct venet_stats *)dev->priv;
++ if (stats == NULL)
++ return;
++ free_percpu(stats->real_stats);
++ kfree(stats);
++ dev->priv = NULL;
++}
++
++/*
++ * The higher levels take care of making this non-reentrant (it's
++ * called with bh's disabled).
++ */
++static int venet_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++ struct net_device_stats *stats;
++ struct net_device *rcv = NULL;
++ int length;
++
++ stats = venet_stats(dev, smp_processor_id());
++ if (unlikely(get_exec_env()->disable_net))
++ goto outf;
++
++ if (skb->protocol == __constant_htons(ETH_P_IP)) {
++ struct iphdr *iph;
++ iph = ip_hdr(skb);
++ if (ipv4_is_multicast(iph->daddr))
++ goto outf;
++ } else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
++ struct ipv6hdr *ip6h;
++ ip6h = ipv6_hdr(skb);
++ if (ipv6_addr_is_multicast(&ip6h->daddr))
++ goto outf;
++ skb_orphan(skb);
++ } else {
++ goto outf;
++ }
++
++ if (venet_change_skb_owner(skb) < 0)
++ goto outf;
++
++ if (unlikely(skb->owner_env->disable_net))
++ goto outf;
++
++ rcv = skb->owner_env->_venet_dev;
++ if (!rcv)
++ /* VE going down */
++ goto outf;
++
++ dev_hold(rcv);
++
++ if (!(rcv->flags & IFF_UP)) {
++ /* Target VE does not want to receive packets */
++ dev_put(rcv);
++ goto outf;
++ }
++
++ skb->pkt_type = PACKET_HOST;
++ skb->dev = rcv;
++
++ skb_reset_mac_header(skb);
++ memset(skb->data - dev->hard_header_len, 0, dev->hard_header_len);
++
++ dst_release(skb->dst);
++ skb->dst = NULL;
++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
++ nf_conntrack_put(skb->nfct);
++ skb->nfct = NULL;
++#endif
++ length = skb->len;
++
++ netif_rx(skb);
++
++ stats->tx_bytes += length;
++ stats->tx_packets++;
++ if (rcv) {
++ struct net_device_stats *rcv_stats;
++
++ rcv_stats = venet_stats(rcv, smp_processor_id());
++ rcv_stats->rx_bytes += length;
++ rcv_stats->rx_packets++;
++ dev_put(rcv);
++ }
++
++ return 0;
++
++outf:
++ kfree_skb(skb);
++ ++stats->tx_dropped;
++ return 0;
++}
++
++static struct net_device_stats *get_stats(struct net_device *dev)
++{
++ int i;
++ struct venet_stats *stats;
++
++ stats = (struct venet_stats *)dev->priv;
++ memset(&stats->stats, 0, sizeof(struct net_device_stats));
++ for (i=0; i < NR_CPUS; i++) {
++ struct net_device_stats *dev_stats;
++
++ if (!cpu_possible(i))
++ continue;
++ dev_stats = venet_stats(dev, i);
++ stats->stats.rx_bytes += dev_stats->rx_bytes;
++ stats->stats.tx_bytes += dev_stats->tx_bytes;
++ stats->stats.rx_packets += dev_stats->rx_packets;
++ stats->stats.tx_packets += dev_stats->tx_packets;
++ }
++
++ return &stats->stats;
++}
++
++/* Initialize the rest of the LOOPBACK device. */
++int venet_init_dev(struct net_device *dev)
++{
++ struct venet_stats *stats;
++
++ dev->hard_start_xmit = venet_xmit;
++ stats = kzalloc(sizeof(struct venet_stats), GFP_KERNEL);
++ if (stats == NULL)
++ goto fail;
++ stats->real_stats = alloc_percpu(struct net_device_stats);
++ if (stats->real_stats == NULL)
++ goto fail_free;
++ dev->priv = stats;
++
++ dev->get_stats = get_stats;
++ dev->open = venet_open;
++ dev->stop = venet_close;
++ dev->destructor = venet_destructor;
++
++ /*
++ * Fill in the generic fields of the device structure.
++ */
++ dev->type = ARPHRD_VOID;
++ dev->hard_header_len = ETH_HLEN;
++ dev->mtu = 1500; /* eth_mtu */
++ dev->tx_queue_len = 0;
++
++ memset(dev->broadcast, 0xFF, ETH_ALEN);
++
++ /* New-style flags. */
++ dev->flags = IFF_BROADCAST|IFF_NOARP|IFF_POINTOPOINT;
++ return 0;
++
++fail_free:
++ kfree(stats);
++fail:
++ return -ENOMEM;
++}
++
++static int
++venet_set_op(struct net_device *dev, u32 data,
++ int (*fop)(struct net_device *, u32))
++{
++
++ struct ve_struct *ve;
++ int ret = 0;
++
++ read_lock(&ve_list_lock);
++ for_each_ve(ve) {
++ struct ve_struct *ve_old;
++
++ ve_old = set_exec_env(ve);
++ read_lock(&dev_base_lock);
++ for_each_netdev(ve->ve_netns, dev) {
++ if (dev->hard_start_xmit == venet_xmit)
++ ret = fop(dev, data);
++ }
++ read_unlock(&dev_base_lock);
++ set_exec_env(ve_old);
++
++ if (ret < 0)
++ break;
++ }
++ read_unlock(&ve_list_lock);
++ return ret;
++}
++
++static unsigned long common_features;
++
++static int venet_op_set_sg(struct net_device *dev, u32 data)
++{
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
++
++ if (data)
++ common_features |= NETIF_F_SG;
++ else
++ common_features &= ~NETIF_F_SG;
++
++ return venet_set_op(dev, data, ethtool_op_set_sg);
++}
++
++static int venet_op_set_tx_csum(struct net_device *dev, u32 data)
++{
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
++
++ if (data)
++ common_features |= NETIF_F_IP_CSUM;
++ else
++ common_features &= ~NETIF_F_IP_CSUM;
++
++ return venet_set_op(dev, data, ethtool_op_set_tx_csum);
++}
++
++#define venet_op_set_rx_csum venet_op_set_tx_csum
++
++static struct ethtool_ops venet_ethtool_ops = {
++ .get_sg = ethtool_op_get_sg,
++ .set_sg = venet_op_set_sg,
++ .get_tx_csum = ethtool_op_get_tx_csum,
++ .set_tx_csum = venet_op_set_tx_csum,
++ .get_rx_csum = ethtool_op_get_tx_csum,
++ .set_rx_csum = venet_op_set_rx_csum,
++ .get_tso = ethtool_op_get_tso,
++};
++
++static void venet_setup(struct net_device *dev)
++{
++ dev->init = venet_init_dev;
++ /*
++ * No other features, as they are:
++ * - checksumming is required, and nobody else will done our job
++ */
++ dev->features |= NETIF_F_VENET | NETIF_F_VIRTUAL | NETIF_F_LLTX |
++ NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED;
++
++ dev->features |= common_features;
++
++ SET_ETHTOOL_OPS(dev, &venet_ethtool_ops);
++}
++
++#ifdef CONFIG_PROC_FS
++static int veinfo_seq_show(struct seq_file *m, void *v)
++{
++ struct ve_struct *ve;
++ struct ip_entry_struct *entry;
++
++ ve = list_entry((struct list_head *)v, struct ve_struct, ve_list);
++
++ seq_printf(m, "%10u %5u %5u", ve->veid,
++ ve->class_id, atomic_read(&ve->pcounter));
++ read_lock(&veip_hash_lock);
++ if (ve->veip == NULL)
++ goto unlock;
++ list_for_each_entry (entry, &ve->veip->ip_lh, ve_list) {
++ char addr[40];
++
++ if (entry->active_env == NULL)
++ continue;
++
++ veaddr_print(addr, sizeof(addr), &entry->addr);
++ if (entry->addr.family == AF_INET)
++ seq_printf(m, " %15s", addr);
++ else
++ seq_printf(m, " %39s", addr);
++ }
++unlock:
++ read_unlock(&veip_hash_lock);
++ seq_putc(m, '\n');
++ return 0;
++}
++
++static struct seq_operations veinfo_seq_op = {
++ .start = ve_seq_start,
++ .next = ve_seq_next,
++ .stop = ve_seq_stop,
++ .show = veinfo_seq_show,
++};
++
++static int veinfo_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &veinfo_seq_op);
++}
++
++static struct file_operations proc_veinfo_operations = {
++ .open = veinfo_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static void *veip_seq_start(struct seq_file *m, loff_t *pos)
++{
++ loff_t l;
++ struct list_head *p;
++ int i;
++
++ l = *pos;
++ write_lock_irq(&veip_hash_lock);
++ if (l == 0)
++ return ip_entry_hash_table;
++ for (i = 0; i < VEIP_HASH_SZ; i++) {
++ list_for_each(p, ip_entry_hash_table + i) {
++ if (--l == 0)
++ return p;
++ }
++ }
++ return NULL;
++}
++
++static void *veip_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ struct list_head *p;
++
++ p = (struct list_head *)v;
++ while (1) {
++ p = p->next;
++ if (p < ip_entry_hash_table ||
++ p >= ip_entry_hash_table + VEIP_HASH_SZ) {
++ (*pos)++;
++ return p;
++ }
++ if (++p >= ip_entry_hash_table + VEIP_HASH_SZ)
++ return NULL;
++ }
++ return NULL;
++}
++
++static void veip_seq_stop(struct seq_file *m, void *v)
++{
++ write_unlock_irq(&veip_hash_lock);
++}
++
++static struct seq_operations veip_seq_op = {
++ .start = veip_seq_start,
++ .next = veip_seq_next,
++ .stop = veip_seq_stop,
++ .show = veip_seq_show,
++};
++
++static int veip_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &veip_seq_op);
++}
++
++static struct file_operations proc_veip_operations = {
++ .open = veip_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++#endif
++
++static int real_ve_ip_map(envid_t veid, int op, struct sockaddr __user *uaddr,
++ int addrlen)
++{
++ int err;
++ struct ve_struct *ve;
++ struct ve_addr_struct addr;
++
++ err = -EPERM;
++ if (!capable(CAP_SETVEID))
++ goto out;
++
++ err = sockaddr_to_veaddr(uaddr, addrlen, &addr);
++ if (err < 0)
++ goto out;
++
++ switch (op)
++ {
++ case VE_IP_ADD:
++ ve = get_ve_by_id(veid);
++ err = -ESRCH;
++ if (!ve)
++ goto out;
++
++ down_read(&ve->op_sem);
++ if (ve->is_running)
++ err = veip_entry_add(ve, &addr);
++ up_read(&ve->op_sem);
++ put_ve(ve);
++ break;
++
++ case VE_IP_DEL:
++ err = veip_entry_del(veid, &addr);
++ break;
++ default:
++ err = -EINVAL;
++ }
++
++out:
++ return err;
++}
++
++int venet_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ err = -ENOTTY;
++ switch(cmd) {
++ case VENETCTL_VE_IP_MAP: {
++ struct vzctl_ve_ip_map s;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = real_ve_ip_map(s.veid, s.op, s.addr, s.addrlen);
++ break;
++ }
++ }
++ return err;
++}
++
++#ifdef CONFIG_COMPAT
++int compat_venet_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ switch(cmd) {
++ case VENETCTL_COMPAT_VE_IP_MAP: {
++ struct compat_vzctl_ve_ip_map cs;
++
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++
++ err = real_ve_ip_map(cs.veid, cs.op, compat_ptr(cs.addr),
++ cs.addrlen);
++ break;
++ }
++ default:
++ err = venet_ioctl(file, cmd, arg);
++ break;
++ }
++ return err;
++}
++#endif
++
++static struct vzioctlinfo venetcalls = {
++ .type = VENETCTLTYPE,
++ .ioctl = venet_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = compat_venet_ioctl,
++#endif
++ .owner = THIS_MODULE,
++};
++
++int venet_dev_start(struct ve_struct *ve)
++{
++ struct net_device *dev_venet;
++ int err;
++
++ dev_venet = alloc_netdev(0, "venet%d", venet_setup);
++ if (!dev_venet)
++ return -ENOMEM;
++ dev_net_set(dev_venet, ve->ve_netns);
++ err = dev_alloc_name(dev_venet, dev_venet->name);
++ if (err<0)
++ goto err;
++ if ((err = register_netdev(dev_venet)) != 0)
++ goto err;
++ ve->_venet_dev = dev_venet;
++ return 0;
++err:
++ free_netdev(dev_venet);
++ printk(KERN_ERR "VENET initialization error err=%d\n", err);
++ return err;
++}
++
++static int venet_start(void *data)
++{
++ struct ve_struct *env;
++ int err;
++
++ env = (struct ve_struct *)data;
++ if (env->veip)
++ return -EEXIST;
++
++ err = veip_start(env);
++ if (err != 0)
++ return err;
++
++ err = venet_dev_start(env);
++ if (err)
++ goto err_free;
++ return 0;
++
++err_free:
++ veip_stop(env);
++ return err;
++}
++
++static void venet_stop(void *data)
++{
++ struct ve_struct *env;
++ struct net_device *dev;
++
++ env = (struct ve_struct *)data;
++ veip_stop(env);
++
++ dev = env->_venet_dev;
++ if (dev == NULL)
++ return;
++
++ unregister_netdev(dev);
++ env->_venet_dev = NULL;
++ free_netdev(dev);
++}
++
++static struct ve_hook venet_ve_hook = {
++ .init = venet_start,
++ .fini = venet_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_NET,
++};
++
++__init int venet_init(void)
++{
++#ifdef CONFIG_PROC_FS
++ struct proc_dir_entry *de;
++#endif
++ int i, err;
++
++ if (get_ve0()->_venet_dev != NULL)
++ return -EEXIST;
++
++ for (i = 0; i < VEIP_HASH_SZ; i++)
++ INIT_LIST_HEAD(ip_entry_hash_table + i);
++
++ err = venet_start(get_ve0());
++ if (err)
++ return err;
++
++#ifdef CONFIG_PROC_FS
++ de = proc_create("veinfo", S_IFREG | S_IRUSR, glob_proc_vz_dir,
++ &proc_veinfo_operations);
++ if (de == NULL)
++ printk(KERN_WARNING "venet: can't make veinfo proc entry\n");
++
++ de = proc_create("veip", S_IFREG | S_IRUSR, proc_vz_dir,
++ &proc_veip_operations);
++ if (de == NULL)
++ printk(KERN_WARNING "venet: can't make veip proc entry\n");
++#endif
++
++ ve_hook_register(VE_SS_CHAIN, &venet_ve_hook);
++ vzioctl_register(&venetcalls);
++ return 0;
++}
++
++__exit void venet_exit(void)
++{
++ vzioctl_unregister(&venetcalls);
++ ve_hook_unregister(&venet_ve_hook);
++
++#ifdef CONFIG_PROC_FS
++ remove_proc_entry("veip", proc_vz_dir);
++ remove_proc_entry("veinfo", glob_proc_vz_dir);
++#endif
++ venet_stop(get_ve0());
++ veip_cleanup();
++}
++
++module_init(venet_init);
++module_exit(venet_exit);
+diff --git a/drivers/net/vzethdev.c b/drivers/net/vzethdev.c
+new file mode 100644
+index 0000000..1414618
+--- /dev/null
++++ b/drivers/net/vzethdev.c
+@@ -0,0 +1,692 @@
++/*
++ * veth.c
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++/*
++ * Virtual ethernet device used to change VE ownership on packets
++ */
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/fs.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/socket.h>
++#include <linux/errno.h>
++#include <linux/fcntl.h>
++#include <linux/in.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/nsproxy.h>
++#include <linux/tcp.h>
++#include <linux/proc_fs.h>
++#include <linux/seq_file.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/unistd.h>
++
++#include <linux/inet.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <net/ip.h>
++#include <linux/skbuff.h>
++#include <net/sock.h>
++#include <linux/if_ether.h> /* For the statistics structure. */
++#include <linux/if_arp.h> /* For ARPHRD_ETHER */
++#include <linux/ethtool.h>
++#include <linux/ve_proto.h>
++#include <linux/veth.h>
++#include <linux/vzctl.h>
++#include <linux/vzctl_veth.h>
++
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/vzcalluser.h>
++#include <linux/nfcalls.h>
++
++static LIST_HEAD(veth_hwaddr_list);
++static DEFINE_RWLOCK(ve_hwaddr_lock);
++static DECLARE_MUTEX(hwaddr_sem);
++
++struct net_device * veth_dev_start(char *dev_addr, char *name);
++
++struct veth_struct *hwaddr_entry_lookup(char *name)
++{
++ struct veth_struct *entry;
++
++ list_for_each_entry(entry, &veth_hwaddr_list, hwaddr_list) {
++ BUG_ON(entry->pair == NULL);
++ if (strncmp(name, entry->pair->name, IFNAMSIZ) == 0)
++ return entry;
++ }
++ return NULL;
++}
++
++int veth_entry_add(struct ve_struct *ve, char *dev_addr, char *name,
++ char *dev_addr_ve, char *name_ve)
++{
++ struct net_device *dev_ve;
++ struct net_device *dev_ve0;
++ struct ve_struct *old_env;
++ char dev_name[IFNAMSIZ];
++ int err;
++
++ down(&hwaddr_sem);
++
++ if (name[0] == '\0')
++ snprintf(dev_name, sizeof(dev_name), "vz%d.%%d", ve->veid);
++ else {
++ memcpy(dev_name, name, IFNAMSIZ - 1);
++ dev_name[IFNAMSIZ - 1] = '\0';
++ }
++ dev_ve0 = veth_dev_start(dev_addr, dev_name);
++ if (IS_ERR(dev_ve0)) {
++ err = PTR_ERR(dev_ve0);
++ goto err;
++ }
++
++ old_env = set_exec_env(ve);
++ if (name_ve[0] == '\0')
++ sprintf(dev_name, "eth%%d");
++ else {
++ memcpy(dev_name, name_ve, IFNAMSIZ - 1);
++ dev_name[IFNAMSIZ - 1] = '\0';
++ }
++ dev_ve = veth_dev_start(dev_addr_ve, dev_name);
++ if (IS_ERR(dev_ve)) {
++ err = PTR_ERR(dev_ve);
++ goto err_ve;
++ }
++ set_exec_env(old_env);
++ veth_from_netdev(dev_ve)->pair = dev_ve0;
++ veth_from_netdev(dev_ve0)->pair = dev_ve;
++
++ write_lock(&ve_hwaddr_lock);
++ list_add(&(veth_from_netdev(dev_ve)->hwaddr_list), &veth_hwaddr_list);
++ write_unlock(&ve_hwaddr_lock);
++
++ up(&hwaddr_sem);
++ return 0;
++
++err_ve:
++ set_exec_env(old_env);
++ unregister_netdev(dev_ve0);
++err:
++ up(&hwaddr_sem);
++ return err;
++}
++
++void veth_pair_del(struct ve_struct *env, struct veth_struct *entry)
++{
++ struct net_device *dev;
++ struct ve_struct *old_env;
++
++ write_lock(&ve_hwaddr_lock);
++ list_del(&entry->hwaddr_list);
++ write_unlock(&ve_hwaddr_lock);
++
++ dev = entry->pair;
++ BUG_ON(entry->pair == NULL);
++
++ veth_from_netdev(dev)->pair = NULL;
++ entry->pair = NULL;
++ rtnl_lock();
++ old_env = set_exec_env(dev->owner_env);
++ dev_close(dev);
++
++ /*
++ * Now device from VE0 does not send or receive anything,
++ * i.e. dev->hard_start_xmit won't be called.
++ */
++ set_exec_env(env);
++ unregister_netdevice(veth_to_netdev(entry));
++ set_exec_env(dev->owner_env);
++ unregister_netdevice(dev);
++ set_exec_env(old_env);
++ rtnl_unlock();
++}
++
++int veth_entry_del(struct ve_struct *ve, char *name)
++{
++ struct veth_struct *found;
++ int err;
++
++ err = -ENODEV;
++ down(&hwaddr_sem);
++ found = hwaddr_entry_lookup(name);
++ if (found == NULL)
++ goto out;
++ if (veth_to_netdev(found)->owner_env != ve)
++ goto out;
++
++ err = 0;
++ veth_pair_del(ve, found);
++
++out:
++ up(&hwaddr_sem);
++ return err;
++}
++
++int veth_allow_change_mac(envid_t veid, char *name, int allow)
++{
++ struct ve_struct *ve;
++ struct veth_struct *found;
++ int err;
++
++ err = -ESRCH;
++ ve = get_ve_by_id(veid);
++ if (!ve)
++ return err;
++
++ down_read(&ve->op_sem);
++ if (!ve->is_running)
++ goto out_ve;
++ err = -ENODEV;
++ down(&hwaddr_sem);
++ found = hwaddr_entry_lookup(name);
++ if (found == NULL)
++ goto out_sem;
++ if (veth_to_netdev(found)->owner_env != ve)
++ goto out_sem;
++
++ err = 0;
++ found->allow_mac_change = allow;
++
++out_sem:
++ up(&hwaddr_sem);
++out_ve:
++ up_read(&ve->op_sem);
++ put_ve(ve);
++ return err;
++}
++
++/*
++ * Device functions
++ */
++
++static int veth_open(struct net_device *dev)
++{
++ return 0;
++}
++
++static int veth_close(struct net_device *master)
++{
++ return 0;
++}
++
++static void veth_destructor(struct net_device *dev)
++{
++ free_percpu(veth_from_netdev(dev)->real_stats);
++ free_netdev(dev);
++}
++
++static struct net_device_stats *get_stats(struct net_device *dev)
++{
++ int i;
++ struct net_device_stats *stats;
++
++ stats = &veth_from_netdev(dev)->stats;
++ memset(stats, 0, sizeof(struct net_device_stats));
++ for (i = 0; i < NR_CPUS; i++) {
++ struct net_device_stats *dev_stats;
++
++ if (!cpu_possible(i))
++ continue;
++ dev_stats = veth_stats(dev, i);
++ stats->rx_bytes += dev_stats->rx_bytes;
++ stats->tx_bytes += dev_stats->tx_bytes;
++ stats->rx_packets += dev_stats->rx_packets;
++ stats->tx_packets += dev_stats->tx_packets;
++ }
++
++ return stats;
++}
++
++/*
++ * The higher levels take care of making this non-reentrant (it's
++ * called with bh's disabled).
++ */
++static int veth_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++ struct net_device_stats *stats;
++ struct net_device *rcv = NULL;
++ struct veth_struct *entry;
++ int length;
++
++ stats = veth_stats(dev, smp_processor_id());
++ if (unlikely(get_exec_env()->disable_net))
++ goto outf;
++
++ entry = veth_from_netdev(dev);
++ rcv = entry->pair;
++ if (!rcv)
++ /* VE going down */
++ goto outf;
++
++ if (!(rcv->flags & IFF_UP)) {
++ /* Target VE does not want to receive packets */
++ goto outf;
++ }
++
++ if (unlikely(rcv->owner_env->disable_net))
++ goto outf;
++ /* Filtering */
++ if (ve_is_super(dev->owner_env) &&
++ !veth_from_netdev(rcv)->allow_mac_change) {
++ /* from VE0 to VEX */
++ if (ve_is_super(rcv->owner_env))
++ goto out;
++ if (is_multicast_ether_addr(
++ ((struct ethhdr *)skb->data)->h_dest))
++ goto out;
++ if (compare_ether_addr(((struct ethhdr *)skb->data)->h_dest,
++ rcv->dev_addr))
++ goto outf;
++ } else if (!ve_is_super(dev->owner_env) &&
++ !entry->allow_mac_change) {
++ /* from VE to VE0 */
++ if (compare_ether_addr(((struct ethhdr *)skb->data)->h_source,
++ dev->dev_addr))
++ goto outf;
++ }
++
++out:
++ skb->owner_env = rcv->owner_env;
++
++ skb->dev = rcv;
++ skb->pkt_type = PACKET_HOST;
++ skb->protocol = eth_type_trans(skb, rcv);
++
++ if (skb->protocol != __constant_htons(ETH_P_IP))
++ skb_orphan(skb);
++
++ dst_release(skb->dst);
++ skb->dst = NULL;
++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
++ nf_conntrack_put(skb->nfct);
++ skb->nfct = NULL;
++#endif
++ length = skb->len;
++
++ netif_rx(skb);
++
++ stats->tx_bytes += length;
++ stats->tx_packets++;
++ if (rcv) {
++ struct net_device_stats *rcv_stats;
++ rcv_stats = veth_stats(rcv, smp_processor_id());
++ rcv_stats->rx_bytes += length;
++ rcv_stats->rx_packets++;
++ }
++
++ return 0;
++
++outf:
++ kfree_skb(skb);
++ stats->tx_dropped++;
++ return 0;
++}
++
++static int veth_set_mac(struct net_device *dev, void *p)
++{
++ struct sockaddr *addr = p;
++
++ if (!ve_is_super(dev->owner_env) &&
++ !veth_from_netdev(dev)->allow_mac_change)
++ return -EPERM;
++ if (netif_running(dev))
++ return -EBUSY;
++ if (!is_valid_ether_addr(addr->sa_data))
++ return -EADDRNOTAVAIL;
++
++ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
++
++ return 0;
++}
++
++int veth_init_dev(struct net_device *dev)
++{
++ dev->hard_start_xmit = veth_xmit;
++ dev->get_stats = get_stats;
++ dev->open = veth_open;
++ dev->stop = veth_close;
++ dev->destructor = veth_destructor;
++
++ ether_setup(dev);
++ dev->set_mac_address = veth_set_mac;
++
++ /* remove setted by ether_setup() handler */
++ dev->change_mtu = NULL;
++
++ dev->tx_queue_len = 0;
++
++ veth_from_netdev(dev)->real_stats =
++ alloc_percpu(struct net_device_stats);
++ if (veth_from_netdev(dev)->real_stats == NULL)
++ return -ENOMEM;
++
++ return 0;
++}
++
++static int
++veth_set_op(struct net_device *dev, u32 data,
++ int (*fop)(struct net_device *, u32))
++{
++ struct net_device *pair;
++ int ret = 0;
++
++ ret = fop(dev, data);
++ if (ret < 0)
++ goto out;
++
++ pair = veth_from_netdev(dev)->pair;
++ if (pair)
++ ret = fop(pair, data);
++out:
++ return ret;
++}
++
++static int veth_op_set_sg(struct net_device *dev, u32 data)
++{
++ return veth_set_op(dev, data, ethtool_op_set_sg);
++}
++
++static int veth_op_set_tx_csum(struct net_device *dev, u32 data)
++{
++ return veth_set_op(dev, data, ethtool_op_set_tx_csum);
++}
++
++#define veth_op_set_rx_csum veth_op_set_tx_csum
++
++static struct ethtool_ops veth_ethtool_ops = {
++ .get_sg = ethtool_op_get_sg,
++ .set_sg = veth_op_set_sg,
++ .get_tx_csum = ethtool_op_get_tx_csum,
++ .set_tx_csum = veth_op_set_tx_csum,
++ .get_rx_csum = ethtool_op_get_tx_csum,
++ .set_rx_csum = veth_op_set_rx_csum,
++ .get_tso = ethtool_op_get_tso,
++};
++
++static void veth_setup(struct net_device *dev)
++{
++ dev->init = veth_init_dev;
++ /*
++ * No other features, as they are:
++ * - checksumming is required, and nobody else will done our job
++ */
++ dev->features |= NETIF_F_VENET | NETIF_F_VIRTUAL | NETIF_F_LLTX |
++ NETIF_F_HIGHDMA;
++
++ SET_ETHTOOL_OPS(dev, &veth_ethtool_ops);
++}
++
++#ifdef CONFIG_PROC_FS
++#define ADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
++#define ADDR_ARG(x) (x)[0],(x)[1],(x)[2],(x)[3],(x)[4],(x)[5]
++static int vehwaddr_seq_show(struct seq_file *m, void *v)
++{
++ struct list_head *p;
++ struct veth_struct *entry;
++
++ p = (struct list_head *)v;
++ if (p == &veth_hwaddr_list) {
++ seq_puts(m, "Version: 1.0\n");
++ return 0;
++ }
++ entry = list_entry(p, struct veth_struct, hwaddr_list);
++ seq_printf(m, ADDR_FMT " %16s ",
++ ADDR_ARG(entry->pair->dev_addr), entry->pair->name);
++ seq_printf(m, ADDR_FMT " %16s %10u %5s\n",
++ ADDR_ARG(veth_to_netdev(entry)->dev_addr),
++ veth_to_netdev(entry)->name,
++ VEID(veth_to_netdev(entry)->owner_env),
++ entry->allow_mac_change ? "allow" : "deny");
++ return 0;
++}
++
++static void *vehwaddr_seq_start(struct seq_file *m, loff_t *pos)
++{
++ read_lock(&ve_hwaddr_lock);
++ return seq_list_start_head(&veth_hwaddr_list, *pos);
++}
++
++static void *vehwaddr_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ return seq_list_next(v, &veth_hwaddr_list, pos);
++}
++
++static void vehwaddr_seq_stop(struct seq_file *m, void *v)
++{
++ read_unlock(&ve_hwaddr_lock);
++}
++
++static struct seq_operations vehwaddr_seq_op = {
++ .start = vehwaddr_seq_start,
++ .next = vehwaddr_seq_next,
++ .stop = vehwaddr_seq_stop,
++ .show = vehwaddr_seq_show,
++};
++
++static int vehwaddr_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &vehwaddr_seq_op);
++}
++
++static struct file_operations proc_vehwaddr_operations = {
++ .open = vehwaddr_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++#endif
++
++int real_ve_hwaddr(envid_t veid, int op,
++ unsigned char *dev_addr, int addrlen, char *name,
++ unsigned char *dev_addr_ve, int addrlen_ve, char *name_ve)
++{
++ int err;
++ struct ve_struct *ve;
++ char ve_addr[ETH_ALEN];
++
++ err = -EPERM;
++ if (!capable(CAP_NET_ADMIN))
++ goto out;
++
++ err = -EINVAL;
++ switch (op) {
++ case VE_ETH_ADD:
++ if (addrlen != ETH_ALEN)
++ goto out;
++ if (addrlen_ve != ETH_ALEN && addrlen_ve != 0)
++ goto out;
++ /* If ve addr is not set then we use dev_addr[3] & 0x80 for it */
++ if (addrlen_ve == 0 && (dev_addr[3] & 0x80))
++ goto out;
++ if (addrlen_ve == 0) {
++ memcpy(ve_addr, dev_addr, ETH_ALEN);
++ ve_addr[3] |= 0x80;
++ } else {
++ memcpy(ve_addr, dev_addr_ve, ETH_ALEN);
++ }
++
++ ve = get_ve_by_id(veid);
++ err = -ESRCH;
++ if (!ve)
++ goto out;
++
++ down_read(&ve->op_sem);
++ if (ve->is_running)
++ err = veth_entry_add(ve, dev_addr, name, ve_addr, name_ve);
++ up_read(&ve->op_sem);
++ put_ve(ve);
++ break;
++
++ case VE_ETH_DEL:
++ if (name[0] == '\0')
++ goto out;
++ ve = get_ve_by_id(veid);
++ err = -ESRCH;
++ if (!ve)
++ goto out;
++
++ down_read(&ve->op_sem);
++ if (ve->is_running)
++ err = veth_entry_del(ve, name);
++ up_read(&ve->op_sem);
++ put_ve(ve);
++ break;
++ case VE_ETH_ALLOW_MAC_CHANGE:
++ case VE_ETH_DENY_MAC_CHANGE:
++ err = veth_allow_change_mac(veid, name,
++ op == VE_ETH_ALLOW_MAC_CHANGE);
++ break;
++ }
++
++out:
++ return err;
++}
++
++int veth_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ err = -ENOTTY;
++ switch(cmd) {
++ case VETHCTL_VE_HWADDR: {
++ struct vzctl_ve_hwaddr s;
++
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = real_ve_hwaddr(s.veid, s.op, s.dev_addr, s.addrlen,
++ s.dev_name, s.dev_addr_ve, s.addrlen_ve,
++ s.dev_name_ve);
++ }
++ break;
++ }
++ return err;
++}
++
++static struct vzioctlinfo vethcalls = {
++ .type = VETHCTLTYPE,
++ .ioctl = veth_ioctl,
++ .compat_ioctl = veth_ioctl,
++ .owner = THIS_MODULE,
++};
++
++struct net_device * veth_dev_start(char *dev_addr, char *name)
++{
++ struct net_device *dev;
++ int err;
++
++ if (!is_valid_ether_addr(dev_addr))
++ return ERR_PTR(-EADDRNOTAVAIL);
++
++ dev = alloc_netdev(sizeof(struct veth_struct), name, veth_setup);
++ if (!dev)
++ return ERR_PTR(-ENOMEM);
++ dev->nd_net = get_exec_env()->ve_netns;
++ if (strchr(dev->name, '%')) {
++ err = dev_alloc_name(dev, dev->name);
++ if (err < 0)
++ goto err;
++ }
++ if ((err = register_netdev(dev)) != 0)
++ goto err;
++
++ memcpy(dev->dev_addr, dev_addr, ETH_ALEN);
++ dev->addr_len = ETH_ALEN;
++
++ return dev;
++err:
++ free_netdev(dev);
++ printk(KERN_ERR "%s initialization error err=%d\n", name, err);
++ return ERR_PTR(err);
++}
++
++static int veth_start(void *data)
++{
++ return 0;
++}
++
++static void veth_stop(void *data)
++{
++ struct ve_struct *env;
++ struct veth_struct *entry, *tmp;
++
++ env = (struct ve_struct *)data;
++ down(&hwaddr_sem);
++ list_for_each_entry_safe(entry, tmp, &veth_hwaddr_list, hwaddr_list)
++ if (VEID(env) == VEID(veth_to_netdev(entry)->owner_env))
++ veth_pair_del(env, entry);
++ up(&hwaddr_sem);
++}
++
++static struct ve_hook veth_ve_hook = {
++ .init = veth_start,
++ .fini = veth_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_NET,
++};
++
++__init int veth_init(void)
++{
++#ifdef CONFIG_PROC_FS
++ struct proc_dir_entry *de;
++
++ de = proc_create("veth", S_IFREG|S_IRUSR, proc_vz_dir,
++ &proc_vehwaddr_operations);
++ if (de == NULL)
++ printk(KERN_WARNING "veth: can't make vehwaddr proc entry\n");
++#endif
++
++ ve_hook_register(VE_SS_CHAIN, &veth_ve_hook);
++ vzioctl_register(&vethcalls);
++ KSYMRESOLVE(veth_open);
++ KSYMMODRESOLVE(vzethdev);
++ return 0;
++}
++
++__exit void veth_exit(void)
++{
++ struct veth_struct *entry;
++ struct list_head *tmp, *n;
++ struct ve_struct *ve;
++
++ KSYMMODUNRESOLVE(vzethdev);
++ KSYMUNRESOLVE(veth_open);
++ vzioctl_unregister(&vethcalls);
++ ve_hook_unregister(&veth_ve_hook);
++#ifdef CONFIG_PROC_FS
++ remove_proc_entry("veth", proc_vz_dir);
++#endif
++
++ down(&hwaddr_sem);
++ list_for_each_safe(tmp, n, &veth_hwaddr_list) {
++ entry = list_entry(tmp, struct veth_struct, hwaddr_list);
++ ve = get_ve(veth_to_netdev(entry)->owner_env);
++
++ veth_pair_del(ve, entry);
++
++ put_ve(ve);
++ }
++ up(&hwaddr_sem);
++}
++
++module_init(veth_init);
++module_exit(veth_exit);
++
++MODULE_AUTHOR("Andrey Mirkin <amirkin@sw.ru>");
++MODULE_DESCRIPTION("Virtuozzo Virtual Ethernet Device");
++MODULE_LICENSE("GPL v2");
++
+diff --git a/fs/Kconfig b/fs/Kconfig
+index abccb5d..59091ad 100644
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -562,13 +562,22 @@ config QUOTA_NETLINK_INTERFACE
+ config PRINT_QUOTA_WARNING
+ bool "Print quota warnings to console (OBSOLETE)"
+ depends on QUOTA
+- default y
++ default n
+ help
+ If you say Y here, quota warnings (about exceeding softlimit, reaching
+ hardlimit, etc.) will be printed to the process' controlling terminal.
+ Note that this behavior is currently deprecated and may go away in
+ future. Please use notification via netlink socket instead.
+
++config QUOTA_COMPAT
++ bool "Compatibility with older quotactl interface"
++ depends on QUOTA
++ help
++ This option enables compatibility layer for older version
++ of quotactl interface with byte granularity (QUOTAON at 0x0100,
++ GETQUOTA at 0x0D00). Interface versions older than that one and
++ with block granularity are still not supported.
++
+ config QFMT_V1
+ tristate "Old quota format support"
+ depends on QUOTA
+@@ -584,6 +593,40 @@ config QFMT_V2
+ This quota format allows using quotas with 32-bit UIDs/GIDs. If you
+ need this functionality say Y here.
+
++config SIM_FS
++ tristate "VPS filesystem"
++ depends on VZ_QUOTA
++ default m
++ help
++ This file system is a part of Virtuozzo. It intoduces a fake
++ superblock and blockdev to VE to hide real device and show
++ statfs results taken from quota.
++
++config VZ_QUOTA
++ tristate "Virtuozzo Disk Quota support"
++ select QUOTA
++ select QUOTA_COMPAT
++ select VZ_DEV
++ default m
++ help
++ Virtuozzo Disk Quota imposes disk quota on directories with their
++ files and subdirectories in total. Such disk quota is used to
++ account and limit disk usage by Virtuozzo VPS, but also may be used
++ separately.
++
++config VZ_QUOTA_UNLOAD
++ bool "Unloadable Virtuozzo Disk Quota module"
++ depends on VZ_QUOTA=m
++ default n
++ help
++ Make Virtuozzo Disk Quota module unloadable.
++ Doesn't work reliably now.
++
++config VZ_QUOTA_UGID
++ bool "Per-user and per-group quota in Virtuozzo quota partitions"
++ depends on VZ_QUOTA!=n
++ default y
++
+ config QUOTACTL
+ bool
+ depends on XFS_QUOTA || QUOTA
+diff --git a/fs/Makefile b/fs/Makefile
+index a1482a5..8a040bf 100644
+--- a/fs/Makefile
++++ b/fs/Makefile
+@@ -54,9 +54,15 @@ obj-$(CONFIG_QUOTA) += dquot.o
+ obj-$(CONFIG_QFMT_V1) += quota_v1.o
+ obj-$(CONFIG_QFMT_V2) += quota_v2.o
+ obj-$(CONFIG_QUOTACTL) += quota.o
++obj-$(CONFIG_VZ_QUOTA) += vzdquota.o
++vzdquota-y += vzdquot.o vzdq_mgmt.o vzdq_ops.o vzdq_tree.o
++vzdquota-$(CONFIG_VZ_QUOTA_UGID) += vzdq_ugid.o
++vzdquota-$(CONFIG_VZ_QUOTA_UGID) += vzdq_file.o
+
+ obj-$(CONFIG_DNOTIFY) += dnotify.o
+
++obj-$(CONFIG_SIM_FS) += simfs.o
++
+ obj-$(CONFIG_PROC_FS) += proc/
+ obj-y += partitions/
+ obj-$(CONFIG_SYSFS) += sysfs/
+diff --git a/fs/aio.c b/fs/aio.c
+index f658441..2742c37 100644
+--- a/fs/aio.c
++++ b/fs/aio.c
+@@ -43,13 +43,16 @@
+ #endif
+
+ /*------ sysctl variables----*/
+-static DEFINE_SPINLOCK(aio_nr_lock);
++DEFINE_SPINLOCK(aio_nr_lock);
++EXPORT_SYMBOL_GPL(aio_nr_lock);
+ unsigned long aio_nr; /* current system wide number of aio requests */
++EXPORT_SYMBOL_GPL(aio_nr);
+ unsigned long aio_max_nr = 0x10000; /* system wide maximum number of aio requests */
+ /*----end sysctl variables---*/
+
+ static struct kmem_cache *kiocb_cachep;
+-static struct kmem_cache *kioctx_cachep;
++struct kmem_cache *kioctx_cachep;
++EXPORT_SYMBOL_GPL(kioctx_cachep);
+
+ static struct workqueue_struct *aio_wq;
+
+@@ -60,7 +63,7 @@ static DECLARE_WORK(fput_work, aio_fput_routine);
+ static DEFINE_SPINLOCK(fput_lock);
+ static LIST_HEAD(fput_head);
+
+-static void aio_kick_handler(struct work_struct *);
++void aio_kick_handler(struct work_struct *);
+ static void aio_queue_work(struct kioctx *);
+
+ /* aio_setup
+@@ -327,7 +330,7 @@ static void aio_cancel_all(struct kioctx *ctx)
+ spin_unlock_irq(&ctx->ctx_lock);
+ }
+
+-static void wait_for_all_aios(struct kioctx *ctx)
++void wait_for_all_aios(struct kioctx *ctx)
+ {
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+@@ -350,6 +353,7 @@ static void wait_for_all_aios(struct kioctx *ctx)
+ out:
+ spin_unlock_irq(&ctx->ctx_lock);
+ }
++EXPORT_SYMBOL_GPL(wait_for_all_aios);
+
+ /* wait_on_sync_kiocb:
+ * Waits on the given sync kiocb to complete.
+@@ -836,7 +840,7 @@ static inline void aio_run_all_iocbs(struct kioctx *ctx)
+ * space.
+ * Run on aiod's context.
+ */
+-static void aio_kick_handler(struct work_struct *work)
++void aio_kick_handler(struct work_struct *work)
+ {
+ struct kioctx *ctx = container_of(work, struct kioctx, wq.work);
+ mm_segment_t oldfs = get_fs();
+@@ -857,7 +861,7 @@ static void aio_kick_handler(struct work_struct *work)
+ if (requeue)
+ queue_delayed_work(aio_wq, &ctx->wq, 0);
+ }
+-
++EXPORT_SYMBOL_GPL(aio_kick_handler);
+
+ /*
+ * Called by kick_iocb to queue the kiocb for retry
+diff --git a/fs/autofs/init.c b/fs/autofs/init.c
+index cea5219..1217caf 100644
+--- a/fs/autofs/init.c
++++ b/fs/autofs/init.c
+@@ -25,6 +25,7 @@ static struct file_system_type autofs_fs_type = {
+ .name = "autofs",
+ .get_sb = autofs_get_sb,
+ .kill_sb = autofs_kill_sb,
++ .fs_flags = FS_VIRTUALIZED,
+ };
+
+ static int __init init_autofs_fs(void)
+diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
+index dda510d..1f6e222 100644
+--- a/fs/autofs/inode.c
++++ b/fs/autofs/inode.c
+@@ -78,7 +78,7 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
+
+ *uid = current->uid;
+ *gid = current->gid;
+- *pgrp = task_pgrp_nr(current);
++ *pgrp = task_pgrp_vnr(current);
+
+ *minproto = *maxproto = AUTOFS_PROTO_VERSION;
+
+diff --git a/fs/autofs/root.c b/fs/autofs/root.c
+index 8aacade..f273f47 100644
+--- a/fs/autofs/root.c
++++ b/fs/autofs/root.c
+@@ -362,7 +362,7 @@ static int autofs_root_unlink(struct inode *dir, struct dentry *dentry)
+
+ /* This allows root to remove symlinks */
+ lock_kernel();
+- if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN)) {
++ if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_SYS_ADMIN)) {
+ unlock_kernel();
+ return -EACCES;
+ }
+@@ -556,7 +556,7 @@ static int autofs_root_ioctl(struct inode *inode, struct file *filp,
+ _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT)
+ return -ENOTTY;
+
+- if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
++ if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+
+ switch(cmd) {
+diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
+index 69a2f5c..aa0d0b0 100644
+--- a/fs/autofs4/autofs_i.h
++++ b/fs/autofs4/autofs_i.h
+@@ -100,7 +100,7 @@ struct autofs_sb_info {
+ u32 magic;
+ int pipefd;
+ struct file *pipe;
+- pid_t oz_pgrp;
++ struct pid *oz_pgrp;
+ int catatonic;
+ int version;
+ int sub_version;
+@@ -134,7 +134,7 @@ static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry)
+ filesystem without "magic".) */
+
+ static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
+- return sbi->catatonic || task_pgrp_nr(current) == sbi->oz_pgrp;
++ return sbi->catatonic || task_pgrp(current) == sbi->oz_pgrp;
+ }
+
+ /* Does a dentry have some pending activity? */
+diff --git a/fs/autofs4/init.c b/fs/autofs4/init.c
+index 723a1c5..01ac1e0 100644
+--- a/fs/autofs4/init.c
++++ b/fs/autofs4/init.c
+@@ -25,6 +25,7 @@ static struct file_system_type autofs_fs_type = {
+ .name = "autofs",
+ .get_sb = autofs_get_sb,
+ .kill_sb = autofs4_kill_sb,
++ .fs_flags = FS_VIRTUALIZED,
+ };
+
+ static int __init init_autofs4_fs(void)
+diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
+index 7bb3e5b..dc86587 100644
+--- a/fs/autofs4/inode.c
++++ b/fs/autofs4/inode.c
+@@ -169,6 +169,8 @@ void autofs4_kill_sb(struct super_block *sb)
+ /* Clean up and release dangling references */
+ autofs4_force_release(sbi);
+
++ put_pid(sbi->oz_pgrp);
++
+ sb->s_fs_info = NULL;
+ kfree(sbi);
+
+@@ -190,7 +192,7 @@ static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt)
+ seq_printf(m, ",uid=%u", root_inode->i_uid);
+ if (root_inode->i_gid != 0)
+ seq_printf(m, ",gid=%u", root_inode->i_gid);
+- seq_printf(m, ",pgrp=%d", sbi->oz_pgrp);
++ seq_printf(m, ",pgrp=%d", pid_vnr(sbi->oz_pgrp));
+ seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ);
+ seq_printf(m, ",minproto=%d", sbi->min_proto);
+ seq_printf(m, ",maxproto=%d", sbi->max_proto);
+@@ -235,7 +237,7 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
+
+ *uid = current->uid;
+ *gid = current->gid;
+- *pgrp = task_pgrp_nr(current);
++ *pgrp = task_pgrp_vnr(current);
+
+ *minproto = AUTOFS_MIN_PROTO_VERSION;
+ *maxproto = AUTOFS_MAX_PROTO_VERSION;
+@@ -320,6 +322,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
+ int pipefd;
+ struct autofs_sb_info *sbi;
+ struct autofs_info *ino;
++ pid_t pgrp;
+
+ sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+ if (!sbi)
+@@ -332,7 +335,6 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
+ sbi->pipe = NULL;
+ sbi->catatonic = 1;
+ sbi->exp_timeout = 0;
+- sbi->oz_pgrp = task_pgrp_nr(current);
+ sbi->sb = s;
+ sbi->version = 0;
+ sbi->sub_version = 0;
+@@ -371,7 +373,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
+
+ /* Can this call block? */
+ if (parse_options(data, &pipefd, &root_inode->i_uid, &root_inode->i_gid,
+- &sbi->oz_pgrp, &sbi->type, &sbi->min_proto,
++ &pgrp, &sbi->type, &sbi->min_proto,
+ &sbi->max_proto)) {
+ printk("autofs: called with bogus options\n");
+ goto fail_dput;
+@@ -399,12 +401,20 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
+ sbi->version = sbi->max_proto;
+ sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
+
+- DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp);
++ DPRINTK("pipe fd = %d, pgrp = %u", pipefd, pgrp);
++
++ sbi->oz_pgrp = find_get_pid(pgrp);
++
++ if (!sbi->oz_pgrp) {
++ printk("autofs: could not find process group %d\n", pgrp);
++ goto fail_dput;
++ }
++
+ pipe = fget(pipefd);
+
+ if (!pipe) {
+ printk("autofs: could not open pipe file descriptor\n");
+- goto fail_dput;
++ goto fail_put_pid;
+ }
+ if (!pipe->f_op || !pipe->f_op->write)
+ goto fail_fput;
+@@ -425,6 +435,8 @@ fail_fput:
+ printk("autofs: pipe file descriptor does not contain proper ops\n");
+ fput(pipe);
+ /* fall through */
++fail_put_pid:
++ put_pid(sbi->oz_pgrp);
+ fail_dput:
+ dput(root);
+ goto fail_free;
+diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
+index 2a41c2a..a3191d3 100644
+--- a/fs/autofs4/root.c
++++ b/fs/autofs4/root.c
+@@ -689,7 +689,7 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
+ struct autofs_info *p_ino;
+
+ /* This allows root to remove symlinks */
+- if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
++ if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_SYS_ADMIN))
+ return -EACCES;
+
+ if (atomic_dec_and_test(&ino->count)) {
+@@ -883,7 +883,7 @@ static int autofs4_root_ioctl(struct inode *inode, struct file *filp,
+ _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT)
+ return -ENOTTY;
+
+- if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
++ if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+
+ switch(cmd) {
+diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
+index 35216d1..dbdad95 100644
+--- a/fs/autofs4/waitq.c
++++ b/fs/autofs4/waitq.c
+@@ -148,6 +148,16 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
+ struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet;
+
+ pktsz = sizeof(*packet);
++#if defined CONFIG_X86_64 && defined CONFIG_IA32_EMULATION
++ /*
++ * On x86_64 autofs_v5_packet struct padded with 4 bytes
++ * it broke autofs daemon worked in ia32 emulation mode
++ *
++ * reduce size if work in 32-bit mode to satisfy userspace hope
++ */
++ if (test_thread_flag(TIF_IA32))
++ pktsz -= 4;
++#endif
+
+ packet->wait_queue_token = wq->wait_queue_token;
+ packet->len = wq->name.len;
+diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
+index 204cfd1..cbc5262 100644
+--- a/fs/binfmt_aout.c
++++ b/fs/binfmt_aout.c
+@@ -375,12 +375,12 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+ if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
+ (N_MAGIC(ex) != NMAGIC) && printk_ratelimit())
+ {
+- printk(KERN_NOTICE "executable not page aligned\n");
++ ve_printk(VE_LOG, KERN_NOTICE "executable not page aligned\n");
+ }
+
+ if ((fd_offset & ~PAGE_MASK) != 0 && printk_ratelimit())
+ {
+- printk(KERN_WARNING
++ ve_printk(VE_LOG, KERN_WARNING
+ "fd_offset is not page aligned. Please convert program: %s\n",
+ bprm->file->f_path.dentry->d_name.name);
+ }
+@@ -489,7 +489,7 @@ static int load_aout_library(struct file *file)
+
+ if (printk_ratelimit())
+ {
+- printk(KERN_WARNING
++ ve_printk(VE_LOG, KERN_WARNING
+ "N_TXTOFF is not page aligned. Please convert library: %s\n",
+ file->f_path.dentry->d_name.name);
+ }
+diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
+index 655ed8d..659bd08 100644
+--- a/fs/binfmt_elf.c
++++ b/fs/binfmt_elf.c
+@@ -432,7 +432,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
+ eppnt = elf_phdata;
+ for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
+ if (eppnt->p_type == PT_LOAD) {
+- int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
++ int elf_type = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECPRIO;
+ int elf_prot = 0;
+ unsigned long vaddr = 0;
+ unsigned long k, map_addr;
+@@ -814,7 +814,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ if (elf_ppnt->p_flags & PF_X)
+ elf_prot |= PROT_EXEC;
+
+- elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
++ elf_flags = MAP_PRIVATE | MAP_DENYWRITE |
++ MAP_EXECUTABLE | MAP_EXECPRIO;
+
+ vaddr = elf_ppnt->p_vaddr;
+ if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
+@@ -949,7 +950,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ set_binfmt(&elf_format);
+
+ #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
+- retval = arch_setup_additional_pages(bprm, executable_stack);
++ retval = arch_setup_additional_pages(bprm, executable_stack, 0);
+ if (retval < 0) {
+ send_sig(SIGKILL, current, 0);
+ goto out;
+diff --git a/fs/block_dev.c b/fs/block_dev.c
+index aff5421..54756e5 100644
+--- a/fs/block_dev.c
++++ b/fs/block_dev.c
+@@ -1307,7 +1307,7 @@ int __invalidate_device(struct block_device *bdev)
+ * hold).
+ */
+ shrink_dcache_sb(sb);
+- res = invalidate_inodes(sb);
++ res = invalidate_inodes_check(sb, 1);
+ drop_super(sb);
+ }
+ invalidate_bdev(bdev);
+diff --git a/fs/buffer.c b/fs/buffer.c
+index ac78d4c..c375336 100644
+--- a/fs/buffer.c
++++ b/fs/buffer.c
+@@ -700,6 +700,8 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
+ static int __set_page_dirty(struct page *page,
+ struct address_space *mapping, int warn)
+ {
++ int acct = 0;
++
+ if (unlikely(!mapping))
+ return !TestSetPageDirty(page);
+
+@@ -714,12 +716,14 @@ static int __set_page_dirty(struct page *page,
+ __inc_zone_page_state(page, NR_FILE_DIRTY);
+ __inc_bdi_stat(mapping->backing_dev_info,
+ BDI_RECLAIMABLE);
+- task_io_account_write(PAGE_CACHE_SIZE);
++ acct = 1;
+ }
+ radix_tree_tag_set(&mapping->page_tree,
+ page_index(page), PAGECACHE_TAG_DIRTY);
+ }
+ spin_unlock_irq(&mapping->tree_lock);
++ if (acct)
++ task_io_account_write(page, PAGE_CACHE_SIZE, 0);
+ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+
+ return 1;
+diff --git a/fs/char_dev.c b/fs/char_dev.c
+index 3cb7cda..228de49 100644
+--- a/fs/char_dev.c
++++ b/fs/char_dev.c
+@@ -22,6 +22,8 @@
+ #include <linux/mutex.h>
+ #include <linux/backing-dev.h>
+
++#include <linux/ve_proto.h>
++
+ #ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+ #endif
+diff --git a/fs/compat.c b/fs/compat.c
+index 075d050..dc2674c 100644
+--- a/fs/compat.c
++++ b/fs/compat.c
+@@ -26,6 +26,7 @@
+ #include <linux/file.h>
+ #include <linux/fdtable.h>
+ #include <linux/vfs.h>
++#include <linux/virtinfo.h>
+ #include <linux/ioctl.h>
+ #include <linux/init.h>
+ #include <linux/smb.h>
+@@ -73,6 +74,18 @@ int compat_printk(const char *fmt, ...)
+
+ #include "read_write.h"
+
++int ve_compat_printk(int dst, const char *fmt, ...)
++{
++ va_list ap;
++ int ret;
++ if (!compat_log)
++ return 0;
++ va_start(ap, fmt);
++ ret = ve_vprintk(dst, fmt, ap);
++ va_end(ap);
++ return ret;
++}
++
+ /*
+ * Not all architectures have sys_utime, so implement this in terms
+ * of sys_utimes.
+@@ -244,6 +257,8 @@ asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_sta
+ struct kstatfs tmp;
+ error = vfs_statfs(path.dentry, &tmp);
+ if (!error)
++ error = faudit_statfs(path.mnt->mnt_sb, &tmp);
++ if (!error)
+ error = put_compat_statfs(buf, &tmp);
+ path_put(&path);
+ }
+@@ -262,6 +277,8 @@ asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user
+ goto out;
+ error = vfs_statfs(file->f_path.dentry, &tmp);
+ if (!error)
++ error = faudit_statfs(file->f_vfsmnt->mnt_sb, &tmp);
++ if (!error)
+ error = put_compat_statfs(buf, &tmp);
+ fput(file);
+ out:
+@@ -312,6 +329,8 @@ asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t s
+ struct kstatfs tmp;
+ error = vfs_statfs(path.dentry, &tmp);
+ if (!error)
++ error = faudit_statfs(path.mnt->mnt_sb, &tmp);
++ if (!error)
+ error = put_compat_statfs64(buf, &tmp);
+ path_put(&path);
+ }
+@@ -333,6 +352,8 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c
+ goto out;
+ error = vfs_statfs(file->f_path.dentry, &tmp);
+ if (!error)
++ error = faudit_statfs(file->f_vfsmnt->mnt_sb, &tmp);
++ if (!error)
+ error = put_compat_statfs64(buf, &tmp);
+ fput(file);
+ out:
+@@ -1355,6 +1376,10 @@ int compat_do_execve(char * filename,
+ struct file *file;
+ int retval;
+
++ retval = virtinfo_gencall(VIRTINFO_DOEXECVE, NULL);
++ if (retval)
++ return retval;
++
+ retval = -ENOMEM;
+ bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
+ if (!bprm)
+diff --git a/fs/dcache.c b/fs/dcache.c
+index e7a1a99..18ea10d 100644
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -27,13 +27,20 @@
+ #include <linux/module.h>
+ #include <linux/mount.h>
+ #include <linux/file.h>
++#include <linux/namei.h>
+ #include <asm/uaccess.h>
+ #include <linux/security.h>
+ #include <linux/seqlock.h>
+ #include <linux/swap.h>
+ #include <linux/bootmem.h>
++#include <linux/kernel_stat.h>
++#include <linux/vzstat.h>
++#include <linux/fdtable.h>
++#include <net/inet_sock.h>
+ #include "internal.h"
+
++#include <bc/dcache.h>
++#include <bc/dcache_op.h>
+
+ int sysctl_vfs_cache_pressure __read_mostly = 100;
+ EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
+@@ -43,7 +50,7 @@ __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
+
+ EXPORT_SYMBOL(dcache_lock);
+
+-static struct kmem_cache *dentry_cache __read_mostly;
++struct kmem_cache *dentry_cache __read_mostly;
+
+ #define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))
+
+@@ -172,6 +179,7 @@ static struct dentry *d_kill(struct dentry *dentry)
+
+ list_del(&dentry->d_u.d_child);
+ dentry_stat.nr_dentry--; /* For d_free, below */
++ preempt_enable_no_resched();
+ /*drops the locks, at that point nobody can reach this dentry */
+ dentry_iput(dentry);
+ parent = dentry->d_parent;
+@@ -210,21 +218,31 @@ static struct dentry *d_kill(struct dentry *dentry)
+
+ void dput(struct dentry *dentry)
+ {
++ struct user_beancounter *ub;
++ unsigned long d_ubsize;
++
+ if (!dentry)
+ return;
+
+ repeat:
+ if (atomic_read(&dentry->d_count) == 1)
+ might_sleep();
+- if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
+- return;
++ preempt_disable();
++ if (unlikely(ub_dentry_on)) {
++ spin_lock(&dcache_lock);
++ if (!atomic_dec_and_test(&dentry->d_count)) {
++ ub_dentry_uncharge_locked(dentry);
++ spin_unlock(&dcache_lock);
++ goto out_preempt;
++ }
++ } else {
++ if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
++ goto out_preempt;
++ }
+
+ spin_lock(&dentry->d_lock);
+- if (atomic_read(&dentry->d_count)) {
+- spin_unlock(&dentry->d_lock);
+- spin_unlock(&dcache_lock);
+- return;
+- }
++ if (atomic_read(&dentry->d_count))
++ goto out_unlock;
+
+ /*
+ * AV: ->d_delete() is _NOT_ allowed to block now.
+@@ -240,8 +258,12 @@ repeat:
+ dentry->d_flags |= DCACHE_REFERENCED;
+ dentry_lru_add(dentry);
+ }
++out_unlock:
+ spin_unlock(&dentry->d_lock);
++ ub_dentry_uncharge_locked(dentry);
+ spin_unlock(&dcache_lock);
++out_preempt:
++ preempt_enable();
+ return;
+
+ unhash_it:
+@@ -249,9 +271,18 @@ unhash_it:
+ kill_it:
+ /* if dentry was on the d_lru list delete it from there */
+ dentry_lru_del(dentry);
++
++ ub = dentry->dentry_bc.d_ub;
++ d_ubsize = dentry->dentry_bc.d_ubsize;
+ dentry = d_kill(dentry);
+- if (dentry)
++ preempt_disable();
++ if (unlikely(ub_dentry_on)) {
++ uncharge_dcache(ub, d_ubsize);
++ put_beancounter(ub);
++ }
++ if (dentry)
+ goto repeat;
++ preempt_enable();
+ }
+
+ /**
+@@ -317,6 +348,7 @@ static inline struct dentry * __dget_locked(struct dentry *dentry)
+ {
+ atomic_inc(&dentry->d_count);
+ dentry_lru_del_init(dentry);
++ ub_dentry_charge_nofail(dentry);
+ return dentry;
+ }
+
+@@ -419,6 +451,7 @@ static void prune_one_dentry(struct dentry * dentry)
+ __acquires(dcache_lock)
+ {
+ __d_drop(dentry);
++ preempt_disable();
+ dentry = d_kill(dentry);
+
+ /*
+@@ -434,6 +467,7 @@ static void prune_one_dentry(struct dentry * dentry)
+ dentry->d_op->d_delete(dentry);
+ dentry_lru_del_init(dentry);
+ __d_drop(dentry);
++ preempt_disable();
+ dentry = d_kill(dentry);
+ spin_lock(&dcache_lock);
+ }
+@@ -727,6 +761,8 @@ void shrink_dcache_for_umount(struct super_block *sb)
+
+ dentry = sb->s_root;
+ sb->s_root = NULL;
++ /* "/" was also charged in d_alloc_root() */
++ ub_dentry_uncharge(dentry);
+ atomic_dec(&dentry->d_count);
+ shrink_dcache_for_umount_subtree(dentry);
+
+@@ -886,12 +922,18 @@ void shrink_dcache_parent(struct dentry * parent)
+ */
+ static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
+ {
++ int res = -1;
++
++ KSTAT_PERF_ENTER(shrink_dcache)
+ if (nr) {
+ if (!(gfp_mask & __GFP_FS))
+- return -1;
++ goto out;
+ prune_dcache(nr);
+ }
+- return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
++ res = (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
++out:
++ KSTAT_PERF_LEAVE(shrink_dcache)
++ return res;
+ }
+
+ static struct shrinker dcache_shrinker = {
+@@ -914,21 +956,27 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
+ struct dentry *dentry;
+ char *dname;
+
++ dname = NULL;
++ if (name->len > DNAME_INLINE_LEN-1) {
++ dname = kmalloc(name->len + 1, GFP_KERNEL);
++ if (!dname)
++ goto err_name;
++ }
++
++ ub_dentry_alloc_start();
++
+ dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
+ if (!dentry)
+- return NULL;
++ goto err_alloc;
+
+- if (name->len > DNAME_INLINE_LEN-1) {
+- dname = kmalloc(name->len + 1, GFP_KERNEL);
+- if (!dname) {
+- kmem_cache_free(dentry_cache, dentry);
+- return NULL;
+- }
+- } else {
++ preempt_disable();
++ if (dname == NULL)
+ dname = dentry->d_iname;
+- }
+ dentry->d_name.name = dname;
+
++ if (ub_dentry_alloc(dentry))
++ goto err_charge;
++
+ dentry->d_name.len = name->len;
+ dentry->d_name.hash = name->hash;
+ memcpy(dname, name->name, name->len);
+@@ -959,12 +1007,27 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
+ }
+
+ spin_lock(&dcache_lock);
+- if (parent)
++ if (parent) {
+ list_add(&dentry->d_u.d_child, &parent->d_subdirs);
++ if (parent->d_flags & DCACHE_VIRTUAL)
++ dentry->d_flags |= DCACHE_VIRTUAL;
++ }
+ dentry_stat.nr_dentry++;
+ spin_unlock(&dcache_lock);
++ preempt_enable();
++ ub_dentry_alloc_end();
+
+ return dentry;
++
++err_charge:
++ preempt_enable();
++ kmem_cache_free(dentry_cache, dentry);
++err_alloc:
++ if (name->len > DNAME_INLINE_LEN - 1)
++ kfree(dname);
++ ub_dentry_alloc_end();
++err_name:
++ return NULL;
+ }
+
+ struct dentry *d_alloc_name(struct dentry *parent, const char *name)
+@@ -1371,12 +1434,12 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
+ unsigned int hash = name->hash;
+ const unsigned char *str = name->name;
+ struct hlist_head *head = d_hash(parent,hash);
+- struct dentry *found = NULL;
+ struct hlist_node *node;
+- struct dentry *dentry;
++ struct dentry *dentry, *found;
+
+ rcu_read_lock();
+
++ found = NULL;
+ hlist_for_each_entry_rcu(dentry, node, head, d_hash) {
+ struct qstr *qstr;
+
+@@ -1416,6 +1479,10 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
+
+ atomic_inc(&dentry->d_count);
+ found = dentry;
++
++ if (ub_dentry_charge(found))
++ goto charge_failure;
++
+ spin_unlock(&dentry->d_lock);
+ break;
+ next:
+@@ -1424,6 +1491,14 @@ next:
+ rcu_read_unlock();
+
+ return found;
++
++charge_failure:
++ spin_unlock(&found->d_lock);
++ rcu_read_unlock();
++ /* dentry is now unhashed, just kill it */
++ dput(found);
++ /* ... and fail lookup */
++ return NULL;
+ }
+
+ /**
+@@ -1892,6 +1967,16 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
+ }
+
+ /**
++ * d_root_check - checks if dentry is accessible from current's fs root
++ * @dentry: dentry to be verified
++ * @vfsmnt: vfsmnt to which the dentry belongs
++ */
++int d_root_check(struct path *path)
++{
++ return PTR_ERR(d_path(path, NULL, 0));
++}
++
++/**
+ * __d_path - return the path of a dentry
+ * @path: the dentry/vfsmount to report
+ * @root: root vfsmnt/dentry (may be modified by this function)
+@@ -1915,18 +2000,21 @@ char *__d_path(const struct path *path, struct path *root,
+ struct vfsmount *vfsmnt = path->mnt;
+ char *end = buffer + buflen;
+ char *retval;
++ int deleted;
++ struct vfsmount *oldmnt = vfsmnt;
+
+ spin_lock(&vfsmount_lock);
+- prepend(&end, &buflen, "\0", 1);
+- if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
+- (prepend(&end, &buflen, " (deleted)", 10) != 0))
++ if (buffer) {
++ prepend(&end, &buflen, "\0", 1);
++ if (buflen < 1)
+ goto Elong;
++ }
++ deleted = (!IS_ROOT(dentry) && d_unhashed(dentry));
+
+- if (buflen < 1)
+- goto Elong;
+ /* Get '/' right */
+ retval = end-1;
+- *retval = '/';
++ if (buffer)
++ *retval = '/';
+
+ for (;;) {
+ struct dentry * parent;
+@@ -1944,20 +2032,43 @@ char *__d_path(const struct path *path, struct path *root,
+ }
+ parent = dentry->d_parent;
+ prefetch(parent);
+- if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
+- (prepend(&end, &buflen, "/", 1) != 0))
++ if (buffer && ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
++ (prepend(&end, &buflen, "/", 1) != 0)))
+ goto Elong;
+ retval = end;
+ dentry = parent;
+ }
+
+ out:
++ if (deleted && buffer &&
++ prepend(&end, &buflen, " (deleted)", 10) != 0)
++ goto Elong;
++out_err:
+ spin_unlock(&vfsmount_lock);
+- return retval;
++ return buffer ? retval : NULL;
+
+ global_root:
++ /*
++ * We traversed the tree upward and reached a root, but the given
++ * lookup terminal point wasn't encountered. It means either that the
++ * dentry is out of our scope or belongs to an abstract space like
++ * sock_mnt or pipe_mnt. Check for it.
++ *
++ * There are different options to check it.
++ * We may assume that any dentry tree is unreachable unless it's
++ * connected to `root' (defined as fs root of init aka child reaper)
++ * and expose all paths that are not connected to it.
++ * The other option is to allow exposing of known abstract spaces
++ * explicitly and hide the path information for other cases.
++ * This approach is more safe, let's take it. 2001/04/22 SAW
++ */
++ if (!(oldmnt->mnt_sb->s_flags & MS_NOUSER)) {
++ retval = ERR_PTR(-EINVAL);
++ goto out_err;
++ }
++
+ retval += 1; /* hit the slash */
+- if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
++ if (buffer && prepend_name(&retval, &buflen, &dentry->d_name) != 0)
+ goto Elong;
+ root->mnt = vfsmnt;
+ root->dentry = dentry;
+@@ -1965,8 +2076,9 @@ global_root:
+
+ Elong:
+ retval = ERR_PTR(-ENAMETOOLONG);
+- goto out;
++ goto out_err;
+ }
++EXPORT_SYMBOL(__d_path);
+
+ /**
+ * d_path - return the path of a dentry
+@@ -1993,8 +2105,11 @@ char *d_path(const struct path *path, char *buf, int buflen)
+ * thus don't need to be hashed. They also don't need a name until a
+ * user wants to identify the object in /proc/pid/fd/. The little hack
+ * below allows us to generate a name for these objects on demand:
++ *
++ * pipefs and socketfs methods assume valid buffer, d_root_check()
++ * supplies NULL one for access checks.
+ */
+- if (path->dentry->d_op && path->dentry->d_op->d_dname)
++ if (buf && path->dentry->d_op && path->dentry->d_op->d_dname)
+ return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
+
+ read_lock(&current->fs->lock);
+@@ -2009,6 +2124,231 @@ char *d_path(const struct path *path, char *buf, int buflen)
+ return res;
+ }
+
++#ifdef CONFIG_VE
++#include <net/sock.h>
++#include <linux/ip.h>
++#include <linux/file.h>
++#include <linux/mnt_namespace.h>
++#include <linux/vzratelimit.h>
++
++static void mark_sub_tree_virtual(struct dentry *d)
++{
++ struct dentry *orig_root;
++
++ orig_root = d;
++ while (1) {
++ spin_lock(&d->d_lock);
++ d->d_flags |= DCACHE_VIRTUAL;
++ spin_unlock(&d->d_lock);
++
++ if (!list_empty(&d->d_subdirs)) {
++ d = list_entry(d->d_subdirs.next,
++ struct dentry, d_u.d_child);
++ continue;
++ }
++ if (d == orig_root)
++ break;
++ while (d == list_entry(d->d_parent->d_subdirs.prev,
++ struct dentry, d_u.d_child)) {
++ d = d->d_parent;
++ if (d == orig_root)
++ goto out;
++ }
++ d = list_entry(d->d_u.d_child.next,
++ struct dentry, d_u.d_child);
++ }
++out:
++ return;
++}
++
++void mark_tree_virtual(struct path *path)
++{
++ struct vfsmount *orig_rootmnt;
++ struct vfsmount *m = path->mnt;
++ struct dentry *d = path->dentry;
++
++ spin_lock(&dcache_lock);
++ spin_lock(&vfsmount_lock);
++ orig_rootmnt = m;
++ while (1) {
++ mark_sub_tree_virtual(d);
++ if (!list_empty(&m->mnt_mounts)) {
++ m = list_entry(m->mnt_mounts.next,
++ struct vfsmount, mnt_child);
++ d = m->mnt_root;
++ continue;
++ }
++ if (m == orig_rootmnt)
++ break;
++ while (m == list_entry(m->mnt_parent->mnt_mounts.prev,
++ struct vfsmount, mnt_child)) {
++ m = m->mnt_parent;
++ if (m == orig_rootmnt)
++ goto out;
++ }
++ m = list_entry(m->mnt_child.next,
++ struct vfsmount, mnt_child);
++ d = m->mnt_root;
++ }
++out:
++ spin_unlock(&vfsmount_lock);
++ spin_unlock(&dcache_lock);
++}
++EXPORT_SYMBOL(mark_tree_virtual);
++
++static struct vz_rate_info area_ri = { 20, 10*HZ };
++#define VE_AREA_ACC_CHECK 0x0001
++#define VE_AREA_ACC_DENY 0x0002
++#define VE_AREA_EXEC_CHECK 0x0010
++#define VE_AREA_EXEC_DENY 0x0020
++#define VE0_AREA_ACC_CHECK 0x0100
++#define VE0_AREA_ACC_DENY 0x0200
++#define VE0_AREA_EXEC_CHECK 0x1000
++#define VE0_AREA_EXEC_DENY 0x2000
++int ve_area_access_check = 0;
++
++static void print_connection_info(struct task_struct *tsk)
++{
++ struct files_struct *files;
++ struct fdtable *fdt;
++ int fd;
++
++ files = get_files_struct(tsk);
++ if (!files)
++ return;
++
++ spin_lock(&files->file_lock);
++ fdt = files_fdtable(files);
++ for (fd = 0; fd < fdt->max_fds; fd++) {
++ struct file *file;
++ struct inode *inode;
++ struct socket *socket;
++ struct sock *sk;
++ struct inet_sock *inet;
++
++ file = fdt->fd[fd];
++ if (file == NULL)
++ continue;
++
++ inode = file->f_dentry->d_inode;
++ if (!S_ISSOCK(inode->i_mode))
++ continue;
++
++ socket = SOCKET_I(inode);
++ if (socket == NULL)
++ continue;
++
++ sk = socket->sk;
++ if ((sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
++ || sk->sk_type != SOCK_STREAM)
++ continue;
++
++ inet = inet_sk(sk);
++ printk(KERN_ALERT "connection from %u.%u.%u.%u:%u to port %u\n",
++ NIPQUAD(inet->daddr), ntohs(inet->dport),
++ inet->num);
++ }
++ spin_unlock(&files->file_lock);
++ put_files_struct(files);
++}
++
++static void check_alert(struct path *path, char *str)
++{
++ struct task_struct *tsk;
++ unsigned long page;
++ struct super_block *sb;
++ char *p;
++
++ if (!vz_ratelimit(&area_ri))
++ return;
++
++ tsk = current;
++ p = ERR_PTR(-ENOMEM);
++ page = __get_free_page(GFP_KERNEL);
++ if (page) {
++ spin_lock(&dcache_lock);
++ p = __d_path(path, &tsk->fs->root, (char *)page, PAGE_SIZE);
++ spin_unlock(&dcache_lock);
++ }
++ if (IS_ERR(p))
++ p = "(undefined)";
++
++ sb = path->dentry->d_sb;
++ printk(KERN_ALERT "%s check alert! file:[%s] from %d/%s, dev%x\n"
++ "Task %d/%d[%s] from VE%d, execenv %d\n",
++ str, p, sb->s_type->owner_env->veid,
++ sb->s_type->name, sb->s_dev,
++ tsk->pid, task_pid_vnr(tsk), tsk->comm,
++ VE_TASK_INFO(tsk)->owner_env->veid,
++ get_exec_env()->veid);
++
++ free_page(page);
++
++ print_connection_info(tsk);
++
++ read_lock(&tasklist_lock);
++ tsk = tsk->parent;
++ get_task_struct(tsk);
++ read_unlock(&tasklist_lock);
++
++ printk(KERN_ALERT "Parent %d/%d[%s] from VE%d\n",
++ tsk->pid, task_pid_vnr(tsk), tsk->comm,
++ VE_TASK_INFO(tsk)->owner_env->veid);
++
++ print_connection_info(tsk);
++ put_task_struct(tsk);
++ dump_stack();
++}
++#endif
++
++int check_area_access_ve(struct path *path)
++{
++#ifdef CONFIG_VE
++ int check, alert, deny;
++
++ if (ve_is_super(get_exec_env())) {
++ check = ve_area_access_check & VE0_AREA_ACC_CHECK;
++ alert = path->dentry->d_flags & DCACHE_VIRTUAL;
++ deny = ve_area_access_check & VE0_AREA_ACC_DENY;
++ } else {
++ check = ve_area_access_check & VE_AREA_ACC_CHECK;
++ alert = !(path->dentry->d_flags & DCACHE_VIRTUAL);
++ deny = ve_area_access_check & VE_AREA_ACC_DENY;
++ }
++
++ if (check && alert)
++ check_alert(path, "Access");
++ if (deny && alert)
++ return -EACCES;
++#endif
++ return 0;
++}
++
++#if 0
++int check_area_execute_ve(struct dentry *dentry, struct vfsmount *mnt)
++{
++#ifdef CONFIG_VE
++ int check, alert, deny;
++
++ if (ve_is_super(get_exec_env())) {
++ check = ve_area_access_check & VE0_AREA_EXEC_CHECK;
++ alert = dentry->d_flags & DCACHE_VIRTUAL;
++ deny = ve_area_access_check & VE0_AREA_EXEC_DENY;
++ } else {
++ check = ve_area_access_check & VE_AREA_EXEC_CHECK;
++ alert = !(dentry->d_flags & DCACHE_VIRTUAL);
++ deny = ve_area_access_check & VE_AREA_EXEC_DENY;
++ }
++
++ if (check && alert)
++ check_alert(mnt, dentry, "Exec");
++ if (deny && alert)
++ return -EACCES;
++#endif
++ return 0;
++}
++#endif
++
+ /*
+ * Helper function for dentry_operations.d_dname() members
+ */
+@@ -2201,10 +2541,12 @@ resume:
+ goto repeat;
+ }
+ atomic_dec(&dentry->d_count);
++ ub_dentry_uncharge_locked(dentry);
+ }
+ if (this_parent != root) {
+ next = this_parent->d_u.d_child.next;
+ atomic_dec(&this_parent->d_count);
++ ub_dentry_uncharge_locked(this_parent);
+ this_parent = this_parent->d_parent;
+ goto resume;
+ }
+diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
+index 488eb42..b4da9f7 100644
+--- a/fs/devpts/inode.c
++++ b/fs/devpts/inode.c
+@@ -23,6 +23,7 @@
+ #include <linux/parser.h>
+ #include <linux/fsnotify.h>
+ #include <linux/seq_file.h>
++#include <linux/ve.h>
+
+ #define DEVPTS_SUPER_MAGIC 0x1cd1
+
+@@ -30,18 +31,26 @@
+
+ extern int pty_limit; /* Config limit on Unix98 ptys */
+ static DEFINE_IDA(allocated_ptys);
++#ifdef CONFIG_VE
++#define __ve_allocated_ptys(ve) (*((ve)->allocated_ptys))
++#define ve_allocated_ptys __ve_allocated_ptys(get_exec_env())
++#else
++#define __ve_allocated_ptys(ve) allocated_ptys
++#define ve_allocated_ptys allocated_ptys
++#endif
+ static DEFINE_MUTEX(allocated_ptys_lock);
+
++struct devpts_config devpts_config = {.mode = 0600};
++
++#ifndef CONFIG_VE
+ static struct vfsmount *devpts_mnt;
+ static struct dentry *devpts_root;
+-
+-static struct {
+- int setuid;
+- int setgid;
+- uid_t uid;
+- gid_t gid;
+- umode_t mode;
+-} config = {.mode = DEVPTS_DEFAULT_MODE};
++#define config devpts_config
++#else
++#define devpts_mnt (get_exec_env()->devpts_mnt)
++#define devpts_root (get_exec_env()->devpts_root)
++#define config (*(get_exec_env()->devpts_config))
++#endif
+
+ enum {
+ Opt_uid, Opt_gid, Opt_mode,
+@@ -93,7 +102,8 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
+ config.mode = option & S_IALLUGO;
+ break;
+ default:
+- printk(KERN_ERR "devpts: called with bogus options\n");
++ ve_printk(VE_LOG, KERN_ERR
++ "devpts: called with bogus options\n");
+ return -EINVAL;
+ }
+ }
+@@ -157,13 +167,15 @@ static int devpts_get_sb(struct file_system_type *fs_type,
+ return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt);
+ }
+
+-static struct file_system_type devpts_fs_type = {
++struct file_system_type devpts_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "devpts",
+ .get_sb = devpts_get_sb,
+ .kill_sb = kill_anon_super,
+ };
+
++EXPORT_SYMBOL(devpts_fs_type);
++
+ /*
+ * The normal naming convention is simply /dev/pts/<number>; this conforms
+ * to the System V naming convention
+@@ -183,12 +195,12 @@ int devpts_new_index(void)
+ int ida_ret;
+
+ retry:
+- if (!ida_pre_get(&allocated_ptys, GFP_KERNEL)) {
++ if (!ida_pre_get(&ve_allocated_ptys, GFP_KERNEL)) {
+ return -ENOMEM;
+ }
+
+ mutex_lock(&allocated_ptys_lock);
+- ida_ret = ida_get_new(&allocated_ptys, &index);
++ ida_ret = ida_get_new(&ve_allocated_ptys, &index);
+ if (ida_ret < 0) {
+ mutex_unlock(&allocated_ptys_lock);
+ if (ida_ret == -EAGAIN)
+@@ -197,7 +209,7 @@ retry:
+ }
+
+ if (index >= pty_limit) {
+- ida_remove(&allocated_ptys, index);
++ ida_remove(&ve_allocated_ptys, index);
+ mutex_unlock(&allocated_ptys_lock);
+ return -EIO;
+ }
+@@ -208,7 +220,7 @@ retry:
+ void devpts_kill_index(int idx)
+ {
+ mutex_lock(&allocated_ptys_lock);
+- ida_remove(&allocated_ptys, idx);
++ ida_remove(&ve_allocated_ptys, idx);
+ mutex_unlock(&allocated_ptys_lock);
+ }
+
+@@ -278,6 +290,17 @@ void devpts_pty_kill(int number)
+ mutex_unlock(&devpts_root->d_inode->i_mutex);
+ }
+
++void prepare_tty(void)
++{
++#ifdef CONFIG_VE
++ get_ve0()->allocated_ptys = &allocated_ptys;
++ /*
++ * in this case, tty_register_driver() setups
++ * owner_env correctly right from the bootup
++ */
++#endif
++}
++
+ static int __init init_devpts_fs(void)
+ {
+ int err = register_filesystem(&devpts_fs_type);
+@@ -286,11 +309,13 @@ static int __init init_devpts_fs(void)
+ if (IS_ERR(devpts_mnt))
+ err = PTR_ERR(devpts_mnt);
+ }
++ prepare_tty();
+ return err;
+ }
+
+ static void __exit exit_devpts_fs(void)
+ {
++ /* the code is never called, the argument is irrelevant */
+ unregister_filesystem(&devpts_fs_type);
+ mntput(devpts_mnt);
+ }
+diff --git a/fs/direct-io.c b/fs/direct-io.c
+index 9606ee8..84f0486 100644
+--- a/fs/direct-io.c
++++ b/fs/direct-io.c
+@@ -660,7 +660,7 @@ submit_page_section(struct dio *dio, struct page *page,
+ /*
+ * Read accounting is performed in submit_bio()
+ */
+- task_io_account_write(len);
++ task_io_account_write(page, len, 1);
+ }
+
+ /*
+diff --git a/fs/dquot.c b/fs/dquot.c
+index 8ec4d6c..6d70056 100644
+--- a/fs/dquot.c
++++ b/fs/dquot.c
+@@ -162,7 +162,9 @@ static struct quota_format_type *find_quota_format(int id)
+ struct quota_format_type *actqf;
+
+ spin_lock(&dq_list_lock);
+- for (actqf = quota_formats; actqf && actqf->qf_fmt_id != id; actqf = actqf->qf_next);
++ for (actqf = quota_formats;
++ actqf && (actqf->qf_fmt_id != id || actqf->qf_ops == NULL);
++ actqf = actqf->qf_next);
+ if (!actqf || !try_module_get(actqf->qf_owner)) {
+ int qm;
+
+diff --git a/fs/eventpoll.c b/fs/eventpoll.c
+index 7cc0eb7..19f36d0 100644
+--- a/fs/eventpoll.c
++++ b/fs/eventpoll.c
+@@ -31,6 +31,7 @@
+ #include <linux/eventpoll.h>
+ #include <linux/mount.h>
+ #include <linux/bitops.h>
++#include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/anon_inodes.h>
+ #include <asm/uaccess.h>
+@@ -102,11 +103,6 @@
+
+ #define EP_UNACTIVE_PTR ((void *) -1L)
+
+-struct epoll_filefd {
+- struct file *file;
+- int fd;
+-};
+-
+ /*
+ * Node that is linked into the "wake_task_list" member of the "struct poll_safewake".
+ * It is used to keep track on all tasks that are currently inside the wake_up() code
+@@ -129,79 +125,6 @@ struct poll_safewake {
+ spinlock_t lock;
+ };
+
+-/*
+- * Each file descriptor added to the eventpoll interface will
+- * have an entry of this type linked to the "rbr" RB tree.
+- */
+-struct epitem {
+- /* RB tree node used to link this structure to the eventpoll RB tree */
+- struct rb_node rbn;
+-
+- /* List header used to link this structure to the eventpoll ready list */
+- struct list_head rdllink;
+-
+- /*
+- * Works together "struct eventpoll"->ovflist in keeping the
+- * single linked chain of items.
+- */
+- struct epitem *next;
+-
+- /* The file descriptor information this item refers to */
+- struct epoll_filefd ffd;
+-
+- /* Number of active wait queue attached to poll operations */
+- int nwait;
+-
+- /* List containing poll wait queues */
+- struct list_head pwqlist;
+-
+- /* The "container" of this item */
+- struct eventpoll *ep;
+-
+- /* List header used to link this item to the "struct file" items list */
+- struct list_head fllink;
+-
+- /* The structure that describe the interested events and the source fd */
+- struct epoll_event event;
+-};
+-
+-/*
+- * This structure is stored inside the "private_data" member of the file
+- * structure and rapresent the main data sructure for the eventpoll
+- * interface.
+- */
+-struct eventpoll {
+- /* Protect the this structure access */
+- spinlock_t lock;
+-
+- /*
+- * This mutex is used to ensure that files are not removed
+- * while epoll is using them. This is held during the event
+- * collection loop, the file cleanup path, the epoll file exit
+- * code and the ctl operations.
+- */
+- struct mutex mtx;
+-
+- /* Wait queue used by sys_epoll_wait() */
+- wait_queue_head_t wq;
+-
+- /* Wait queue used by file->poll() */
+- wait_queue_head_t poll_wait;
+-
+- /* List of ready file descriptors */
+- struct list_head rdllist;
+-
+- /* RB tree root used to store monitored fd structs */
+- struct rb_root rbr;
+-
+- /*
+- * This is a single linked list that chains all the "struct epitem" that
+- * happened while transfering ready events to userspace w/out
+- * holding ->lock.
+- */
+- struct epitem *ovflist;
+-};
+-
+ /* Wait structure used by the poll hooks */
+ struct eppoll_entry {
+ /* List header used to link this structure to the "struct epitem" */
+@@ -229,7 +152,8 @@ struct ep_pqueue {
+ /*
+ * This mutex is used to serialize ep_free() and eventpoll_release_file().
+ */
+-static struct mutex epmutex;
++struct mutex epmutex;
++EXPORT_SYMBOL_GPL(epmutex);
+
+ /* Safe wake up implementation */
+ static struct poll_safewake psw;
+@@ -482,10 +406,11 @@ static unsigned int ep_eventpoll_poll(struct file *file, poll_table *wait)
+ }
+
+ /* File callbacks that implement the eventpoll file behaviour */
+-static const struct file_operations eventpoll_fops = {
++const struct file_operations eventpoll_fops = {
+ .release = ep_eventpoll_release,
+ .poll = ep_eventpoll_poll
+ };
++EXPORT_SYMBOL(eventpoll_fops);
+
+ /* Fast test to see if the file is an evenpoll file */
+ static inline int is_file_epoll(struct file *f)
+@@ -557,7 +482,7 @@ static int ep_alloc(struct eventpoll **pep)
+ * are protected by the "mtx" mutex, and ep_find() must be called with
+ * "mtx" held.
+ */
+-static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd)
++struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd)
+ {
+ int kcmp;
+ struct rb_node *rbp;
+@@ -583,6 +508,7 @@ static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd)
+
+ return epir;
+ }
++EXPORT_SYMBOL_GPL(ep_find);
+
+ /*
+ * This is the callback that is passed to the wait queue wakeup
+@@ -695,7 +621,7 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi)
+ /*
+ * Must be called with "mtx" held.
+ */
+-static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
++int ep_insert(struct eventpoll *ep, struct epoll_event *event,
+ struct file *tfile, int fd)
+ {
+ int error, revents, pwake = 0;
+@@ -792,6 +718,7 @@ error_unregister:
+ error_return:
+ return error;
+ }
++EXPORT_SYMBOL(ep_insert);
+
+ /*
+ * Modify the interest event mask by dropping an event if the new mask
+@@ -1089,6 +1016,7 @@ asmlinkage long sys_epoll_create(int size)
+
+ return sys_epoll_create1(0);
+ }
++EXPORT_SYMBOL(sys_epoll_create);
+
+ /*
+ * The following function implements the controller interface for
+diff --git a/fs/exec.c b/fs/exec.c
+index cecee50..64cf4c2 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -26,6 +26,7 @@
+ #include <linux/file.h>
+ #include <linux/fdtable.h>
+ #include <linux/mm.h>
++#include <linux/virtinfo.h>
+ #include <linux/stat.h>
+ #include <linux/fcntl.h>
+ #include <linux/smp_lock.h>
+@@ -55,6 +56,8 @@
+ #include <asm/mmu_context.h>
+ #include <asm/tlb.h>
+
++#include <bc/vmpages.h>
++
+ #ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+ #endif
+@@ -70,6 +73,8 @@ int suid_dumpable = 0;
+
+ /* The maximal length of core_pattern is also specified in sysctl.c */
+
++int sysctl_at_vsyscall;
++
+ static LIST_HEAD(formats);
+ static DEFINE_RWLOCK(binfmt_lock);
+
+@@ -239,9 +244,13 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
+ struct vm_area_struct *vma = NULL;
+ struct mm_struct *mm = bprm->mm;
+
+- bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
++ if (ub_memory_charge(mm, PAGE_SIZE, VM_STACK_FLAGS | mm->def_flags,
++ NULL, UB_SOFT))
++ goto fail_charge;
++
++ bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL_UBC);
+ if (!vma)
+- goto err;
++ goto fail_alloc;
+
+ down_write(&mm->mmap_sem);
+ vma->vm_mm = mm;
+@@ -275,7 +284,9 @@ err:
+ bprm->vma = NULL;
+ kmem_cache_free(vm_area_cachep, vma);
+ }
+-
++fail_alloc:
++ ub_memory_uncharge(mm, PAGE_SIZE, VM_STACK_FLAGS | mm->def_flags, NULL);
++fail_charge:
+ return err;
+ }
+
+@@ -723,10 +734,11 @@ int kernel_read(struct file *file, unsigned long offset,
+
+ EXPORT_SYMBOL(kernel_read);
+
+-static int exec_mmap(struct mm_struct *mm)
++static int exec_mmap(struct linux_binprm *bprm)
+ {
+ struct task_struct *tsk;
+- struct mm_struct * old_mm, *active_mm;
++ struct mm_struct *old_mm, *active_mm, *mm;
++ int ret;
+
+ /* Notify parent that we're no longer interested in the old VM */
+ tsk = current;
+@@ -746,6 +758,10 @@ static int exec_mmap(struct mm_struct *mm)
+ return -EINTR;
+ }
+ }
++
++ ret = 0;
++ mm = bprm->mm;
++ mm->vps_dumpable = 1;
+ task_lock(tsk);
+ active_mm = tsk->active_mm;
+ tsk->mm = mm;
+@@ -753,15 +769,25 @@ static int exec_mmap(struct mm_struct *mm)
+ activate_mm(active_mm, mm);
+ task_unlock(tsk);
+ arch_pick_mmap_layout(mm);
++ bprm->mm = NULL; /* We're using it now */
++
++#ifdef CONFIG_VZ_GENCALLS
++ if (virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_EXECMMAP,
++ bprm) & NOTIFY_FAIL) {
++ /* similar to binfmt_elf */
++ send_sig(SIGKILL, current, 0);
++ ret = -ENOMEM;
++ }
++#endif
+ if (old_mm) {
+ up_read(&old_mm->mmap_sem);
+ BUG_ON(active_mm != old_mm);
+ mm_update_next_owner(old_mm);
+ mmput(old_mm);
+- return 0;
++ return ret;
+ }
+ mmdrop(active_mm);
+- return 0;
++ return ret;
+ }
+
+ /*
+@@ -859,6 +885,10 @@ static int de_thread(struct task_struct *tsk)
+ transfer_pid(leader, tsk, PIDTYPE_PGID);
+ transfer_pid(leader, tsk, PIDTYPE_SID);
+ list_replace_rcu(&leader->tasks, &tsk->tasks);
++#ifdef CONFIG_VE
++ list_replace_rcu(&leader->ve_task_info.vetask_list,
++ &tsk->ve_task_info.vetask_list);
++#endif
+
+ tsk->group_leader = tsk;
+ leader->group_leader = tsk;
+@@ -976,12 +1006,10 @@ int flush_old_exec(struct linux_binprm * bprm)
+ /*
+ * Release all of the old mmap stuff
+ */
+- retval = exec_mmap(bprm->mm);
++ retval = exec_mmap(bprm);
+ if (retval)
+ goto out;
+
+- bprm->mm = NULL; /* We're using it now */
+-
+ /* This is the point of no return */
+ current->sas_ss_sp = current->sas_ss_size = 0;
+
+@@ -1283,6 +1311,10 @@ int do_execve(char * filename,
+ struct files_struct *displaced;
+ int retval;
+
++ retval = virtinfo_gencall(VIRTINFO_DOEXECVE, NULL);
++ if (retval)
++ return retval;
++
+ retval = unshare_files(&displaced);
+ if (retval)
+ goto out_ret;
+@@ -1526,7 +1558,7 @@ static int zap_process(struct task_struct *start)
+ signal_wake_up(t, 1);
+ nr++;
+ }
+- } while_each_thread(start, t);
++ } while_each_thread_ve(start, t);
+
+ return nr;
+ }
+@@ -1581,7 +1613,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
+ * next_thread().
+ */
+ rcu_read_lock();
+- for_each_process(g) {
++ for_each_process_ve(g) {
+ if (g == tsk->group_leader)
+ continue;
+ if (g->flags & PF_KTHREAD)
+@@ -1596,7 +1628,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
+ }
+ break;
+ }
+- } while_each_thread(g, p);
++ } while_each_thread_ve(g, p);
+ }
+ rcu_read_unlock();
+ done:
+@@ -1732,7 +1764,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
+ /*
+ * If another thread got here first, or we are not dumpable, bail out.
+ */
+- if (mm->core_state || !get_dumpable(mm)) {
++ if (mm->core_state || !get_dumpable(mm) || mm->vps_dumpable != 1) {
+ up_write(&mm->mmap_sem);
+ goto fail;
+ }
+diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
+index 80c97fd..c03ef38 100644
+--- a/fs/ext2/namei.c
++++ b/fs/ext2/namei.c
+@@ -31,6 +31,7 @@
+ */
+
+ #include <linux/pagemap.h>
++#include <linux/quotaops.h>
+ #include "ext2.h"
+ #include "xattr.h"
+ #include "acl.h"
+@@ -257,6 +258,8 @@ static int ext2_unlink(struct inode * dir, struct dentry *dentry)
+ struct page * page;
+ int err = -ENOENT;
+
++ DQUOT_INIT(inode);
++
+ de = ext2_find_entry (dir, dentry, &page);
+ if (!de)
+ goto out;
+@@ -299,6 +302,9 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
+ struct ext2_dir_entry_2 * old_de;
+ int err = -ENOENT;
+
++ if (new_inode)
++ DQUOT_INIT(new_inode);
++
+ old_de = ext2_find_entry (old_dir, old_dentry, &old_page);
+ if (!old_de)
+ goto out;
+diff --git a/fs/ext2/super.c b/fs/ext2/super.c
+index fd88c7b..f4ff824 100644
+--- a/fs/ext2/super.c
++++ b/fs/ext2/super.c
+@@ -1401,7 +1401,7 @@ static struct file_system_type ext2_fs_type = {
+ .name = "ext2",
+ .get_sb = ext2_get_sb,
+ .kill_sb = kill_block_super,
+- .fs_flags = FS_REQUIRES_DEV,
++ .fs_flags = FS_REQUIRES_DEV | FS_VIRTUALIZED,
+ };
+
+ static int __init init_ext2_fs(void)
+diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c
+index 0d0c701..d4d3c11 100644
+--- a/fs/ext3/ioctl.c
++++ b/fs/ext3/ioctl.c
+@@ -87,7 +87,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
+ * the relevant capability.
+ */
+ if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) {
+- if (!capable(CAP_SYS_RESOURCE)) {
++ if (!capable(CAP_SYS_ADMIN)) {
+ mutex_unlock(&inode->i_mutex);
+ err = -EPERM;
+ goto flags_out;
+diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
+index de13e91..565bca4 100644
+--- a/fs/ext3/namei.c
++++ b/fs/ext3/namei.c
+@@ -1347,7 +1347,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
+ if (err)
+ ext3_std_error(dir->i_sb, err);
+ brelse(bh);
+- return 0;
++ return err;
+ }
+
+ /*
+diff --git a/fs/ext3/super.c b/fs/ext3/super.c
+index f38a5af..75e629a 100644
+--- a/fs/ext3/super.c
++++ b/fs/ext3/super.c
+@@ -2938,7 +2938,7 @@ static struct file_system_type ext3_fs_type = {
+ .name = "ext3",
+ .get_sb = ext3_get_sb,
+ .kill_sb = kill_block_super,
+- .fs_flags = FS_REQUIRES_DEV,
++ .fs_flags = FS_REQUIRES_DEV | FS_VIRTUALIZED,
+ };
+
+ static int __init init_ext3_fs(void)
+diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
+index 7a6c2f1..ec237c2 100644
+--- a/fs/ext4/ioctl.c
++++ b/fs/ext4/ioctl.c
+@@ -79,7 +79,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ * the relevant capability.
+ */
+ if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
+- if (!capable(CAP_SYS_RESOURCE))
++ if (!capable(CAP_SYS_ADMIN))
+ goto flags_out;
+ }
+
+diff --git a/fs/fcntl.c b/fs/fcntl.c
+index ac4f7db..016a0cb 100644
+--- a/fs/fcntl.c
++++ b/fs/fcntl.c
+@@ -124,6 +124,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
+ }
+ return sys_dup3(oldfd, newfd, 0);
+ }
++EXPORT_SYMBOL_GPL(sys_dup2);
+
+ asmlinkage long sys_dup(unsigned int fildes)
+ {
+@@ -147,6 +148,9 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
+ struct inode * inode = filp->f_path.dentry->d_inode;
+ int error = 0;
+
++ if (!capable(CAP_SYS_RAWIO) && !odirect_enable)
++ arg &= ~O_DIRECT;
++
+ /*
+ * O_APPEND cannot be cleared if the file is marked as append-only
+ * and the file is open for write.
+diff --git a/fs/file.c b/fs/file.c
+index f313314..b3cf859 100644
+--- a/fs/file.c
++++ b/fs/file.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/fs.h>
+ #include <linux/mm.h>
++#include <linux/module.h>
+ #include <linux/time.h>
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+@@ -20,6 +21,8 @@
+ #include <linux/rcupdate.h>
+ #include <linux/workqueue.h>
+
++#include <bc/kmem.h>
++
+ struct fdtable_defer {
+ spinlock_t lock;
+ struct work_struct wq;
+@@ -41,9 +44,9 @@ static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list);
+ static inline void * alloc_fdmem(unsigned int size)
+ {
+ if (size <= PAGE_SIZE)
+- return kmalloc(size, GFP_KERNEL);
++ return kmalloc(size, GFP_KERNEL_UBC);
+ else
+- return vmalloc(size);
++ return ub_vmalloc(size);
+ }
+
+ static inline void free_fdarr(struct fdtable *fdt)
+@@ -162,7 +165,7 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
+ if (unlikely(nr > sysctl_nr_open))
+ nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
+
+- fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
++ fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL_UBC);
+ if (!fdt)
+ goto out;
+ fdt->max_fds = nr;
+@@ -197,7 +200,7 @@ out:
+ * Return <0 error code on error; 1 on successful completion.
+ * The files->file_lock should be held on entry, and will be held on exit.
+ */
+-static int expand_fdtable(struct files_struct *files, int nr)
++int expand_fdtable(struct files_struct *files, int nr)
+ __releases(files->file_lock)
+ __acquires(files->file_lock)
+ {
+@@ -237,6 +240,7 @@ static int expand_fdtable(struct files_struct *files, int nr)
+ }
+ return 1;
+ }
++EXPORT_SYMBOL_GPL(expand_fdtable);
+
+ /*
+ * Expand files.
+diff --git a/fs/file_table.c b/fs/file_table.c
+index f45a449..108a41b 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -21,9 +21,14 @@
+ #include <linux/fsnotify.h>
+ #include <linux/sysctl.h>
+ #include <linux/percpu_counter.h>
++#include <linux/ve.h>
+
+ #include <asm/atomic.h>
+
++#include <bc/beancounter.h>
++#include <bc/kmem.h>
++#include <bc/misc.h>
++
+ /* sysctl tunables... */
+ struct files_stat_struct files_stat = {
+ .max_files = NR_FILE
+@@ -37,13 +42,16 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
+ static inline void file_free_rcu(struct rcu_head *head)
+ {
+ struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
++ put_ve(f->owner_env);
+ kmem_cache_free(filp_cachep, f);
+ }
+
+ static inline void file_free(struct file *f)
+ {
+- percpu_counter_dec(&nr_files);
+ file_check_state(f);
++ if (f->f_ub == get_ub0())
++ percpu_counter_dec(&nr_files);
++ ub_file_uncharge(f);
+ call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
+ }
+
+@@ -97,11 +105,14 @@ struct file *get_empty_filp(void)
+ struct task_struct *tsk;
+ static int old_max;
+ struct file * f;
++ int acct;
+
++ acct = (get_exec_ub() == get_ub0());
+ /*
+ * Privileged users can go above max_files
+ */
+- if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) {
++ if (acct && get_nr_files() >= files_stat.max_files &&
++ !capable(CAP_SYS_ADMIN)) {
+ /*
+ * percpu_counters are inaccurate. Do an expensive check before
+ * we go and fail.
+@@ -114,7 +125,13 @@ struct file *get_empty_filp(void)
+ if (f == NULL)
+ goto fail;
+
+- percpu_counter_inc(&nr_files);
++ if (ub_file_charge(f))
++ goto fail_ch;
++ if (acct)
++ percpu_counter_inc(&nr_files);
++
++ f->owner_env = get_ve(get_exec_env());
++
+ if (security_file_alloc(f))
+ goto fail_sec;
+
+@@ -141,6 +158,10 @@ fail_sec:
+ file_free(f);
+ fail:
+ return NULL;
++
++fail_ch:
++ kmem_cache_free(filp_cachep, f);
++ return NULL;
+ }
+
+ EXPORT_SYMBOL(get_empty_filp);
+diff --git a/fs/filesystems.c b/fs/filesystems.c
+index f37f872..3dca4a7 100644
+--- a/fs/filesystems.c
++++ b/fs/filesystems.c
+@@ -12,6 +12,9 @@
+ #include <linux/kmod.h>
+ #include <linux/init.h>
+ #include <linux/module.h>
++#include <linux/sched.h> /* for 'current' */
++#include <linux/mount.h>
++#include <linux/ve.h>
+ #include <asm/uaccess.h>
+
+ /*
+@@ -21,8 +24,8 @@
+ * During the unload module must call unregister_filesystem().
+ * We can access the fields of list element if:
+ * 1) spinlock is held or
+- * 2) we hold the reference to the module.
+- * The latter can be guaranteed by call of try_module_get(); if it
++ * 2) we hold the reference to the element.
++ * The latter can be guaranteed by call of try_filesystem(); if it
+ * returned 0 we must skip the element, otherwise we got the reference.
+ * Once the reference is obtained we can drop the spinlock.
+ */
+@@ -30,24 +33,46 @@
+ static struct file_system_type *file_systems;
+ static DEFINE_RWLOCK(file_systems_lock);
+
++int try_get_filesystem(struct file_system_type *fs)
++{
++ if (try_module_get(fs->owner)) {
++ (void)get_ve(fs->owner_env);
++ return 1;
++ }
++ return 0;
++}
++
+ /* WARNING: This can be used only if we _already_ own a reference */
+ void get_filesystem(struct file_system_type *fs)
+ {
++ (void)get_ve(fs->owner_env);
+ __module_get(fs->owner);
+ }
+
+ void put_filesystem(struct file_system_type *fs)
+ {
+ module_put(fs->owner);
++ put_ve(fs->owner_env);
++}
++
++static inline int check_ve_fstype(struct file_system_type *p,
++ struct ve_struct *env)
++{
++ return ((p->fs_flags & FS_VIRTUALIZED) ||
++ ve_accessible_strict(p->owner_env, env));
+ }
+
+-static struct file_system_type **find_filesystem(const char *name, unsigned len)
++static struct file_system_type **find_filesystem(const char *name, unsigned len,
++ struct ve_struct *env)
+ {
+ struct file_system_type **p;
+- for (p=&file_systems; *p; p=&(*p)->next)
++ for (p=&file_systems; *p; p=&(*p)->next) {
++ if (!check_ve_fstype(*p, env))
++ continue;
+ if (strlen((*p)->name) == len &&
+ strncmp((*p)->name, name, len) == 0)
+ break;
++ }
+ return p;
+ }
+
+@@ -73,8 +98,12 @@ int register_filesystem(struct file_system_type * fs)
+ if (fs->next)
+ return -EBUSY;
+ INIT_LIST_HEAD(&fs->fs_supers);
++ if (fs->owner_env == NULL)
++ fs->owner_env = get_ve0();
++ if (fs->proto == NULL)
++ fs->proto = fs;
+ write_lock(&file_systems_lock);
+- p = find_filesystem(fs->name, strlen(fs->name));
++ p = find_filesystem(fs->name, strlen(fs->name), fs->owner_env);
+ if (*p)
+ res = -EBUSY;
+ else
+@@ -118,6 +147,75 @@ int unregister_filesystem(struct file_system_type * fs)
+
+ EXPORT_SYMBOL(unregister_filesystem);
+
++#ifdef CONFIG_VE
++int register_ve_fs_type(struct ve_struct *ve, struct file_system_type *template,
++ struct file_system_type **p_fs_type, struct vfsmount **p_mnt)
++{
++ struct vfsmount *mnt;
++ struct file_system_type *local_fs_type;
++ int ret;
++
++ local_fs_type = kzalloc(sizeof(*local_fs_type) + sizeof(void *),
++ GFP_KERNEL);
++ if (local_fs_type == NULL)
++ return -ENOMEM;
++
++ local_fs_type->name = template->name;
++ local_fs_type->fs_flags = template->fs_flags;
++ local_fs_type->get_sb = template->get_sb;
++ local_fs_type->kill_sb = template->kill_sb;
++ local_fs_type->owner = template->owner;
++ local_fs_type->owner_env = ve;
++ local_fs_type->proto = template;
++
++ get_filesystem(local_fs_type); /* get_ve() inside */
++
++ ret = register_filesystem(local_fs_type);
++ if (ret)
++ goto reg_err;
++
++ if (p_mnt == NULL)
++ goto done;
++
++ mnt = vfs_kern_mount(local_fs_type, 0, local_fs_type->name, NULL);
++ if (IS_ERR(mnt))
++ goto mnt_err;
++
++ *p_mnt = mnt;
++done:
++ *p_fs_type = local_fs_type;
++ return 0;
++
++mnt_err:
++ ret = PTR_ERR(mnt);
++ unregister_filesystem(local_fs_type); /* does not put */
++
++reg_err:
++ put_filesystem(local_fs_type);
++ kfree(local_fs_type);
++ printk(KERN_DEBUG
++ "register_ve_fs_type(\"%s\") err=%d\n", template->name, ret);
++ return ret;
++}
++
++EXPORT_SYMBOL(register_ve_fs_type);
++
++void unregister_ve_fs_type(struct file_system_type *local_fs_type,
++ struct vfsmount *local_fs_mount)
++{
++ if (local_fs_mount == NULL && local_fs_type == NULL)
++ return;
++
++ unregister_filesystem(local_fs_type);
++ umount_ve_fs_type(local_fs_type);
++ if (local_fs_mount)
++ kern_umount(local_fs_mount); /* alias to mntput, drop our ref */
++ put_filesystem(local_fs_type);
++}
++
++EXPORT_SYMBOL(unregister_ve_fs_type);
++#endif
++
+ static int fs_index(const char __user * __name)
+ {
+ struct file_system_type * tmp;
+@@ -131,11 +229,14 @@ static int fs_index(const char __user * __name)
+
+ err = -EINVAL;
+ read_lock(&file_systems_lock);
+- for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
++ for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next) {
++ if (!check_ve_fstype(tmp, get_exec_env()))
++ continue;
+ if (strcmp(tmp->name,name) == 0) {
+ err = index;
+ break;
+ }
++ index++;
+ }
+ read_unlock(&file_systems_lock);
+ putname(name);
+@@ -148,9 +249,15 @@ static int fs_name(unsigned int index, char __user * buf)
+ int len, res;
+
+ read_lock(&file_systems_lock);
+- for (tmp = file_systems; tmp; tmp = tmp->next, index--)
+- if (index <= 0 && try_module_get(tmp->owner))
+- break;
++ for (tmp = file_systems; tmp; tmp = tmp->next) {
++ if (!check_ve_fstype(tmp, get_exec_env()))
++ continue;
++ if (!index) {
++ if (try_get_filesystem(tmp))
++ break;
++ } else
++ index--;
++ }
+ read_unlock(&file_systems_lock);
+ if (!tmp)
+ return -EINVAL;
+@@ -168,8 +275,9 @@ static int fs_maxindex(void)
+ int index;
+
+ read_lock(&file_systems_lock);
+- for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
+- ;
++ for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next)
++ if (check_ve_fstype(tmp, get_exec_env()))
++ index++;
+ read_unlock(&file_systems_lock);
+ return index;
+ }
+@@ -205,9 +313,10 @@ int get_filesystem_list(char * buf)
+ read_lock(&file_systems_lock);
+ tmp = file_systems;
+ while (tmp && len < PAGE_SIZE - 80) {
+- len += sprintf(buf+len, "%s\t%s\n",
+- (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
+- tmp->name);
++ if (check_ve_fstype(tmp, get_exec_env()))
++ len += sprintf(buf+len, "%s\t%s\n",
++ (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
++ tmp->name);
+ tmp = tmp->next;
+ }
+ read_unlock(&file_systems_lock);
+@@ -221,14 +330,14 @@ struct file_system_type *get_fs_type(const char *name)
+ unsigned len = dot ? dot - name : strlen(name);
+
+ read_lock(&file_systems_lock);
+- fs = *(find_filesystem(name, len));
+- if (fs && !try_module_get(fs->owner))
++ fs = *(find_filesystem(name, len, get_exec_env()));
++ if (fs && !try_get_filesystem(fs))
+ fs = NULL;
+ read_unlock(&file_systems_lock);
+ if (!fs && (request_module("%.*s", len, name) == 0)) {
+ read_lock(&file_systems_lock);
+- fs = *(find_filesystem(name, len));
+- if (fs && !try_module_get(fs->owner))
++ fs = *(find_filesystem(name, len, get_exec_env()));
++ if (fs && !try_get_filesystem(fs))
+ fs = NULL;
+ read_unlock(&file_systems_lock);
+ }
+diff --git a/fs/fuse/control.c b/fs/fuse/control.c
+index 4f3cab3..755be17 100644
+--- a/fs/fuse/control.c
++++ b/fs/fuse/control.c
+@@ -10,6 +10,8 @@
+
+ #include <linux/init.h>
+ #include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/ve_proto.h>
+
+ #define FUSE_CTL_SUPER_MAGIC 0x65735543
+
+@@ -17,7 +19,11 @@
+ * This is non-NULL when the single instance of the control filesystem
+ * exists. Protected by fuse_mutex
+ */
++#ifdef CONFIG_VE
++#define fuse_control_sb (get_exec_env()->_fuse_control_sb)
++#else
+ static struct super_block *fuse_control_sb;
++#endif
+
+ static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file)
+ {
+@@ -211,12 +217,51 @@ static struct file_system_type fuse_ctl_fs_type = {
+ .kill_sb = fuse_ctl_kill_sb,
+ };
+
++#ifdef CONFIG_VE
++static int fuse_ctl_start(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ if (ve->fuse_ctl_fs_type != NULL)
++ return -EBUSY;
++
++ return register_ve_fs_type(ve, &fuse_ctl_fs_type,
++ &ve->fuse_ctl_fs_type, NULL);
++}
++
++static void fuse_ctl_stop(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ if (ve->fuse_ctl_fs_type == NULL)
++ return;
++
++ unregister_ve_fs_type(ve->fuse_ctl_fs_type, NULL);
++ ve->fuse_ctl_fs_type = NULL;
++}
++
++static struct ve_hook fuse_ctl_ve_hook = {
++ .init = fuse_ctl_start,
++ .fini = fuse_ctl_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_FS,
++};
++#endif
++
+ int __init fuse_ctl_init(void)
+ {
+- return register_filesystem(&fuse_ctl_fs_type);
++ int err;
++
++ err = register_filesystem(&fuse_ctl_fs_type);
++ if (err == 0)
++ ve_hook_register(VE_SS_CHAIN, &fuse_ctl_ve_hook);
++ return err;
+ }
+
+ void fuse_ctl_cleanup(void)
+ {
++ ve_hook_unregister(&fuse_ctl_ve_hook);
+ unregister_filesystem(&fuse_ctl_fs_type);
+ }
+diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
+index 3a87607..541375e 100644
+--- a/fs/fuse/fuse_i.h
++++ b/fs/fuse/fuse_i.h
+@@ -45,7 +45,11 @@
+ #define FUSE_ALLOW_OTHER (1 << 1)
+
+ /** List of active connections */
++#ifdef CONFIG_VE
++#define fuse_conn_list (get_exec_env()->_fuse_conn_list)
++#else
+ extern struct list_head fuse_conn_list;
++#endif
+
+ /** Global mutex protecting fuse_conn_list and the control filesystem */
+ extern struct mutex fuse_mutex;
+diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
+index d2249f1..eef02a8 100644
+--- a/fs/fuse/inode.c
++++ b/fs/fuse/inode.c
+@@ -19,13 +19,16 @@
+ #include <linux/random.h>
+ #include <linux/sched.h>
+ #include <linux/exportfs.h>
++#include <linux/ve_proto.h>
+
+ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
+ MODULE_DESCRIPTION("Filesystem in Userspace");
+ MODULE_LICENSE("GPL");
+
+ static struct kmem_cache *fuse_inode_cachep;
++#ifndef CONFIG_VE
+ struct list_head fuse_conn_list;
++#endif
+ DEFINE_MUTEX(fuse_mutex);
+
+ #define FUSE_SUPER_MAGIC 0x65735546
+@@ -1033,6 +1036,41 @@ static void fuse_sysfs_cleanup(void)
+ kobject_put(fuse_kobj);
+ }
+
++#ifdef CONFIG_VE
++static int fuse_start(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ if (ve->fuse_fs_type != NULL)
++ return -EBUSY;
++
++ INIT_LIST_HEAD(&ve->_fuse_conn_list);
++ return register_ve_fs_type(ve, &fuse_fs_type, &ve->fuse_fs_type, NULL);
++}
++
++static void fuse_stop(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ if (ve->fuse_fs_type == NULL)
++ return;
++
++ unregister_ve_fs_type(ve->fuse_fs_type, NULL);
++ kfree(ve->fuse_fs_type);
++ ve->fuse_fs_type = NULL;
++ BUG_ON(!list_empty(&ve->_fuse_conn_list));
++}
++
++static struct ve_hook fuse_ve_hook = {
++ .init = fuse_start,
++ .fini = fuse_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_FS,
++};
++#endif
++
+ static int __init fuse_init(void)
+ {
+ int res;
+@@ -1057,6 +1095,7 @@ static int __init fuse_init(void)
+ if (res)
+ goto err_sysfs_cleanup;
+
++ ve_hook_register(VE_SS_CHAIN, &fuse_ve_hook);
+ return 0;
+
+ err_sysfs_cleanup:
+@@ -1073,6 +1112,7 @@ static void __exit fuse_exit(void)
+ {
+ printk(KERN_DEBUG "fuse exit\n");
+
++ ve_hook_unregister(&fuse_ve_hook);
+ fuse_ctl_cleanup();
+ fuse_sysfs_cleanup();
+ fuse_fs_cleanup();
+diff --git a/fs/inode.c b/fs/inode.c
+index 0487ddb..156c8fb 100644
+--- a/fs/inode.c
++++ b/fs/inode.c
+@@ -8,10 +8,13 @@
+ #include <linux/mm.h>
+ #include <linux/dcache.h>
+ #include <linux/init.h>
++#include <linux/kernel_stat.h>
+ #include <linux/quotaops.h>
+ #include <linux/slab.h>
+ #include <linux/writeback.h>
+ #include <linux/module.h>
++#include <linux/nsproxy.h>
++#include <linux/mnt_namespace.h>
+ #include <linux/backing-dev.h>
+ #include <linux/wait.h>
+ #include <linux/hash.h>
+@@ -22,6 +25,7 @@
+ #include <linux/bootmem.h>
+ #include <linux/inotify.h>
+ #include <linux/mount.h>
++#include <linux/vzstat.h>
+
+ /*
+ * This is needed for the following functions:
+@@ -97,7 +101,8 @@ static DEFINE_MUTEX(iprune_mutex);
+ */
+ struct inodes_stat_t inodes_stat;
+
+-static struct kmem_cache * inode_cachep __read_mostly;
++struct kmem_cache * inode_cachep __read_mostly;
++
+
+ static void wake_up_inode(struct inode *inode)
+ {
+@@ -108,11 +113,13 @@ static void wake_up_inode(struct inode *inode)
+ wake_up_bit(&inode->i_state, __I_LOCK);
+ }
+
++static struct address_space_operations vfs_empty_aops;
++struct inode_operations vfs_empty_iops;
++static struct file_operations vfs_empty_fops;
++EXPORT_SYMBOL(vfs_empty_iops);
++
+ static struct inode *alloc_inode(struct super_block *sb)
+ {
+- static const struct address_space_operations empty_aops;
+- static struct inode_operations empty_iops;
+- static const struct file_operations empty_fops;
+ struct inode *inode;
+
+ if (sb->s_op->alloc_inode)
+@@ -127,8 +134,8 @@ static struct inode *alloc_inode(struct super_block *sb)
+ inode->i_blkbits = sb->s_blocksize_bits;
+ inode->i_flags = 0;
+ atomic_set(&inode->i_count, 1);
+- inode->i_op = &empty_iops;
+- inode->i_fop = &empty_fops;
++ inode->i_op = &vfs_empty_iops;
++ inode->i_fop = &vfs_empty_fops;
+ inode->i_nlink = 1;
+ atomic_set(&inode->i_writecount, 0);
+ inode->i_size = 0;
+@@ -152,15 +159,15 @@ static struct inode *alloc_inode(struct super_block *sb)
+ }
+
+ spin_lock_init(&inode->i_lock);
+- lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
++ lockdep_set_class(&inode->i_lock, &sb->s_type->proto->i_lock_key);
+
+ mutex_init(&inode->i_mutex);
+- lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);
++ lockdep_set_class(&inode->i_mutex, &sb->s_type->proto->i_mutex_key);
+
+ init_rwsem(&inode->i_alloc_sem);
+- lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);
++ lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->proto->i_alloc_sem_key);
+
+- mapping->a_ops = &empty_aops;
++ mapping->a_ops = &vfs_empty_aops;
+ mapping->host = inode;
+ mapping->flags = 0;
+ mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
+@@ -311,13 +318,76 @@ static void dispose_list(struct list_head *head)
+ spin_unlock(&inode_lock);
+ }
+
++static void show_header(struct inode *inode)
++{
++ struct super_block *sb = inode->i_sb;
++
++ printk("VFS: Busy inodes after unmount. "
++ "sb = %p, fs type = %s, sb count = %d, "
++ "sb->s_root = %s\n", sb,
++ (sb->s_type != NULL) ? sb->s_type->name : "",
++ sb->s_count,
++ (sb->s_root != NULL) ?
++ (char *)sb->s_root->d_name.name : "");
++}
++
++static void show_inode(struct inode *inode)
++{
++ struct dentry *d;
++ struct vfsmount *mnt;
++ int i;
++
++ printk("inode = %p, inode->i_count = %d, "
++ "inode->i_nlink = %d, "
++ "inode->i_mode = %d, "
++ "inode->i_state = %ld, "
++ "inode->i_flags = %d, "
++ "inode->i_devices.next = %p, "
++ "inode->i_devices.prev = %p, "
++ "inode->i_ino = %ld\n",
++ inode,
++ atomic_read(&inode->i_count),
++ inode->i_nlink,
++ inode->i_mode,
++ inode->i_state,
++ inode->i_flags,
++ inode->i_devices.next,
++ inode->i_devices.prev,
++ inode->i_ino);
++ printk("inode dump: ");
++ for (i = 0; i < sizeof(*inode); i++)
++ printk("%2.2x ", *((u_char *)inode + i));
++ printk("\n");
++ list_for_each_entry(d, &inode->i_dentry, d_alias) {
++ printk(" d_alias %s d_count=%d d_flags=%x\n",
++ d->d_name.name, atomic_read(&d->d_count), d->d_flags);
++ for (i = 0; i < sizeof(*d); i++)
++ printk("%2.2x ", *((u_char *)d + i));
++ printk("\n");
++ }
++
++ spin_lock(&vfsmount_lock);
++ list_for_each_entry(mnt, &get_task_mnt_ns(current)->list, mnt_list) {
++ if (mnt->mnt_sb != inode->i_sb)
++ continue;
++ printk("mnt=%p count=%d flags=%x exp_mask=%x\n",
++ mnt, atomic_read(&mnt->mnt_count),
++ mnt->mnt_flags,
++ mnt->mnt_expiry_mark);
++ for (i = 0; i < sizeof(*mnt); i++)
++ printk("%2.2x ", *((u_char *)mnt + i));
++ printk("\n");
++ }
++ spin_unlock(&vfsmount_lock);
++}
++
+ /*
+ * Invalidate all inodes for a device.
+ */
+-static int invalidate_list(struct list_head *head, struct list_head *dispose)
++static int invalidate_list(struct list_head *head, struct list_head *dispose, int check)
+ {
+ struct list_head *next;
+- int busy = 0, count = 0;
++ int busy = 0, count = 0, once = 1;
+
+ next = head->next;
+ for (;;) {
+@@ -344,6 +414,14 @@ static int invalidate_list(struct list_head *head, struct list_head *dispose)
+ continue;
+ }
+ busy = 1;
++
++ if (check) {
++ if (once) {
++ once = 0;
++ show_header(inode);
++ }
++ show_inode(inode);
++ }
+ }
+ /* only unused inodes may be cached with i_count zero */
+ inodes_stat.nr_unused -= count;
+@@ -358,7 +436,7 @@ static int invalidate_list(struct list_head *head, struct list_head *dispose)
+ * fails because there are busy inodes then a non zero value is returned.
+ * If the discard is successful all the inodes have been discarded.
+ */
+-int invalidate_inodes(struct super_block * sb)
++int invalidate_inodes_check(struct super_block * sb, int check)
+ {
+ int busy;
+ LIST_HEAD(throw_away);
+@@ -366,7 +444,7 @@ int invalidate_inodes(struct super_block * sb)
+ mutex_lock(&iprune_mutex);
+ spin_lock(&inode_lock);
+ inotify_unmount_inodes(&sb->s_inodes);
+- busy = invalidate_list(&sb->s_inodes, &throw_away);
++ busy = invalidate_list(&sb->s_inodes, &throw_away, check);
+ spin_unlock(&inode_lock);
+
+ dispose_list(&throw_away);
+@@ -375,7 +453,7 @@ int invalidate_inodes(struct super_block * sb)
+ return busy;
+ }
+
+-EXPORT_SYMBOL(invalidate_inodes);
++EXPORT_SYMBOL(invalidate_inodes_check);
+
+ static int can_unuse(struct inode *inode)
+ {
+@@ -465,6 +543,7 @@ static void prune_icache(int nr_to_scan)
+ */
+ static int shrink_icache_memory(int nr, gfp_t gfp_mask)
+ {
++ KSTAT_PERF_ENTER(shrink_icache)
+ if (nr) {
+ /*
+ * Nasty deadlock avoidance. We may hold various FS locks,
+@@ -475,6 +554,7 @@ static int shrink_icache_memory(int nr, gfp_t gfp_mask)
+ return -1;
+ prune_icache(nr);
+ }
++ KSTAT_PERF_LEAVE(shrink_icache)
+ return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
+ }
+
+@@ -584,7 +664,7 @@ void unlock_new_inode(struct inode *inode)
+ */
+ mutex_destroy(&inode->i_mutex);
+ mutex_init(&inode->i_mutex);
+- lockdep_set_class(&inode->i_mutex, &type->i_mutex_dir_key);
++ lockdep_set_class(&inode->i_mutex, &type->proto->i_mutex_dir_key);
+ }
+ #endif
+ /*
+diff --git a/fs/inotify.c b/fs/inotify.c
+index 690e725..01ddb06 100644
+--- a/fs/inotify.c
++++ b/fs/inotify.c
+@@ -32,6 +32,7 @@
+ #include <linux/list.h>
+ #include <linux/writeback.h>
+ #include <linux/inotify.h>
++#include <linux/mount.h>
+
+ static atomic_t inotify_cookie;
+
+@@ -69,19 +70,6 @@ static atomic_t inotify_cookie;
+ * inotify_add_watch() to the final put_inotify_watch().
+ */
+
+-/*
+- * struct inotify_handle - represents an inotify instance
+- *
+- * This structure is protected by the mutex 'mutex'.
+- */
+-struct inotify_handle {
+- struct idr idr; /* idr mapping wd -> watch */
+- struct mutex mutex; /* protects this bad boy */
+- struct list_head watches; /* list of watches */
+- atomic_t count; /* reference count */
+- u32 last_wd; /* the last wd allocated */
+- const struct inotify_operations *in_ops; /* inotify caller operations */
+-};
+
+ static inline void get_inotify_handle(struct inotify_handle *ih)
+ {
+@@ -118,6 +106,9 @@ void put_inotify_watch(struct inotify_watch *watch)
+ struct inotify_handle *ih = watch->ih;
+
+ iput(watch->inode);
++ path_put(&watch->path);
++ watch->path.dentry = NULL;
++ watch->path.mnt = NULL;
+ ih->in_ops->destroy_watch(watch);
+ put_inotify_handle(ih);
+ }
+@@ -476,6 +467,8 @@ void inotify_init_watch(struct inotify_watch *watch)
+ INIT_LIST_HEAD(&watch->i_list);
+ atomic_set(&watch->count, 0);
+ get_inotify_watch(watch); /* initial get */
++ watch->path.dentry = NULL;
++ watch->path.mnt = NULL;
+ }
+ EXPORT_SYMBOL_GPL(inotify_init_watch);
+
+@@ -616,8 +609,8 @@ EXPORT_SYMBOL_GPL(inotify_find_update_watch);
+ * Caller must ensure it only calls inotify_add_watch() once per watch.
+ * Calls inotify_handle_get_wd() so may sleep.
+ */
+-s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
+- struct inode *inode, u32 mask)
++s32 __inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
++ struct path *path, struct inode * inode, u32 mask)
+ {
+ int ret = 0;
+ int newly_watched;
+@@ -645,6 +638,10 @@ s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
+ * Save a reference to the inode and bump the ref count to make it
+ * official. We hold a reference to nameidata, which makes this safe.
+ */
++ if (path) {
++ path_get(path);
++ watch->path = *path;
++ }
+ watch->inode = igrab(inode);
+
+ /* Add the watch to the handle's and the inode's list */
+@@ -666,6 +663,18 @@ out:
+ }
+ EXPORT_SYMBOL_GPL(inotify_add_watch);
+
++s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
++ struct inode *inode, u32 mask)
++{
++ return __inotify_add_watch(ih, watch, NULL, inode, mask);
++}
++
++s32 inotify_add_watch_dget(struct inotify_handle *ih,
++ struct inotify_watch *watch, struct path *p, u32 mask)
++{
++ return __inotify_add_watch(ih, watch, p, p->dentry->d_inode, mask);
++}
++
+ /**
+ * inotify_clone_watch - put the watch next to existing one
+ * @old: already installed watch
+diff --git a/fs/inotify_user.c b/fs/inotify_user.c
+index d85c7d9..62e2d29 100644
+--- a/fs/inotify_user.c
++++ b/fs/inotify_user.c
+@@ -20,6 +20,7 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
+@@ -66,47 +67,6 @@ static int inotify_max_queued_events __read_mostly;
+ * first event, or to inotify_destroy().
+ */
+
+-/*
+- * struct inotify_device - represents an inotify instance
+- *
+- * This structure is protected by the mutex 'mutex'.
+- */
+-struct inotify_device {
+- wait_queue_head_t wq; /* wait queue for i/o */
+- struct mutex ev_mutex; /* protects event queue */
+- struct mutex up_mutex; /* synchronizes watch updates */
+- struct list_head events; /* list of queued events */
+- atomic_t count; /* reference count */
+- struct user_struct *user; /* user who opened this dev */
+- struct inotify_handle *ih; /* inotify handle */
+- struct fasync_struct *fa; /* async notification */
+- unsigned int queue_size; /* size of the queue (bytes) */
+- unsigned int event_count; /* number of pending events */
+- unsigned int max_events; /* maximum number of events */
+-};
+-
+-/*
+- * struct inotify_kernel_event - An inotify event, originating from a watch and
+- * queued for user-space. A list of these is attached to each instance of the
+- * device. In read(), this list is walked and all events that can fit in the
+- * buffer are returned.
+- *
+- * Protected by dev->ev_mutex of the device in which we are queued.
+- */
+-struct inotify_kernel_event {
+- struct inotify_event event; /* the user-space event */
+- struct list_head list; /* entry in inotify_device's list */
+- char *name; /* filename, if any */
+-};
+-
+-/*
+- * struct inotify_user_watch - our version of an inotify_watch, we add
+- * a reference to the associated inotify_device.
+- */
+-struct inotify_user_watch {
+- struct inotify_device *dev; /* associated device */
+- struct inotify_watch wdata; /* inotify watch data */
+-};
+
+ #ifdef CONFIG_SYSCTL
+
+@@ -383,8 +343,7 @@ static int find_inode(const char __user *dirname, struct path *path,
+ *
+ * Callers must hold dev->up_mutex.
+ */
+-static int create_watch(struct inotify_device *dev, struct inode *inode,
+- u32 mask)
++int inotify_create_watch(struct inotify_device *dev, struct path *p, u32 mask)
+ {
+ struct inotify_user_watch *watch;
+ int ret;
+@@ -404,12 +363,13 @@ static int create_watch(struct inotify_device *dev, struct inode *inode,
+ atomic_inc(&dev->user->inotify_watches);
+
+ inotify_init_watch(&watch->wdata);
+- ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
++ ret = inotify_add_watch_dget(dev->ih, &watch->wdata, p, mask);
+ if (ret < 0)
+ free_inotify_user_watch(&watch->wdata);
+
+ return ret;
+ }
++EXPORT_SYMBOL(inotify_create_watch);
+
+ /* Device Interface */
+
+@@ -565,7 +525,7 @@ static long inotify_ioctl(struct file *file, unsigned int cmd,
+ return ret;
+ }
+
+-static const struct file_operations inotify_fops = {
++const struct file_operations inotify_fops = {
+ .poll = inotify_poll,
+ .read = inotify_read,
+ .fasync = inotify_fasync,
+@@ -573,6 +533,7 @@ static const struct file_operations inotify_fops = {
+ .unlocked_ioctl = inotify_ioctl,
+ .compat_ioctl = inotify_ioctl,
+ };
++EXPORT_SYMBOL(inotify_fops);
+
+ static const struct inotify_operations inotify_user_ops = {
+ .handle_event = inotify_dev_queue_event,
+@@ -662,6 +623,7 @@ asmlinkage long sys_inotify_init(void)
+ {
+ return sys_inotify_init1(0);
+ }
++EXPORT_SYMBOL(sys_inotify_init);
+
+ asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
+ {
+@@ -698,7 +660,7 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 m
+ mutex_lock(&dev->up_mutex);
+ ret = inotify_find_update_watch(dev->ih, inode, mask);
+ if (ret == -ENOENT)
+- ret = create_watch(dev, inode, mask);
++ ret = inotify_create_watch(dev, &path, mask);
+ mutex_unlock(&dev->up_mutex);
+
+ path_put(&path);
+diff --git a/fs/ioprio.c b/fs/ioprio.c
+index da3cc46..e39b34f 100644
+--- a/fs/ioprio.c
++++ b/fs/ioprio.c
+@@ -26,6 +26,8 @@
+ #include <linux/syscalls.h>
+ #include <linux/security.h>
+ #include <linux/pid_namespace.h>
++#include <linux/nsproxy.h>
++#include <bc/io_prio.h>
+
+ static int set_task_ioprio(struct task_struct *task, int ioprio)
+ {
+@@ -71,8 +73,11 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
+ int data = IOPRIO_PRIO_DATA(ioprio);
+ struct task_struct *p, *g;
+ struct user_struct *user;
+- struct pid *pgrp;
+ int ret;
++ struct pid *pgrp;
++
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
+
+ switch (class) {
+ case IOPRIO_CLASS_RT:
+@@ -130,17 +135,23 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
+ if (!user)
+ break;
+
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ if (p->uid != who)
+ continue;
+ ret = set_task_ioprio(p, ioprio);
+ if (ret)
+ goto free_uid;
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ free_uid:
+ if (who)
+ free_uid(user);
+ break;
++ case IOPRIO_WHO_UBC:
++ if (class != IOPRIO_CLASS_BE)
++ return -ERANGE;
++
++ ret = bc_set_ioprio(who, data);
++ break;
+ default:
+ ret = -EINVAL;
+ }
+@@ -185,9 +196,9 @@ asmlinkage long sys_ioprio_get(int which, int who)
+ {
+ struct task_struct *g, *p;
+ struct user_struct *user;
+- struct pid *pgrp;
+ int ret = -ESRCH;
+ int tmpio;
++ struct pid *pgrp;
+
+ read_lock(&tasklist_lock);
+ switch (which) {
+@@ -223,7 +234,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
+ if (!user)
+ break;
+
+- do_each_thread(g, p) {
++ do_each_thread_ve(g, p) {
+ if (p->uid != user->uid)
+ continue;
+ tmpio = get_task_ioprio(p);
+@@ -233,7 +244,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
+ ret = tmpio;
+ else
+ ret = ioprio_best(ret, tmpio);
+- } while_each_thread(g, p);
++ } while_each_thread_ve(g, p);
+
+ if (who)
+ free_uid(user);
+diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
+index 31668b6..d7f9400 100644
+--- a/fs/lockd/clntproc.c
++++ b/fs/lockd/clntproc.c
+@@ -156,12 +156,15 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
+ {
+ struct nlm_rqst *call;
+ int status;
++ struct ve_struct *ve;
+
+ nlm_get_host(host);
+ call = nlm_alloc_call(host);
+ if (call == NULL)
+ return -ENOMEM;
+
++ ve = set_exec_env(host->owner_env);
++
+ nlmclnt_locks_init_private(fl, host);
+ /* Set up the argument struct */
+ nlmclnt_setlockargs(call, fl);
+@@ -181,6 +184,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
+ fl->fl_ops = NULL;
+
+ dprintk("lockd: clnt proc returns %d\n", status);
++ (void)set_exec_env(ve);
+ return status;
+ }
+ EXPORT_SYMBOL_GPL(nlmclnt_proc);
+diff --git a/fs/lockd/host.c b/fs/lockd/host.c
+index a17664c..cfa0cf3 100644
+--- a/fs/lockd/host.c
++++ b/fs/lockd/host.c
+@@ -53,6 +53,7 @@ static struct nlm_host *nlm_lookup_host(int server,
+ struct nlm_host *host;
+ struct nsm_handle *nsm = NULL;
+ int hash;
++ struct ve_struct *ve;
+
+ dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
+ ", p=%d, v=%u, my role=%s, name=%.*s)\n",
+@@ -78,10 +79,14 @@ static struct nlm_host *nlm_lookup_host(int server,
+ * different NLM rpc_clients into one single nlm_host object.
+ * This would allow us to have one nlm_host per address.
+ */
++
++ ve = get_exec_env();
+ chain = &nlm_hosts[hash];
+ hlist_for_each_entry(host, pos, chain, h_hash) {
+ if (!nlm_cmp_addr(&host->h_addr, sin))
+ continue;
++ if (!ve_accessible_strict(host->owner_env, ve))
++ continue;
+
+ /* See if we have an NSM handle for this client */
+ if (!nsm)
+@@ -141,6 +146,7 @@ static struct nlm_host *nlm_lookup_host(int server,
+ spin_lock_init(&host->h_lock);
+ INIT_LIST_HEAD(&host->h_granted);
+ INIT_LIST_HEAD(&host->h_reclaim);
++ host->owner_env = ve;
+
+ nrhosts++;
+ out:
+@@ -454,6 +460,52 @@ nlm_gc_hosts(void)
+ next_gc = jiffies + NLM_HOST_COLLECT;
+ }
+
++#ifdef CONFIG_VE
++void ve_nlm_shutdown_hosts(struct ve_struct *ve)
++{
++ envid_t veid = ve->veid;
++ int i;
++
++ dprintk("lockd: shutting down host module for ve %d\n", veid);
++ mutex_lock(&nlm_host_mutex);
++
++ /* Perform a garbage collection pass */
++ for (i = 0; i < NLM_HOST_NRHASH; i++) {
++ struct nlm_host *host;
++ struct hlist_node *pos;
++
++ hlist_for_each_entry(host, pos, &nlm_hosts[i], h_hash) {
++ struct rpc_clnt *clnt;
++
++ if (ve != host->owner_env)
++ continue;
++
++ hlist_del(&host->h_hash);
++ if (host->h_nsmhandle)
++ host->h_nsmhandle->sm_monitored = 0;
++ dprintk("lockd: delete host %s ve %d\n", host->h_name,
++ veid);
++ if ((clnt = host->h_rpcclnt) != NULL) {
++ if (!list_empty(&clnt->cl_tasks)) {
++ struct rpc_xprt *xprt;
++
++ printk(KERN_WARNING
++ "lockd: active RPC handle\n");
++ rpc_killall_tasks(clnt);
++ xprt = clnt->cl_xprt;
++ xprt_disconnect_done(xprt);
++ xprt->ops->close(xprt);
++ } else
++ rpc_shutdown_client(clnt);
++ }
++ kfree(host);
++ nrhosts--;
++ }
++ }
++
++ mutex_unlock(&nlm_host_mutex);
++}
++#endif
+
+ /*
+ * Manage NSM handles
+diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
+index 5bd9bf0..2a9b08c 100644
+--- a/fs/lockd/svc.c
++++ b/fs/lockd/svc.c
+@@ -27,6 +27,7 @@
+ #include <linux/mutex.h>
+ #include <linux/kthread.h>
+ #include <linux/freezer.h>
++#include <linux/ve_proto.h>
+
+ #include <linux/sunrpc/types.h>
+ #include <linux/sunrpc/stats.h>
+@@ -48,11 +49,13 @@ struct nlmsvc_binding * nlmsvc_ops;
+ EXPORT_SYMBOL(nlmsvc_ops);
+
+ static DEFINE_MUTEX(nlmsvc_mutex);
+-static unsigned int nlmsvc_users;
+-static struct task_struct *nlmsvc_task;
+-static struct svc_rqst *nlmsvc_rqst;
+-int nlmsvc_grace_period;
+-unsigned long nlmsvc_timeout;
++#ifndef CONFIG_VE
++static unsigned int _nlmsvc_users;
++static struct task_struct *_nlmsvc_task;
++static struct svc_rqst *_nlmsvc_rqst;
++int _nlmsvc_grace_period;
++unsigned long _nlmsvc_timeout;
++#endif
+
+ /*
+ * These can be set at insmod time (useful for NFS as root filesystem),
+@@ -175,6 +178,10 @@ lockd(void *vrqstp)
+ */
+ err = svc_recv(rqstp, timeout);
+ if (err == -EAGAIN || err == -EINTR) {
++#ifdef CONFIG_VE
++ if (!get_exec_env()->is_running)
++ break;
++#endif
+ preverr = err;
+ continue;
+ }
+@@ -328,12 +335,12 @@ lockd_down(void)
+ } else {
+ printk(KERN_ERR "lockd_down: no users! task=%p\n",
+ nlmsvc_task);
+- BUG();
++ goto out;
+ }
+
+ if (!nlmsvc_task) {
+ printk(KERN_ERR "lockd_down: no lockd running.\n");
+- BUG();
++ goto out;
+ }
+ kthread_stop(nlmsvc_task);
+ svc_exit_thread(nlmsvc_rqst);
+@@ -478,6 +485,29 @@ static int lockd_authenticate(struct svc_rqst *rqstp)
+ return SVC_DENIED;
+ }
+
++#ifdef CONFIG_VE
++extern void ve_nlm_shutdown_hosts(struct ve_struct *ve);
++
++static int ve_lockd_start(void *data)
++{
++ return 0;
++}
++
++static void ve_lockd_stop(void *data)
++{
++ struct ve_struct *ve = (struct ve_struct *)data;
++
++ ve_nlm_shutdown_hosts(ve);
++ flush_scheduled_work();
++}
++
++static struct ve_hook lockd_hook = {
++ .init = ve_lockd_start,
++ .fini = ve_lockd_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_FS,
++};
++#endif
+
+ param_set_min_max(port, int, simple_strtol, 0, 65535)
+ param_set_min_max(grace_period, unsigned long, simple_strtoul,
+@@ -505,16 +535,20 @@ module_param(nsm_use_hostnames, bool, 0644);
+
+ static int __init init_nlm(void)
+ {
++ ve_hook_register(VE_SS_CHAIN, &lockd_hook);
+ #ifdef CONFIG_SYSCTL
+ nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
+- return nlm_sysctl_table ? 0 : -ENOMEM;
+-#else
+- return 0;
++ if (nlm_sysctl_table == NULL) {
++ ve_hook_unregister(&lockd_hook);
++ return -ENOMEM;
++ }
+ #endif
++ return 0;
+ }
+
+ static void __exit exit_nlm(void)
+ {
++ ve_hook_unregister(&lockd_hook);
+ /* FIXME: delete all NLM clients */
+ nlm_shutdown_hosts();
+ #ifdef CONFIG_SYSCTL
+diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
+index 198b4e5..7c9fb4f 100644
+--- a/fs/lockd/svcsubs.c
++++ b/fs/lockd/svcsubs.c
+@@ -335,6 +335,9 @@ nlmsvc_is_client(void *data, struct nlm_host *dummy)
+ {
+ struct nlm_host *host = data;
+
++ if (!ve_accessible_strict(host->owner_env, get_exec_env()))
++ return 0;
++
+ if (host->h_server) {
+ /* we are destroying locks even though the client
+ * hasn't asked us too, so don't unmonitor the
+diff --git a/fs/locks.c b/fs/locks.c
+index 5eb259e..c2f755b 100644
+--- a/fs/locks.c
++++ b/fs/locks.c
+@@ -130,6 +130,8 @@
+
+ #include <asm/uaccess.h>
+
++#include <bc/misc.h>
++
+ #define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
+ #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
+ #define IS_LEASE(fl) (fl->fl_flags & FL_LEASE)
+@@ -146,9 +148,25 @@ static LIST_HEAD(blocked_list);
+ static struct kmem_cache *filelock_cache __read_mostly;
+
+ /* Allocate an empty lock structure. */
+-static struct file_lock *locks_alloc_lock(void)
++static struct file_lock *locks_alloc_lock(int charge)
+ {
+- return kmem_cache_alloc(filelock_cache, GFP_KERNEL);
++ struct file_lock *fl;
++
++ fl = kmem_cache_alloc(filelock_cache, GFP_KERNEL);
++#ifdef CONFIG_BEANCOUNTERS
++ if (fl == NULL)
++ goto out;
++ fl->fl_charged = 0;
++ if (!charge)
++ goto out;
++ if (!ub_flock_charge(fl, 1))
++ goto out;
++
++ kmem_cache_free(filelock_cache, fl);
++ fl = NULL;
++out:
++#endif
++ return fl;
+ }
+
+ static void locks_release_private(struct file_lock *fl)
+@@ -173,6 +191,7 @@ static void locks_free_lock(struct file_lock *fl)
+ BUG_ON(!list_empty(&fl->fl_block));
+ BUG_ON(!list_empty(&fl->fl_link));
+
++ ub_flock_uncharge(fl);
+ locks_release_private(fl);
+ kmem_cache_free(filelock_cache, fl);
+ }
+@@ -276,7 +295,7 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock,
+ if (type < 0)
+ return type;
+
+- fl = locks_alloc_lock();
++ fl = locks_alloc_lock(type != F_UNLCK);
+ if (fl == NULL)
+ return -ENOMEM;
+
+@@ -463,7 +482,7 @@ static int lease_init(struct file *filp, int type, struct file_lock *fl)
+ /* Allocate a file_lock initialised to this type of lease */
+ static struct file_lock *lease_alloc(struct file *filp, int type)
+ {
+- struct file_lock *fl = locks_alloc_lock();
++ struct file_lock *fl = locks_alloc_lock(1);
+ int error = -ENOMEM;
+
+ if (fl == NULL)
+@@ -734,8 +753,13 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
+ goto find_conflict;
+
+ if (request->fl_type != F_UNLCK) {
++ /*
++ * Nont F_UNLCK request must be already charged in
++ * flock_make_lock(). Actually new_fl must be charged not the
++ * request, but we try to fail earlier.
++ */
+ error = -ENOMEM;
+- new_fl = locks_alloc_lock();
++ new_fl = locks_alloc_lock(0);
+ if (new_fl == NULL)
+ goto out;
+ error = 0;
+@@ -787,6 +811,10 @@ find_conflict:
+ }
+ if (request->fl_flags & FL_ACCESS)
+ goto out;
++
++ set_flock_charged(new_fl);
++ unset_flock_charged(request);
++
+ locks_copy_lock(new_fl, request);
+ locks_insert_lock(before, new_fl);
+ new_fl = NULL;
+@@ -818,8 +846,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
+ if (!(request->fl_flags & FL_ACCESS) &&
+ (request->fl_type != F_UNLCK ||
+ request->fl_start != 0 || request->fl_end != OFFSET_MAX)) {
+- new_fl = locks_alloc_lock();
+- new_fl2 = locks_alloc_lock();
++ if (request->fl_type != F_UNLCK)
++ new_fl = locks_alloc_lock(1);
++ else
++ new_fl = NULL;
++ new_fl2 = locks_alloc_lock(0);
+ }
+
+ lock_kernel();
+@@ -953,7 +984,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
+ * bail out.
+ */
+ error = -ENOLCK; /* "no luck" */
+- if (right && left == right && !new_fl2)
++ if (right && left == right && !(request->fl_type == F_UNLCK || new_fl2))
+ goto out;
+
+ error = 0;
+@@ -964,23 +995,32 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
+ goto out;
+ }
+
+- if (!new_fl) {
+- error = -ENOLCK;
++ error = -ENOLCK;
++ if (!new_fl)
++ goto out;
++ if (right && (left == right) && ub_flock_charge(new_fl, 1))
+ goto out;
+- }
+ locks_copy_lock(new_fl, request);
+ locks_insert_lock(before, new_fl);
+ new_fl = NULL;
++ error = 0;
+ }
+ if (right) {
+ if (left == right) {
+ /* The new lock breaks the old one in two pieces,
+ * so we have to use the second new lock.
+ */
++ error = -ENOLCK;
++ if (added && ub_flock_charge(new_fl2,
++ request->fl_type != F_UNLCK))
++ goto out;
++ /* FIXME move all fl_charged manipulations in ub code */
++ set_flock_charged(new_fl2);
+ left = new_fl2;
+ new_fl2 = NULL;
+ locks_copy_lock(left, right);
+ locks_insert_lock(before, left);
++ error = 0;
+ }
+ right->fl_start = request->fl_end + 1;
+ locks_wake_up_blocks(right);
+@@ -1365,7 +1405,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+
+ if (arg != F_UNLCK) {
+ error = -ENOMEM;
+- new_fl = locks_alloc_lock();
++ new_fl = locks_alloc_lock(1);
+ if (new_fl == NULL)
+ goto out;
+
+@@ -1608,6 +1648,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
+ out:
+ return error;
+ }
++EXPORT_SYMBOL_GPL(sys_flock);
+
+ /**
+ * vfs_test_lock - test file byte range lock
+@@ -1768,7 +1809,7 @@ static int do_lock_file_wait(struct file *filp, unsigned int cmd,
+ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
+ struct flock __user *l)
+ {
+- struct file_lock *file_lock = locks_alloc_lock();
++ struct file_lock *file_lock = locks_alloc_lock(0);
+ struct flock flock;
+ struct inode *inode;
+ struct file *f;
+@@ -1886,7 +1927,7 @@ out:
+ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
+ struct flock64 __user *l)
+ {
+- struct file_lock *file_lock = locks_alloc_lock();
++ struct file_lock *file_lock = locks_alloc_lock(0);
+ struct flock64 flock;
+ struct inode *inode;
+ struct file *f;
+@@ -2156,6 +2197,8 @@ static int locks_show(struct seq_file *f, void *v)
+ struct file_lock *fl, *bfl;
+
+ fl = list_entry(v, struct file_lock, fl_link);
++ if (!ve_accessible(fl->fl_file->owner_env, get_exec_env()))
++ goto out;
+
+ lock_get_status(f, fl, (long)f->private, "");
+
+@@ -2163,6 +2206,7 @@ static int locks_show(struct seq_file *f, void *v)
+ lock_get_status(f, bfl, (long)f->private, " ->");
+
+ f->private++;
++out:
+ return 0;
+ }
+
+@@ -2272,7 +2316,7 @@ EXPORT_SYMBOL(lock_may_write);
+ static int __init filelock_init(void)
+ {
+ filelock_cache = kmem_cache_create("file_lock_cache",
+- sizeof(struct file_lock), 0, SLAB_PANIC,
++ sizeof(struct file_lock), 0, SLAB_PANIC|SLAB_UBC,
+ init_once);
+ return 0;
+ }
+diff --git a/fs/namei.c b/fs/namei.c
+index 4ea63ed..a7c0e50 100644
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -141,6 +141,7 @@ char * getname(const char __user * filename)
+ {
+ char *tmp, *result;
+
++ /*ub_dentry_checkup();*/
+ result = ERR_PTR(-ENOMEM);
+ tmp = __getname();
+ if (tmp) {
+@@ -431,6 +432,21 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
+ if (!dentry)
+ dentry = d_lookup(parent, name);
+
++ /*
++ * The revalidation rules are simple:
++ * d_revalidate operation is called when we're about to use a cached
++ * dentry rather than call d_lookup.
++ * d_revalidate method may unhash the dentry itself or return FALSE, in
++ * which case if the dentry can be released d_lookup will be called.
++ *
++ * Additionally, by request of NFS people
++ * (http://linux.bkbits.net:8080/linux-2.4/cset@1.181?nav=index.html|src/|src/fs|related/fs/namei.c)
++ * d_revalidate is called when `/', `.' or `..' are looked up.
++ * Since re-lookup is impossible on them, we introduce a hack and
++ * return an error in this case.
++ *
++ * 2003/02/19 SAW
++ */
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
+ dentry = do_revalidate(dentry, nd);
+
+@@ -489,6 +505,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
+ struct dentry * result;
+ struct inode *dir = parent->d_inode;
+
++repeat:
+ mutex_lock(&dir->i_mutex);
+ /*
+ * First re-do the cached lookup just in case it was created
+@@ -535,7 +552,7 @@ out_unlock:
+ if (result->d_op && result->d_op->d_revalidate) {
+ result = do_revalidate(result, nd);
+ if (!result)
+- result = ERR_PTR(-ENOENT);
++ goto repeat;
+ }
+ return result;
+ }
+@@ -775,6 +792,13 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
+ read_unlock(&fs->lock);
+ break;
+ }
++#ifdef CONFIG_VE
++ if (nd->path.dentry == get_exec_env()->root_path.dentry &&
++ nd->path.mnt == get_exec_env()->root_path.mnt) {
++ read_unlock(&current->fs->lock);
++ break;
++ }
++#endif
+ read_unlock(&fs->lock);
+ spin_lock(&dcache_lock);
+ if (nd->path.dentry != nd->path.mnt->mnt_root) {
+@@ -816,6 +840,10 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
+ if (dentry->d_op && dentry->d_op->d_revalidate)
+ goto need_revalidate;
+ done:
++ if ((nd->flags & LOOKUP_STRICT) && d_mountpoint(dentry)) {
++ dput(dentry);
++ return -ENOENT;
++ }
+ path->mnt = mnt;
+ path->dentry = dentry;
+ __follow_mount(path);
+@@ -853,6 +881,7 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
+ struct inode *inode;
+ int err;
+ unsigned int lookup_flags = nd->flags;
++ int real_components = 0;
+
+ while (*name=='/')
+ name++;
+@@ -923,6 +952,7 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
+ break;
+ }
+ /* This does the actual lookups.. */
++ real_components++;
+ err = do_lookup(nd, &this, &next);
+ if (err)
+ break;
+@@ -936,6 +966,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
+ goto out_dput;
+
+ if (inode->i_op->follow_link) {
++ err = -ENOENT;
++ if (lookup_flags & LOOKUP_STRICT)
++ goto out_dput;
+ err = do_follow_link(&next, nd);
+ if (err)
+ goto return_err;
+@@ -984,6 +1017,7 @@ last_component:
+ break;
+ inode = next.dentry->d_inode;
+ if ((lookup_flags & LOOKUP_FOLLOW)
++ && !(lookup_flags & LOOKUP_STRICT)
+ && inode && inode->i_op && inode->i_op->follow_link) {
+ err = do_follow_link(&next, nd);
+ if (err)
+@@ -1005,27 +1039,41 @@ lookup_parent:
+ nd->last_type = LAST_NORM;
+ if (this.name[0] != '.')
+ goto return_base;
+- if (this.len == 1)
++ if (this.len == 1) {
+ nd->last_type = LAST_DOT;
+- else if (this.len == 2 && this.name[1] == '.')
++ goto return_reval;
++ } else if (this.len == 2 && this.name[1] == '.') {
+ nd->last_type = LAST_DOTDOT;
+- else
+- goto return_base;
++ goto return_reval;
++ }
++return_base:
++ if (!(nd->flags & LOOKUP_NOAREACHECK)) {
++ err = check_area_access_ve(&nd->path);
++ if (err)
++ break;
++ }
++ return 0;
+ return_reval:
+ /*
+ * We bypassed the ordinary revalidation routines.
+ * We may need to check the cached dentry for staleness.
+ */
+- if (nd->path.dentry && nd->path.dentry->d_sb &&
++ if (!real_components && nd->path.dentry && nd->path.dentry->d_sb &&
+ (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
+ err = -ESTALE;
+ /* Note: we do not d_invalidate() */
+ if (!nd->path.dentry->d_op->d_revalidate(
+ nd->path.dentry, nd))
++ /*
++ * This lookup is for `/' or `.' or `..'.
++ * The filesystem unhashed the dentry itself
++ * inside d_revalidate (otherwise, d_invalidate
++ * wouldn't succeed). As a special courtesy to
++ * NFS we return an error. 2003/02/19 SAW
++ */
+ break;
+ }
+-return_base:
+- return 0;
++ goto return_base;
+ out_dput:
+ path_put_conditional(&next, nd);
+ break;
+@@ -2045,6 +2093,7 @@ asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev)
+ {
+ return sys_mknodat(AT_FDCWD, filename, mode, dev);
+ }
++EXPORT_SYMBOL_GPL(sys_mknod);
+
+ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+ {
+@@ -2105,6 +2154,7 @@ asmlinkage long sys_mkdir(const char __user *pathname, int mode)
+ {
+ return sys_mkdirat(AT_FDCWD, pathname, mode);
+ }
++EXPORT_SYMBOL_GPL(sys_mkdir);
+
+ /*
+ * We try to drop the dentry early: we should have
+@@ -2132,6 +2182,7 @@ void dentry_unhash(struct dentry *dentry)
+ spin_unlock(&dentry->d_lock);
+ spin_unlock(&dcache_lock);
+ }
++EXPORT_SYMBOL(sys_symlink);
+
+ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+ {
+@@ -2212,6 +2263,7 @@ asmlinkage long sys_rmdir(const char __user *pathname)
+ {
+ return do_rmdir(AT_FDCWD, pathname);
+ }
++EXPORT_SYMBOL_GPL(sys_rmdir);
+
+ int vfs_unlink(struct inode *dir, struct dentry *dentry)
+ {
+@@ -2312,6 +2364,7 @@ asmlinkage long sys_unlink(const char __user *pathname)
+ {
+ return do_unlinkat(AT_FDCWD, pathname);
+ }
++EXPORT_SYMBOL_GPL(sys_unlink);
+
+ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+ {
+@@ -2475,6 +2528,7 @@ asmlinkage long sys_link(const char __user *oldname, const char __user *newname)
+ {
+ return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+ }
++EXPORT_SYMBOL(sys_rename);
+
+ /*
+ * The worst of all namespace operations - renaming directory. "Perverted"
+@@ -2586,6 +2640,9 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+ const char *old_name;
+
++ if (DQUOT_RENAME(old_dentry->d_inode, old_dir, new_dir))
++ return -EXDEV;
++
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+
+diff --git a/fs/namespace.c b/fs/namespace.c
+index 6e283c9..746610b 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -37,6 +37,7 @@
+
+ /* spinlock for vfsmount related operations, inplace of dcache_lock */
+ __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
++EXPORT_SYMBOL(vfsmount_lock);
+
+ static int event;
+ static DEFINE_IDA(mnt_id_ida);
+@@ -44,7 +45,8 @@ static DEFINE_IDA(mnt_group_ida);
+
+ static struct list_head *mount_hashtable __read_mostly;
+ static struct kmem_cache *mnt_cache __read_mostly;
+-static struct rw_semaphore namespace_sem;
++struct rw_semaphore namespace_sem;
++EXPORT_SYMBOL_GPL(namespace_sem);
+
+ /* /sys/fs */
+ struct kobject *fs_kobj;
+@@ -116,11 +118,12 @@ struct vfsmount *alloc_vfsmnt(const char *name)
+ goto out_free_cache;
+
+ if (name) {
+- mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
++ mnt->mnt_devname = kstrdup(name, GFP_KERNEL_UBC);
+ if (!mnt->mnt_devname)
+ goto out_free_id;
+ }
+
++ mnt->owner = VEID(get_exec_env());
+ atomic_set(&mnt->mnt_count, 1);
+ INIT_LIST_HEAD(&mnt->mnt_hash);
+ INIT_LIST_HEAD(&mnt->mnt_child);
+@@ -797,15 +800,48 @@ static void show_type(struct seq_file *m, struct super_block *sb)
+ }
+ }
+
++static int prepare_mnt_root_mangle(struct path *path,
++ char **path_buf, char **ret_path)
++{
++ /* skip FS_NOMOUNT mounts (rootfs) */
++ if (path->mnt->mnt_sb->s_flags & MS_NOUSER)
++ return -EACCES;
++
++ *path_buf = (char *)__get_free_page(GFP_KERNEL);
++ if (!*path_buf)
++ return -ENOMEM;
++
++ *ret_path = d_path(path, *path_buf, PAGE_SIZE);
++ if (IS_ERR(*ret_path)) {
++ free_page((unsigned long)*path_buf);
++ /*
++ * This means that the file position will be incremented, i.e.
++ * the total number of "invisible" vfsmnt will leak.
++ */
++ return -EACCES;
++ }
++ return 0;
++}
++
+ static int show_vfsmnt(struct seq_file *m, void *v)
+ {
+ struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
+- int err = 0;
++ int err;
+ struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
++ char *path_buf, *path;
+
+- mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
++ err = prepare_mnt_root_mangle(&mnt_path, &path_buf, &path);
++ if (err < 0)
++ return (err == -EACCES ? 0 : err);
++
++ if (ve_is_super(get_exec_env()) ||
++ !(mnt->mnt_sb->s_type->fs_flags & FS_MANGLE_PROC))
++ mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
++ else
++ mangle(m, mnt->mnt_sb->s_type->name);
+ seq_putc(m, ' ');
+- seq_path(m, &mnt_path, " \t\n\\");
++ mangle(m, path);
++ free_page((unsigned long) path_buf);
+ seq_putc(m, ' ');
+ show_type(m, mnt->mnt_sb);
+ seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
+@@ -892,18 +928,27 @@ static int show_vfsstat(struct seq_file *m, void *v)
+ {
+ struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
+ struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
+- int err = 0;
++ char *path_buf, *path;
++ int err;
++
++ err = prepare_mnt_root_mangle(&mnt_path, &path_buf, &path);
++ if (err < 0)
++ return (err == -EACCES ? 0 : err);
+
+ /* device */
+ if (mnt->mnt_devname) {
+ seq_puts(m, "device ");
+- mangle(m, mnt->mnt_devname);
++ if (ve_is_super(get_exec_env()))
++ mangle(m, mnt->mnt_devname);
++ else
++ mangle(m, mnt->mnt_sb->s_type->name);
+ } else
+ seq_puts(m, "no device");
+
+ /* mount point */
+ seq_puts(m, " mounted on ");
+- seq_path(m, &mnt_path, " \t\n\\");
++ mangle(m, path);
++ free_page((unsigned long)path_buf);
+ seq_putc(m, ' ');
+
+ /* file system type */
+@@ -1120,6 +1165,34 @@ static int do_umount(struct vfsmount *mnt, int flags)
+ return retval;
+ }
+
++#ifdef CONFIG_VE
++void umount_ve_fs_type(struct file_system_type *local_fs_type)
++{
++ struct vfsmount *mnt;
++ struct list_head *p, *q;
++ LIST_HEAD(kill);
++ LIST_HEAD(umount_list);
++
++ down_write(&namespace_sem);
++ spin_lock(&vfsmount_lock);
++ list_for_each_safe(p, q, &current->nsproxy->mnt_ns->list) {
++ mnt = list_entry(p, struct vfsmount, mnt_list);
++ if (mnt->mnt_sb->s_type != local_fs_type)
++ continue;
++ list_del(p);
++ list_add(p, &kill);
++ }
++
++ while (!list_empty(&kill)) {
++ mnt = list_entry(kill.next, struct vfsmount, mnt_list);
++ umount_tree(mnt, 1, &umount_list);
++ }
++ spin_unlock(&vfsmount_lock);
++ up_write(&namespace_sem);
++ release_mounts(&umount_list);
++}
++#endif
++
+ /*
+ * Now umount can handle mount points as well as block devices.
+ * This is important for filesystems which use unnamed block devices.
+@@ -1143,7 +1216,7 @@ asmlinkage long sys_umount(char __user * name, int flags)
+ goto dput_and_out;
+
+ retval = -EPERM;
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ goto dput_and_out;
+
+ retval = do_umount(path.mnt, flags);
+@@ -1169,7 +1242,7 @@ asmlinkage long sys_oldumount(char __user * name)
+
+ static int mount_is_safe(struct nameidata *nd)
+ {
+- if (capable(CAP_SYS_ADMIN))
++ if (capable(CAP_VE_SYS_ADMIN))
+ return 0;
+ return -EPERM;
+ #ifdef notyet
+@@ -1439,6 +1512,8 @@ static noinline int do_change_type(struct nameidata *nd, int flag)
+
+ if (nd->path.dentry != nd->path.mnt->mnt_root)
+ return -EINVAL;
++ if (!ve_accessible_veid(nd->path.mnt->owner, get_exec_env()->veid))
++ return -EPERM;
+
+ down_write(&namespace_sem);
+ if (type == MS_SHARED) {
+@@ -1462,7 +1537,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag)
+ * noinline this do_mount helper to save do_mount stack space.
+ */
+ static noinline int do_loopback(struct nameidata *nd, char *old_name,
+- int recurse)
++ int recurse, int mnt_flags)
+ {
+ struct nameidata old_nd;
+ struct vfsmount *mnt = NULL;
+@@ -1492,6 +1567,7 @@ static noinline int do_loopback(struct nameidata *nd, char *old_name,
+ if (!mnt)
+ goto out;
+
++ mnt->mnt_flags |= mnt_flags;
+ err = graft_tree(mnt, &nd->path);
+ if (err) {
+ LIST_HEAD(umount_list);
+@@ -1536,7 +1612,7 @@ static noinline int do_remount(struct nameidata *nd, int flags, int mnt_flags,
+ int err;
+ struct super_block *sb = nd->path.mnt->mnt_sb;
+
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+
+ if (!check_mnt(nd->path.mnt))
+@@ -1545,6 +1621,9 @@ static noinline int do_remount(struct nameidata *nd, int flags, int mnt_flags,
+ if (nd->path.dentry != nd->path.mnt->mnt_root)
+ return -EINVAL;
+
++ if (!ve_accessible_veid(nd->path.mnt->owner, get_exec_env()->veid))
++ return -EPERM;
++
+ down_write(&sb->s_umount);
+ if (flags & MS_BIND)
+ err = change_mount_flags(nd->path.mnt, flags);
+@@ -1577,7 +1656,7 @@ static noinline int do_move_mount(struct nameidata *nd, char *old_name)
+ struct path parent_path;
+ struct vfsmount *p;
+ int err = 0;
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ if (!old_name || !*old_name)
+ return -EINVAL;
+@@ -1585,6 +1664,10 @@ static noinline int do_move_mount(struct nameidata *nd, char *old_name)
+ if (err)
+ return err;
+
++ err = -EPERM;
++ if (!ve_accessible_veid(old_nd.path.mnt->owner, get_exec_env()->veid))
++ goto out_nosem;
++
+ down_write(&namespace_sem);
+ while (d_mountpoint(nd->path.dentry) &&
+ follow_down(&nd->path.mnt, &nd->path.dentry))
+@@ -1642,6 +1725,7 @@ out:
+ up_write(&namespace_sem);
+ if (!err)
+ path_put(&parent_path);
++out_nosem:
+ path_put(&old_nd.path);
+ return err;
+ }
+@@ -1660,7 +1744,7 @@ static noinline int do_new_mount(struct nameidata *nd, char *type, int flags,
+ return -EINVAL;
+
+ /* we need capabilities... */
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+
+ mnt = do_kern_mount(type, flags, name, data);
+@@ -1699,6 +1783,11 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
+ goto unlock;
+
+ newmnt->mnt_flags = mnt_flags;
++
++ /* make this before graft_tree reveals mnt_root to the world... */
++ if (path->dentry->d_flags & DCACHE_VIRTUAL)
++ newmnt->mnt_root->d_flags |= DCACHE_VIRTUAL;
++
+ if ((err = graft_tree(newmnt, path)))
+ goto unlock;
+
+@@ -1953,7 +2042,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
+ retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+ data_page);
+ else if (flags & MS_BIND)
+- retval = do_loopback(&nd, dev_name, flags & MS_REC);
++ retval = do_loopback(&nd, dev_name, flags & MS_REC, mnt_flags);
+ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+ retval = do_change_type(&nd, flags);
+ else if (flags & MS_MOVE)
+@@ -2089,6 +2178,7 @@ out1:
+ free_page(type_page);
+ return retval;
+ }
++EXPORT_SYMBOL_GPL(sys_mount);
+
+ /*
+ * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
+@@ -2131,7 +2221,7 @@ static void chroot_fs_refs(struct path *old_root, struct path *new_root)
+ struct fs_struct *fs;
+
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_ve(g, p) {
+ task_lock(p);
+ fs = p->fs;
+ if (fs) {
+@@ -2146,7 +2236,7 @@ static void chroot_fs_refs(struct path *old_root, struct path *new_root)
+ put_fs_struct(fs);
+ } else
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_ve(g, p);
+ read_unlock(&tasklist_lock);
+ }
+
+@@ -2315,7 +2405,7 @@ void __init mnt_init(void)
+ init_rwsem(&namespace_sem);
+
+ mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
+- 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
++ 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_UBC, NULL);
+
+ mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
+
+@@ -2352,3 +2442,4 @@ void __put_mnt_ns(struct mnt_namespace *ns)
+ release_mounts(&umount_list);
+ kfree(ns);
+ }
++EXPORT_SYMBOL_GPL(__put_mnt_ns);
+diff --git a/fs/nfs/client.c b/fs/nfs/client.c
+index 5ee23e7..8d5d75a 100644
+--- a/fs/nfs/client.c
++++ b/fs/nfs/client.c
+@@ -127,6 +127,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
+
+ atomic_set(&clp->cl_count, 1);
+ clp->cl_cons_state = NFS_CS_INITING;
++ clp->owner_env = get_exec_env();
+
+ memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
+ clp->cl_addrlen = cl_init->addrlen;
+@@ -257,6 +258,7 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
+ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
+ {
+ struct nfs_client *clp;
++ struct ve_struct *ve = get_exec_env();
+
+ spin_lock(&nfs_client_lock);
+ list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
+@@ -272,6 +274,9 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
+
+ if (addr->sa_family != clap->sa_family)
+ continue;
++ if (!ve_accessible_strict(clp->owner_env, ve))
++ continue;
++
+ /* Match only the IP address, not the port number */
+ if (!nfs_sockaddr_match_ipaddr(addr, clap))
+ continue;
+@@ -292,6 +297,7 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
+ {
+ struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr;
+ u32 nfsvers = clp->rpc_ops->version;
++ struct ve_struct *ve = get_exec_env();
+
+ spin_lock(&nfs_client_lock);
+ list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) {
+@@ -307,6 +313,9 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
+
+ if (sap->sa_family != clap->sa_family)
+ continue;
++ if (!ve_accessible_strict(clp->owner_env, ve))
++ continue;
++
+ /* Match only the IP address, not the port number */
+ if (!nfs_sockaddr_match_ipaddr(sap, clap))
+ continue;
+@@ -326,7 +335,9 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
+ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data)
+ {
+ struct nfs_client *clp;
++ struct ve_struct *ve;
+
++ ve = get_exec_env();
+ list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
+ /* Don't match clients that failed to initialise properly */
+ if (clp->cl_cons_state < 0)
+diff --git a/fs/nfs/super.c b/fs/nfs/super.c
+index e9b2017..4ffeff3 100644
+--- a/fs/nfs/super.c
++++ b/fs/nfs/super.c
+@@ -51,6 +51,9 @@
+ #include <linux/nfs_xdr.h>
+ #include <linux/magic.h>
+ #include <linux/parser.h>
++#include <linux/ve_proto.h>
++#include <linux/vzcalluser.h>
++#include <linux/ve_nfs.h>
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+@@ -217,7 +220,8 @@ static struct file_system_type nfs_fs_type = {
+ .name = "nfs",
+ .get_sb = nfs_get_sb,
+ .kill_sb = nfs_kill_super,
+- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
++ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|
++ FS_BINARY_MOUNTDATA|FS_VIRTUALIZED,
+ };
+
+ struct file_system_type nfs_xdev_fs_type = {
+@@ -225,7 +229,8 @@ struct file_system_type nfs_xdev_fs_type = {
+ .name = "nfs",
+ .get_sb = nfs_xdev_get_sb,
+ .kill_sb = nfs_kill_super,
+- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
++ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|
++ FS_BINARY_MOUNTDATA|FS_VIRTUALIZED,
+ };
+
+ static const struct super_operations nfs_sops = {
+@@ -292,6 +297,55 @@ static struct shrinker acl_shrinker = {
+ .seeks = DEFAULT_SEEKS,
+ };
+
++#ifdef CONFIG_VE
++static int ve_nfs_start(void *data)
++{
++ return 0;
++}
++
++static void ve_nfs_stop(void *data)
++{
++ struct ve_struct *ve;
++ struct super_block *sb;
++
++ flush_scheduled_work();
++
++ ve = (struct ve_struct *)data;
++ /* Basically, on a valid stop we can be here iff NFS was mounted
++ read-only. In such a case client force-stop is not a problem.
++ If we are here and NFS is read-write, we are in a FORCE stop, so
++ force the client to stop.
++ Lock daemon is already dead.
++ Only superblock client remains. Den */
++ spin_lock(&sb_lock);
++ list_for_each_entry(sb, &super_blocks, s_list) {
++ struct rpc_clnt *clnt;
++ struct rpc_xprt *xprt;
++ if (sb->s_type != &nfs_fs_type)
++ continue;
++ clnt = NFS_SB(sb)->client;
++ if (!ve_accessible_strict(clnt->cl_xprt->owner_env, ve))
++ continue;
++ clnt->cl_broken = 1;
++ rpc_killall_tasks(clnt);
++
++ xprt = clnt->cl_xprt;
++ xprt_disconnect_done(xprt);
++ xprt->ops->close(xprt);
++ }
++ spin_unlock(&sb_lock);
++
++ flush_scheduled_work();
++}
++
++static struct ve_hook nfs_hook = {
++ .init = ve_nfs_start,
++ .fini = ve_nfs_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_NET_POST,
++};
++#endif
++
+ /*
+ * Register the NFS filesystems
+ */
+@@ -312,6 +366,7 @@ int __init register_nfs_fs(void)
+ goto error_2;
+ #endif
+ register_shrinker(&acl_shrinker);
++ ve_hook_register(VE_SS_CHAIN, &nfs_hook);
+ return 0;
+
+ #ifdef CONFIG_NFS_V4
+@@ -330,6 +385,7 @@ error_0:
+ void __exit unregister_nfs_fs(void)
+ {
+ unregister_shrinker(&acl_shrinker);
++ ve_hook_unregister(&nfs_hook);
+ #ifdef CONFIG_NFS_V4
+ unregister_filesystem(&nfs4_fs_type);
+ #endif
+@@ -1955,6 +2011,11 @@ static int nfs_get_sb(struct file_system_type *fs_type,
+ .mntflags = flags,
+ };
+ int error = -ENOMEM;
++ struct ve_struct *ve;
++
++ ve = get_exec_env();
++ if (!ve_is_super(ve) && !(ve->features & VE_FEATURE_NFS))
++ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL);
+@@ -2064,6 +2125,11 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
+ .mntflags = flags,
+ };
+ int error;
++ struct ve_struct *ve;
++
++ ve = get_exec_env();
++ if (!ve_is_super(ve) && !(ve->features & VE_FEATURE_NFS))
++ return -ENODEV;
+
+ dprintk("--> nfs_xdev_get_sb()\n");
+
+diff --git a/fs/open.c b/fs/open.c
+index 07da935..50873f8 100644
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -25,6 +25,7 @@
+ #include <linux/fs.h>
+ #include <linux/personality.h>
+ #include <linux/pagemap.h>
++#include <linux/faudit.h>
+ #include <linux/syscalls.h>
+ #include <linux/rcupdate.h>
+ #include <linux/audit.h>
+@@ -51,7 +52,21 @@ int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+
+ EXPORT_SYMBOL(vfs_statfs);
+
+-static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
++int faudit_statfs(struct super_block *sb, struct kstatfs *buf)
++{
++ struct faudit_statfs_arg arg;
++
++ arg.sb = sb;
++ arg.stat = buf;
++
++ if (virtinfo_notifier_call(VITYPE_FAUDIT, VIRTINFO_FAUDIT_STATFS, &arg)
++ != NOTIFY_DONE)
++ return arg.err;
++ return 0;
++}
++
++static int vfs_statfs_native(struct dentry *dentry, struct vfsmount *mnt,
++ struct statfs *buf)
+ {
+ struct kstatfs st;
+ int retval;
+@@ -60,6 +75,10 @@ static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
+ if (retval)
+ return retval;
+
++ retval = faudit_statfs(mnt->mnt_sb, &st);
++ if (retval)
++ return retval;
++
+ if (sizeof(*buf) == sizeof(st))
+ memcpy(buf, &st, sizeof(st));
+ else {
+@@ -95,7 +114,8 @@ static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
+ return 0;
+ }
+
+-static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
++static int vfs_statfs64(struct dentry *dentry, struct vfsmount *mnt,
++ struct statfs64 *buf)
+ {
+ struct kstatfs st;
+ int retval;
+@@ -104,6 +124,10 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
+ if (retval)
+ return retval;
+
++ retval = faudit_statfs(mnt->mnt_sb, &st);
++ if (retval)
++ return retval;
++
+ if (sizeof(*buf) == sizeof(st))
+ memcpy(buf, &st, sizeof(st));
+ else {
+@@ -130,7 +154,7 @@ asmlinkage long sys_statfs(const char __user *pathname, struct statfs __user * b
+ error = user_path(pathname, &path);
+ if (!error) {
+ struct statfs tmp;
+- error = vfs_statfs_native(path.dentry, &tmp);
++ error = vfs_statfs_native(path.dentry, path.mnt, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+ error = -EFAULT;
+ path_put(&path);
+@@ -149,7 +173,7 @@ asmlinkage long sys_statfs64(const char __user *pathname, size_t sz, struct stat
+ error = user_path(pathname, &path);
+ if (!error) {
+ struct statfs64 tmp;
+- error = vfs_statfs64(path.dentry, &tmp);
++ error = vfs_statfs64(path.dentry, path.mnt, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+ error = -EFAULT;
+ path_put(&path);
+@@ -168,7 +192,7 @@ asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user * buf)
+ file = fget(fd);
+ if (!file)
+ goto out;
+- error = vfs_statfs_native(file->f_path.dentry, &tmp);
++ error = vfs_statfs_native(file->f_path.dentry, file->f_path.mnt, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+ error = -EFAULT;
+ fput(file);
+@@ -189,7 +213,7 @@ asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user
+ file = fget(fd);
+ if (!file)
+ goto out;
+- error = vfs_statfs64(file->f_path.dentry, &tmp);
++ error = vfs_statfs64(file->f_path.dentry, file->f_path.mnt, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
+ error = -EFAULT;
+ fput(file);
+@@ -692,6 +716,7 @@ out_release:
+ out:
+ return error;
+ }
++EXPORT_SYMBOL_GPL(sys_chown);
+
+ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
+ gid_t group, int flag)
+@@ -930,6 +955,7 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
+ return filp;
+ }
+
++int odirect_enable = 0;
+ /*
+ * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
+ * error.
+@@ -951,6 +977,9 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+ return ERR_PTR(-EINVAL);
+ }
+
++ if (!capable(CAP_SYS_RAWIO) && !odirect_enable)
++ flags &= ~O_DIRECT;
++
+ error = -ENFILE;
+ f = get_empty_filp();
+ if (f == NULL) {
+@@ -1041,6 +1070,7 @@ asmlinkage long sys_open(const char __user *filename, int flags, int mode)
+ asmlinkage_protect(3, ret, filename, flags, mode);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(sys_open);
+
+ asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
+ int mode)
+diff --git a/fs/partitions/check.c b/fs/partitions/check.c
+index ecc3330..a8cc218 100644
+--- a/fs/partitions/check.c
++++ b/fs/partitions/check.c
+@@ -131,6 +131,7 @@ char *disk_name(struct gendisk *hd, int part, char *buf)
+
+ return buf;
+ }
++EXPORT_SYMBOL(disk_name);
+
+ const char *bdevname(struct block_device *bdev, char *buf)
+ {
+diff --git a/fs/pipe.c b/fs/pipe.c
+index fcba654..2fd4b51 100644
+--- a/fs/pipe.c
++++ b/fs/pipe.c
+@@ -22,6 +22,8 @@
+ #include <asm/uaccess.h>
+ #include <asm/ioctls.h>
+
++#include <bc/kmem.h>
++
+ /*
+ * We use a start+len construction, which provides full use of the
+ * allocated memory.
+@@ -478,7 +480,7 @@ redo1:
+ int error, atomic = 1;
+
+ if (!page) {
+- page = alloc_page(GFP_HIGHUSER);
++ page = alloc_page(GFP_HIGHUSER | __GFP_UBC);
+ if (unlikely(!page)) {
+ ret = ret ? : -ENOMEM;
+ break;
+@@ -821,7 +823,7 @@ struct pipe_inode_info * alloc_pipe_info(struct inode *inode)
+ {
+ struct pipe_inode_info *pipe;
+
+- pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
++ pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_UBC);
+ if (pipe) {
+ init_waitqueue_head(&pipe->wait);
+ pipe->r_counter = pipe->w_counter = 1;
+@@ -1046,6 +1048,7 @@ int do_pipe(int *fd)
+ {
+ return do_pipe_flags(fd, 0);
+ }
++EXPORT_SYMBOL_GPL(do_pipe);
+
+ /*
+ * sys_pipe() is the normal C calling standard for creating
+diff --git a/fs/proc/array.c b/fs/proc/array.c
+index 71c9be5..8359842 100644
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -82,6 +82,8 @@
+ #include <linux/pid_namespace.h>
+ #include <linux/tracehook.h>
+
++#include <bc/beancounter.h>
++
+ #include <asm/pgtable.h>
+ #include <asm/processor.h>
+ #include "internal.h"
+@@ -208,6 +210,15 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
+ put_group_info(group_info);
+
+ seq_printf(m, "\n");
++
++#ifdef CONFIG_VE
++ if (ve_is_super(get_exec_env())) {
++ seq_printf(m, "envID:\t%d\nVPid:\t%d\n",
++ p->ve_task_info.owner_env->veid, task_pid_vnr(p));
++ seq_printf(m, "PNState:\t%u\nStopState:\t%u\n",
++ p->pn_state, p->stopped_state);
++ }
++#endif
+ }
+
+ static void render_sigset_t(struct seq_file *m, const char *header,
+@@ -247,10 +258,10 @@ static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign,
+ }
+ }
+
+-static inline void task_sig(struct seq_file *m, struct task_struct *p)
++void task_sig(struct seq_file *m, struct task_struct *p)
+ {
+ unsigned long flags;
+- sigset_t pending, shpending, blocked, ignored, caught;
++ sigset_t pending, shpending, blocked, ignored, caught, saved;
+ int num_threads = 0;
+ unsigned long qsize = 0;
+ unsigned long qlim = 0;
+@@ -260,12 +271,14 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
+ sigemptyset(&blocked);
+ sigemptyset(&ignored);
+ sigemptyset(&caught);
++ sigemptyset(&saved);
+
+ rcu_read_lock();
+ if (lock_task_sighand(p, &flags)) {
+ pending = p->pending.signal;
+ shpending = p->signal->shared_pending.signal;
+ blocked = p->blocked;
++ saved = p->saved_sigmask;
+ collect_sigign_sigcatch(p, &ignored, &caught);
+ num_threads = atomic_read(&p->signal->count);
+ qsize = atomic_read(&p->user->sigpending);
+@@ -283,6 +296,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
+ render_sigset_t(m, "SigBlk:\t", &blocked);
+ render_sigset_t(m, "SigIgn:\t", &ignored);
+ render_sigset_t(m, "SigCgt:\t", &caught);
++ render_sigset_t(m, "SigSvd:\t", &saved);
+ }
+
+ static void render_cap_t(struct seq_file *m, const char *header,
+@@ -306,6 +320,20 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p)
+ render_cap_t(m, "CapBnd:\t", &p->cap_bset);
+ }
+
++#ifdef CONFIG_BEANCOUNTERS
++static inline void ub_dump_task_info(struct task_struct *tsk,
++ char *stsk, int ltsk, char *smm, int lmm)
++{
++ print_ub_uid(tsk->task_bc.task_ub, stsk, ltsk);
++ task_lock(tsk);
++ if (tsk->mm)
++ print_ub_uid(tsk->mm->mm_ub, smm, lmm);
++ else
++ strncpy(smm, "N/A", lmm);
++ task_unlock(tsk);
++}
++#endif
++
+ static inline void task_context_switch_counts(struct seq_file *m,
+ struct task_struct *p)
+ {
+@@ -319,6 +347,9 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *task)
+ {
+ struct mm_struct *mm = get_task_mm(task);
++#ifdef CONFIG_BEANCOUNTERS
++ char tsk_ub_info[64], mm_ub_info[64];
++#endif
+
+ task_name(m, task);
+ task_state(m, ns, pid, task);
+@@ -334,6 +365,14 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,
+ task_show_regs(m, task);
+ #endif
+ task_context_switch_counts(m, task);
++#ifdef CONFIG_BEANCOUNTERS
++ ub_dump_task_info(task,
++ tsk_ub_info, sizeof(tsk_ub_info),
++ mm_ub_info, sizeof(mm_ub_info));
++
++ seq_printf(m, "TaskUB:\t%s\n", tsk_ub_info);
++ seq_printf(m, "MMUB:\t%s\n", mm_ub_info);
++#endif
+ return 0;
+ }
+
+@@ -356,6 +395,10 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ unsigned long rsslim = 0;
+ char tcomm[sizeof(task->comm)];
+ unsigned long flags;
++#ifdef CONFIG_BEANCOUNTERS
++ char ub_task_info[64];
++ char ub_mm_info[64];
++#endif
+
+ state = *get_task_state(task);
+ vsize = eip = esp = 0;
+@@ -434,6 +477,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ priority = task_prio(task);
+ nice = task_nice(task);
+
++#ifndef CONFIG_VE
+ /* Temporary variable needed for gcc-2.96 */
+ /* convert timespec -> nsec*/
+ start_time =
+@@ -441,10 +485,25 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ + task->real_start_time.tv_nsec;
+ /* convert nsec -> ticks */
+ start_time = nsec_to_clock_t(start_time);
++#else
++ start_time = ve_relative_clock(&task->start_time);
++#endif
++
++#ifdef CONFIG_BEANCOUNTERS
++ ub_dump_task_info(task, ub_task_info, sizeof(ub_task_info),
++ ub_mm_info, sizeof(ub_mm_info));
++#endif
+
+ seq_printf(m, "%d (%s) %c %d %d %d %d %d %u %lu \
+ %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
+-%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld\n",
++%lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld"
++#ifdef CONFIG_VE
++ " 0 0 0 0 0 0 0 %d %u"
++#endif
++#ifdef CONFIG_BEANCOUNTERS
++ " %s %s"
++#endif
++ "\n",
+ pid_nr_ns(pid, ns),
+ tcomm,
+ state,
+@@ -491,7 +550,16 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ task->policy,
+ (unsigned long long)delayacct_blkio_ticks(task),
+ cputime_to_clock_t(gtime),
+- cputime_to_clock_t(cgtime));
++ cputime_to_clock_t(cgtime)
++#ifdef CONFIG_VE
++ , task_pid_vnr(task),
++ VEID(VE_TASK_INFO(task)->owner_env)
++#endif
++#ifdef CONFIG_BEANCOUNTERS
++ , ub_task_info,
++ ub_mm_info
++#endif
++ );
+ if (mm)
+ mmput(mm);
+ return 0;
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index a28840b..98f82bc 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -187,10 +187,12 @@ static int proc_cwd_link(struct inode *inode, struct path *path)
+ }
+ if (fs) {
+ read_lock(&fs->lock);
+- *path = fs->pwd;
+- path_get(&fs->pwd);
++ result = d_root_check(&fs->pwd);
++ if (result == 0) {
++ *path = fs->pwd;
++ path_get(&fs->pwd);
++ }
+ read_unlock(&fs->lock);
+- result = 0;
+ put_fs_struct(fs);
+ }
+ return result;
+@@ -538,17 +540,31 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer)
+ static int proc_fd_access_allowed(struct inode *inode)
+ {
+ struct task_struct *task;
+- int allowed = 0;
++ int err;
++
+ /* Allow access to a task's file descriptors if it is us or we
+ * may use ptrace attach to the process and find out that
+ * information.
+ */
++ err = -ENOENT;
+ task = get_proc_task(inode);
+ if (task) {
+- allowed = ptrace_may_access(task, PTRACE_MODE_READ);
++ if (ptrace_may_access(task, PTRACE_MODE_READ))
++ err = 0;
++ else
++ /*
++ * This clever ptrace_may_attach() may play a trick
++ * on us. If the task is zombie it will consider this
++ * task to be not dumpable at all and will deny any
++ * ptracing in VE. Not a big deal for ptrace(), but
++ * following the link will fail with the -EACCESS
++ * reason. Some software is unable to stand such a
++ * swindle and refuses to work :(
++ */
++ err = (task->mm ? -EACCES : -ENOENT);
+ put_task_struct(task);
+ }
+- return allowed;
++ return err;
+ }
+
+ static int proc_setattr(struct dentry *dentry, struct iattr *attr)
+@@ -1023,6 +1039,8 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
+ if ((oom_adjust < OOM_ADJUST_MIN || oom_adjust > OOM_ADJUST_MAX) &&
+ oom_adjust != OOM_DISABLE)
+ return -EINVAL;
++ if (oom_adjust == OOM_DISABLE && !ve_is_super(get_exec_env()))
++ return -EPERM;
+ if (*end == '\n')
+ end++;
+ task = get_proc_task(file->f_path.dentry->d_inode);
+@@ -1277,6 +1295,7 @@ void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
+ mm->exe_file = new_exe_file;
+ mm->num_exe_file_vmas = 0;
+ }
++EXPORT_SYMBOL(set_mm_exe_file);
+
+ struct file *get_mm_exe_file(struct mm_struct *mm)
+ {
+@@ -1315,10 +1334,15 @@ static int proc_exe_link(struct inode *inode, struct path *exe_path)
+ exe_file = get_mm_exe_file(mm);
+ mmput(mm);
+ if (exe_file) {
+- *exe_path = exe_file->f_path;
+- path_get(&exe_file->f_path);
++ int result;
++
++ result = d_root_check(&exe_file->f_path);
++ if (result == 0) {
++ *exe_path = exe_file->f_path;
++ path_get(&exe_file->f_path);
++ }
+ fput(exe_file);
+- return 0;
++ return result;
+ } else
+ return -ENOENT;
+ }
+@@ -1326,13 +1350,14 @@ static int proc_exe_link(struct inode *inode, struct path *exe_path)
+ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
+ {
+ struct inode *inode = dentry->d_inode;
+- int error = -EACCES;
++ int error;
+
+ /* We don't need a base pointer in the /proc filesystem */
+ path_put(&nd->path);
+
+ /* Are we allowed to snoop on the tasks file descriptors? */
+- if (!proc_fd_access_allowed(inode))
++ error = proc_fd_access_allowed(inode);
++ if (error < 0)
+ goto out;
+
+ error = PROC_I(inode)->op.proc_get_link(inode, &nd->path);
+@@ -1367,12 +1392,13 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
+
+ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int buflen)
+ {
+- int error = -EACCES;
++ int error;
+ struct inode *inode = dentry->d_inode;
+ struct path path;
+
+ /* Are we allowed to snoop on the tasks file descriptors? */
+- if (!proc_fd_access_allowed(inode))
++ error = proc_fd_access_allowed(inode);
++ if (error < 0)
+ goto out;
+
+ error = PROC_I(inode)->op.proc_get_link(inode, &path);
+@@ -1613,6 +1639,7 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
+ struct files_struct *files = NULL;
+ struct file *file;
+ int fd = proc_fd(inode);
++ int err = -ENOENT;
+
+ if (task) {
+ files = get_files_struct(task);
+@@ -1625,7 +1652,8 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
+ */
+ spin_lock(&files->file_lock);
+ file = fcheck_files(files, fd);
+- if (file) {
++ err = -EACCES;
++ if (file && !d_root_check(&file->f_path)) {
+ if (path) {
+ *path = file->f_path;
+ path_get(&file->f_path);
+@@ -1643,7 +1671,7 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
+ spin_unlock(&files->file_lock);
+ put_files_struct(files);
+ }
+- return -ENOENT;
++ return err;
+ }
+
+ static int proc_fd_link(struct inode *inode, struct path *path)
+@@ -2410,7 +2438,7 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
+ struct task_struct *t = task;
+
+ task_io_accounting_add(&acct, &task->signal->ioac);
+- while_each_thread(task, t)
++ while_each_thread_ve(task, t)
+ task_io_accounting_add(&acct, &t->ioac);
+
+ unlock_task_sighand(task, &flags);
+diff --git a/fs/proc/generic.c b/fs/proc/generic.c
+index 7821589..44604d5 100644
+--- a/fs/proc/generic.c
++++ b/fs/proc/generic.c
+@@ -228,6 +228,10 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
+ struct proc_dir_entry *de = PDE(inode);
+ int error;
+
++ if ((iattr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) &&
++ LPDE(inode) == PDE(inode))
++ return -EPERM;
++
+ error = inode_change_ok(inode, iattr);
+ if (error)
+ goto out;
+@@ -236,9 +240,12 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
+ if (error)
+ goto out;
+
+- de->uid = inode->i_uid;
+- de->gid = inode->i_gid;
+- de->mode = inode->i_mode;
++ if (iattr->ia_valid & ATTR_UID)
++ de->uid = inode->i_uid;
++ if (iattr->ia_valid & ATTR_GID)
++ de->gid = inode->i_gid;
++ if (iattr->ia_valid & ATTR_MODE)
++ de->mode = inode->i_mode;
+ out:
+ return error;
+ }
+@@ -369,29 +376,61 @@ static struct dentry_operations proc_dentry_operations =
+ .d_delete = proc_delete_dentry,
+ };
+
++static struct proc_dir_entry *__proc_lookup(struct proc_dir_entry *dir,
++ const char *name, int namelen)
++{
++ struct proc_dir_entry *de;
++
++ for (de = dir->subdir; de ; de = de->next) {
++ if (de->namelen != namelen)
++ continue;
++ if (memcmp(de->name, name, namelen))
++ continue;
++ break;
++ }
++ return de;
++}
++
+ /*
+ * Don't create negative dentries here, return -ENOENT by hand
+ * instead.
+ */
+-struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
+- struct dentry *dentry)
++struct dentry *proc_lookup_de(struct proc_dir_entry *de,
++ struct proc_dir_entry *lde,
++ struct inode *dir, struct dentry *dentry)
+ {
+ struct inode *inode = NULL;
+ int error = -ENOENT;
+
+ lock_kernel();
+ spin_lock(&proc_subdir_lock);
+- for (de = de->subdir; de ; de = de->next) {
+- if (de->namelen != dentry->d_name.len)
+- continue;
+- if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
++ de = __proc_lookup(de, dentry->d_name.name, dentry->d_name.len);
++ if (lde != NULL)
++ lde = __proc_lookup(lde, dentry->d_name.name,
++ dentry->d_name.len);
++
++ if (de == NULL)
++ de = lde;
++
++ if (de != NULL) {
++ /*
++ * de lde meaning inode(g,l)
++ * ------------------------------------
++ * NULL NULL -ENOENT *
++ * X NULL global X NULL
++ * NULL X local X X
++ * X Y both X Y
++ */
++ {
+ unsigned int ino;
+
+ ino = de->low_ino;
+ de_get(de);
++ if (lde != NULL)
++ de_get(lde);
+ spin_unlock(&proc_subdir_lock);
+ error = -EINVAL;
+- inode = proc_get_inode(dir->i_sb, ino, de);
++ inode = proc_get_inode(dir->i_sb, ino, de, lde);
+ goto out_unlock;
+ }
+ }
+@@ -406,13 +445,15 @@ out_unlock:
+ }
+ if (de)
+ de_put(de);
++ if (lde)
++ de_put(lde);
+ return ERR_PTR(error);
+ }
+
+ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+ {
+- return proc_lookup_de(PDE(dir), dir, dentry);
++ return proc_lookup_de(PDE(dir), LPDE(dir), dir, dentry);
+ }
+
+ /*
+@@ -424,13 +465,14 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
+ * value of the readdir() call, as long as it's non-negative
+ * for success..
+ */
+-int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
+- filldir_t filldir)
++int proc_readdir_de(struct proc_dir_entry *de, struct proc_dir_entry *lde,
++ struct file *filp, void *dirent, filldir_t filldir)
+ {
+ unsigned int ino;
+ int i;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ int ret = 0;
++ struct proc_dir_entry *ode = de, *fde = NULL;
+
+ lock_kernel();
+
+@@ -453,25 +495,19 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
+ /* fall through */
+ default:
+ spin_lock(&proc_subdir_lock);
+- de = de->subdir;
+ i -= 2;
+- for (;;) {
+- if (!de) {
+- ret = 1;
+- spin_unlock(&proc_subdir_lock);
+- goto out;
+- }
+- if (!i)
+- break;
+- de = de->next;
+- i--;
+- }
+-
+- do {
++repeat:
++ de = de->subdir;
++ while (de != NULL) {
+ struct proc_dir_entry *next;
+
+- /* filldir passes info to user space */
+ de_get(de);
++ if (i-- > 0 || (fde != NULL &&
++ __proc_lookup(fde,
++ de->name, de->namelen)))
++ goto skip;
++
++ /* filldir passes info to user space */
+ spin_unlock(&proc_subdir_lock);
+ if (filldir(dirent, de->name, de->namelen, filp->f_pos,
+ de->low_ino, de->mode >> 12) < 0) {
+@@ -480,10 +516,17 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
+ }
+ spin_lock(&proc_subdir_lock);
+ filp->f_pos++;
++skip:
+ next = de->next;
+ de_put(de);
+ de = next;
+- } while (de);
++ }
++
++ if (fde == NULL && lde != NULL && lde != ode) {
++ de = lde;
++ fde = ode;
++ goto repeat;
++ }
+ spin_unlock(&proc_subdir_lock);
+ }
+ ret = 1;
+@@ -495,7 +538,7 @@ int proc_readdir(struct file *filp, void *dirent, filldir_t filldir)
+ {
+ struct inode *inode = filp->f_path.dentry->d_inode;
+
+- return proc_readdir_de(PDE(inode), filp, dirent, filldir);
++ return proc_readdir_de(PDE(inode), LPDE(inode), filp, dirent, filldir);
+ }
+
+ /*
+diff --git a/fs/proc/inode.c b/fs/proc/inode.c
+index 8bb03f0..f680759 100644
+--- a/fs/proc/inode.c
++++ b/fs/proc/inode.c
+@@ -449,7 +449,7 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
+ #endif
+
+ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
+- struct proc_dir_entry *de)
++ struct proc_dir_entry *de, struct proc_dir_entry *lde)
+ {
+ struct inode * inode;
+
+@@ -463,6 +463,9 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ PROC_I(inode)->fd = 0;
+ PROC_I(inode)->pde = de;
++#ifdef CONFIG_VE
++ PROC_I(inode)->lpde = lde;
++#endif
+
+ if (de->mode) {
+ inode->i_mode = de->mode;
+@@ -509,9 +512,11 @@ int proc_fill_super(struct super_block *s)
+ s->s_magic = PROC_SUPER_MAGIC;
+ s->s_op = &proc_sops;
+ s->s_time_gran = 1;
+-
+- de_get(&proc_root);
+- root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
++
++ de_get(get_exec_env()->proc_root);
++ de_get(&glob_proc_root);
++ root_inode = proc_get_inode(s, PROC_ROOT_INO,
++ &glob_proc_root, get_exec_env()->proc_root);
+ if (!root_inode)
+ goto out_no_root;
+ root_inode->i_uid = 0;
+diff --git a/fs/proc/internal.h b/fs/proc/internal.h
+index 4422023..24ffc99 100644
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -12,6 +12,12 @@
+ #include <linux/proc_fs.h>
+
+ extern struct proc_dir_entry proc_root;
++#ifdef CONFIG_VE
++extern struct proc_dir_entry glob_proc_root;
++#else
++#define glob_proc_root proc_root
++#endif
++
+ #ifdef CONFIG_PROC_SYSCTL
+ extern int proc_sys_init(void);
+ #else
+@@ -63,7 +69,6 @@ extern const struct file_operations proc_smaps_operations;
+ extern const struct file_operations proc_clear_refs_operations;
+ extern const struct file_operations proc_pagemap_operations;
+ extern const struct file_operations proc_net_operations;
+-extern const struct file_operations proc_kmsg_operations;
+ extern const struct inode_operations proc_net_inode_operations;
+
+ void free_proc_entry(struct proc_dir_entry *de);
+@@ -85,10 +90,11 @@ static inline int proc_fd(struct inode *inode)
+ return PROC_I(inode)->fd;
+ }
+
+-struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *ino,
++struct dentry *proc_lookup_de(struct proc_dir_entry *de,
++ struct proc_dir_entry *lpde, struct inode *ino,
+ struct dentry *dentry);
+-int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
+- filldir_t filldir);
++int proc_readdir_de(struct proc_dir_entry *de, struct proc_dir_entry *lpde,
++ struct file *filp, void *dirent, filldir_t filldir);
+
+ struct pde_opener {
+ struct inode *inode;
+diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
+index 9fd5df3..d1cbe06 100644
+--- a/fs/proc/kmsg.c
++++ b/fs/proc/kmsg.c
+@@ -11,6 +11,8 @@
+ #include <linux/kernel.h>
+ #include <linux/poll.h>
+ #include <linux/fs.h>
++#include <linux/veprintk.h>
++#include <linux/module.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -42,7 +44,7 @@ static ssize_t kmsg_read(struct file *file, char __user *buf,
+
+ static unsigned int kmsg_poll(struct file *file, poll_table *wait)
+ {
+- poll_wait(file, &log_wait, wait);
++ poll_wait(file, &ve_log_wait, wait);
+ if (do_syslog(9, NULL, 0))
+ return POLLIN | POLLRDNORM;
+ return 0;
+@@ -55,3 +57,4 @@ const struct file_operations proc_kmsg_operations = {
+ .open = kmsg_open,
+ .release = kmsg_release,
+ };
++EXPORT_SYMBOL_GPL(proc_kmsg_operations);
+diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
+index 29e20c6..89bfb76 100644
+--- a/fs/proc/proc_misc.c
++++ b/fs/proc/proc_misc.c
+@@ -33,6 +33,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/swap.h>
+ #include <linux/slab.h>
++#include <linux/virtinfo.h>
+ #include <linux/genhd.h>
+ #include <linux/smp.h>
+ #include <linux/signal.h>
+@@ -49,6 +50,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/crash_dump.h>
+ #include <linux/pid_namespace.h>
++#include <linux/vmstat.h>
+ #include <linux/bootmem.h>
+ #include <asm/uaccess.h>
+ #include <asm/pgtable.h>
+@@ -87,19 +89,39 @@ static int loadavg_read_proc(char *page, char **start, off_t off,
+ int a, b, c;
+ int len;
+ unsigned long seq;
++ long running, threads;
++ struct ve_struct *ve;
+
++ ve = get_exec_env();
+ do {
+ seq = read_seqbegin(&xtime_lock);
+- a = avenrun[0] + (FIXED_1/200);
+- b = avenrun[1] + (FIXED_1/200);
+- c = avenrun[2] + (FIXED_1/200);
++ if (ve_is_super(ve)) {
++ a = avenrun[0] + (FIXED_1/200);
++ b = avenrun[1] + (FIXED_1/200);
++ c = avenrun[2] + (FIXED_1/200);
++#ifdef CONFIG_VE
++ } else {
++ a = ve->avenrun[0] + (FIXED_1/200);
++ b = ve->avenrun[1] + (FIXED_1/200);
++ c = ve->avenrun[2] + (FIXED_1/200);
++#endif
++ }
+ } while (read_seqretry(&xtime_lock, seq));
++ if (ve_is_super(ve)) {
++ running = nr_running();
++ threads = nr_threads;
++#ifdef CONFIG_VE
++ } else {
++ running = nr_running_ve(ve);
++ threads = atomic_read(&ve->pcounter);
++#endif
++ }
+
+- len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
++ len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%ld %d\n",
+ LOAD_INT(a), LOAD_FRAC(a),
+ LOAD_INT(b), LOAD_FRAC(b),
+ LOAD_INT(c), LOAD_FRAC(c),
+- nr_running(), nr_threads,
++ running, threads,
+ task_active_pid_ns(current)->last_pid);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+ }
+@@ -114,6 +136,13 @@ static int uptime_read_proc(char *page, char **start, off_t off,
+
+ do_posix_clock_monotonic_gettime(&uptime);
+ monotonic_to_bootbased(&uptime);
++#ifdef CONFIG_VE
++ if (!ve_is_super(get_exec_env())) {
++ set_normalized_timespec(&uptime,
++ uptime.tv_sec - get_exec_env()->start_timespec.tv_sec,
++ uptime.tv_nsec - get_exec_env()->start_timespec.tv_nsec);
++ }
++#endif
+ cputime_to_timespec(idletime, &idle);
+ len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
+ (unsigned long) uptime.tv_sec,
+@@ -132,29 +161,50 @@ int __attribute__((weak)) arch_report_meminfo(char *page)
+ static int meminfo_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+ {
+- struct sysinfo i;
++ struct meminfo mi;
+ int len;
+- unsigned long committed;
+- unsigned long allowed;
++ unsigned long dummy;
+ struct vmalloc_info vmi;
+- long cached;
++
++ get_zone_counts(&mi.active, &mi.inactive, &dummy);
+
+ /*
+ * display in kilobytes.
+ */
+ #define K(x) ((x) << (PAGE_SHIFT - 10))
+- si_meminfo(&i);
+- si_swapinfo(&i);
+- committed = atomic_long_read(&vm_committed_space);
+- allowed = ((totalram_pages - hugetlb_total_pages())
++ si_meminfo(&mi.si);
++ si_swapinfo(&mi.si);
++ mi.committed_space = atomic_read(&vm_committed_space);
++ mi.swapcache = total_swapcache_pages;
++ mi.allowed = ((totalram_pages - hugetlb_total_pages())
+ * sysctl_overcommit_ratio / 100) + total_swap_pages;
+
+- cached = global_page_state(NR_FILE_PAGES) -
+- total_swapcache_pages - i.bufferram;
+- if (cached < 0)
+- cached = 0;
++ mi.cache = global_page_state(NR_FILE_PAGES) -
++ total_swapcache_pages - mi.si.bufferram;
++ if (mi.cache < 0)
++ mi.cache = 0;
+
+ get_vmalloc_info(&vmi);
++ mi.vmalloc_used = vmi.used >> PAGE_SHIFT;
++ mi.vmalloc_largest = vmi.largest_chunk >> PAGE_SHIFT;
++ mi.vmalloc_total = VMALLOC_TOTAL >> PAGE_SHIFT;
++
++ mi.pi.nr_file_dirty = global_page_state(NR_FILE_DIRTY);
++ mi.pi.nr_writeback = global_page_state(NR_WRITEBACK);
++ mi.pi.nr_anon_pages = global_page_state(NR_ANON_PAGES);
++ mi.pi.nr_file_mapped = global_page_state(NR_FILE_MAPPED);
++ mi.pi.nr_slab_rec = global_page_state(NR_SLAB_RECLAIMABLE);
++ mi.pi.nr_slab_unrec = global_page_state(NR_SLAB_UNRECLAIMABLE);
++ mi.pi.nr_pagetable = global_page_state(NR_PAGETABLE);
++ mi.pi.nr_unstable_nfs = global_page_state(NR_UNSTABLE_NFS);
++ mi.pi.nr_bounce = global_page_state(NR_BOUNCE);
++ mi.pi.nr_writeback_temp = global_page_state(NR_WRITEBACK_TEMP);
++
++#ifdef CONFIG_BEANCOUNTERS
++ if (virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_MEMINFO, &mi)
++ & NOTIFY_FAIL)
++ return -ENOMSG;
++#endif
+
+ /*
+ * Tagged format, for easy grepping and expansion.
+@@ -194,41 +244,42 @@ static int meminfo_read_proc(char *page, char **start, off_t off,
+ "VmallocTotal: %8lu kB\n"
+ "VmallocUsed: %8lu kB\n"
+ "VmallocChunk: %8lu kB\n",
+- K(i.totalram),
+- K(i.freeram),
+- K(i.bufferram),
+- K(cached),
+- K(total_swapcache_pages),
+- K(global_page_state(NR_ACTIVE)),
+- K(global_page_state(NR_INACTIVE)),
++ K(mi.si.totalram),
++ K(mi.si.freeram),
++ K(mi.si.bufferram),
++ K(mi.cache),
++ K(mi.swapcache),
++ K(mi.active),
++ K(mi.inactive),
+ #ifdef CONFIG_HIGHMEM
+- K(i.totalhigh),
+- K(i.freehigh),
+- K(i.totalram-i.totalhigh),
+- K(i.freeram-i.freehigh),
+-#endif
+- K(i.totalswap),
+- K(i.freeswap),
+- K(global_page_state(NR_FILE_DIRTY)),
+- K(global_page_state(NR_WRITEBACK)),
+- K(global_page_state(NR_ANON_PAGES)),
+- K(global_page_state(NR_FILE_MAPPED)),
+- K(global_page_state(NR_SLAB_RECLAIMABLE) +
+- global_page_state(NR_SLAB_UNRECLAIMABLE)),
+- K(global_page_state(NR_SLAB_RECLAIMABLE)),
+- K(global_page_state(NR_SLAB_UNRECLAIMABLE)),
+- K(global_page_state(NR_PAGETABLE)),
++ K(mi.si.totalhigh),
++ K(mi.si.freehigh),
++ K(mi.si.totalram-mi.si.totalhigh),
++ K(mi.si.freeram-mi.si.freehigh),
++#endif
++ K(mi.si.totalswap),
++ K(mi.si.freeswap),
++ K(mi.pi.nr_file_dirty),
++ K(mi.pi.nr_writeback),
++ K(mi.pi.nr_anon_pages),
++ K(mi.pi.nr_file_mapped),
++ K(mi.pi.nr_slab_rec +
++ mi.pi.nr_slab_unrec),
++ K(mi.pi.nr_slab_rec),
++ K(mi.pi.nr_slab_unrec),
++ K(mi.pi.nr_pagetable),
+ #ifdef CONFIG_QUICKLIST
+ K(quicklist_total_size()),
+ #endif
+- K(global_page_state(NR_UNSTABLE_NFS)),
+- K(global_page_state(NR_BOUNCE)),
+- K(global_page_state(NR_WRITEBACK_TEMP)),
+- K(allowed),
+- K(committed),
+- (unsigned long)VMALLOC_TOTAL >> 10,
+- vmi.used >> 10,
+- vmi.largest_chunk >> 10
++ K(mi.pi.nr_unstable_nfs),
++ K(mi.pi.nr_bounce),
++ K(mi.pi.nr_writeback_temp),
++ K(mi.allowed),
++ K(mi.committed_space),
++ K(mi.vmalloc_total),
++ K(mi.vmalloc_used),
++ K(mi.vmalloc_largest),
++ K(quicklist_total_size())
+ );
+
+ len += hugetlb_report_meminfo(page + len);
+@@ -500,25 +551,21 @@ static const struct file_operations proc_vmalloc_operations = {
+ #define arch_irq_stat() 0
+ #endif
+
+-static int show_stat(struct seq_file *p, void *v)
++static void show_stat_ve0(struct seq_file *p)
+ {
+ int i;
+- unsigned long jif;
+ cputime64_t user, nice, system, idle, iowait, irq, softirq, steal;
+ cputime64_t guest;
+ u64 sum = 0;
+- struct timespec boottime;
+ unsigned int *per_irq_sum;
+
+ per_irq_sum = kzalloc(sizeof(unsigned int)*NR_IRQS, GFP_KERNEL);
+ if (!per_irq_sum)
+- return -ENOMEM;
++ return;
+
+ user = nice = system = idle = iowait =
+ irq = softirq = steal = cputime64_zero;
+ guest = cputime64_zero;
+- getboottime(&boottime);
+- jif = boottime.tv_sec;
+
+ for_each_possible_cpu(i) {
+ int j;
+@@ -580,9 +627,85 @@ static int show_stat(struct seq_file *p, void *v)
+
+ for (i = 0; i < NR_IRQS; i++)
+ seq_printf(p, " %u", per_irq_sum[i]);
++ kfree(per_irq_sum);
++ seq_printf(p, "\nswap %lu %lu\n",
++ vm_events(PSWPIN), vm_events(PSWPOUT));
++}
++
++#ifdef CONFIG_VE
++static void show_stat_ve(struct seq_file *p, struct ve_struct *ve)
++{
++ int i;
++ u64 user, nice, system;
++ cycles_t idle, iowait;
++ cpumask_t ve_cpus;
++
++ ve_cpu_online_map(ve, &ve_cpus);
++
++ user = nice = system = idle = iowait = 0;
++ for_each_cpu_mask(i, ve_cpus) {
++ user += VE_CPU_STATS(ve, i)->user;
++ nice += VE_CPU_STATS(ve, i)->nice;
++ system += VE_CPU_STATS(ve, i)->system;
++ idle += ve_sched_get_idle_time(ve, i);
++ iowait += ve_sched_get_iowait_time(ve, i);
++ }
++
++ seq_printf(p, "cpu %llu %llu %llu %llu %llu 0 0 0\n",
++ (unsigned long long)cputime64_to_clock_t(user),
++ (unsigned long long)cputime64_to_clock_t(nice),
++ (unsigned long long)cputime64_to_clock_t(system),
++ (unsigned long long)cycles_to_clocks(idle),
++ (unsigned long long)cycles_to_clocks(iowait));
++
++ for_each_cpu_mask(i, ve_cpus) {
++ user = VE_CPU_STATS(ve, i)->user;
++ nice = VE_CPU_STATS(ve, i)->nice;
++ system = VE_CPU_STATS(ve, i)->system;
++ idle = ve_sched_get_idle_time(ve, i);
++ iowait = ve_sched_get_iowait_time(ve, i);
++ seq_printf(p, "cpu%d %llu %llu %llu %llu %llu 0 0 0\n",
++ i,
++ (unsigned long long)cputime64_to_clock_t(user),
++ (unsigned long long)cputime64_to_clock_t(nice),
++ (unsigned long long)cputime64_to_clock_t(system),
++ (unsigned long long)cycles_to_clocks(idle),
++ (unsigned long long)cycles_to_clocks(iowait));
++ }
++ seq_printf(p, "intr 0\nswap 0 0\n");
++}
++#endif
++
++int show_stat(struct seq_file *p, void *v)
++{
++ extern unsigned long total_forks;
++ unsigned long seq, jif;
++ struct ve_struct *env;
++ unsigned long __nr_running, __nr_iowait;
++
++ do {
++ seq = read_seqbegin(&xtime_lock);
++ jif = - wall_to_monotonic.tv_sec;
++ if (wall_to_monotonic.tv_nsec)
++ --jif;
++ } while (read_seqretry(&xtime_lock, seq));
++
++ env = get_exec_env();
++ if (ve_is_super(env)) {
++ show_stat_ve0(p);
++ __nr_running = nr_running();
++ __nr_iowait = nr_iowait();
++ }
++#ifdef CONFIG_VE
++ else {
++ show_stat_ve(p, env);
++ __nr_running = nr_running_ve(env);
++ __nr_iowait = nr_iowait_ve(env);
++ }
++#endif
+
+ seq_printf(p,
+- "\nctxt %llu\n"
++ "ctxt %llu\n"
+ "btime %lu\n"
+ "processes %lu\n"
+ "procs_running %lu\n"
+@@ -590,10 +713,9 @@ static int show_stat(struct seq_file *p, void *v)
+ nr_context_switches(),
+ (unsigned long)jif,
+ total_forks,
+- nr_running(),
+- nr_iowait());
++ __nr_running,
++ __nr_iowait);
+
+- kfree(per_irq_sum);
+ return 0;
+ }
+
+@@ -680,7 +802,8 @@ static int cmdline_read_proc(char *page, char **start, off_t off,
+ {
+ int len;
+
+- len = sprintf(page, "%s\n", saved_command_line);
++ len = sprintf(page, "%s\n",
++ ve_is_super(get_exec_env()) ? saved_command_line : "quiet");
+ return proc_calc_metrics(page, start, off, count, eof, len);
+ }
+
+@@ -711,11 +834,16 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
+ if (count) {
+- char c;
++ int i, cnt;
++ char c[32];
+
+- if (get_user(c, buf))
++ cnt = min(count, sizeof(c));
++ if (copy_from_user(c, buf, cnt))
+ return -EFAULT;
+- __handle_sysrq(c, NULL, 0);
++
++
++ for (i = 0; i < cnt && c[i] != '\n'; i++)
++ __handle_sysrq(c[i], NULL, 0);
+ }
+ return count;
+ }
+@@ -863,38 +991,39 @@ void __init proc_misc_init(void)
+ static struct {
+ char *name;
+ int (*read_proc)(char*,char**,off_t,int,int*,void*);
++ struct proc_dir_entry *parent;
+ } *p, simple_ones[] = {
+- {"loadavg", loadavg_read_proc},
+- {"uptime", uptime_read_proc},
+- {"meminfo", meminfo_read_proc},
+- {"version", version_read_proc},
++ {"loadavg", loadavg_read_proc, &glob_proc_root},
++ {"uptime", uptime_read_proc, &glob_proc_root},
++ {"meminfo", meminfo_read_proc, &glob_proc_root},
++ {"version", version_read_proc, &glob_proc_root},
+ #ifdef CONFIG_PROC_HARDWARE
+ {"hardware", hardware_read_proc},
+ #endif
+ #ifdef CONFIG_STRAM_PROC
+ {"stram", stram_read_proc},
+ #endif
+- {"filesystems", filesystems_read_proc},
+- {"cmdline", cmdline_read_proc},
++ {"filesystems", filesystems_read_proc, &glob_proc_root},
++ {"cmdline", cmdline_read_proc, &glob_proc_root},
+ {"execdomains", execdomains_read_proc},
+ {NULL,}
+ };
+ for (p = simple_ones; p->name; p++)
+- create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
++ create_proc_read_entry(p->name, 0, p->parent, p->read_proc, NULL);
+
+- proc_symlink("mounts", NULL, "self/mounts");
++ proc_symlink("mounts", &glob_proc_root, "self/mounts");
+
+ /* And now for trickier ones */
+ #ifdef CONFIG_PRINTK
+ proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
+ #endif
+- proc_create("locks", 0, NULL, &proc_locks_operations);
++ proc_create("locks", 0, &glob_proc_root, &proc_locks_operations);
+ proc_create("devices", 0, NULL, &proc_devinfo_operations);
+- proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
++ proc_create("cpuinfo", 0, &glob_proc_root, &proc_cpuinfo_operations);
+ #ifdef CONFIG_BLOCK
+ proc_create("partitions", 0, NULL, &proc_partitions_operations);
+ #endif
+- proc_create("stat", 0, NULL, &proc_stat_operations);
++ proc_create("stat", 0, &glob_proc_root, &proc_stat_operations);
+ proc_create("interrupts", 0, NULL, &proc_interrupts_operations);
+ #ifdef CONFIG_SLABINFO
+ proc_create("slabinfo",S_IWUSR|S_IRUGO,NULL,&proc_slabinfo_operations);
+@@ -907,13 +1036,13 @@ void __init proc_misc_init(void)
+ #endif
+ proc_create("buddyinfo", S_IRUGO, NULL, &fragmentation_file_operations);
+ proc_create("pagetypeinfo", S_IRUGO, NULL, &pagetypeinfo_file_ops);
+- proc_create("vmstat", S_IRUGO, NULL, &proc_vmstat_file_operations);
++ proc_create("vmstat", S_IRUGO, &glob_proc_root, &proc_vmstat_file_operations);
+ proc_create("zoneinfo", S_IRUGO, NULL, &proc_zoneinfo_file_operations);
+ #ifdef CONFIG_BLOCK
+ proc_create("diskstats", 0, NULL, &proc_diskstats_operations);
+ #endif
+ #ifdef CONFIG_MODULES
+- proc_create("modules", 0, NULL, &proc_modules_operations);
++ proc_create("modules", 0, &glob_proc_root, &proc_modules_operations);
+ #endif
+ #ifdef CONFIG_SCHEDSTATS
+ proc_create("schedstat", 0, NULL, &proc_schedstat_operations);
+diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
+index 7bc296f..8cc47f6 100644
+--- a/fs/proc/proc_net.c
++++ b/fs/proc/proc_net.c
+@@ -127,7 +127,7 @@ static struct dentry *proc_tgid_net_lookup(struct inode *dir,
+ de = ERR_PTR(-ENOENT);
+ net = get_proc_task_net(dir);
+ if (net != NULL) {
+- de = proc_lookup_de(net->proc_net, dir, dentry);
++ de = proc_lookup_de(net->proc_net, NULL, dir, dentry);
+ put_net(net);
+ }
+ return de;
+@@ -165,7 +165,8 @@ static int proc_tgid_net_readdir(struct file *filp, void *dirent,
+ ret = -EINVAL;
+ net = get_proc_task_net(filp->f_path.dentry->d_inode);
+ if (net != NULL) {
+- ret = proc_readdir_de(net->proc_net, filp, dirent, filldir);
++ ret = proc_readdir_de(net->proc_net, NULL,
++ filp, dirent, filldir);
+ put_net(net);
+ }
+ return ret;
+@@ -234,7 +235,7 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = {
+
+ int __init proc_net_init(void)
+ {
+- proc_symlink("net", NULL, "self/net");
++ proc_symlink("net", &glob_proc_root, "self/net");
+
+ return register_pernet_subsys(&proc_net_ns_ops);
+ }
+diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
+index f9a8b89..a44cbaa 100644
+--- a/fs/proc/proc_sysctl.c
++++ b/fs/proc/proc_sysctl.c
+@@ -399,7 +399,7 @@ static struct proc_dir_entry *proc_sys_root;
+
+ int proc_sys_init(void)
+ {
+- proc_sys_root = proc_mkdir("sys", NULL);
++ proc_sys_root = proc_mkdir("sys", &glob_proc_root);
+ proc_sys_root->proc_iops = &proc_sys_dir_operations;
+ proc_sys_root->proc_fops = &proc_sys_dir_file_operations;
+ proc_sys_root->nlink = 0;
+diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c
+index d153946..d139eed 100644
+--- a/fs/proc/proc_tty.c
++++ b/fs/proc/proc_tty.c
+@@ -13,6 +13,7 @@
+ #include <linux/stat.h>
+ #include <linux/tty.h>
+ #include <linux/seq_file.h>
++#include <linux/sched.h>
+ #include <linux/bitops.h>
+
+ /*
+@@ -70,6 +71,9 @@ static int show_tty_driver(struct seq_file *m, void *v)
+ dev_t from = MKDEV(p->major, p->minor_start);
+ dev_t to = from + p->num;
+
++ if (!ve_accessible_strict(p->owner_env, get_exec_env()))
++ goto out;
++
+ if (&p->tty_drivers == tty_drivers.next) {
+ /* pseudo-drivers first */
+ seq_printf(m, "%-20s /dev/%-8s ", "/dev/tty", "tty");
+@@ -97,6 +101,7 @@ static int show_tty_driver(struct seq_file *m, void *v)
+ }
+ if (from != to)
+ show_tty_range(m, p, from, to - from);
++out:
+ return 0;
+ }
+
+diff --git a/fs/proc/root.c b/fs/proc/root.c
+index 9511753..bc93788 100644
+--- a/fs/proc/root.c
++++ b/fs/proc/root.c
+@@ -43,6 +43,9 @@ static int proc_get_sb(struct file_system_type *fs_type,
+ struct super_block *sb;
+ struct pid_namespace *ns;
+ struct proc_inode *ei;
++#ifdef CONFIG_VE
++ struct vfsmount *proc_mnt = fs_type->owner_env->proc_mnt;
++#endif
+
+ if (proc_mnt) {
+ /* Seed the root directory with a pid so it doesn't need
+@@ -96,11 +99,12 @@ static void proc_kill_sb(struct super_block *sb)
+ put_pid_ns(ns);
+ }
+
+-static struct file_system_type proc_fs_type = {
++struct file_system_type proc_fs_type = {
+ .name = "proc",
+ .get_sb = proc_get_sb,
+ .kill_sb = proc_kill_sb,
+ };
++EXPORT_SYMBOL(proc_fs_type);
+
+ void __init proc_root_init(void)
+ {
+@@ -110,6 +114,11 @@ void __init proc_root_init(void)
+ err = register_filesystem(&proc_fs_type);
+ if (err)
+ return;
++
++#ifdef CONFIG_VE
++ get_ve0()->proc_root = &proc_root;
++#endif
++
+ proc_mnt = kern_mount_data(&proc_fs_type, &init_pid_ns);
+ err = PTR_ERR(proc_mnt);
+ if (IS_ERR(proc_mnt)) {
+@@ -117,16 +126,22 @@ void __init proc_root_init(void)
+ return;
+ }
+
++#ifdef CONFIG_VE
++ get_ve0()->proc_mnt = proc_mnt;
++#endif
++
+ proc_misc_init();
+
+ proc_net_init();
+
+ #ifdef CONFIG_SYSVIPC
+- proc_mkdir("sysvipc", NULL);
++ proc_mkdir("sysvipc", &glob_proc_root);
+ #endif
+- proc_mkdir("fs", NULL);
++ proc_mkdir("fs", &glob_proc_root);
++ proc_mkdir("fs", NULL); /* care about proc_mkdir("fs/xxx", NULL); */
++
+ proc_mkdir("driver", NULL);
+- proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
++ proc_mkdir("fs/nfsd", &glob_proc_root); /* somewhere for the nfsd filesystem to be mounted */
+ #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
+ /* just give it a mountpoint */
+ proc_mkdir("openprom", NULL);
+@@ -211,6 +226,22 @@ struct proc_dir_entry proc_root = {
+ .parent = &proc_root,
+ };
+
++#ifdef CONFIG_VE
++struct proc_dir_entry glob_proc_root = {
++ .low_ino = PROC_ROOT_INO,
++ .namelen = 5,
++ .name = "/proc",
++ .mode = S_IFDIR | S_IRUGO | S_IXUGO,
++ .nlink = 2,
++ .count = ATOMIC_INIT(1),
++ .proc_iops = &proc_root_inode_operations,
++ .proc_fops = &proc_root_operations,
++ .parent = &glob_proc_root,
++};
++
++EXPORT_SYMBOL(glob_proc_root);
++#endif
++
+ int pid_ns_prepare_proc(struct pid_namespace *ns)
+ {
+ struct vfsmount *mnt;
+diff --git a/fs/quota.c b/fs/quota.c
+index 7f4386e..374b682 100644
+--- a/fs/quota.c
++++ b/fs/quota.c
+@@ -18,6 +18,7 @@
+ #include <linux/capability.h>
+ #include <linux/quotaops.h>
+ #include <linux/types.h>
++#include <linux/device_cgroup.h>
+
+ /* Check validity of generic quotactl commands */
+ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+@@ -81,11 +82,11 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid
+ if (cmd == Q_GETQUOTA) {
+ if (((type == USRQUOTA && current->euid != id) ||
+ (type == GRPQUOTA && !in_egroup_p(id))) &&
+- !capable(CAP_SYS_ADMIN))
++ !capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ }
+ else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+
+ return 0;
+@@ -132,10 +133,10 @@ static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t i
+ if (cmd == Q_XGETQUOTA) {
+ if (((type == XQM_USRQUOTA && current->euid != id) ||
+ (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
+- !capable(CAP_SYS_ADMIN))
++ !capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) {
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ }
+
+@@ -177,6 +178,8 @@ static void quota_sync_sb(struct super_block *sb, int type)
+ continue;
+ if (!sb_has_quota_enabled(sb, cnt))
+ continue;
++ if (!sb_dqopt(sb)->files[cnt])
++ continue;
+ mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, I_MUTEX_QUOTA);
+ truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
+ mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex);
+@@ -213,7 +216,7 @@ restart:
+ sb->s_count++;
+ spin_unlock(&sb_lock);
+ down_read(&sb->s_umount);
+- if (sb->s_root && sb->s_qcop->quota_sync)
++ if (sb->s_root && sb->s_qcop && sb->s_qcop->quota_sync)
+ quota_sync_sb(sb, type);
+ up_read(&sb->s_umount);
+ spin_lock(&sb_lock);
+@@ -344,6 +347,7 @@ static inline struct super_block *quotactl_block(const char __user *special)
+ struct block_device *bdev;
+ struct super_block *sb;
+ char *tmp = getname(special);
++ int error;
+
+ if (IS_ERR(tmp))
+ return ERR_CAST(tmp);
+@@ -351,6 +355,13 @@ static inline struct super_block *quotactl_block(const char __user *special)
+ putname(tmp);
+ if (IS_ERR(bdev))
+ return ERR_CAST(bdev);
++
++ error = devcgroup_inode_permission(bdev->bd_inode, MAY_QUOTACTL);
++ if (error) {
++ bdput(bdev);
++ return ERR_PTR(error);
++ }
++
+ sb = get_super(bdev);
+ bdput(bdev);
+ if (!sb)
+@@ -362,6 +373,215 @@ static inline struct super_block *quotactl_block(const char __user *special)
+ #endif
+ }
+
++#ifdef CONFIG_QUOTA_COMPAT
++
++#define QC_QUOTAON 0x0100 /* enable quotas */
++#define QC_QUOTAOFF 0x0200 /* disable quotas */
++/* GETQUOTA, SETQUOTA and SETUSE which were at 0x0300-0x0500 has now other parameteres */
++#define QC_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
++#define QC_SETQLIM 0x0700 /* set limits */
++/* GETSTATS at 0x0800 is now longer... */
++#define QC_GETINFO 0x0900 /* get info about quotas - graces, flags... */
++#define QC_SETINFO 0x0A00 /* set info about quotas */
++#define QC_SETGRACE 0x0B00 /* set inode and block grace */
++#define QC_SETFLAGS 0x0C00 /* set flags for quota */
++#define QC_GETQUOTA 0x0D00 /* get limits and usage */
++#define QC_SETQUOTA 0x0E00 /* set limits and usage */
++#define QC_SETUSE 0x0F00 /* set usage */
++/* 0x1000 used by old RSQUASH */
++#define QC_GETSTATS 0x1100 /* get collected stats */
++
++struct compat_dqblk {
++ unsigned int dqb_ihardlimit;
++ unsigned int dqb_isoftlimit;
++ unsigned int dqb_curinodes;
++ unsigned int dqb_bhardlimit;
++ unsigned int dqb_bsoftlimit;
++ qsize_t dqb_curspace;
++ __kernel_time_t dqb_btime;
++ __kernel_time_t dqb_itime;
++};
++
++struct compat_dqinfo {
++ unsigned int dqi_bgrace;
++ unsigned int dqi_igrace;
++ unsigned int dqi_flags;
++ unsigned int dqi_blocks;
++ unsigned int dqi_free_blk;
++ unsigned int dqi_free_entry;
++};
++
++struct compat_dqstats {
++ __u32 lookups;
++ __u32 drops;
++ __u32 reads;
++ __u32 writes;
++ __u32 cache_hits;
++ __u32 allocated_dquots;
++ __u32 free_dquots;
++ __u32 syncs;
++ __u32 version;
++};
++
++asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t id, void __user *addr);
++static long compat_quotactl(unsigned int cmds, unsigned int type,
++ const char __user *special, qid_t id,
++ void __user *addr)
++{
++ struct super_block *sb;
++ long ret;
++
++ sb = NULL;
++ switch (cmds) {
++ case QC_QUOTAON:
++ return sys_quotactl(QCMD(Q_QUOTAON, type),
++ special, id, addr);
++
++ case QC_QUOTAOFF:
++ return sys_quotactl(QCMD(Q_QUOTAOFF, type),
++ special, id, addr);
++
++ case QC_SYNC:
++ return sys_quotactl(QCMD(Q_SYNC, type),
++ special, id, addr);
++
++ case QC_GETQUOTA: {
++ struct if_dqblk idq;
++ struct compat_dqblk cdq;
++
++ sb = quotactl_block(special);
++ ret = PTR_ERR(sb);
++ if (IS_ERR(sb))
++ break;
++ ret = check_quotactl_valid(sb, type, Q_GETQUOTA, id);
++ if (ret)
++ break;
++ ret = sb->s_qcop->get_dqblk(sb, type, id, &idq);
++ if (ret)
++ break;
++ cdq.dqb_ihardlimit = idq.dqb_ihardlimit;
++ cdq.dqb_isoftlimit = idq.dqb_isoftlimit;
++ cdq.dqb_curinodes = idq.dqb_curinodes;
++ cdq.dqb_bhardlimit = idq.dqb_bhardlimit;
++ cdq.dqb_bsoftlimit = idq.dqb_bsoftlimit;
++ cdq.dqb_curspace = idq.dqb_curspace;
++ cdq.dqb_btime = idq.dqb_btime;
++ cdq.dqb_itime = idq.dqb_itime;
++ ret = 0;
++ if (copy_to_user(addr, &cdq, sizeof(cdq)))
++ ret = -EFAULT;
++ break;
++ }
++
++ case QC_SETQUOTA:
++ case QC_SETUSE:
++ case QC_SETQLIM: {
++ struct if_dqblk idq;
++ struct compat_dqblk cdq;
++
++ sb = quotactl_block(special);
++ ret = PTR_ERR(sb);
++ if (IS_ERR(sb))
++ break;
++ ret = check_quotactl_valid(sb, type, Q_SETQUOTA, id);
++ if (ret)
++ break;
++ ret = -EFAULT;
++ if (copy_from_user(&cdq, addr, sizeof(cdq)))
++ break;
++ idq.dqb_ihardlimit = cdq.dqb_ihardlimit;
++ idq.dqb_isoftlimit = cdq.dqb_isoftlimit;
++ idq.dqb_curinodes = cdq.dqb_curinodes;
++ idq.dqb_bhardlimit = cdq.dqb_bhardlimit;
++ idq.dqb_bsoftlimit = cdq.dqb_bsoftlimit;
++ idq.dqb_curspace = cdq.dqb_curspace;
++ idq.dqb_valid = 0;
++ if (cmds == QC_SETQUOTA || cmds == QC_SETQLIM)
++ idq.dqb_valid |= QIF_LIMITS;
++ if (cmds == QC_SETQUOTA || cmds == QC_SETUSE)
++ idq.dqb_valid |= QIF_USAGE;
++ ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
++ break;
++ }
++
++ case QC_GETINFO: {
++ struct if_dqinfo iinf;
++ struct compat_dqinfo cinf;
++
++ sb = quotactl_block(special);
++ ret = PTR_ERR(sb);
++ if (IS_ERR(sb))
++ break;
++ ret = check_quotactl_valid(sb, type, Q_GETQUOTA, id);
++ if (ret)
++ break;
++ ret = sb->s_qcop->get_info(sb, type, &iinf);
++ if (ret)
++ break;
++ cinf.dqi_bgrace = iinf.dqi_bgrace;
++ cinf.dqi_igrace = iinf.dqi_igrace;
++ cinf.dqi_flags = 0;
++ if (iinf.dqi_flags & DQF_INFO_DIRTY)
++ cinf.dqi_flags |= 0x0010;
++ cinf.dqi_blocks = 0;
++ cinf.dqi_free_blk = 0;
++ cinf.dqi_free_entry = 0;
++ ret = 0;
++ if (copy_to_user(addr, &cinf, sizeof(cinf)))
++ ret = -EFAULT;
++ break;
++ }
++
++ case QC_SETINFO:
++ case QC_SETGRACE:
++ case QC_SETFLAGS: {
++ struct if_dqinfo iinf;
++ struct compat_dqinfo cinf;
++
++ sb = quotactl_block(special);
++ ret = PTR_ERR(sb);
++ if (IS_ERR(sb))
++ break;
++ ret = check_quotactl_valid(sb, type, Q_SETINFO, id);
++ if (ret)
++ break;
++ ret = -EFAULT;
++ if (copy_from_user(&cinf, addr, sizeof(cinf)))
++ break;
++ iinf.dqi_bgrace = cinf.dqi_bgrace;
++ iinf.dqi_igrace = cinf.dqi_igrace;
++ iinf.dqi_flags = cinf.dqi_flags;
++ iinf.dqi_valid = 0;
++ if (cmds == QC_SETINFO || cmds == QC_SETGRACE)
++ iinf.dqi_valid |= IIF_BGRACE | IIF_IGRACE;
++ if (cmds == QC_SETINFO || cmds == QC_SETFLAGS)
++ iinf.dqi_valid |= IIF_FLAGS;
++ ret = sb->s_qcop->set_info(sb, type, &iinf);
++ break;
++ }
++
++ case QC_GETSTATS: {
++ struct compat_dqstats stat;
++
++ memset(&stat, 0, sizeof(stat));
++ stat.version = 6*10000+5*100+0;
++ ret = 0;
++ if (copy_to_user(addr, &stat, sizeof(stat)))
++ ret = -EFAULT;
++ break;
++ }
++
++ default:
++ ret = -ENOSYS;
++ break;
++ }
++ if (sb && !IS_ERR(sb))
++ drop_super(sb);
++ return ret;
++}
++
++#endif
++
+ /*
+ * This is the system call interface. This communicates with
+ * the user-level programs. Currently this only supports diskquota
+@@ -377,6 +597,11 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t
+ cmds = cmd >> SUBCMDSHIFT;
+ type = cmd & SUBCMDMASK;
+
++#ifdef CONFIG_QUOTA_COMPAT
++ if (cmds >= 0x0100 && cmds < 0x3000)
++ return compat_quotactl(cmds, type, special, id, addr);
++#endif
++
+ if (cmds != Q_SYNC || special) {
+ sb = quotactl_block(special);
+ if (IS_ERR(sb))
+diff --git a/fs/read_write.c b/fs/read_write.c
+index 9ba495d..be9f68e 100644
+--- a/fs/read_write.c
++++ b/fs/read_write.c
+@@ -21,6 +21,8 @@
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+
++#include <bc/beancounter.h>
++
+ const struct file_operations generic_ro_fops = {
+ .llseek = generic_file_llseek,
+ .read = do_sync_read,
+@@ -334,6 +336,29 @@ static inline void file_pos_write(struct file *file, loff_t pos)
+ file->f_pos = pos;
+ }
+
++static inline void bc_acct_write(size_t bytes)
++{
++ struct user_beancounter *ub;
++
++ if (bytes > 0) {
++ ub = get_exec_ub();
++ ub_percpu_inc(ub, write);
++ ub_percpu_add(ub, wchar, bytes);
++ }
++}
++
++static inline void bc_acct_read(size_t bytes)
++{
++ struct user_beancounter *ub;
++
++ if (bytes > 0) {
++ ub = get_exec_ub();
++ ub_percpu_inc(ub, read);
++ ub_percpu_add(ub, rchar, bytes);
++ }
++}
++
++
+ asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
+ {
+ struct file *file;
+@@ -346,6 +371,8 @@ asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
+ ret = vfs_read(file, buf, count, &pos);
+ file_pos_write(file, pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_read(ret);
+ }
+
+ return ret;
+@@ -363,6 +390,8 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t co
+ ret = vfs_write(file, buf, count, &pos);
+ file_pos_write(file, pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_write(ret);
+ }
+
+ return ret;
+@@ -384,6 +413,8 @@ asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf,
+ if (file->f_mode & FMODE_PREAD)
+ ret = vfs_read(file, buf, count, &pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_read(ret);
+ }
+
+ return ret;
+@@ -405,6 +436,8 @@ asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf,
+ if (file->f_mode & FMODE_PWRITE)
+ ret = vfs_write(file, buf, count, &pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_write(ret);
+ }
+
+ return ret;
+@@ -650,6 +683,8 @@ sys_readv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen)
+ ret = vfs_readv(file, vec, vlen, &pos);
+ file_pos_write(file, pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_read(ret);
+ }
+
+ if (ret > 0)
+@@ -671,6 +706,8 @@ sys_writev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen)
+ ret = vfs_writev(file, vec, vlen, &pos);
+ file_pos_write(file, pos);
+ fput_light(file, fput_needed);
++
++ bc_acct_write(ret);
+ }
+
+ if (ret > 0)
+diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
+index c1add28..3ca5049 100644
+--- a/fs/reiserfs/namei.c
++++ b/fs/reiserfs/namei.c
+@@ -859,6 +859,9 @@ static int reiserfs_rmdir(struct inode *dir, struct dentry *dentry)
+ INITIALIZE_PATH(path);
+ struct reiserfs_dir_entry de;
+
++ inode = dentry->d_inode;
++ DQUOT_INIT(inode);
++
+ /* we will be doing 2 balancings and update 2 stat data, we change quotas
+ * of the owner of the directory and of the owner of the parent directory.
+ * The quota structure is possibly deleted only on last iput => outside
+@@ -883,8 +886,6 @@ static int reiserfs_rmdir(struct inode *dir, struct dentry *dentry)
+ goto end_rmdir;
+ }
+
+- inode = dentry->d_inode;
+-
+ reiserfs_update_inode_transaction(inode);
+ reiserfs_update_inode_transaction(dir);
+
+@@ -947,6 +948,7 @@ static int reiserfs_unlink(struct inode *dir, struct dentry *dentry)
+ unsigned long savelink;
+
+ inode = dentry->d_inode;
++ DQUOT_INIT(inode);
+
+ /* in this transaction we can be doing at max two balancings and update
+ * two stat datas, we change quotas of the owner of the directory and of
+@@ -1254,6 +1256,8 @@ static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+
+ old_inode = old_dentry->d_inode;
+ new_dentry_inode = new_dentry->d_inode;
++ if (new_dentry_inode)
++ DQUOT_INIT(new_dentry_inode);
+
+ // make sure, that oldname still exists and points to an object we
+ // are going to rename
+diff --git a/fs/select.c b/fs/select.c
+index da0e882..e0eb1cd 100644
+--- a/fs/select.c
++++ b/fs/select.c
+@@ -27,6 +27,8 @@
+
+ #include <asm/uaccess.h>
+
++#include <bc/kmem.h>
++
+ struct poll_table_page {
+ struct poll_table_page * next;
+ struct poll_table_entry * entry;
+@@ -332,7 +334,8 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
+ if (size > sizeof(stack_fds) / 6) {
+ /* Not enough space in on-stack array; must use kmalloc */
+ ret = -ENOMEM;
+- bits = kmalloc(6 * size, GFP_KERNEL);
++ bits = kmalloc(6 * size, size > PAGE_SIZE / 6 ?
++ GFP_KERNEL_UBC : GFP_KERNEL);
+ if (!bits)
+ goto out_nofds;
+ }
+@@ -678,7 +681,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
+
+ len = min(todo, POLLFD_PER_PAGE);
+ size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;
+- walk = walk->next = kmalloc(size, GFP_KERNEL);
++ walk = walk->next = kmalloc(size, GFP_KERNEL_UBC);
+ if (!walk) {
+ err = -ENOMEM;
+ goto out_fds;
+@@ -710,7 +713,7 @@ out_fds:
+ return err;
+ }
+
+-static long do_restart_poll(struct restart_block *restart_block)
++long do_restart_poll(struct restart_block *restart_block)
+ {
+ struct pollfd __user *ufds = (struct pollfd __user*)restart_block->arg0;
+ int nfds = restart_block->arg1;
+@@ -726,6 +729,7 @@ static long do_restart_poll(struct restart_block *restart_block)
+ }
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(do_restart_poll);
+
+ asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,
+ long timeout_msecs)
+diff --git a/fs/seq_file.c b/fs/seq_file.c
+index bd20f7f..007b419 100644
+--- a/fs/seq_file.c
++++ b/fs/seq_file.c
+@@ -32,7 +32,7 @@ int seq_open(struct file *file, const struct seq_operations *op)
+ struct seq_file *p = file->private_data;
+
+ if (!p) {
+- p = kmalloc(sizeof(*p), GFP_KERNEL);
++ p = kmalloc(sizeof(*p), GFP_KERNEL_UBC);
+ if (!p)
+ return -ENOMEM;
+ file->private_data = p;
+@@ -87,7 +87,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
+ m->version = file->f_version;
+ /* grab buffer if we didn't have one */
+ if (!m->buf) {
+- m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
++ m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL_UBC);
+ if (!m->buf)
+ goto Enomem;
+ }
+@@ -128,7 +128,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
+ goto Fill;
+ m->op->stop(m, p);
+ kfree(m->buf);
+- m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
++ m->buf = kmalloc(m->size <<= 1, GFP_KERNEL_UBC);
+ if (!m->buf)
+ goto Enomem;
+ m->count = 0;
+@@ -200,7 +200,7 @@ static int traverse(struct seq_file *m, loff_t offset)
+ return 0;
+ }
+ if (!m->buf) {
+- m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
++ m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL_UBC);
+ if (!m->buf)
+ return -ENOMEM;
+ }
+@@ -239,7 +239,7 @@ static int traverse(struct seq_file *m, loff_t offset)
+ Eoverflow:
+ m->op->stop(m, p);
+ kfree(m->buf);
+- m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
++ m->buf = kmalloc(m->size <<= 1, GFP_KERNEL_UBC);
+ return !m->buf ? -ENOMEM : -EAGAIN;
+ }
+
+@@ -385,6 +385,8 @@ int seq_path(struct seq_file *m, struct path *path, char *esc)
+ if (m->count < m->size) {
+ char *s = m->buf + m->count;
+ char *p = d_path(path, s, m->size - m->count);
++ if (IS_ERR(p) && PTR_ERR(p) != -ENAMETOOLONG)
++ return 0;
+ if (!IS_ERR(p)) {
+ s = mangle_path(s, p, esc);
+ if (s) {
+@@ -482,7 +484,7 @@ static void single_stop(struct seq_file *p, void *v)
+ int single_open(struct file *file, int (*show)(struct seq_file *, void *),
+ void *data)
+ {
+- struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
++ struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_UBC);
+ int res = -ENOMEM;
+
+ if (op) {
+@@ -526,7 +528,7 @@ void *__seq_open_private(struct file *f, const struct seq_operations *ops,
+ void *private;
+ struct seq_file *seq;
+
+- private = kzalloc(psize, GFP_KERNEL);
++ private = kzalloc(psize, GFP_KERNEL_UBC);
+ if (private == NULL)
+ goto out;
+
+diff --git a/fs/simfs.c b/fs/simfs.c
+new file mode 100644
+index 0000000..366a3ed
+--- /dev/null
++++ b/fs/simfs.c
+@@ -0,0 +1,332 @@
++/*
++ * fs/simfs.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/fs.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <linux/namei.h>
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/mount.h>
++#include <linux/vzquota.h>
++#include <linux/statfs.h>
++#include <linux/virtinfo.h>
++#include <linux/faudit.h>
++#include <linux/genhd.h>
++#include <linux/reiserfs_fs.h>
++
++#include <asm/unistd.h>
++#include <asm/uaccess.h>
++
++#define SIMFS_GET_LOWER_FS_SB(sb) sb->s_root->d_sb
++
++static struct super_operations sim_super_ops;
++
++static int sim_getattr(struct vfsmount *mnt, struct dentry *dentry,
++ struct kstat *stat)
++{
++ struct super_block *sb;
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ if (!inode->i_op->getattr) {
++ generic_fillattr(inode, stat);
++ if (!stat->blksize) {
++ unsigned blocks;
++
++ sb = inode->i_sb;
++ blocks = (stat->size + sb->s_blocksize-1) >>
++ sb->s_blocksize_bits;
++ stat->blocks = (sb->s_blocksize / 512) * blocks;
++ stat->blksize = sb->s_blocksize;
++ }
++ } else {
++ int err;
++
++ err = inode->i_op->getattr(mnt, dentry, stat);
++ if (err)
++ return err;
++ }
++
++ sb = mnt->mnt_sb;
++ if (sb->s_op == &sim_super_ops)
++ stat->dev = sb->s_dev;
++ return 0;
++}
++
++static void quota_get_stat(struct super_block *sb, struct kstatfs *buf)
++{
++ int err;
++ struct dq_stat qstat;
++ struct virt_info_quota q;
++ long free_file, adj_file;
++ s64 blk, free_blk, adj_blk;
++ int bsize_bits;
++
++ q.super = sb;
++ q.qstat = &qstat;
++ err = virtinfo_notifier_call(VITYPE_QUOTA, VIRTINFO_QUOTA_GETSTAT, &q);
++ if (err != NOTIFY_OK)
++ return;
++
++ bsize_bits = ffs(buf->f_bsize) - 1;
++
++ if (qstat.bsoftlimit > qstat.bcurrent)
++ free_blk = (qstat.bsoftlimit - qstat.bcurrent) >> bsize_bits;
++ else
++ free_blk = 0;
++ /*
++ * In the regular case, we always set buf->f_bfree and buf->f_blocks to
++ * the values reported by quota. In case of real disk space shortage,
++ * we adjust the values. We want this adjustment to look as if the
++ * total disk space were reduced, not as if the usage were increased.
++ * -- SAW
++ */
++ adj_blk = 0;
++ if (buf->f_bfree < free_blk)
++ adj_blk = free_blk - buf->f_bfree;
++ buf->f_bfree = free_blk - adj_blk;
++
++ if (free_blk < buf->f_bavail)
++ buf->f_bavail = free_blk;
++
++ blk = (qstat.bsoftlimit >> bsize_bits) - adj_blk;
++ buf->f_blocks = blk > LONG_MAX ? LONG_MAX : blk;
++
++ free_file = qstat.isoftlimit - qstat.icurrent;
++ if (free_file < 0)
++ free_file = 0;
++ if (buf->f_type == REISERFS_SUPER_MAGIC)
++ /*
++ * reiserfs doesn't initialize f_ffree and f_files values of
++ * kstatfs because it doesn't have an inode limit.
++ */
++ buf->f_ffree = free_file;
++ adj_file = 0;
++ if (buf->f_ffree < free_file)
++ adj_file = free_file - buf->f_ffree;
++ buf->f_ffree = free_file - adj_file;
++ buf->f_files = qstat.isoftlimit - adj_file;
++}
++
++static int sim_statfs(struct super_block *sb, struct kstatfs *buf)
++{
++ int err;
++ struct super_block *lsb;
++ struct kstatfs statbuf;
++
++ err = 0;
++ if (sb->s_op != &sim_super_ops)
++ return 0;
++
++ memset(&statbuf, 0, sizeof(statbuf));
++ lsb = SIMFS_GET_LOWER_FS_SB(sb);
++
++ err = -ENOSYS;
++ if (lsb && lsb->s_op && lsb->s_op->statfs)
++ err = lsb->s_op->statfs(lsb->s_root, &statbuf);
++ if (err)
++ return err;
++
++ quota_get_stat(sb, &statbuf);
++
++ buf->f_files = statbuf.f_files;
++ buf->f_ffree = statbuf.f_ffree;
++ buf->f_blocks = statbuf.f_blocks;
++ buf->f_bfree = statbuf.f_bfree;
++ buf->f_bavail = statbuf.f_bavail;
++ return 0;
++}
++
++static int sim_systemcall(struct vnotifier_block *me, unsigned long n,
++ void *d, int old_ret)
++{
++ int err;
++
++ switch (n) {
++ case VIRTINFO_FAUDIT_STAT: {
++ struct faudit_stat_arg *arg;
++
++ arg = (struct faudit_stat_arg *)d;
++ err = sim_getattr(arg->mnt, arg->dentry, arg->stat);
++ arg->err = err;
++ }
++ break;
++ case VIRTINFO_FAUDIT_STATFS: {
++ struct faudit_statfs_arg *arg;
++
++ arg = (struct faudit_statfs_arg *)d;
++ err = sim_statfs(arg->sb, arg->stat);
++ arg->err = err;
++ }
++ break;
++ default:
++ return old_ret;
++ }
++ return (err ? NOTIFY_BAD : NOTIFY_OK);
++}
++
++static struct inode *sim_quota_root(struct super_block *sb)
++{
++ return sb->s_root->d_inode;
++}
++
++/*
++ * NOTE: We need to setup s_bdev field on super block, since sys_quotactl()
++ * does lookup_bdev() and get_super() which are comparing sb->s_bdev.
++ * so this is a MUST if we want unmodified sys_quotactl
++ * to work correctly on /dev/simfs inside VE
++ */
++static int sim_init_blkdev(struct super_block *sb)
++{
++ static struct hd_struct fake_hd;
++ struct block_device *blkdev;
++
++ blkdev = bdget(sb->s_dev);
++ if (blkdev == NULL)
++ return -ENOMEM;
++
++ blkdev->bd_part = &fake_hd; /* required for bdev_read_only() */
++ sb->s_bdev = blkdev;
++
++ return 0;
++}
++
++static void sim_free_blkdev(struct super_block *sb)
++{
++ /* set bd_part back to NULL */
++ sb->s_bdev->bd_part = NULL;
++ bdput(sb->s_bdev);
++}
++
++static void sim_quota_init(struct super_block *sb)
++{
++ struct virt_info_quota viq;
++
++ viq.super = sb;
++ virtinfo_notifier_call(VITYPE_QUOTA, VIRTINFO_QUOTA_ON, &viq);
++}
++
++static void sim_quota_free(struct super_block *sb)
++{
++ struct virt_info_quota viq;
++
++ viq.super = sb;
++ virtinfo_notifier_call(VITYPE_QUOTA, VIRTINFO_QUOTA_OFF, &viq);
++}
++
++static struct super_operations sim_super_ops = {
++ .get_quota_root = sim_quota_root,
++};
++
++static int sim_fill_super(struct super_block *s, void *data)
++{
++ int err;
++ struct nameidata *nd;
++
++ err = set_anon_super(s, NULL);
++ if (err)
++ goto out;
++
++ err = 0;
++ nd = (struct nameidata *)data;
++ s->s_fs_info = mntget(nd->path.mnt);
++ s->s_root = dget(nd->path.dentry);
++ s->s_op = &sim_super_ops;
++out:
++ return err;
++}
++
++static int sim_get_sb(struct file_system_type *type, int flags,
++ const char *dev_name, void *opt, struct vfsmount *mnt)
++{
++ int err;
++ struct nameidata nd;
++ struct super_block *sb;
++
++ err = -EINVAL;
++ if (opt == NULL)
++ goto out;
++
++ err = path_lookup(opt, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd);
++ if (err)
++ goto out;
++
++ sb = sget(type, NULL, sim_fill_super, &nd);
++ err = PTR_ERR(sb);
++ if (IS_ERR(sb))
++ goto out_path;
++
++ err = sim_init_blkdev(sb);
++ if (err)
++ goto out_killsb;
++
++ sim_quota_init(sb);
++
++ path_put(&nd.path);
++ return simple_set_mnt(mnt, sb);
++
++out_killsb:
++ up_write(&sb->s_umount);
++ deactivate_super(sb);
++out_path:
++ path_put(&nd.path);
++out:
++ return err;
++}
++
++static void sim_kill_sb(struct super_block *sb)
++{
++ dput(sb->s_root);
++ sb->s_root = NULL;
++ mntput((struct vfsmount *)(sb->s_fs_info));
++
++ sim_quota_free(sb);
++ sim_free_blkdev(sb);
++
++ kill_anon_super(sb);
++}
++
++static struct file_system_type sim_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "simfs",
++ .get_sb = sim_get_sb,
++ .kill_sb = sim_kill_sb,
++ .fs_flags = FS_MANGLE_PROC,
++};
++
++static struct vnotifier_block sim_syscalls = {
++ .notifier_call = sim_systemcall,
++};
++
++static int __init init_simfs(void)
++{
++ int err;
++
++ err = register_filesystem(&sim_fs_type);
++ if (err)
++ return err;
++
++ virtinfo_notifier_register(VITYPE_FAUDIT, &sim_syscalls);
++ return 0;
++}
++
++static void __exit exit_simfs(void)
++{
++ virtinfo_notifier_unregister(VITYPE_FAUDIT, &sim_syscalls);
++ unregister_filesystem(&sim_fs_type);
++}
++
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Open Virtuozzo Simulation of File System");
++MODULE_LICENSE("GPL v2");
++
++module_init(init_simfs);
++module_exit(exit_simfs);
+diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
+index e37fe4d..1992fc0 100644
+--- a/fs/smbfs/sock.c
++++ b/fs/smbfs/sock.c
+@@ -99,6 +99,7 @@ smb_close_socket(struct smb_sb_info *server)
+
+ VERBOSE("closing socket %p\n", sock);
+ sock->sk->sk_data_ready = server->data_ready;
++ sock->sk->sk_user_data = NULL;
+ server->sock_file = NULL;
+ fput(file);
+ }
+diff --git a/fs/stat.c b/fs/stat.c
+index 7c46fbe..684baed 100644
+--- a/fs/stat.c
++++ b/fs/stat.c
+@@ -14,6 +14,7 @@
+ #include <linux/security.h>
+ #include <linux/syscalls.h>
+ #include <linux/pagemap.h>
++#include <linux/faudit.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+@@ -41,11 +42,19 @@ int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+ {
+ struct inode *inode = dentry->d_inode;
+ int retval;
++ struct faudit_stat_arg arg;
+
+ retval = security_inode_getattr(mnt, dentry);
+ if (retval)
+ return retval;
+
++ arg.mnt = mnt;
++ arg.dentry = dentry;
++ arg.stat = stat;
++ if (virtinfo_notifier_call(VITYPE_FAUDIT, VIRTINFO_FAUDIT_STAT, &arg)
++ != NOTIFY_DONE)
++ return arg.err;
++
+ if (inode->i_op->getattr)
+ return inode->i_op->getattr(mnt, dentry, stat);
+
+diff --git a/fs/super.c b/fs/super.c
+index e931ae9..60a7209 100644
+--- a/fs/super.c
++++ b/fs/super.c
+@@ -38,12 +38,15 @@
+ #include <linux/kobject.h>
+ #include <linux/mutex.h>
+ #include <linux/file.h>
++#include <linux/ve_proto.h>
+ #include <asm/uaccess.h>
+ #include "internal.h"
+
+
+ LIST_HEAD(super_blocks);
++EXPORT_SYMBOL_GPL(super_blocks);
+ DEFINE_SPINLOCK(sb_lock);
++EXPORT_SYMBOL_GPL(sb_lock);
+
+ /**
+ * alloc_super - create new superblock
+@@ -73,13 +76,15 @@ static struct super_block *alloc_super(struct file_system_type *type)
+ INIT_LIST_HEAD(&s->s_dentry_lru);
+ init_rwsem(&s->s_umount);
+ mutex_init(&s->s_lock);
+- lockdep_set_class(&s->s_umount, &type->s_umount_key);
++ lockdep_set_class(&s->s_umount,
++ &type->proto->s_umount_key);
+ /*
+ * The locking rules for s_lock are up to the
+ * filesystem. For example ext3fs has different
+ * lock ordering than usbfs:
+ */
+- lockdep_set_class(&s->s_lock, &type->s_lock_key);
++ lockdep_set_class(&s->s_lock,
++ &type->proto->s_lock_key);
+ down_write(&s->s_umount);
+ s->s_count = S_BIAS;
+ atomic_set(&s->s_active, 1);
+@@ -304,7 +309,7 @@ void generic_shutdown_super(struct super_block *sb)
+ sop->put_super(sb);
+
+ /* Forget any remaining inodes */
+- if (invalidate_inodes(sb)) {
++ if (invalidate_inodes_check(sb, 1)) {
+ printk("VFS: Busy inodes after unmount of %s. "
+ "Self-destruct in 5 seconds. Have a nice day...\n",
+ sb->s_id);
+@@ -533,17 +538,26 @@ rescan:
+ spin_unlock(&sb_lock);
+ return NULL;
+ }
++EXPORT_SYMBOL(user_get_super);
+
+ asmlinkage long sys_ustat(unsigned dev, struct ustat __user * ubuf)
+ {
++ dev_t kdev;
+ struct super_block *s;
+ struct ustat tmp;
+ struct kstatfs sbuf;
+- int err = -EINVAL;
++ int err;
++
++ kdev = new_decode_dev(dev);
++ err = get_device_perms_ve(S_IFBLK, kdev, FMODE_READ);
++ if (err)
++ goto out;
++
++ err = -EINVAL;
++ s = user_get_super(kdev);
++ if (s == NULL)
++ goto out;
+
+- s = user_get_super(new_decode_dev(dev));
+- if (s == NULL)
+- goto out;
+ err = vfs_statfs(s->s_root, &sbuf);
+ drop_super(s);
+ if (err)
+@@ -685,6 +699,13 @@ void emergency_remount(void)
+ static struct idr unnamed_dev_idr;
+ static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */
+
++/* for compatibility with coreutils still unaware of new minor sizes */
++int unnamed_dev_majors[] = {
++ 0, 144, 145, 146, 242, 243, 244, 245,
++ 246, 247, 248, 249, 250, 251, 252, 253
++};
++EXPORT_SYMBOL(unnamed_dev_majors);
++
+ int set_anon_super(struct super_block *s, void *data)
+ {
+ int dev;
+@@ -702,13 +723,13 @@ int set_anon_super(struct super_block *s, void *data)
+ else if (error)
+ return -EAGAIN;
+
+- if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
++ if ((dev & MAX_ID_MASK) >= (1 << MINORBITS)) {
+ spin_lock(&unnamed_dev_lock);
+ idr_remove(&unnamed_dev_idr, dev);
+ spin_unlock(&unnamed_dev_lock);
+ return -EMFILE;
+ }
+- s->s_dev = MKDEV(0, dev & MINORMASK);
++ s->s_dev = make_unnamed_dev(dev);
+ return 0;
+ }
+
+@@ -716,8 +737,9 @@ EXPORT_SYMBOL(set_anon_super);
+
+ void kill_anon_super(struct super_block *sb)
+ {
+- int slot = MINOR(sb->s_dev);
++ int slot;
+
++ slot = unnamed_dev_idx(sb->s_dev);
+ generic_shutdown_super(sb);
+ spin_lock(&unnamed_dev_lock);
+ idr_remove(&unnamed_dev_idr, slot);
+diff --git a/fs/sync.c b/fs/sync.c
+index 2967562..9b03c39 100644
+--- a/fs/sync.c
++++ b/fs/sync.c
+@@ -14,6 +14,8 @@
+ #include <linux/quotaops.h>
+ #include <linux/buffer_head.h>
+
++#include <bc/beancounter.h>
++
+ #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \
+ SYNC_FILE_RANGE_WAIT_AFTER)
+
+@@ -38,7 +40,14 @@ static void do_sync(unsigned long wait)
+
+ asmlinkage long sys_sync(void)
+ {
++ struct user_beancounter *ub;
++
++ ub = get_exec_ub();
++ ub_percpu_inc(ub, sync);
++
+ do_sync(1);
++
++ ub_percpu_inc(ub, sync_done);
+ return 0;
+ }
+
+@@ -80,6 +89,7 @@ long do_fsync(struct file *file, int datasync)
+ int ret;
+ int err;
+ struct address_space *mapping = file->f_mapping;
++ struct user_beancounter *ub;
+
+ if (!file->f_op || !file->f_op->fsync) {
+ /* Why? We can still call filemap_fdatawrite */
+@@ -87,6 +97,12 @@ long do_fsync(struct file *file, int datasync)
+ goto out;
+ }
+
++ ub = get_exec_ub();
++ if (datasync)
++ ub_percpu_inc(ub, fdsync);
++ else
++ ub_percpu_inc(ub, fsync);
++
+ ret = filemap_fdatawrite(mapping);
+
+ /*
+@@ -101,6 +117,11 @@ long do_fsync(struct file *file, int datasync)
+ err = filemap_fdatawait(mapping);
+ if (!ret)
+ ret = err;
++
++ if (datasync)
++ ub_percpu_inc(ub, fdsync_done);
++ else
++ ub_percpu_inc(ub, fsync_done);
+ out:
+ return ret;
+ }
+@@ -252,12 +273,16 @@ int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+ loff_t endbyte, unsigned int flags)
+ {
+ int ret;
++ struct user_beancounter *ub;
+
+ if (!mapping) {
+ ret = -EINVAL;
+- goto out;
++ goto out_noacct;
+ }
+
++ ub = get_exec_ub();
++ ub_percpu_inc(ub, frsync);
++
+ ret = 0;
+ if (flags & SYNC_FILE_RANGE_WAIT_BEFORE) {
+ ret = wait_on_page_writeback_range(mapping,
+@@ -280,6 +305,8 @@ int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+ endbyte >> PAGE_CACHE_SHIFT);
+ }
+ out:
++ ub_percpu_inc(ub, frsync_done);
++out_noacct:
+ return ret;
+ }
+ EXPORT_SYMBOL_GPL(do_sync_mapping_range);
+diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
+index 006fc64..9aec999 100644
+--- a/fs/sysfs/bin.c
++++ b/fs/sysfs/bin.c
+@@ -177,6 +177,9 @@ static int open(struct inode * inode, struct file * file)
+ struct bin_buffer *bb = NULL;
+ int error;
+
++ if (!ve_sysfs_alowed())
++ return 0;
++
+ /* binary file operations requires both @sd and its parent */
+ if (!sysfs_get_active_two(attr_sd))
+ return -ENODEV;
+@@ -238,6 +241,9 @@ const struct file_operations bin_fops = {
+
+ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
+ {
++ if (!ve_sysfs_alowed())
++ return 0;
++
+ BUG_ON(!kobj || !kobj->sd || !attr);
+
+ return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+@@ -252,6 +258,8 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
+
+ void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
+ {
++ if (!ve_sysfs_alowed())
++ return;
+ sysfs_hash_and_remove(kobj->sd, attr->attr.name);
+ }
+
+diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
+index aedaeba..5c15f5d 100644
+--- a/fs/sysfs/dir.c
++++ b/fs/sysfs/dir.c
+@@ -508,6 +508,9 @@ static void sysfs_drop_dentry(struct sysfs_dirent *sd)
+ struct inode *inode;
+ struct dentry *dentry;
+
++ if (!ve_sysfs_alowed())
++ return;
++
+ inode = ilookup(sysfs_sb, sd->s_ino);
+ if (!inode)
+ return;
+@@ -679,12 +682,15 @@ int sysfs_create_dir(struct kobject * kobj)
+ struct sysfs_dirent *parent_sd, *sd;
+ int error = 0;
+
++ if (!ve_sysfs_alowed())
++ return 0;
++
+ BUG_ON(!kobj);
+
+ if (kobj->parent)
+ parent_sd = kobj->parent->sd;
+ else
+- parent_sd = &sysfs_root;
++ parent_sd = ve_sysfs_root;
+
+ error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
+ if (!error)
+@@ -785,6 +791,9 @@ void sysfs_remove_dir(struct kobject * kobj)
+ {
+ struct sysfs_dirent *sd = kobj->sd;
+
++ if (!ve_sysfs_alowed())
++ return;
++
+ spin_lock(&sysfs_assoc_lock);
+ kobj->sd = NULL;
+ spin_unlock(&sysfs_assoc_lock);
+@@ -800,6 +809,9 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
+ const char *dup_name = NULL;
+ int error;
+
++ if (!ve_sysfs_alowed())
++ return 0;
++
+ mutex_lock(&sysfs_rename_mutex);
+
+ error = 0;
+@@ -868,7 +880,7 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
+
+ mutex_lock(&sysfs_rename_mutex);
+ BUG_ON(!sd->s_parent);
+- new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root;
++ new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : ve_sysfs_root;
+
+ error = 0;
+ if (sd->s_parent == new_parent_sd)
+diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
+index c9e4e50..6389078 100644
+--- a/fs/sysfs/file.c
++++ b/fs/sysfs/file.c
+@@ -516,6 +516,8 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
+
+ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
+ {
++ if (!ve_sysfs_alowed())
++ return 0;
+ BUG_ON(!kobj || !kobj->sd || !attr);
+
+ return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
+@@ -614,6 +616,8 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file);
+
+ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
+ {
++ if (!ve_sysfs_alowed())
++ return;
+ sysfs_hash_and_remove(kobj->sd, attr->name);
+ }
+
+diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
+index fe61194..628afe3 100644
+--- a/fs/sysfs/group.c
++++ b/fs/sysfs/group.c
+@@ -62,6 +62,8 @@ static int internal_create_group(struct kobject *kobj, int update,
+ struct sysfs_dirent *sd;
+ int error;
+
++ if (!ve_sysfs_alowed())
++ return 0;
+ BUG_ON(!kobj || (!update && !kobj->sd));
+
+ /* Updates may happen before the object has been instantiated */
+@@ -131,6 +133,9 @@ void sysfs_remove_group(struct kobject * kobj,
+ struct sysfs_dirent *dir_sd = kobj->sd;
+ struct sysfs_dirent *sd;
+
++ if (!ve_sysfs_alowed())
++ return;
++
+ if (grp->name) {
+ sd = sysfs_get_dirent(dir_sd, grp->name);
+ if (!sd) {
+diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
+index eb53c63..a09bfa5 100644
+--- a/fs/sysfs/inode.c
++++ b/fs/sysfs/inode.c
+@@ -20,8 +20,6 @@
+ #include <linux/sched.h>
+ #include "sysfs.h"
+
+-extern struct super_block * sysfs_sb;
+-
+ static const struct address_space_operations sysfs_aops = {
+ .readpage = simple_readpage,
+ .write_begin = simple_write_begin,
+diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
+index 14f0023..974bf82 100644
+--- a/fs/sysfs/mount.c
++++ b/fs/sysfs/mount.c
+@@ -15,6 +15,7 @@
+ #include <linux/fs.h>
+ #include <linux/mount.h>
+ #include <linux/pagemap.h>
++#include <linux/module.h>
+ #include <linux/init.h>
+
+ #include "sysfs.h"
+@@ -22,8 +23,11 @@
+ /* Random magic number */
+ #define SYSFS_MAGIC 0x62656572
+
+-static struct vfsmount *sysfs_mount;
++#ifndef CONFIG_VE
++struct vfsmount *sysfs_mount;
+ struct super_block * sysfs_sb = NULL;
++#endif
++
+ struct kmem_cache *sysfs_dir_cachep;
+
+ static const struct super_operations sysfs_ops = {
+@@ -39,6 +43,13 @@ struct sysfs_dirent sysfs_root = {
+ .s_ino = 1,
+ };
+
++static void init_ve0_sysfs_root(void)
++{
++#ifdef CONFIG_VE
++ get_ve0()->_sysfs_root = &sysfs_root;
++#endif
++}
++
+ static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+ {
+ struct inode *inode;
+@@ -52,7 +63,7 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+ sysfs_sb = sb;
+
+ /* get root inode, initialize and unlock it */
+- inode = sysfs_get_inode(&sysfs_root);
++ inode = sysfs_get_inode(ve_sysfs_root);
+ if (!inode) {
+ pr_debug("sysfs: could not get root inode\n");
+ return -ENOMEM;
+@@ -65,7 +76,7 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
+ iput(inode);
+ return -ENOMEM;
+ }
+- root->d_fsdata = &sysfs_root;
++ root->d_fsdata = ve_sysfs_root;
+ sb->s_root = root;
+ return 0;
+ }
+@@ -76,16 +87,19 @@ static int sysfs_get_sb(struct file_system_type *fs_type,
+ return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
+ }
+
+-static struct file_system_type sysfs_fs_type = {
++struct file_system_type sysfs_fs_type = {
+ .name = "sysfs",
+ .get_sb = sysfs_get_sb,
+ .kill_sb = kill_anon_super,
+ };
+
++EXPORT_SYMBOL(sysfs_fs_type);
++
+ int __init sysfs_init(void)
+ {
+ int err = -ENOMEM;
+
++ init_ve0_sysfs_root();
+ sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
+ sizeof(struct sysfs_dirent),
+ 0, 0, NULL);
+diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
+index a3ba217..12ce2f5 100644
+--- a/fs/sysfs/symlink.c
++++ b/fs/sysfs/symlink.c
+@@ -28,10 +28,13 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
+ struct sysfs_addrm_cxt acxt;
+ int error;
+
++ if (!ve_sysfs_alowed())
++ return 0;
++
+ BUG_ON(!name);
+
+ if (!kobj)
+- parent_sd = &sysfs_root;
++ parent_sd = ve_sysfs_root;
+ else
+ parent_sd = kobj->sd;
+
+@@ -114,8 +117,11 @@ void sysfs_remove_link(struct kobject * kobj, const char * name)
+ {
+ struct sysfs_dirent *parent_sd = NULL;
+
++ if(!ve_sysfs_alowed())
++ return;
++
+ if (!kobj)
+- parent_sd = &sysfs_root;
++ parent_sd = ve_sysfs_root;
+ else
+ parent_sd = kobj->sd;
+
+diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
+index a5db496..ff709ab 100644
+--- a/fs/sysfs/sysfs.h
++++ b/fs/sysfs/sysfs.h
+@@ -8,67 +8,17 @@
+ * This file is released under the GPLv2.
+ */
+
+-struct sysfs_open_dirent;
+-
+-/* type-specific structures for sysfs_dirent->s_* union members */
+-struct sysfs_elem_dir {
+- struct kobject *kobj;
+- /* children list starts here and goes through sd->s_sibling */
+- struct sysfs_dirent *children;
+-};
+-
+-struct sysfs_elem_symlink {
+- struct sysfs_dirent *target_sd;
+-};
+-
+-struct sysfs_elem_attr {
+- struct attribute *attr;
+- struct sysfs_open_dirent *open;
+-};
+-
+-struct sysfs_elem_bin_attr {
+- struct bin_attribute *bin_attr;
+-};
+-
+-/*
+- * sysfs_dirent - the building block of sysfs hierarchy. Each and
+- * every sysfs node is represented by single sysfs_dirent.
+- *
+- * As long as s_count reference is held, the sysfs_dirent itself is
+- * accessible. Dereferencing s_elem or any other outer entity
+- * requires s_active reference.
+- */
+-struct sysfs_dirent {
+- atomic_t s_count;
+- atomic_t s_active;
+- struct sysfs_dirent *s_parent;
+- struct sysfs_dirent *s_sibling;
+- const char *s_name;
+-
+- union {
+- struct sysfs_elem_dir s_dir;
+- struct sysfs_elem_symlink s_symlink;
+- struct sysfs_elem_attr s_attr;
+- struct sysfs_elem_bin_attr s_bin_attr;
+- };
+-
+- unsigned int s_flags;
+- ino_t s_ino;
+- umode_t s_mode;
+- struct iattr *s_iattr;
+-};
+-
+-#define SD_DEACTIVATED_BIAS INT_MIN
+-
+-#define SYSFS_TYPE_MASK 0x00ff
+-#define SYSFS_DIR 0x0001
+-#define SYSFS_KOBJ_ATTR 0x0002
+-#define SYSFS_KOBJ_BIN_ATTR 0x0004
+-#define SYSFS_KOBJ_LINK 0x0008
+-#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
+-
+-#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK
+-#define SYSFS_FLAG_REMOVED 0x0200
++#ifndef CONFIG_VE
++extern struct vfsmount *sysfs_mount;
++extern struct super_block *sysfs_sb;
++#define ve_sysfs_alowed() 1
++#else
++#include <linux/sched.h>
++#include <linux/ve.h>
++#define sysfs_mount (get_exec_env()->sysfs_mnt)
++#define sysfs_sb (get_exec_env()->sysfs_sb)
++#define ve_sysfs_alowed() (sysfs_sb != NULL)
++#endif
+
+ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
+ {
+@@ -88,8 +38,12 @@ struct sysfs_addrm_cxt {
+ /*
+ * mount.c
+ */
++#ifdef CONFIG_VE
++#define ve_sysfs_root (get_exec_env()->_sysfs_root)
++#else
+ extern struct sysfs_dirent sysfs_root;
+-extern struct super_block *sysfs_sb;
++#define ve_sysfs_root (&sysfs_root)
++#endif
+ extern struct kmem_cache *sysfs_dir_cachep;
+
+ /*
+diff --git a/fs/vzdq_file.c b/fs/vzdq_file.c
+new file mode 100644
+index 0000000..4d814d9
+--- /dev/null
++++ b/fs/vzdq_file.c
+@@ -0,0 +1,923 @@
++/*
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains Virtuozzo quota files as proc entry implementation.
++ * It is required for std quota tools to work correctly as they are expecting
++ * aquota.user and aquota.group files.
++ */
++
++#include <linux/ctype.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++#include <linux/sysctl.h>
++#include <linux/mount.h>
++#include <linux/mnt_namespace.h>
++#include <linux/quotaio_v2.h>
++#include <asm/uaccess.h>
++
++#include <linux/ve.h>
++#include <linux/ve_proto.h>
++#include <linux/vzdq_tree.h>
++#include <linux/vzquota.h>
++
++/* ----------------------------------------------------------------------
++ *
++ * File read operation
++ *
++ * FIXME: functions in this section (as well as many functions in vzdq_ugid.c,
++ * perhaps) abuse vz_quota_sem.
++ * Taking a global semaphore for lengthy and user-controlled operations inside
++ * VPSs is not a good idea in general.
++ * In this case, the reasons for taking this semaphore are completely unclear,
++ * especially taking into account that the only function that has comments
++ * about the necessity to be called under this semaphore
++ * (create_proc_quotafile) is actually called OUTSIDE it.
++ *
++ * --------------------------------------------------------------------- */
++
++#define DQBLOCK_SIZE 1024
++#define DQUOTBLKNUM 21U
++#define DQTREE_DEPTH 4
++#define TREENUM_2_BLKNUM(num) (((num) + 1) << 1)
++#define ISINDBLOCK(num) ((num)%2 != 0)
++#define FIRST_DATABLK 2 /* first even number */
++#define LAST_IND_LEVEL (DQTREE_DEPTH - 1)
++#define CONVERT_LEVEL(level) ((level) * (QUOTAID_EBITS/QUOTAID_BBITS))
++#define GETLEVINDX(ind, lev) (((ind) >> QUOTAID_BBITS*(lev)) \
++ & QUOTATREE_BMASK)
++
++#if (QUOTAID_EBITS / QUOTAID_BBITS) != (QUOTATREE_DEPTH / DQTREE_DEPTH)
++#error xBITS and DQTREE_DEPTH does not correspond
++#endif
++
++#define BLOCK_NOT_FOUND 1
++
++/* data for quota file -- one per proc entry */
++struct quotatree_data {
++ struct list_head list;
++ struct vz_quota_master *qmblk;
++ int type; /* type of the tree */
++};
++
++/* serialized by vz_quota_sem */
++static LIST_HEAD(qf_data_head);
++
++static const u_int32_t vzquota_magics[] = V2_INITQMAGICS;
++static const u_int32_t vzquota_versions[] = V2_INITQVERSIONS;
++static const char aquota_user[] = "aquota.user";
++static const char aquota_group[] = "aquota.group";
++
++
++static inline loff_t get_depoff(int depth)
++{
++ loff_t res = 1;
++ while (depth) {
++ res += (1 << ((depth - 1)*QUOTAID_EBITS + 1));
++ depth--;
++ }
++ return res;
++}
++
++static inline loff_t get_blknum(loff_t num, int depth)
++{
++ loff_t res;
++ res = (num << 1) + get_depoff(depth);
++ return res;
++}
++
++static int get_depth(loff_t num)
++{
++ int i;
++ for (i = 0; i < DQTREE_DEPTH; i++) {
++ if (num >= get_depoff(i) && (i == DQTREE_DEPTH - 1
++ || num < get_depoff(i + 1)))
++ return i;
++ }
++ return -1;
++}
++
++static inline loff_t get_offset(loff_t num)
++{
++ loff_t res, tmp;
++
++ tmp = get_depth(num);
++ if (tmp < 0)
++ return -1;
++ num -= get_depoff(tmp);
++ BUG_ON(num < 0);
++ res = num >> 1;
++
++ return res;
++}
++
++static inline loff_t get_quot_blk_num(struct quotatree_tree *tree, int level)
++{
++ /* return maximum available block num */
++ return tree->levels[level].freenum;
++}
++
++static inline loff_t get_block_num(struct quotatree_tree *tree)
++{
++ loff_t ind_blk_num, quot_blk_num, max_ind, max_quot;
++
++ quot_blk_num = get_quot_blk_num(tree, CONVERT_LEVEL(DQTREE_DEPTH) - 1);
++ max_quot = TREENUM_2_BLKNUM(quot_blk_num);
++ ind_blk_num = get_quot_blk_num(tree, CONVERT_LEVEL(DQTREE_DEPTH - 1));
++ max_ind = (quot_blk_num) ? get_blknum(ind_blk_num, LAST_IND_LEVEL)
++ : get_blknum(ind_blk_num, 0);
++
++ return (max_ind > max_quot) ? max_ind + 1 : max_quot + 1;
++}
++
++/* Write quota file header */
++static int read_header(void *buf, struct quotatree_tree *tree,
++ struct dq_info *dq_ugid_info, int type)
++{
++ struct v2_disk_dqheader *dqh;
++ struct v2_disk_dqinfo *dq_disk_info;
++
++ dqh = buf;
++ dq_disk_info = buf + sizeof(struct v2_disk_dqheader);
++
++ dqh->dqh_magic = vzquota_magics[type];
++ dqh->dqh_version = vzquota_versions[type];
++
++ dq_disk_info->dqi_bgrace = dq_ugid_info[type].bexpire;
++ dq_disk_info->dqi_igrace = dq_ugid_info[type].iexpire;
++ dq_disk_info->dqi_flags = 0; /* no flags */
++ dq_disk_info->dqi_blocks = get_block_num(tree);
++ dq_disk_info->dqi_free_blk = 0; /* first block in the file */
++ dq_disk_info->dqi_free_entry = FIRST_DATABLK;
++
++ return 0;
++}
++
++static int get_block_child(int depth, struct quotatree_node *p, u_int32_t *buf)
++{
++ int i, j, lev_num;
++
++ lev_num = QUOTATREE_DEPTH/DQTREE_DEPTH - 1;
++ for (i = 0; i < BLOCK_SIZE/sizeof(u_int32_t); i++) {
++ struct quotatree_node *next, *parent;
++
++ parent = p;
++ next = p;
++ for (j = lev_num; j >= 0; j--) {
++ if (!next->blocks[GETLEVINDX(i,j)]) {
++ buf[i] = 0;
++ goto bad_branch;
++ }
++ parent = next;
++ next = next->blocks[GETLEVINDX(i,j)];
++ }
++ buf[i] = (depth == DQTREE_DEPTH - 1) ?
++ TREENUM_2_BLKNUM(parent->num)
++ : get_blknum(next->num, depth + 1);
++
++ bad_branch:
++ ;
++ }
++
++ return 0;
++}
++
++/*
++ * Write index block to disk (or buffer)
++ * @buf has length 256*sizeof(u_int32_t) bytes
++ */
++static int read_index_block(int num, u_int32_t *buf,
++ struct quotatree_tree *tree)
++{
++ struct quotatree_node *p;
++ u_int32_t index;
++ loff_t off;
++ int depth, res;
++
++ res = BLOCK_NOT_FOUND;
++ index = 0;
++ depth = get_depth(num);
++ off = get_offset(num);
++ if (depth < 0 || off < 0)
++ return -EINVAL;
++
++ list_for_each_entry(p, &tree->levels[CONVERT_LEVEL(depth)].usedlh,
++ list) {
++ if (p->num >= off)
++ res = 0;
++ if (p->num != off)
++ continue;
++ get_block_child(depth, p, buf);
++ break;
++ }
++
++ return res;
++}
++
++static inline void convert_quot_format(struct v2_disk_dqblk *dq,
++ struct vz_quota_ugid *vzq)
++{
++ dq->dqb_id = vzq->qugid_id;
++ dq->dqb_ihardlimit = vzq->qugid_stat.ihardlimit;
++ dq->dqb_isoftlimit = vzq->qugid_stat.isoftlimit;
++ dq->dqb_curinodes = vzq->qugid_stat.icurrent;
++ dq->dqb_bhardlimit = vzq->qugid_stat.bhardlimit / QUOTABLOCK_SIZE;
++ dq->dqb_bsoftlimit = vzq->qugid_stat.bsoftlimit / QUOTABLOCK_SIZE;
++ dq->dqb_curspace = vzq->qugid_stat.bcurrent;
++ dq->dqb_btime = vzq->qugid_stat.btime;
++ dq->dqb_itime = vzq->qugid_stat.itime;
++}
++
++static int read_dquot(loff_t num, void *buf, struct quotatree_tree *tree)
++{
++ int res, i, entries = 0;
++ struct v2_disk_dqdbheader *dq_header;
++ struct quotatree_node *p;
++ struct v2_disk_dqblk *blk = buf + sizeof(struct v2_disk_dqdbheader);
++
++ res = BLOCK_NOT_FOUND;
++ dq_header = buf;
++ memset(dq_header, 0, sizeof(*dq_header));
++
++ list_for_each_entry(p, &(tree->levels[QUOTATREE_DEPTH - 1].usedlh),
++ list) {
++ if (TREENUM_2_BLKNUM(p->num) >= num)
++ res = 0;
++ if (TREENUM_2_BLKNUM(p->num) != num)
++ continue;
++
++ for (i = 0; i < QUOTATREE_BSIZE; i++) {
++ if (!p->blocks[i])
++ continue;
++ convert_quot_format(blk + entries,
++ (struct vz_quota_ugid *)p->blocks[i]);
++ entries++;
++ res = 0;
++ }
++ break;
++ }
++ dq_header->dqdh_entries = entries;
++
++ return res;
++}
++
++static int read_block(int num, void *buf, struct quotatree_tree *tree,
++ struct dq_info *dq_ugid_info, int magic)
++{
++ int res;
++
++ memset(buf, 0, DQBLOCK_SIZE);
++ if (!num)
++ res = read_header(buf, tree, dq_ugid_info, magic);
++ else if (ISINDBLOCK(num))
++ res = read_index_block(num, (u_int32_t*)buf, tree);
++ else
++ res = read_dquot(num, buf, tree);
++
++ return res;
++}
++
++/*
++ * FIXME: this function can handle quota files up to 2GB only.
++ */
++static int read_proc_quotafile(char *page, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ off_t blk_num, blk_off, buf_off;
++ char *tmp;
++ size_t buf_size;
++ struct quotatree_data *qtd;
++ struct quotatree_tree *tree;
++ struct dq_info *dqi;
++ int res;
++
++ *start = NULL;
++ tmp = kmalloc(DQBLOCK_SIZE, GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++
++ qtd = data;
++ down(&vz_quota_sem);
++ down(&qtd->qmblk->dq_sem);
++
++ res = 0;
++ tree = QUGID_TREE(qtd->qmblk, qtd->type);
++ if (!tree) {
++ *eof = 1;
++ goto out_dq;
++ }
++
++ dqi = &qtd->qmblk->dq_ugid_info[qtd->type];
++
++ buf_off = 0;
++ buf_size = count;
++ blk_num = off / DQBLOCK_SIZE;
++ blk_off = off % DQBLOCK_SIZE;
++
++ while (buf_size > 0) {
++ off_t len;
++
++ len = min((size_t)(DQBLOCK_SIZE-blk_off), buf_size);
++ res = read_block(blk_num, tmp, tree, dqi, qtd->type);
++ if (res < 0)
++ goto out_err;
++ if (res == BLOCK_NOT_FOUND) {
++ *eof = 1;
++ break;
++ }
++ memcpy(page + buf_off, tmp + blk_off, len);
++
++ blk_num++;
++ buf_size -= len;
++ blk_off = 0;
++ buf_off += len;
++ }
++ res = buf_off;
++
++out_err:
++ *start += count;
++out_dq:
++ up(&qtd->qmblk->dq_sem);
++ up(&vz_quota_sem);
++ kfree(tmp);
++
++ return res;
++}
++
++
++/* ----------------------------------------------------------------------
++ *
++ * /proc/vz/vzaquota/QID/aquota.* files
++ *
++ * FIXME: this code lacks serialization of read/readdir/lseek.
++ * However, this problem should be fixed after the mainstream issue of what
++ * appears to be non-atomic read and update of file position in sys_read.
++ *
++ * --------------------------------------------------------------------- */
++
++static inline unsigned long vzdq_aquot_getino(dev_t dev)
++{
++ return 0xec000000UL + dev;
++}
++
++static inline dev_t vzdq_aquot_getidev(struct inode *inode)
++{
++ return (dev_t)(unsigned long)PROC_I(inode)->op.proc_get_link;
++}
++
++static inline void vzdq_aquot_setidev(struct inode *inode, dev_t dev)
++{
++ PROC_I(inode)->op.proc_get_link = (void *)(unsigned long)dev;
++}
++
++static ssize_t vzdq_aquotf_read(struct file *file,
++ char __user *buf, size_t size, loff_t *ppos)
++{
++ char *page;
++ size_t bufsize;
++ ssize_t l, l2, copied;
++ char *start;
++ struct inode *inode;
++ struct block_device *bdev;
++ struct super_block *sb;
++ struct quotatree_data data;
++ int eof, err;
++
++ err = -ENOMEM;
++ page = (char *)__get_free_page(GFP_KERNEL);
++ if (page == NULL)
++ goto out_err;
++
++ err = -ENODEV;
++ inode = file->f_dentry->d_inode;
++ bdev = bdget(vzdq_aquot_getidev(inode));
++ if (bdev == NULL)
++ goto out_err;
++ sb = get_super(bdev);
++ bdput(bdev);
++ if (sb == NULL)
++ goto out_err;
++ data.qmblk = vzquota_find_qmblk(sb);
++ data.type = PROC_I(inode)->fd - 1;
++ drop_super(sb);
++ if (data.qmblk == NULL || data.qmblk == VZ_QUOTA_BAD)
++ goto out_err;
++
++ copied = 0;
++ l = l2 = 0;
++ while (1) {
++ bufsize = min(size, (size_t)PAGE_SIZE);
++ if (bufsize <= 0)
++ break;
++
++ l = read_proc_quotafile(page, &start, *ppos, bufsize,
++ &eof, &data);
++ if (l <= 0)
++ break;
++
++ l2 = copy_to_user(buf, page, l);
++ copied += l - l2;
++ if (l2)
++ break;
++
++ buf += l;
++ size -= l;
++ *ppos += (unsigned long)start;
++ l = l2 = 0;
++ }
++
++ qmblk_put(data.qmblk);
++ free_page((unsigned long)page);
++ if (copied)
++ return copied;
++ else if (l2) /* last copy_to_user failed */
++ return -EFAULT;
++ else /* read error or EOF */
++ return l;
++
++out_err:
++ if (page != NULL)
++ free_page((unsigned long)page);
++ return err;
++}
++
++static struct file_operations vzdq_aquotf_file_operations = {
++ .read = &vzdq_aquotf_read,
++};
++
++static struct inode_operations vzdq_aquotf_inode_operations = {
++};
++
++
++/* ----------------------------------------------------------------------
++ *
++ * /proc/vz/vzaquota/QID directory
++ *
++ * --------------------------------------------------------------------- */
++
++static int vzdq_aquotq_readdir(struct file *file, void *data, filldir_t filler)
++{
++ loff_t n;
++ int err;
++
++ n = file->f_pos;
++ for (err = 0; !err; n++) {
++ /* ppc32 can't cmp 2 long long's in switch, calls __cmpdi2() */
++ switch ((unsigned long)n) {
++ case 0:
++ err = (*filler)(data, ".", 1, n,
++ file->f_dentry->d_inode->i_ino,
++ DT_DIR);
++ break;
++ case 1:
++ err = (*filler)(data, "..", 2, n,
++ parent_ino(file->f_dentry), DT_DIR);
++ break;
++ case 2:
++ err = (*filler)(data, aquota_user,
++ sizeof(aquota_user)-1, n,
++ file->f_dentry->d_inode->i_ino
++ + USRQUOTA + 1,
++ DT_REG);
++ break;
++ case 3:
++ err = (*filler)(data, aquota_group,
++ sizeof(aquota_group)-1, n,
++ file->f_dentry->d_inode->i_ino
++ + GRPQUOTA + 1,
++ DT_REG);
++ break;
++ default:
++ goto out;
++ }
++ }
++out:
++ file->f_pos = n;
++ return err;
++}
++
++struct vzdq_aquotq_lookdata {
++ dev_t dev;
++ int type;
++ struct vz_quota_master *qmblk;
++};
++
++static int vzdq_aquotq_looktest(struct inode *inode, void *data)
++{
++ struct vzdq_aquotq_lookdata *d;
++
++ d = data;
++ return inode->i_op == &vzdq_aquotf_inode_operations &&
++ vzdq_aquot_getidev(inode) == d->dev &&
++ PROC_I(inode)->fd == d->type + 1;
++}
++
++static int vzdq_aquotq_lookset(struct inode *inode, void *data)
++{
++ struct vzdq_aquotq_lookdata *d;
++ struct super_block *sb;
++ struct quotatree_data qtd;
++ struct quotatree_tree *tree;
++
++ d = data;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
++ inode->i_ino = vzdq_aquot_getino(d->dev) + d->type + 1;
++ inode->i_mode = S_IFREG | S_IRUSR;
++ inode->i_uid = 0;
++ inode->i_gid = 0;
++ inode->i_nlink = 1;
++ inode->i_op = &vzdq_aquotf_inode_operations;
++ inode->i_fop = &vzdq_aquotf_file_operations;
++ PROC_I(inode)->fd = d->type + 1;
++ vzdq_aquot_setidev(inode, d->dev);
++
++ /* Setting size */
++ sb = user_get_super(d->dev);
++ if (sb == NULL)
++ return -ENODEV;
++ qtd.qmblk = vzquota_find_qmblk(sb);
++ drop_super(sb);
++
++ if (qtd.qmblk == NULL)
++ return -ESRCH;
++ if (qtd.qmblk == VZ_QUOTA_BAD)
++ return -EIO;
++
++ qtd.type = PROC_I(inode)->fd - 1;
++ tree = QUGID_TREE(qtd.qmblk, qtd.type);
++ inode->i_size = get_block_num(tree) * 1024;
++ return 0;
++}
++
++static int vzdq_aquotq_revalidate(struct dentry *vdentry, struct nameidata *nd)
++{
++ return 0;
++}
++
++static struct dentry_operations vzdq_aquotq_dentry_operations = {
++ .d_revalidate = &vzdq_aquotq_revalidate,
++};
++
++static struct vz_quota_master *find_qmblk_by_dev(dev_t dev)
++{
++ struct super_block *sb;
++ struct vz_quota_master *qmblk;
++
++ qmblk = NULL;
++ sb = user_get_super(dev);
++ if (sb != NULL) {
++ qmblk = vzquota_find_qmblk(sb);
++ drop_super(sb);
++
++ if (qmblk == VZ_QUOTA_BAD)
++ qmblk = NULL;
++ }
++
++ return qmblk;
++}
++
++static struct dentry *vzdq_aquotq_lookup(struct inode *dir,
++ struct dentry *dentry,
++ struct nameidata *nd)
++{
++ struct inode *inode;
++ struct vzdq_aquotq_lookdata d;
++ int k;
++
++ if (dentry->d_name.len == sizeof(aquota_user)-1) {
++ if (memcmp(dentry->d_name.name, aquota_user,
++ sizeof(aquota_user)-1))
++ goto out;
++ k = USRQUOTA;
++ } else if (dentry->d_name.len == sizeof(aquota_group)-1) {
++ if (memcmp(dentry->d_name.name, aquota_group,
++ sizeof(aquota_group)-1))
++ goto out;
++ k = GRPQUOTA;
++ } else
++ goto out;
++ d.dev = vzdq_aquot_getidev(dir);
++ d.type = k;
++ d.qmblk = find_qmblk_by_dev(d.dev);
++ if (d.qmblk == NULL)
++ goto out;
++
++ inode = iget5_locked(dir->i_sb, dir->i_ino + k + 1,
++ vzdq_aquotq_looktest, vzdq_aquotq_lookset, &d);
++ if (inode == NULL)
++ goto out;
++ unlock_new_inode(inode);
++ dentry->d_op = &vzdq_aquotq_dentry_operations;
++ d_add(dentry, inode);
++ return NULL;
++
++out:
++ return ERR_PTR(-ENOENT);
++}
++
++static struct file_operations vzdq_aquotq_file_operations = {
++ .read = &generic_read_dir,
++ .readdir = &vzdq_aquotq_readdir,
++};
++
++static struct inode_operations vzdq_aquotq_inode_operations = {
++ .lookup = &vzdq_aquotq_lookup,
++};
++
++
++/* ----------------------------------------------------------------------
++ *
++ * /proc/vz/vzaquota directory
++ *
++ * --------------------------------------------------------------------- */
++
++struct vzdq_aquot_de {
++ struct list_head list;
++ struct vfsmount *mnt;
++};
++
++static int vzdq_aquot_buildmntlist(struct ve_struct *ve,
++ struct list_head *head)
++{
++ struct vfsmount *rmnt, *mnt;
++ struct vzdq_aquot_de *p;
++ int err;
++
++#ifdef CONFIG_VE
++ rmnt = mntget(ve->root_path.mnt);
++#else
++ read_lock(&current->fs->lock);
++ rmnt = mntget(current->fs->rootmnt);
++ read_unlock(&current->fs->lock);
++#endif
++ mnt = rmnt;
++ spin_lock(&vfsmount_lock);
++ while (1) {
++ list_for_each_entry(p, head, list) {
++ if (p->mnt->mnt_sb == mnt->mnt_sb)
++ goto skip;
++ }
++
++ err = -ENOMEM;
++ p = kmalloc(sizeof(*p), GFP_ATOMIC);
++ if (p == NULL)
++ goto out;
++ p->mnt = mntget(mnt);
++ list_add_tail(&p->list, head);
++
++skip:
++ err = 0;
++ if (list_empty(&mnt->mnt_mounts)) {
++ while (1) {
++ if (mnt == rmnt)
++ goto out;
++ if (mnt->mnt_child.next !=
++ &mnt->mnt_parent->mnt_mounts)
++ break;
++ mnt = mnt->mnt_parent;
++ }
++ mnt = list_entry(mnt->mnt_child.next,
++ struct vfsmount, mnt_child);
++ } else
++ mnt = list_entry(mnt->mnt_mounts.next,
++ struct vfsmount, mnt_child);
++ }
++out:
++ spin_unlock(&vfsmount_lock);
++ mntput(rmnt);
++ return err;
++}
++
++static void vzdq_aquot_releasemntlist(struct ve_struct *ve,
++ struct list_head *head)
++{
++ struct vzdq_aquot_de *p;
++
++ while (!list_empty(head)) {
++ p = list_entry(head->next, typeof(*p), list);
++ mntput(p->mnt);
++ list_del(&p->list);
++ kfree(p);
++ }
++}
++
++static int vzdq_aquotd_readdir(struct file *file, void *data, filldir_t filler)
++{
++ struct ve_struct *ve, *old_ve;
++ struct list_head mntlist;
++ struct vzdq_aquot_de *de;
++ struct super_block *sb;
++ struct vz_quota_master *qmblk;
++ loff_t i, n;
++ char buf[24];
++ int l, err;
++
++ i = 0;
++ n = file->f_pos;
++ ve = file->f_dentry->d_sb->s_type->owner_env;
++ old_ve = set_exec_env(ve);
++
++ INIT_LIST_HEAD(&mntlist);
++#ifdef CONFIG_VE
++ /*
++ * The only reason of disabling readdir for the host system is that
++ * this readdir can be slow and CPU consuming with large number of VPSs
++ * (or just mount points).
++ */
++ err = ve_is_super(ve);
++#else
++ err = 0;
++#endif
++ if (!err) {
++ err = vzdq_aquot_buildmntlist(ve, &mntlist);
++ if (err)
++ goto out_err;
++ }
++
++ if (i >= n) {
++ if ((*filler)(data, ".", 1, i,
++ file->f_dentry->d_inode->i_ino, DT_DIR))
++ goto out_fill;
++ }
++ i++;
++
++ if (i >= n) {
++ if ((*filler)(data, "..", 2, i,
++ parent_ino(file->f_dentry), DT_DIR))
++ goto out_fill;
++ }
++ i++;
++
++ list_for_each_entry (de, &mntlist, list) {
++ sb = de->mnt->mnt_sb;
++ if (get_device_perms_ve(S_IFBLK, sb->s_dev, FMODE_QUOTACTL))
++ continue;
++
++ qmblk = vzquota_find_qmblk(sb);
++ if (qmblk == NULL || qmblk == VZ_QUOTA_BAD)
++ continue;
++
++ qmblk_put(qmblk);
++ i++;
++ if (i <= n)
++ continue;
++
++ l = sprintf(buf, "%08x", new_encode_dev(sb->s_dev));
++ if ((*filler)(data, buf, l, i - 1,
++ vzdq_aquot_getino(sb->s_dev), DT_DIR))
++ break;
++ }
++
++out_fill:
++ err = 0;
++ file->f_pos = i;
++out_err:
++ vzdq_aquot_releasemntlist(ve, &mntlist);
++ (void)set_exec_env(old_ve);
++ return err;
++}
++
++static int vzdq_aquotd_looktest(struct inode *inode, void *data)
++{
++ return inode->i_op == &vzdq_aquotq_inode_operations &&
++ vzdq_aquot_getidev(inode) == (dev_t)(unsigned long)data;
++}
++
++static int vzdq_aquotd_lookset(struct inode *inode, void *data)
++{
++ dev_t dev;
++
++ dev = (dev_t)(unsigned long)data;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
++ inode->i_ino = vzdq_aquot_getino(dev);
++ inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
++ inode->i_uid = 0;
++ inode->i_gid = 0;
++ inode->i_nlink = 2;
++ inode->i_op = &vzdq_aquotq_inode_operations;
++ inode->i_fop = &vzdq_aquotq_file_operations;
++ vzdq_aquot_setidev(inode, dev);
++ return 0;
++}
++
++static struct dentry *vzdq_aquotd_lookup(struct inode *dir,
++ struct dentry *dentry,
++ struct nameidata *nd)
++{
++ struct ve_struct *ve, *old_ve;
++ const unsigned char *s;
++ int l;
++ dev_t dev;
++ struct inode *inode;
++
++ ve = dir->i_sb->s_type->owner_env;
++ old_ve = set_exec_env(ve);
++#ifdef CONFIG_VE
++ /*
++ * Lookup is much lighter than readdir, so it can be allowed for the
++ * host system. But it would be strange to be able to do lookup only
++ * without readdir...
++ */
++ if (ve_is_super(ve))
++ goto out;
++#endif
++
++ dev = 0;
++ l = dentry->d_name.len;
++ if (l <= 0)
++ goto out;
++ for (s = dentry->d_name.name; l > 0; s++, l--) {
++ if (!isxdigit(*s))
++ goto out;
++ if (dev & ~(~0UL >> 4))
++ goto out;
++ dev <<= 4;
++ if (isdigit(*s))
++ dev += *s - '0';
++ else if (islower(*s))
++ dev += *s - 'a' + 10;
++ else
++ dev += *s - 'A' + 10;
++ }
++ dev = new_decode_dev(dev);
++
++ if (get_device_perms_ve(S_IFBLK, dev, FMODE_QUOTACTL))
++ goto out;
++
++ inode = iget5_locked(dir->i_sb, vzdq_aquot_getino(dev),
++ vzdq_aquotd_looktest, vzdq_aquotd_lookset,
++ (void *)(unsigned long)dev);
++ if (inode == NULL)
++ goto out;
++ unlock_new_inode(inode);
++
++ d_add(dentry, inode);
++ (void)set_exec_env(old_ve);
++ return NULL;
++
++out:
++ (void)set_exec_env(old_ve);
++ return ERR_PTR(-ENOENT);
++}
++
++static struct file_operations vzdq_aquotd_file_operations = {
++ .read = &generic_read_dir,
++ .readdir = &vzdq_aquotd_readdir,
++};
++
++static struct inode_operations vzdq_aquotd_inode_operations = {
++ .lookup = &vzdq_aquotd_lookup,
++};
++
++
++/* ----------------------------------------------------------------------
++ *
++ * Initialization and deinitialization
++ *
++ * --------------------------------------------------------------------- */
++static int fake_data;
++static struct ctl_table fake_table[] = {
++ {
++ .ctl_name = CTL_UNNUMBERED,
++ .procname = ".fake",
++ .mode = 0600,
++ .proc_handler = proc_dointvec,
++ .data = &fake_data,
++ .maxlen = sizeof(int),
++ },
++ { }
++};
++
++static struct ctl_path fake_path[] = {
++ { .ctl_name = CTL_FS, .procname = "fs", },
++ { .ctl_name = FS_DQSTATS, .procname = "quota", },
++ { }
++};
++
++/*
++ * FIXME: creation of proc entries here is unsafe with respect to module
++ * unloading.
++ */
++void vzaquota_init(void)
++{
++ struct proc_dir_entry *de;
++
++ de = proc_create("vzaquota", S_IFDIR | S_IRUSR | S_IXUSR,
++ glob_proc_vz_dir, &vzdq_aquotd_file_operations);
++ if (de != NULL)
++ de->proc_iops = &vzdq_aquotd_inode_operations;
++ else
++ printk("VZDQ: vz/vzaquota creation failed\n");
++
++ register_sysctl_glob_paths(fake_path, fake_table, 1);
++}
++
++void vzaquota_fini(void)
++{
++ remove_proc_entry("vz/vzaquota", NULL);
++}
+diff --git a/fs/vzdq_mgmt.c b/fs/vzdq_mgmt.c
+new file mode 100644
+index 0000000..7dda882
+--- /dev/null
++++ b/fs/vzdq_mgmt.c
+@@ -0,0 +1,753 @@
++/*
++ * Copyright (C) 2001, 2002, 2004, 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ */
++
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/list.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/dcache.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/writeback.h>
++#include <linux/gfp.h>
++#include <linux/module.h>
++#include <asm/uaccess.h>
++#include <linux/proc_fs.h>
++#include <linux/quota.h>
++#include <linux/vzctl_quota.h>
++#include <linux/vzquota.h>
++
++
++/* ----------------------------------------------------------------------
++ * Switching quota on.
++ * --------------------------------------------------------------------- */
++
++/*
++ * check limits copied from user
++ */
++int vzquota_check_sane_limits(struct dq_stat *qstat)
++{
++ int err;
++
++ err = -EINVAL;
++
++ /* softlimit must be less then hardlimit */
++ if (qstat->bsoftlimit > qstat->bhardlimit)
++ goto out;
++
++ if (qstat->isoftlimit > qstat->ihardlimit)
++ goto out;
++
++ err = 0;
++out:
++ return err;
++}
++
++/*
++ * check usage values copied from user
++ */
++int vzquota_check_sane_values(struct dq_stat *qstat)
++{
++ int err;
++
++ err = -EINVAL;
++
++ /* expiration time must not be set if softlimit was not exceeded */
++ if (qstat->bcurrent < qstat->bsoftlimit && qstat->btime != 0)
++ goto out;
++
++ if (qstat->icurrent < qstat->isoftlimit && qstat->itime != 0)
++ goto out;
++
++ err = vzquota_check_sane_limits(qstat);
++out:
++ return err;
++}
++
++/*
++ * create new quota master block
++ * this function should:
++ * - copy limits and usage parameters from user buffer;
++ * - allock, initialize quota block and insert it to hash;
++ */
++static int vzquota_create(unsigned int quota_id,
++ struct vz_quota_stat __user *u_qstat, int compat)
++{
++ int err;
++ struct vz_quota_stat qstat;
++ struct vz_quota_master *qmblk;
++
++ down(&vz_quota_sem);
++
++ err = -EFAULT;
++ if (!compat) {
++ if (copy_from_user(&qstat, u_qstat, sizeof(qstat)))
++ goto out;
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_vz_quota_stat cqstat;
++ if (copy_from_user(&cqstat, u_qstat, sizeof(cqstat)))
++ goto out;
++ compat_dqstat2dqstat(&cqstat.dq_stat, &qstat.dq_stat);
++ compat_dqinfo2dqinfo(&cqstat.dq_info, &qstat.dq_info);
++#endif
++ }
++
++ err = -EINVAL;
++ if (quota_id == 0)
++ goto out;
++
++ if (vzquota_check_sane_values(&qstat.dq_stat))
++ goto out;
++ err = 0;
++ qmblk = vzquota_alloc_master(quota_id, &qstat);
++
++ if (IS_ERR(qmblk)) /* ENOMEM or EEXIST */
++ err = PTR_ERR(qmblk);
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++/**
++ * vzquota_on - turn quota on
++ *
++ * This function should:
++ * - find and get refcnt of directory entry for quota root and corresponding
++ * mountpoint;
++ * - find corresponding quota block and mark it with given path;
++ * - check quota tree;
++ * - initialize quota for the tree root.
++ */
++static int vzquota_on(unsigned int quota_id, const char __user *quota_root,
++ char __user *buf)
++{
++ int err;
++ struct path path;
++ struct vz_quota_master *qmblk;
++ struct super_block *dqsb;
++
++ dqsb = NULL;
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EBUSY;
++ if (qmblk->dq_state != VZDQ_STARTING)
++ goto out;
++
++ err = user_path(quota_root, &path);
++ if (err)
++ goto out;
++ /* init path must be a directory */
++ err = -ENOTDIR;
++ if (!S_ISDIR(path.dentry->d_inode->i_mode))
++ goto out_path;
++
++ qmblk->dq_root_path = path;
++ qmblk->dq_sb = path.dentry->d_inode->i_sb;
++ err = vzquota_get_super(qmblk->dq_sb);
++ if (err)
++ goto out_super;
++
++ /*
++ * Serialization with quota initialization and operations is performed
++ * through generation check: generation is memorized before qmblk is
++ * found and compared under inode_qmblk_lock with assignment.
++ *
++ * Note that the dentry tree is shrunk only for high-level logical
++ * serialization, purely as a courtesy to the user: to have consistent
++ * quota statistics, files should be closed etc. on quota on.
++ */
++ err = vzquota_on_qmblk(qmblk->dq_sb, qmblk->dq_root_path.dentry->d_inode,
++ qmblk, buf);
++ if (err)
++ goto out_init;
++ qmblk->dq_state = VZDQ_WORKING;
++
++ up(&vz_quota_sem);
++ return 0;
++
++out_init:
++ dqsb = qmblk->dq_sb;
++out_super:
++ /* clear for qmblk_put/quota_free_master */
++ qmblk->dq_sb = NULL;
++ qmblk->dq_root_path.dentry = NULL;
++ qmblk->dq_root_path.mnt = NULL;
++out_path:
++ path_put(&path);
++out:
++ if (dqsb)
++ vzquota_put_super(dqsb);
++ up(&vz_quota_sem);
++ return err;
++}
++
++
++/* ----------------------------------------------------------------------
++ * Switching quota off.
++ * --------------------------------------------------------------------- */
++
++/*
++ * destroy quota block by ID
++ */
++static int vzquota_destroy(unsigned int quota_id)
++{
++ int err;
++ struct vz_quota_master *qmblk;
++ struct path root;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EBUSY;
++ if (qmblk->dq_state == VZDQ_WORKING)
++ goto out; /* quota_off first */
++
++ list_del_init(&qmblk->dq_hash);
++ root = qmblk->dq_root_path;
++ qmblk->dq_root_path.dentry = NULL;
++ qmblk->dq_root_path.mnt = NULL;
++
++ if (qmblk->dq_sb)
++ vzquota_put_super(qmblk->dq_sb);
++ up(&vz_quota_sem);
++
++ qmblk_put(qmblk);
++ path_put(&root);
++ return 0;
++
++out:
++ up(&vz_quota_sem);
++ return err;
++}
++
++/**
++ * vzquota_off - turn quota off
++ */
++
++static int __vzquota_sync_list(struct list_head *lh,
++ struct vz_quota_master *qmblk,
++ enum writeback_sync_modes sync_mode)
++{
++ struct writeback_control wbc;
++ LIST_HEAD(list);
++ struct vz_quota_ilink *qlnk;
++ struct inode *inode;
++ int err, ret;
++
++ memset(&wbc, 0, sizeof(wbc));
++ wbc.sync_mode = sync_mode;
++
++ err = ret = 0;
++ while (!list_empty(lh)) {
++ if (need_resched()) {
++ inode_qmblk_unlock(qmblk->dq_sb);
++ schedule();
++ inode_qmblk_lock(qmblk->dq_sb);
++ continue;
++ }
++
++ qlnk = list_first_entry(lh, struct vz_quota_ilink, list);
++ list_move(&qlnk->list, &list);
++
++ inode = igrab(QLNK_INODE(qlnk));
++ if (!inode)
++ continue;
++
++ inode_qmblk_unlock(qmblk->dq_sb);
++
++ wbc.nr_to_write = LONG_MAX;
++ ret = sync_inode(inode, &wbc);
++ if (ret)
++ err = ret;
++ iput(inode);
++
++ inode_qmblk_lock(qmblk->dq_sb);
++ }
++
++ list_splice(&list, lh);
++ return err;
++}
++
++static int vzquota_sync_list(struct list_head *lh,
++ struct vz_quota_master *qmblk)
++{
++ (void)__vzquota_sync_list(lh, qmblk, WB_SYNC_NONE);
++ return __vzquota_sync_list(lh, qmblk, WB_SYNC_ALL);
++}
++
++static int vzquota_sync_inodes(struct vz_quota_master *qmblk)
++{
++ int err;
++ LIST_HEAD(qlnk_list);
++
++ list_splice_init(&qmblk->dq_ilink_list, &qlnk_list);
++ err = vzquota_sync_list(&qlnk_list, qmblk);
++ if (!err && !list_empty(&qmblk->dq_ilink_list))
++ err = -EBUSY;
++ list_splice(&qlnk_list, &qmblk->dq_ilink_list);
++
++ return err;
++}
++
++static int vzquota_off(unsigned int quota_id, char __user *buf, int force)
++{
++ int err, ret;
++ struct vz_quota_master *qmblk;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EALREADY;
++ if (qmblk->dq_state != VZDQ_WORKING)
++ goto out;
++
++ inode_qmblk_lock(qmblk->dq_sb); /* protects dq_ilink_list also */
++ ret = vzquota_sync_inodes(qmblk);
++ inode_qmblk_unlock(qmblk->dq_sb);
++
++ err = vzquota_off_qmblk(qmblk->dq_sb, qmblk, buf, force);
++ if (err)
++ goto out;
++
++ err = ret;
++ /* vzquota_destroy will free resources */
++ qmblk->dq_state = VZDQ_STOPING;
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++
++/* ----------------------------------------------------------------------
++ * Other VZQUOTA ioctl's.
++ * --------------------------------------------------------------------- */
++
++/*
++ * this function should:
++ * - set new limits/buffer under quota master block lock
++ * - if new softlimit less then usage, then set expiration time
++ * - no need to alloc ugid hash table - we'll do that on demand
++ */
++int vzquota_update_limit(struct dq_stat *_qstat,
++ struct dq_stat *qstat)
++{
++ int err;
++
++ err = -EINVAL;
++ if (vzquota_check_sane_limits(qstat))
++ goto out;
++
++ err = 0;
++
++ /* limits */
++ _qstat->bsoftlimit = qstat->bsoftlimit;
++ _qstat->bhardlimit = qstat->bhardlimit;
++ /*
++ * If the soft limit is exceeded, administrator can override the moment
++ * when the grace period for limit exceeding ends.
++ * Specifying the moment may be useful if the soft limit is set to be
++ * lower than the current usage. In the latter case, if the grace
++ * period end isn't specified, the grace period will start from the
++ * moment of the first write operation.
++ * There is a race with the user level. Soft limit may be already
++ * exceeded before the limit change, and grace period end calculated by
++ * the kernel will be overriden. User level may check if the limit is
++ * already exceeded, but check and set calls are not atomic.
++ * This race isn't dangerous. Under normal cicrumstances, the
++ * difference between the grace period end calculated by the kernel and
++ * the user level should be not greater than as the difference between
++ * the moments of check and set calls, i.e. not bigger than the quota
++ * timer resolution - 1 sec.
++ */
++ if (qstat->btime != (time_t)0 &&
++ _qstat->bcurrent >= _qstat->bsoftlimit)
++ _qstat->btime = qstat->btime;
++
++ _qstat->isoftlimit = qstat->isoftlimit;
++ _qstat->ihardlimit = qstat->ihardlimit;
++ if (qstat->itime != (time_t)0 &&
++ _qstat->icurrent >= _qstat->isoftlimit)
++ _qstat->itime = qstat->itime;
++
++out:
++ return err;
++}
++
++/*
++ * set new quota limits.
++ * this function should:
++ * copy new limits from user level
++ * - find quota block
++ * - set new limits and flags.
++ */
++static int vzquota_setlimit(unsigned int quota_id,
++ struct vz_quota_stat __user *u_qstat, int compat)
++{
++ int err;
++ struct vz_quota_stat qstat;
++ struct vz_quota_master *qmblk;
++
++ down(&vz_quota_sem); /* for hash list protection */
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EFAULT;
++ if (!compat) {
++ if (copy_from_user(&qstat, u_qstat, sizeof(qstat)))
++ goto out;
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_vz_quota_stat cqstat;
++ if (copy_from_user(&cqstat, u_qstat, sizeof(cqstat)))
++ goto out;
++ compat_dqstat2dqstat(&cqstat.dq_stat, &qstat.dq_stat);
++ compat_dqinfo2dqinfo(&cqstat.dq_info, &qstat.dq_info);
++#endif
++ }
++
++ qmblk_data_write_lock(qmblk);
++ err = vzquota_update_limit(&qmblk->dq_stat, &qstat.dq_stat);
++ if (err == 0)
++ qmblk->dq_info = qstat.dq_info;
++ qmblk_data_write_unlock(qmblk);
++
++out:
++ up(&vz_quota_sem);
++ return err;
++}
++
++/*
++ * get quota limits.
++ * very simple - just return stat buffer to user
++ */
++static int vzquota_getstat(unsigned int quota_id,
++ struct vz_quota_stat __user *u_qstat, int compat)
++{
++ int err;
++ struct vz_quota_stat qstat;
++ struct vz_quota_master *qmblk;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ qmblk_data_read_lock(qmblk);
++ /* copy whole buffer under lock */
++ memcpy(&qstat.dq_stat, &qmblk->dq_stat, sizeof(qstat.dq_stat));
++ memcpy(&qstat.dq_info, &qmblk->dq_info, sizeof(qstat.dq_info));
++ qmblk_data_read_unlock(qmblk);
++
++ if (!compat)
++ err = copy_to_user(u_qstat, &qstat, sizeof(qstat));
++ else {
++#ifdef CONFIG_COMPAT
++ struct compat_vz_quota_stat cqstat;
++ dqstat2compat_dqstat(&qstat.dq_stat, &cqstat.dq_stat);
++ dqinfo2compat_dqinfo(&qstat.dq_info, &cqstat.dq_info);
++ err = copy_to_user(u_qstat, &cqstat, sizeof(cqstat));
++#endif
++ }
++ if (err)
++ err = -EFAULT;
++
++out:
++ up(&vz_quota_sem);
++ return err;
++}
++
++/*
++ * This is a system call to turn per-VE disk quota on.
++ * Note this call is allowed to run ONLY from VE0
++ */
++long do_vzquotactl(int cmd, unsigned int quota_id,
++ struct vz_quota_stat __user *qstat, const char __user *ve_root,
++ int compat)
++{
++ int ret;
++ int force = 0;
++
++ ret = -EPERM;
++ /* access allowed only from root of VE0 */
++ if (!capable(CAP_SYS_RESOURCE) ||
++ !capable(CAP_SYS_ADMIN))
++ goto out;
++
++ switch (cmd) {
++ case VZ_DQ_CREATE:
++ ret = vzquota_create(quota_id, qstat, compat);
++ break;
++ case VZ_DQ_DESTROY:
++ ret = vzquota_destroy(quota_id);
++ break;
++ case VZ_DQ_ON:
++ /*
++ * qstat is just a pointer to userspace buffer to
++ * store busy files path in case of vzquota_on fail
++ */
++ ret = vzquota_on(quota_id, ve_root, (char *)qstat);
++ break;
++ case VZ_DQ_OFF_FORCED:
++ force = 1;
++ case VZ_DQ_OFF:
++ /*
++ * ve_root is just a pointer to userspace buffer to
++ * store busy files path in case of vzquota_off fail
++ */
++ ret = vzquota_off(quota_id, (char *)ve_root, force);
++ break;
++ case VZ_DQ_SETLIMIT:
++ ret = vzquota_setlimit(quota_id, qstat, compat);
++ break;
++ case VZ_DQ_GETSTAT:
++ ret = vzquota_getstat(quota_id, qstat, compat);
++ break;
++
++ default:
++ ret = -EINVAL;
++ goto out;
++ }
++
++out:
++ return ret;
++}
++
++
++/* ----------------------------------------------------------------------
++ * Proc filesystem routines
++ * ---------------------------------------------------------------------*/
++
++#if defined(CONFIG_PROC_FS)
++
++#define QUOTA_UINT_LEN 15
++#define QUOTA_TIME_LEN_FMT_UINT "%11u"
++#define QUOTA_NUM_LEN_FMT_UINT "%15u"
++#define QUOTA_NUM_LEN_FMT_ULL "%15Lu"
++#define QUOTA_TIME_LEN_FMT_STR "%11s"
++#define QUOTA_NUM_LEN_FMT_STR "%15s"
++#define QUOTA_PROC_MAX_LINE_LEN 2048
++
++/*
++ * prints /proc/ve_dq header line
++ */
++static int print_proc_header(char * buffer)
++{
++ return sprintf(buffer,
++ "%-11s"
++ QUOTA_NUM_LEN_FMT_STR
++ QUOTA_NUM_LEN_FMT_STR
++ QUOTA_NUM_LEN_FMT_STR
++ QUOTA_TIME_LEN_FMT_STR
++ QUOTA_TIME_LEN_FMT_STR
++ "\n",
++ "qid: path",
++ "usage", "softlimit", "hardlimit", "time", "expire");
++}
++
++/*
++ * prints proc master record id, dentry path
++ */
++static int print_proc_master_id(char * buffer, char * path_buf,
++ struct vz_quota_master * qp)
++{
++ char *path;
++ int over;
++
++ path = NULL;
++ switch (qp->dq_state) {
++ case VZDQ_WORKING:
++ if (!path_buf) {
++ path = "";
++ break;
++ }
++ path = d_path(&qp->dq_root_path, path_buf, PAGE_SIZE);
++ if (IS_ERR(path)) {
++ path = "";
++ break;
++ }
++ /* do not print large path, truncate it */
++ over = strlen(path) -
++ (QUOTA_PROC_MAX_LINE_LEN - 3 - 3 -
++ QUOTA_UINT_LEN);
++ if (over > 0) {
++ path += over - 3;
++ path[0] = path[1] = path[3] = '.';
++ }
++ break;
++ case VZDQ_STARTING:
++ path = "-- started --";
++ break;
++ case VZDQ_STOPING:
++ path = "-- stopped --";
++ break;
++ }
++
++ return sprintf(buffer, "%u: %s\n", qp->dq_id, path);
++}
++
++/*
++ * prints struct vz_quota_stat data
++ */
++static int print_proc_stat(char * buffer, struct dq_stat *qs,
++ struct dq_info *qi)
++{
++ return sprintf(buffer,
++ "%11s"
++ QUOTA_NUM_LEN_FMT_ULL
++ QUOTA_NUM_LEN_FMT_ULL
++ QUOTA_NUM_LEN_FMT_ULL
++ QUOTA_TIME_LEN_FMT_UINT
++ QUOTA_TIME_LEN_FMT_UINT
++ "\n"
++ "%11s"
++ QUOTA_NUM_LEN_FMT_UINT
++ QUOTA_NUM_LEN_FMT_UINT
++ QUOTA_NUM_LEN_FMT_UINT
++ QUOTA_TIME_LEN_FMT_UINT
++ QUOTA_TIME_LEN_FMT_UINT
++ "\n",
++ "1k-blocks",
++ (unsigned long long)qs->bcurrent >> 10,
++ (unsigned long long)qs->bsoftlimit >> 10,
++ (unsigned long long)qs->bhardlimit >> 10,
++ (unsigned int)qs->btime,
++ (unsigned int)qi->bexpire,
++ "inodes",
++ qs->icurrent,
++ qs->isoftlimit,
++ qs->ihardlimit,
++ (unsigned int)qs->itime,
++ (unsigned int)qi->iexpire);
++}
++
++
++/*
++ * for /proc filesystem output
++ */
++static int vzquota_read_proc(char *page, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int len, i;
++ off_t printed = 0;
++ char *p = page;
++ struct vz_quota_master *qp;
++ struct vz_quota_ilink *ql2;
++ struct list_head *listp;
++ char *path_buf;
++
++ path_buf = (char*)__get_free_page(GFP_KERNEL);
++ if (path_buf == NULL)
++ return -ENOMEM;
++
++ len = print_proc_header(p);
++ printed += len;
++ if (off < printed) /* keep header in output */ {
++ *start = p + off;
++ p += len;
++ }
++
++ down(&vz_quota_sem);
++
++ /* traverse master hash table for all records */
++ for (i = 0; i < vzquota_hash_size; i++) {
++ list_for_each(listp, &vzquota_hash_table[i]) {
++ qp = list_entry(listp,
++ struct vz_quota_master, dq_hash);
++
++ /* Skip other VE's information if not root of VE0 */
++ if ((!capable(CAP_SYS_ADMIN) ||
++ !capable(CAP_SYS_RESOURCE))) {
++ ql2 = INODE_QLNK(current->fs->root.dentry->d_inode);
++ if (ql2 == NULL || qp != ql2->qmblk)
++ continue;
++ }
++ /*
++ * Now print the next record
++ */
++ len = 0;
++ /* we print quotaid and path only in VE0 */
++ if (capable(CAP_SYS_ADMIN))
++ len += print_proc_master_id(p+len,path_buf, qp);
++ len += print_proc_stat(p+len, &qp->dq_stat,
++ &qp->dq_info);
++ printed += len;
++ /* skip unnecessary lines */
++ if (printed <= off)
++ continue;
++ p += len;
++ /* provide start offset */
++ if (*start == NULL)
++ *start = p + (off - printed);
++ /* have we printed all requested size? */
++ if (PAGE_SIZE - (p - page) < QUOTA_PROC_MAX_LINE_LEN ||
++ (p - *start) >= count)
++ goto out;
++ }
++ }
++
++ *eof = 1; /* checked all hash */
++out:
++ up(&vz_quota_sem);
++
++ len = 0;
++ if (*start != NULL) {
++ len = (p - *start);
++ if (len > count)
++ len = count;
++ }
++
++ if (path_buf)
++ free_page((unsigned long) path_buf);
++
++ return len;
++}
++
++/*
++ * Register procfs read callback
++ */
++int vzquota_proc_init(void)
++{
++ struct proc_dir_entry *de;
++
++ de = proc_create("vzquota", S_IFREG|S_IRUSR, proc_vz_dir, NULL);
++ if (de == NULL)
++ return -EBUSY;
++
++ de->read_proc = vzquota_read_proc;
++ de->data = NULL;
++ return 0;
++}
++
++void vzquota_proc_release(void)
++{
++ /* Unregister procfs read callback */
++ remove_proc_entry("vzquota", proc_vz_dir);
++}
++
++#endif
+diff --git a/fs/vzdq_ops.c b/fs/vzdq_ops.c
+new file mode 100644
+index 0000000..408bd28
+--- /dev/null
++++ b/fs/vzdq_ops.c
+@@ -0,0 +1,632 @@
++/*
++ * Copyright (C) 2001, 2002, 2004, 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/quota.h>
++#include <linux/vzquota.h>
++
++
++/* ----------------------------------------------------------------------
++ * Quota superblock operations - helper functions.
++ * --------------------------------------------------------------------- */
++
++static inline void vzquota_incr_inodes(struct dq_stat *dqstat,
++ unsigned long number)
++{
++ dqstat->icurrent += number;
++}
++
++static inline void vzquota_incr_space(struct dq_stat *dqstat,
++ __u64 number)
++{
++ dqstat->bcurrent += number;
++}
++
++static inline void vzquota_decr_inodes(struct dq_stat *dqstat,
++ unsigned long number)
++{
++ if (dqstat->icurrent > number)
++ dqstat->icurrent -= number;
++ else
++ dqstat->icurrent = 0;
++ if (dqstat->icurrent < dqstat->isoftlimit)
++ dqstat->itime = (time_t) 0;
++}
++
++static inline void vzquota_decr_space(struct dq_stat *dqstat,
++ __u64 number)
++{
++ if (dqstat->bcurrent > number)
++ dqstat->bcurrent -= number;
++ else
++ dqstat->bcurrent = 0;
++ if (dqstat->bcurrent < dqstat->bsoftlimit)
++ dqstat->btime = (time_t) 0;
++}
++
++/*
++ * better printk() message or use /proc/vzquotamsg interface
++ * similar to /proc/kmsg
++ */
++static inline void vzquota_warn(struct dq_info *dq_info, int dq_id, int flag,
++ const char *fmt)
++{
++ if (dq_info->flags & flag) /* warning already printed for this
++ masterblock */
++ return;
++ printk(fmt, dq_id);
++ dq_info->flags |= flag;
++}
++
++/*
++ * ignore_hardlimit -
++ *
++ * Intended to allow superuser of VE0 to overwrite hardlimits.
++ *
++ * ignore_hardlimit() has a very bad feature:
++ *
++ * writepage() operation for writable mapping of a file with holes
++ * may trigger get_block() with wrong current and as a consequence,
++ * opens a possibility to overcommit hardlimits
++ */
++/* for the reason above, it is disabled now */
++static inline int ignore_hardlimit(struct dq_info *dqstat)
++{
++#if 0
++ return ve_is_super(get_exec_env()) &&
++ capable(CAP_SYS_RESOURCE) &&
++ (dqstat->options & VZ_QUOTA_OPT_RSQUASH);
++#else
++ return 0;
++#endif
++}
++
++static int vzquota_check_inodes(struct dq_info *dq_info,
++ struct dq_stat *dqstat,
++ unsigned long number, int dq_id)
++{
++ if (number == 0)
++ return QUOTA_OK;
++
++ if (dqstat->icurrent + number > dqstat->ihardlimit &&
++ !ignore_hardlimit(dq_info)) {
++ vzquota_warn(dq_info, dq_id, VZ_QUOTA_INODES,
++ "VZ QUOTA: file hardlimit reached for id=%d\n");
++ return NO_QUOTA;
++ }
++
++ if (dqstat->icurrent + number > dqstat->isoftlimit) {
++ if (dqstat->itime == (time_t)0) {
++ vzquota_warn(dq_info, dq_id, 0,
++ "VZ QUOTA: file softlimit exceeded "
++ "for id=%d\n");
++ dqstat->itime = CURRENT_TIME_SECONDS +
++ dq_info->iexpire;
++ } else if (CURRENT_TIME_SECONDS >= dqstat->itime &&
++ !ignore_hardlimit(dq_info)) {
++ vzquota_warn(dq_info, dq_id, VZ_QUOTA_INODES,
++ "VZ QUOTA: file softlimit expired "
++ "for id=%d\n");
++ return NO_QUOTA;
++ }
++ }
++
++ return QUOTA_OK;
++}
++
++static int vzquota_check_space(struct dq_info *dq_info,
++ struct dq_stat *dqstat,
++ __u64 number, int dq_id, char prealloc)
++{
++ if (number == 0)
++ return QUOTA_OK;
++
++ if (prealloc == DQUOT_CMD_FORCE)
++ return QUOTA_OK;
++
++ if (dqstat->bcurrent + number > dqstat->bhardlimit &&
++ !ignore_hardlimit(dq_info)) {
++ if (!prealloc)
++ vzquota_warn(dq_info, dq_id, VZ_QUOTA_SPACE,
++ "VZ QUOTA: disk hardlimit reached "
++ "for id=%d\n");
++ return NO_QUOTA;
++ }
++
++ if (dqstat->bcurrent + number > dqstat->bsoftlimit) {
++ if (dqstat->btime == (time_t)0) {
++ if (!prealloc) {
++ vzquota_warn(dq_info, dq_id, 0,
++ "VZ QUOTA: disk softlimit exceeded "
++ "for id=%d\n");
++ dqstat->btime = CURRENT_TIME_SECONDS
++ + dq_info->bexpire;
++ } else {
++ /*
++ * Original Linux quota doesn't allow
++ * preallocation to exceed softlimit so
++ * exceeding will be always printed
++ */
++ return NO_QUOTA;
++ }
++ } else if (CURRENT_TIME_SECONDS >= dqstat->btime &&
++ !ignore_hardlimit(dq_info)) {
++ if (!prealloc)
++ vzquota_warn(dq_info, dq_id, VZ_QUOTA_SPACE,
++ "VZ QUOTA: disk quota "
++ "softlimit expired "
++ "for id=%d\n");
++ return NO_QUOTA;
++ }
++ }
++
++ return QUOTA_OK;
++}
++
++#ifdef CONFIG_VZ_QUOTA_UGID
++static int vzquota_check_ugid_inodes(struct vz_quota_master *qmblk,
++ struct vz_quota_ugid *qugid[],
++ int type, unsigned long number)
++{
++ struct dq_info *dqinfo;
++ struct dq_stat *dqstat;
++
++ if (qugid[type] == NULL)
++ return QUOTA_OK;
++ if (qugid[type] == VZ_QUOTA_UGBAD)
++ return NO_QUOTA;
++
++ if (type == USRQUOTA && !(qmblk->dq_flags & VZDQ_USRQUOTA))
++ return QUOTA_OK;
++ if (type == GRPQUOTA && !(qmblk->dq_flags & VZDQ_GRPQUOTA))
++ return QUOTA_OK;
++ if (number == 0)
++ return QUOTA_OK;
++
++ dqinfo = &qmblk->dq_ugid_info[type];
++ dqstat = &qugid[type]->qugid_stat;
++
++ if (dqstat->ihardlimit != 0 &&
++ dqstat->icurrent + number > dqstat->ihardlimit)
++ return NO_QUOTA;
++
++ if (dqstat->isoftlimit != 0 &&
++ dqstat->icurrent + number > dqstat->isoftlimit) {
++ if (dqstat->itime == (time_t)0)
++ dqstat->itime = CURRENT_TIME_SECONDS +
++ dqinfo->iexpire;
++ else if (CURRENT_TIME_SECONDS >= dqstat->itime)
++ return NO_QUOTA;
++ }
++
++ return QUOTA_OK;
++}
++
++static int vzquota_check_ugid_space(struct vz_quota_master *qmblk,
++ struct vz_quota_ugid *qugid[],
++ int type, __u64 number, char prealloc)
++{
++ struct dq_info *dqinfo;
++ struct dq_stat *dqstat;
++
++ if (prealloc == DQUOT_CMD_FORCE)
++ return QUOTA_OK;
++
++ if (qugid[type] == NULL)
++ return QUOTA_OK;
++ if (qugid[type] == VZ_QUOTA_UGBAD)
++ return NO_QUOTA;
++
++ if (type == USRQUOTA && !(qmblk->dq_flags & VZDQ_USRQUOTA))
++ return QUOTA_OK;
++ if (type == GRPQUOTA && !(qmblk->dq_flags & VZDQ_GRPQUOTA))
++ return QUOTA_OK;
++ if (number == 0)
++ return QUOTA_OK;
++
++ dqinfo = &qmblk->dq_ugid_info[type];
++ dqstat = &qugid[type]->qugid_stat;
++
++ if (dqstat->bhardlimit != 0 &&
++ dqstat->bcurrent + number > dqstat->bhardlimit)
++ return NO_QUOTA;
++
++ if (dqstat->bsoftlimit != 0 &&
++ dqstat->bcurrent + number > dqstat->bsoftlimit) {
++ if (dqstat->btime == (time_t)0) {
++ if (!prealloc)
++ dqstat->btime = CURRENT_TIME_SECONDS
++ + dqinfo->bexpire;
++ else
++ /*
++ * Original Linux quota doesn't allow
++ * preallocation to exceed softlimit so
++ * exceeding will be always printed
++ */
++ return NO_QUOTA;
++ } else if (CURRENT_TIME_SECONDS >= dqstat->btime)
++ return NO_QUOTA;
++ }
++
++ return QUOTA_OK;
++}
++#endif
++
++/* ----------------------------------------------------------------------
++ * Quota superblock operations
++ * --------------------------------------------------------------------- */
++
++/*
++ * S_NOQUOTA note.
++ * In the current kernel (2.6.8.1), S_NOQUOTA flag is set only for
++ * - quota file (absent in our case)
++ * - after explicit DQUOT_DROP (earlier than clear_inode) in functions like
++ * filesystem-specific new_inode, before the inode gets outside links.
++ * For the latter case, the only quota operation where care about S_NOQUOTA
++ * might be required is vzquota_drop, but there S_NOQUOTA has already been
++ * checked in DQUOT_DROP().
++ * So, S_NOQUOTA may be ignored for now in the VZDQ code.
++ *
++ * The above note is not entirely correct.
++ * Both for ext2 and ext3 filesystems, DQUOT_FREE_INODE is called from
++ * delete_inode if new_inode fails (for example, because of inode quota
++ * limits), so S_NOQUOTA check is needed in free_inode.
++ * This seems to be the dark corner of the current quota API.
++ */
++
++/*
++ * Initialize quota operations for the specified inode.
++ */
++static int vzquota_initialize(struct inode *inode, int type)
++{
++ vzquota_inode_init_call(inode);
++ return 0; /* ignored by caller */
++}
++
++/*
++ * Release quota for the specified inode.
++ */
++static int vzquota_drop(struct inode *inode)
++{
++ vzquota_inode_drop_call(inode);
++ return 0; /* ignored by caller */
++}
++
++/*
++ * Allocate block callback.
++ *
++ * If (prealloc) disk quota exceeding warning is not printed.
++ * See Linux quota to know why.
++ *
++ * Return:
++ * QUOTA_OK == 0 on SUCCESS
++ * NO_QUOTA == 1 if allocation should fail
++ */
++static int vzquota_alloc_space(struct inode *inode,
++ qsize_t number, int prealloc)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++ int ret = QUOTA_OK;
++
++ qmblk = vzquota_inode_data(inode, &data);
++ if (qmblk == VZ_QUOTA_BAD)
++ return NO_QUOTA;
++ if (qmblk != NULL) {
++#ifdef CONFIG_VZ_QUOTA_UGID
++ int cnt;
++ struct vz_quota_ugid * qugid[MAXQUOTAS];
++#endif
++
++ /* checking first */
++ ret = vzquota_check_space(&qmblk->dq_info, &qmblk->dq_stat,
++ number, qmblk->dq_id, prealloc);
++ if (ret == NO_QUOTA)
++ goto no_quota;
++#ifdef CONFIG_VZ_QUOTA_UGID
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ qugid[cnt] = INODE_QLNK(inode)->qugid[cnt];
++ ret = vzquota_check_ugid_space(qmblk, qugid,
++ cnt, number, prealloc);
++ if (ret == NO_QUOTA)
++ goto no_quota;
++ }
++ /* check ok, may increment */
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ if (qugid[cnt] == NULL)
++ continue;
++ vzquota_incr_space(&qugid[cnt]->qugid_stat, number);
++ }
++#endif
++ vzquota_incr_space(&qmblk->dq_stat, number);
++ vzquota_data_unlock(inode, &data);
++ }
++
++ inode_add_bytes(inode, number);
++ might_sleep();
++ return QUOTA_OK;
++
++no_quota:
++ vzquota_data_unlock(inode, &data);
++ return NO_QUOTA;
++}
++
++/*
++ * Allocate inodes callback.
++ *
++ * Return:
++ * QUOTA_OK == 0 on SUCCESS
++ * NO_QUOTA == 1 if allocation should fail
++ */
++static int vzquota_alloc_inode(const struct inode *inode, unsigned long number)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++ int ret = QUOTA_OK;
++
++ qmblk = vzquota_inode_data((struct inode *)inode, &data);
++ if (qmblk == VZ_QUOTA_BAD)
++ return NO_QUOTA;
++ if (qmblk != NULL) {
++#ifdef CONFIG_VZ_QUOTA_UGID
++ int cnt;
++ struct vz_quota_ugid *qugid[MAXQUOTAS];
++#endif
++
++ /* checking first */
++ ret = vzquota_check_inodes(&qmblk->dq_info, &qmblk->dq_stat,
++ number, qmblk->dq_id);
++ if (ret == NO_QUOTA)
++ goto no_quota;
++#ifdef CONFIG_VZ_QUOTA_UGID
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ qugid[cnt] = INODE_QLNK(inode)->qugid[cnt];
++ ret = vzquota_check_ugid_inodes(qmblk, qugid,
++ cnt, number);
++ if (ret == NO_QUOTA)
++ goto no_quota;
++ }
++ /* check ok, may increment */
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ if (qugid[cnt] == NULL)
++ continue;
++ vzquota_incr_inodes(&qugid[cnt]->qugid_stat, number);
++ }
++#endif
++ vzquota_incr_inodes(&qmblk->dq_stat, number);
++ vzquota_data_unlock((struct inode *)inode, &data);
++ }
++
++ might_sleep();
++ return QUOTA_OK;
++
++no_quota:
++ vzquota_data_unlock((struct inode *)inode, &data);
++ return NO_QUOTA;
++}
++
++/*
++ * Free space callback.
++ */
++static int vzquota_free_space(struct inode *inode, qsize_t number)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++
++ qmblk = vzquota_inode_data(inode, &data);
++ if (qmblk == VZ_QUOTA_BAD)
++ return NO_QUOTA; /* isn't checked by the caller */
++ if (qmblk != NULL) {
++#ifdef CONFIG_VZ_QUOTA_UGID
++ int cnt;
++ struct vz_quota_ugid * qugid;
++#endif
++
++ vzquota_decr_space(&qmblk->dq_stat, number);
++#ifdef CONFIG_VZ_QUOTA_UGID
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ qugid = INODE_QLNK(inode)->qugid[cnt];
++ if (qugid == NULL || qugid == VZ_QUOTA_UGBAD)
++ continue;
++ vzquota_decr_space(&qugid->qugid_stat, number);
++ }
++#endif
++ vzquota_data_unlock(inode, &data);
++ }
++ inode_sub_bytes(inode, number);
++ might_sleep();
++ return QUOTA_OK;
++}
++
++/*
++ * Free inodes callback.
++ */
++static int vzquota_free_inode(const struct inode *inode, unsigned long number)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++
++ qmblk = vzquota_inode_data((struct inode *)inode, &data);
++ if (qmblk == VZ_QUOTA_BAD)
++ return NO_QUOTA;
++ if (qmblk != NULL) {
++#ifdef CONFIG_VZ_QUOTA_UGID
++ int cnt;
++ struct vz_quota_ugid * qugid;
++#endif
++
++ vzquota_decr_inodes(&qmblk->dq_stat, number);
++#ifdef CONFIG_VZ_QUOTA_UGID
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ qugid = INODE_QLNK(inode)->qugid[cnt];
++ if (qugid == NULL || qugid == VZ_QUOTA_UGBAD)
++ continue;
++ vzquota_decr_inodes(&qugid->qugid_stat, number);
++ }
++#endif
++ vzquota_data_unlock((struct inode *)inode, &data);
++ }
++ might_sleep();
++ return QUOTA_OK;
++}
++
++void vzquota_inode_off(struct inode * inode)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++
++ /* The call is made through virtinfo, it can be an inode
++ * not controlled by vzquota.
++ */
++ if (inode->i_sb->dq_op != &vz_quota_operations)
++ return;
++
++ qmblk = vzquota_inode_data(inode, &data);
++ if (qmblk == VZ_QUOTA_BAD)
++ return;
++
++ if (qmblk == NULL) {
++ /* Tricky place. If qmblk == NULL, it means that this inode
++ * is not in area controlled by vzquota (except for rare
++ * case of already set S_NOQUOTA). But we have to set
++ * S_NOQUOTA in any case because vzquota can be turned
++ * on later, when this inode is invalid from viewpoint
++ * of vzquota.
++ *
++ * To be safe, we reacquire vzquota lock.
++ */
++ inode_qmblk_lock(inode->i_sb);
++ inode->i_flags |= S_NOQUOTA;
++ inode_qmblk_unlock(inode->i_sb);
++ return;
++ } else {
++ loff_t bytes = inode_get_bytes(inode);
++#ifdef CONFIG_VZ_QUOTA_UGID
++ int cnt;
++ struct vz_quota_ugid * qugid;
++#endif
++
++ inode->i_flags |= S_NOQUOTA;
++
++ vzquota_decr_space(&qmblk->dq_stat, bytes);
++ vzquota_decr_inodes(&qmblk->dq_stat, 1);
++#ifdef CONFIG_VZ_QUOTA_UGID
++ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
++ qugid = INODE_QLNK(inode)->qugid[cnt];
++ if (qugid == NULL || qugid == VZ_QUOTA_UGBAD)
++ continue;
++ vzquota_decr_space(&qugid->qugid_stat, bytes);
++ vzquota_decr_inodes(&qugid->qugid_stat, 1);
++ }
++#endif
++
++ vzquota_data_unlock(inode, &data);
++
++ vzquota_inode_drop_call(inode);
++ }
++}
++
++
++#ifdef CONFIG_VZ_QUOTA_UGID
++
++/*
++ * helper function for quota_transfer
++ * check that we can add inode to this quota_id
++ */
++static int vzquota_transfer_check(struct vz_quota_master *qmblk,
++ struct vz_quota_ugid *qugid[],
++ unsigned int type, __u64 size)
++{
++ if (vzquota_check_ugid_space(qmblk, qugid, type, size, 0) != QUOTA_OK ||
++ vzquota_check_ugid_inodes(qmblk, qugid, type, 1) != QUOTA_OK)
++ return -1;
++ return 0;
++}
++
++int vzquota_transfer_usage(struct inode *inode,
++ int mask,
++ struct vz_quota_ilink *qlnk)
++{
++ struct vz_quota_ugid *qugid_old;
++ __u64 space;
++ int i;
++
++ space = inode_get_bytes(inode);
++ for (i = 0; i < MAXQUOTAS; i++) {
++ if (!(mask & (1 << i)))
++ continue;
++ /*
++ * Do not permit chown a file if its owner does not have
++ * ugid record. This might happen if we somehow exceeded
++ * the UID/GID (e.g. set uglimit less than number of users).
++ */
++ if (INODE_QLNK(inode)->qugid[i] == VZ_QUOTA_UGBAD)
++ return -1;
++ if (vzquota_transfer_check(qlnk->qmblk, qlnk->qugid, i, space))
++ return -1;
++ }
++
++ for (i = 0; i < MAXQUOTAS; i++) {
++ if (!(mask & (1 << i)))
++ continue;
++ qugid_old = INODE_QLNK(inode)->qugid[i];
++ vzquota_decr_space(&qugid_old->qugid_stat, space);
++ vzquota_decr_inodes(&qugid_old->qugid_stat, 1);
++ vzquota_incr_space(&qlnk->qugid[i]->qugid_stat, space);
++ vzquota_incr_inodes(&qlnk->qugid[i]->qugid_stat, 1);
++ }
++ return 0;
++}
++
++/*
++ * Transfer the inode between diffent user/group quotas.
++ */
++static int vzquota_transfer(struct inode *inode, struct iattr *iattr)
++{
++ return vzquota_inode_transfer_call(inode, iattr) ?
++ NO_QUOTA : QUOTA_OK;
++}
++
++#else /* CONFIG_VZ_QUOTA_UGID */
++
++static int vzquota_transfer(struct inode *inode, struct iattr *iattr)
++{
++ return QUOTA_OK;
++}
++
++#endif
++
++/*
++ * Called under following semaphores:
++ * old_d->d_inode->i_sb->s_vfs_rename_sem
++ * old_d->d_inode->i_sem
++ * new_d->d_inode->i_sem
++ * [not verified --SAW]
++ */
++static int vzquota_rename(struct inode *inode,
++ struct inode *old_dir, struct inode *new_dir)
++{
++ return vzquota_rename_check(inode, old_dir, new_dir) ?
++ NO_QUOTA : QUOTA_OK;
++}
++
++/*
++ * Structure of superblock diskquota operations.
++ */
++struct dquot_operations vz_quota_operations = {
++ .initialize = vzquota_initialize,
++ .drop = vzquota_drop,
++ .alloc_space = vzquota_alloc_space,
++ .alloc_inode = vzquota_alloc_inode,
++ .free_space = vzquota_free_space,
++ .free_inode = vzquota_free_inode,
++ .transfer = vzquota_transfer,
++ .rename = vzquota_rename,
++};
+diff --git a/fs/vzdq_tree.c b/fs/vzdq_tree.c
+new file mode 100644
+index 0000000..f4f2152
+--- /dev/null
++++ b/fs/vzdq_tree.c
+@@ -0,0 +1,286 @@
++/*
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains Virtuozzo quota tree implementation
++ */
++
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/vzdq_tree.h>
++
++struct quotatree_tree *quotatree_alloc(void)
++{
++ int l;
++ struct quotatree_tree *tree;
++
++ tree = kmalloc(sizeof(struct quotatree_tree), GFP_KERNEL);
++ if (tree == NULL)
++ goto out;
++
++ for (l = 0; l < QUOTATREE_DEPTH; l++) {
++ INIT_LIST_HEAD(&tree->levels[l].usedlh);
++ INIT_LIST_HEAD(&tree->levels[l].freelh);
++ tree->levels[l].freenum = 0;
++ }
++ tree->root = NULL;
++ tree->leaf_num = 0;
++out:
++ return tree;
++}
++
++static struct quotatree_node *
++quotatree_follow(struct quotatree_tree *tree, quotaid_t id, int level,
++ struct quotatree_find_state *st)
++{
++ void **block;
++ struct quotatree_node *parent;
++ int l, index;
++
++ parent = NULL;
++ block = (void **)&tree->root;
++ l = 0;
++ while (l < level && *block != NULL) {
++ index = (id >> QUOTATREE_BSHIFT(l)) & QUOTATREE_BMASK;
++ parent = *block;
++ block = parent->blocks + index;
++ l++;
++ }
++ if (st != NULL) {
++ st->block = block;
++ st->level = l;
++ }
++
++ return parent;
++}
++
++void *quotatree_find(struct quotatree_tree *tree, quotaid_t id,
++ struct quotatree_find_state *st)
++{
++ quotatree_follow(tree, id, QUOTATREE_DEPTH, st);
++ if (st->level == QUOTATREE_DEPTH)
++ return *st->block;
++ else
++ return NULL;
++}
++
++void *quotatree_leaf_byindex(struct quotatree_tree *tree, unsigned int index)
++{
++ int i, count;
++ struct quotatree_node *p;
++ void *leaf;
++
++ if (QTREE_LEAFNUM(tree) <= index)
++ return NULL;
++
++ count = 0;
++ list_for_each_entry(p, &QTREE_LEAFLVL(tree)->usedlh, list) {
++ for (i = 0; i < QUOTATREE_BSIZE; i++) {
++ leaf = p->blocks[i];
++ if (leaf == NULL)
++ continue;
++ if (count == index)
++ return leaf;
++ count++;
++ }
++ }
++ return NULL;
++}
++
++/* returns data leaf (vz_quota_ugid) after _existent_ ugid (@id)
++ * in the tree... */
++void *quotatree_get_next(struct quotatree_tree *tree, quotaid_t id)
++{
++ int off;
++ struct quotatree_node *parent, *p;
++ struct list_head *lh;
++
++ /* get parent refering correct quota tree node of the last level */
++ parent = quotatree_follow(tree, id, QUOTATREE_DEPTH, NULL);
++ if (!parent)
++ return NULL;
++
++ off = (id & QUOTATREE_BMASK) + 1; /* next ugid */
++ lh = &parent->list;
++ do {
++ p = list_entry(lh, struct quotatree_node, list);
++ for ( ; off < QUOTATREE_BSIZE; off++)
++ if (p->blocks[off])
++ return p->blocks[off];
++ off = 0;
++ lh = lh->next;
++ } while (lh != &QTREE_LEAFLVL(tree)->usedlh);
++
++ return NULL;
++}
++
++int quotatree_insert(struct quotatree_tree *tree, quotaid_t id,
++ struct quotatree_find_state *st, void *data)
++{
++ struct quotatree_node *p;
++ int l, index;
++
++ while (st->level < QUOTATREE_DEPTH) {
++ l = st->level;
++ if (!list_empty(&tree->levels[l].freelh)) {
++ p = list_entry(tree->levels[l].freelh.next,
++ struct quotatree_node, list);
++ list_del(&p->list);
++ } else {
++ p = kmalloc(sizeof(struct quotatree_node), GFP_NOFS | __GFP_NOFAIL);
++ if (p == NULL)
++ return -ENOMEM;
++ /* save block number in the l-level
++ * it uses for quota file generation */
++ p->num = tree->levels[l].freenum++;
++ }
++ list_add(&p->list, &tree->levels[l].usedlh);
++ memset(p->blocks, 0, sizeof(p->blocks));
++ *st->block = p;
++
++ index = (id >> QUOTATREE_BSHIFT(l)) & QUOTATREE_BMASK;
++ st->block = p->blocks + index;
++ st->level++;
++ }
++ tree->leaf_num++;
++ *st->block = data;
++
++ return 0;
++}
++
++static struct quotatree_node *
++quotatree_remove_ptr(struct quotatree_tree *tree, quotaid_t id,
++ int level)
++{
++ struct quotatree_node *parent;
++ struct quotatree_find_state st;
++
++ parent = quotatree_follow(tree, id, level, &st);
++ if (st.level == QUOTATREE_DEPTH)
++ tree->leaf_num--;
++ *st.block = NULL;
++ return parent;
++}
++
++void quotatree_remove(struct quotatree_tree *tree, quotaid_t id)
++{
++ struct quotatree_node *p;
++ int level, i;
++
++ p = quotatree_remove_ptr(tree, id, QUOTATREE_DEPTH);
++ for (level = QUOTATREE_DEPTH - 1; level >= QUOTATREE_CDEPTH; level--) {
++ for (i = 0; i < QUOTATREE_BSIZE; i++)
++ if (p->blocks[i] != NULL)
++ return;
++ list_move(&p->list, &tree->levels[level].freelh);
++ p = quotatree_remove_ptr(tree, id, level);
++ }
++}
++
++#if 0
++static void quotatree_walk(struct quotatree_tree *tree,
++ struct quotatree_node *node_start,
++ quotaid_t id_start,
++ int level_start, int level_end,
++ int (*callback)(struct quotatree_tree *,
++ quotaid_t id,
++ int level,
++ void *ptr,
++ void *data),
++ void *data)
++{
++ struct quotatree_node *p;
++ int l, shift, index;
++ quotaid_t id;
++ struct quotatree_find_state st;
++
++ p = node_start;
++ l = level_start;
++ shift = (QUOTATREE_DEPTH - l) * QUOTAID_BBITS;
++ id = id_start;
++ index = 0;
++
++ /*
++ * Invariants:
++ * shift == (QUOTATREE_DEPTH - l) * QUOTAID_BBITS;
++ * id & ((1 << shift) - 1) == 0
++ * p is l-level node corresponding to id
++ */
++ do {
++ if (!p)
++ break;
++
++ if (l < level_end) {
++ for (; index < QUOTATREE_BSIZE; index++)
++ if (p->blocks[index] != NULL)
++ break;
++ if (index < QUOTATREE_BSIZE) {
++ /* descend */
++ p = p->blocks[index];
++ l++;
++ shift -= QUOTAID_BBITS;
++ id += (quotaid_t)index << shift;
++ index = 0;
++ continue;
++ }
++ }
++
++ if ((*callback)(tree, id, l, p, data))
++ break;
++
++ /* ascend and to the next node */
++ p = quotatree_follow(tree, id, l, &st);
++
++ index = ((id >> shift) & QUOTATREE_BMASK) + 1;
++ l--;
++ shift += QUOTAID_BBITS;
++ id &= ~(((quotaid_t)1 << shift) - 1);
++ } while (l >= level_start);
++}
++#endif
++
++static void free_list(struct list_head *node_list)
++{
++ struct quotatree_node *p, *tmp;
++
++ list_for_each_entry_safe(p, tmp, node_list, list) {
++ list_del(&p->list);
++ kfree(p);
++ }
++}
++
++static inline void quotatree_free_nodes(struct quotatree_tree *tree)
++{
++ int i;
++
++ for (i = 0; i < QUOTATREE_DEPTH; i++) {
++ free_list(&tree->levels[i].usedlh);
++ free_list(&tree->levels[i].freelh);
++ }
++}
++
++static void quotatree_free_leafs(struct quotatree_tree *tree,
++ void (*dtor)(void *))
++{
++ int i;
++ struct quotatree_node *p;
++
++ list_for_each_entry(p, &QTREE_LEAFLVL(tree)->usedlh, list) {
++ for (i = 0; i < QUOTATREE_BSIZE; i++) {
++ if (p->blocks[i] == NULL)
++ continue;
++
++ dtor(p->blocks[i]);
++ }
++ }
++}
++
++void quotatree_free(struct quotatree_tree *tree, void (*dtor)(void *))
++{
++ quotatree_free_leafs(tree, dtor);
++ quotatree_free_nodes(tree);
++ kfree(tree);
++}
+diff --git a/fs/vzdq_ugid.c b/fs/vzdq_ugid.c
+new file mode 100644
+index 0000000..1031149
+--- /dev/null
++++ b/fs/vzdq_ugid.c
+@@ -0,0 +1,1221 @@
++/*
++ * Copyright (C) 2002 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains Virtuozzo UID/GID disk quota implementation
++ */
++
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/smp_lock.h>
++#include <linux/rcupdate.h>
++#include <asm/uaccess.h>
++#include <linux/proc_fs.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/quota.h>
++#include <linux/quotaio_v2.h>
++#include <linux/virtinfo.h>
++#include <linux/namei.h>
++#include <linux/mount.h>
++#include <linux/mnt_namespace.h>
++#include <linux/vmalloc.h>
++
++#include <linux/vzctl.h>
++#include <linux/vzctl_quota.h>
++#include <linux/vzquota.h>
++
++/*
++ * XXX
++ * may be something is needed for sb->s_dquot->info[]?
++ */
++
++#define USRQUOTA_MASK (1 << USRQUOTA)
++#define GRPQUOTA_MASK (1 << GRPQUOTA)
++#define QTYPE2MASK(type) (1 << (type))
++
++static struct kmem_cache *vz_quota_ugid_cachep;
++
++/* guard to protect vz_quota_master from destroy in quota_on/off. Also protects
++ * list on the hash table */
++extern struct semaphore vz_quota_sem;
++
++inline struct vz_quota_ugid *vzquota_get_ugid(struct vz_quota_ugid *qugid)
++{
++ if (qugid != VZ_QUOTA_UGBAD)
++ atomic_inc(&qugid->qugid_count);
++ return qugid;
++}
++
++/* we don't limit users with zero limits */
++static inline int vzquota_fake_stat(struct dq_stat *stat)
++{
++ return stat->bhardlimit == 0 && stat->bsoftlimit == 0 &&
++ stat->ihardlimit == 0 && stat->isoftlimit == 0;
++}
++
++/* callback function for quotatree_free() */
++static inline void vzquota_free_qugid(void *ptr)
++{
++ kmem_cache_free(vz_quota_ugid_cachep, ptr);
++}
++
++/*
++ * destroy ugid, if it have zero refcount, limits and usage
++ * must be called under qmblk->dq_sem
++ */
++void vzquota_put_ugid(struct vz_quota_master *qmblk,
++ struct vz_quota_ugid *qugid)
++{
++ if (qugid == VZ_QUOTA_UGBAD)
++ return;
++ qmblk_data_read_lock(qmblk);
++ if (atomic_dec_and_test(&qugid->qugid_count) &&
++ (qmblk->dq_flags & VZDQUG_FIXED_SET) == 0 &&
++ vzquota_fake_stat(&qugid->qugid_stat) &&
++ qugid->qugid_stat.bcurrent == 0 &&
++ qugid->qugid_stat.icurrent == 0) {
++ quotatree_remove(QUGID_TREE(qmblk, qugid->qugid_type),
++ qugid->qugid_id);
++ qmblk->dq_ugid_count--;
++ vzquota_free_qugid(qugid);
++ }
++ qmblk_data_read_unlock(qmblk);
++}
++
++/*
++ * Get ugid block by its index, like it would present in array.
++ * In reality, this is not array - this is leafs chain of the tree.
++ * NULL if index is out of range.
++ * qmblk semaphore is required to protect the tree.
++ */
++static inline struct vz_quota_ugid *
++vzquota_get_byindex(struct vz_quota_master *qmblk, unsigned int index, int type)
++{
++ return quotatree_leaf_byindex(QUGID_TREE(qmblk, type), index);
++}
++
++/*
++ * get next element from ugid "virtual array"
++ * ugid must be in current array and this array may not be changed between
++ * two accesses (quaranteed by "stopped" quota state and quota semaphore)
++ * qmblk semaphore is required to protect the tree
++ */
++static inline struct vz_quota_ugid *
++vzquota_get_next(struct vz_quota_master *qmblk, struct vz_quota_ugid *qugid)
++{
++ return quotatree_get_next(QUGID_TREE(qmblk, qugid->qugid_type),
++ qugid->qugid_id);
++}
++
++/*
++ * requires dq_sem
++ */
++struct vz_quota_ugid *__vzquota_find_ugid(struct vz_quota_master *qmblk,
++ unsigned int quota_id, int type, int flags)
++{
++ struct vz_quota_ugid *qugid;
++ struct quotatree_tree *tree;
++ struct quotatree_find_state st;
++
++ tree = QUGID_TREE(qmblk, type);
++ qugid = quotatree_find(tree, quota_id, &st);
++ if (qugid)
++ goto success;
++
++ /* caller does not want alloc */
++ if (flags & VZDQUG_FIND_DONT_ALLOC)
++ goto fail;
++
++ if (flags & VZDQUG_FIND_FAKE)
++ goto doit;
++
++ /* check limit */
++ if (qmblk->dq_ugid_count >= qmblk->dq_ugid_max)
++ goto fail;
++
++ /* see comment at VZDQUG_FIXED_SET define */
++ if (qmblk->dq_flags & VZDQUG_FIXED_SET)
++ goto fail;
++
++doit:
++ /* alloc new structure */
++ qugid = kmem_cache_alloc(vz_quota_ugid_cachep,
++ GFP_NOFS | __GFP_NOFAIL);
++ if (qugid == NULL)
++ goto fail;
++
++ /* initialize new structure */
++ qugid->qugid_id = quota_id;
++ memset(&qugid->qugid_stat, 0, sizeof(qugid->qugid_stat));
++ qugid->qugid_type = type;
++ atomic_set(&qugid->qugid_count, 0);
++
++ /* insert in tree */
++ if (quotatree_insert(tree, quota_id, &st, qugid) < 0)
++ goto fail_insert;
++ qmblk->dq_ugid_count++;
++
++success:
++ vzquota_get_ugid(qugid);
++ return qugid;
++
++fail_insert:
++ vzquota_free_qugid(qugid);
++fail:
++ return VZ_QUOTA_UGBAD;
++}
++
++/*
++ * takes dq_sem, may schedule
++ */
++struct vz_quota_ugid *vzquota_find_ugid(struct vz_quota_master *qmblk,
++ unsigned int quota_id, int type, int flags)
++{
++ struct vz_quota_ugid *qugid;
++
++ down(&qmblk->dq_sem);
++ qugid = __vzquota_find_ugid(qmblk, quota_id, type, flags);
++ up(&qmblk->dq_sem);
++
++ return qugid;
++}
++
++/*
++ * destroy all ugid records on given quota master
++ */
++void vzquota_kill_ugid(struct vz_quota_master *qmblk)
++{
++ BUG_ON((qmblk->dq_gid_tree == NULL && qmblk->dq_uid_tree != NULL) ||
++ (qmblk->dq_uid_tree == NULL && qmblk->dq_gid_tree != NULL));
++
++ if (qmblk->dq_uid_tree != NULL) {
++ quotatree_free(qmblk->dq_uid_tree, vzquota_free_qugid);
++ quotatree_free(qmblk->dq_gid_tree, vzquota_free_qugid);
++ }
++}
++
++
++/* ----------------------------------------------------------------------
++ * Management interface to ugid quota for (super)users.
++ * --------------------------------------------------------------------- */
++
++static int vzquota_initialize2(struct inode *inode, int type)
++{
++ return QUOTA_OK;
++}
++
++static int vzquota_drop2(struct inode *inode)
++{
++ return QUOTA_OK;
++}
++
++static int vzquota_alloc_space2(struct inode *inode,
++ qsize_t number, int prealloc)
++{
++ inode_add_bytes(inode, number);
++ return QUOTA_OK;
++}
++
++static int vzquota_alloc_inode2(const struct inode *inode, unsigned long number)
++{
++ return QUOTA_OK;
++}
++
++static int vzquota_free_space2(struct inode *inode, qsize_t number)
++{
++ inode_sub_bytes(inode, number);
++ return QUOTA_OK;
++}
++
++static int vzquota_free_inode2(const struct inode *inode, unsigned long number)
++{
++ return QUOTA_OK;
++}
++
++static int vzquota_transfer2(struct inode *inode, struct iattr *iattr)
++{
++ return QUOTA_OK;
++}
++
++struct dquot_operations vz_quota_operations2 = {
++ .initialize = vzquota_initialize2,
++ .drop = vzquota_drop2,
++ .alloc_space = vzquota_alloc_space2,
++ .alloc_inode = vzquota_alloc_inode2,
++ .free_space = vzquota_free_space2,
++ .free_inode = vzquota_free_inode2,
++ .transfer = vzquota_transfer2,
++};
++
++
++asmlinkage long sys_unlink(const char __user * pathname);
++asmlinkage long sys_rename(const char __user * oldname,
++ const char __user * newname);
++asmlinkage long sys_symlink(const char __user * oldname,
++ const char __user * newname);
++
++/* called under sb->s_umount semaphore */
++static int vz_restore_symlink(struct super_block *sb, char *path, int type)
++{
++ mm_segment_t oldfs;
++ char *newpath;
++ char dest[64];
++ const char *names[] = {
++ [USRQUOTA] "aquota.user",
++ [GRPQUOTA] "aquota.group"
++ };
++ int err;
++
++ newpath = kmalloc(strlen(path) + sizeof(".new"), GFP_KERNEL);
++ if (newpath == NULL)
++ return -ENOMEM;
++
++ strcpy(newpath, path);
++ strcat(newpath, ".new");
++
++ sprintf(dest, "/proc/vz/vzaquota/%08x/%s",
++ new_encode_dev(sb->s_dev), names[type]);
++
++ /*
++ * Lockdep will learn unneeded dependency while unlink(2):
++ * ->s_umount => ->i_mutex/1 => ->i_mutex
++ * Reverse dependency is,
++ * open_namei() => ->i_mutex => lookup_hash() => __lookup_hash()
++ * => ->lookup() \eq vzdq_aquotq_lookup() => find_qmblk_by_dev()
++ * => user_get_super() => ->s_umount
++ *
++ * However, first set of ->i_mutex'es belong to /, second to /proc .
++ * Right fix is to get rid of vz_restore_symlink(), of course.
++ */
++ up_read(&sb->s_umount);
++
++ oldfs = get_fs();
++ set_fs(KERNEL_DS);
++ err = sys_unlink(newpath);
++ if (err < 0 && err != -ENOENT)
++ goto out_restore;
++ err = sys_symlink(dest, newpath);
++ if (err < 0)
++ goto out_restore;
++ err = sys_rename(newpath, path);
++out_restore:
++ set_fs(oldfs);
++
++ down_read(&sb->s_umount);
++ /* umounted meanwhile? */
++ if (err == 0 && !sb->s_root)
++ err = -ENODEV;
++
++ kfree(newpath);
++ return err;
++}
++
++/* called under sb->s_umount semaphore */
++static int vz_quota_on(struct super_block *sb, int type,
++ int format_id, char *path, int remount)
++{
++ struct vz_quota_master *qmblk;
++ int mask, mask2;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++
++ err = vz_restore_symlink(sb, path, type);
++ if (err < 0)
++ goto out_put;
++
++ down(&vz_quota_sem);
++ mask = 0;
++ mask2 = 0;
++ sb->dq_op = &vz_quota_operations2;
++ sb->s_qcop = &vz_quotactl_operations;
++ if (type == USRQUOTA) {
++ mask = DQUOT_USR_ENABLED;
++ mask2 = VZDQ_USRQUOTA;
++ }
++ if (type == GRPQUOTA) {
++ mask = DQUOT_GRP_ENABLED;
++ mask2 = VZDQ_GRPQUOTA;
++ }
++ err = -EBUSY;
++ if (qmblk->dq_flags & mask2)
++ goto out_sem;
++
++ err = 0;
++ qmblk->dq_flags |= mask2;
++ sb->s_dquot.flags |= mask;
++
++out_sem:
++ up(&vz_quota_sem);
++out_put:
++ qmblk_put(qmblk);
++out:
++ return err;
++}
++
++static int vz_quota_off(struct super_block *sb, int type, int remount)
++{
++ struct vz_quota_master *qmblk;
++ int mask2;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ down(&vz_quota_sem);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++
++ mask2 = 0;
++ if (type == USRQUOTA)
++ mask2 = VZDQ_USRQUOTA;
++ if (type == GRPQUOTA)
++ mask2 = VZDQ_GRPQUOTA;
++ err = -EINVAL;
++ if (!(qmblk->dq_flags & mask2))
++ goto out;
++
++ qmblk->dq_flags &= ~mask2;
++ err = 0;
++
++out:
++ up(&vz_quota_sem);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++ return err;
++}
++
++static int vz_quota_sync(struct super_block *sb, int type)
++{
++ return 0; /* vz quota is always uptodate */
++}
++
++static int vz_get_dqblk(struct super_block *sb, int type,
++ qid_t id, struct if_dqblk *di)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid *ugid;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ down(&vz_quota_sem);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++
++ err = 0;
++ ugid = vzquota_find_ugid(qmblk, id, type, VZDQUG_FIND_DONT_ALLOC);
++ if (ugid != VZ_QUOTA_UGBAD) {
++ qmblk_data_read_lock(qmblk);
++ di->dqb_bhardlimit = ugid->qugid_stat.bhardlimit >> 10;
++ di->dqb_bsoftlimit = ugid->qugid_stat.bsoftlimit >> 10;
++ di->dqb_curspace = ugid->qugid_stat.bcurrent;
++ di->dqb_ihardlimit = ugid->qugid_stat.ihardlimit;
++ di->dqb_isoftlimit = ugid->qugid_stat.isoftlimit;
++ di->dqb_curinodes = ugid->qugid_stat.icurrent;
++ di->dqb_btime = ugid->qugid_stat.btime;
++ di->dqb_itime = ugid->qugid_stat.itime;
++ qmblk_data_read_unlock(qmblk);
++ di->dqb_valid = QIF_ALL;
++ vzquota_put_ugid(qmblk, ugid);
++ } else {
++ memset(di, 0, sizeof(*di));
++ di->dqb_valid = QIF_ALL;
++ }
++
++out:
++ up(&vz_quota_sem);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++ return err;
++}
++
++/* must be called under vz_quota_sem */
++static int __vz_set_dqblk(struct vz_quota_master *qmblk,
++ int type, qid_t id, struct if_dqblk *di)
++{
++ struct vz_quota_ugid *ugid;
++
++ ugid = vzquota_find_ugid(qmblk, id, type, 0);
++ if (ugid == VZ_QUOTA_UGBAD)
++ return -ESRCH;
++
++ qmblk_data_write_lock(qmblk);
++ /*
++ * Subtle compatibility breakage.
++ *
++ * Some old non-vz kernel quota didn't start grace period
++ * if the new soft limit happens to be below the usage.
++ * Non-vz kernel quota in 2.4.20 starts the grace period
++ * (if it hasn't been started).
++ * Current non-vz kernel performs even more complicated
++ * manipulations...
++ *
++ * Also, current non-vz kernels have inconsistency related to
++ * the grace time start. In regular operations the grace period
++ * is started if the usage is greater than the soft limit (and,
++ * strangely, is cancelled if the usage is less).
++ * However, set_dqblk starts the grace period if the usage is greater
++ * or equal to the soft limit.
++ *
++ * Here we try to mimic the behavior of the current non-vz kernel.
++ */
++ if (di->dqb_valid & QIF_BLIMITS) {
++ ugid->qugid_stat.bhardlimit =
++ (__u64)di->dqb_bhardlimit << 10;
++ ugid->qugid_stat.bsoftlimit =
++ (__u64)di->dqb_bsoftlimit << 10;
++ if (di->dqb_bsoftlimit == 0 ||
++ ugid->qugid_stat.bcurrent < ugid->qugid_stat.bsoftlimit)
++ ugid->qugid_stat.btime = 0;
++ else if (!(di->dqb_valid & QIF_BTIME))
++ ugid->qugid_stat.btime = CURRENT_TIME_SECONDS
++ + qmblk->dq_ugid_info[type].bexpire;
++ else
++ ugid->qugid_stat.btime = di->dqb_btime;
++ }
++ if (di->dqb_valid & QIF_ILIMITS) {
++ ugid->qugid_stat.ihardlimit = di->dqb_ihardlimit;
++ ugid->qugid_stat.isoftlimit = di->dqb_isoftlimit;
++ if (di->dqb_isoftlimit == 0 ||
++ ugid->qugid_stat.icurrent < ugid->qugid_stat.isoftlimit)
++ ugid->qugid_stat.itime = 0;
++ else if (!(di->dqb_valid & QIF_ITIME))
++ ugid->qugid_stat.itime = CURRENT_TIME_SECONDS
++ + qmblk->dq_ugid_info[type].iexpire;
++ else
++ ugid->qugid_stat.itime = di->dqb_itime;
++ }
++ qmblk_data_write_unlock(qmblk);
++ vzquota_put_ugid(qmblk, ugid);
++
++ return 0;
++}
++
++static int vz_set_dqblk(struct super_block *sb, int type,
++ qid_t id, struct if_dqblk *di)
++{
++ struct vz_quota_master *qmblk;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ down(&vz_quota_sem);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++ err = __vz_set_dqblk(qmblk, type, id, di);
++out:
++ up(&vz_quota_sem);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++ return err;
++}
++
++static int vz_get_dqinfo(struct super_block *sb, int type,
++ struct if_dqinfo *ii)
++{
++ struct vz_quota_master *qmblk;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ down(&vz_quota_sem);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++
++ err = 0;
++ ii->dqi_bgrace = qmblk->dq_ugid_info[type].bexpire;
++ ii->dqi_igrace = qmblk->dq_ugid_info[type].iexpire;
++ ii->dqi_flags = 0;
++ ii->dqi_valid = IIF_ALL;
++
++out:
++ up(&vz_quota_sem);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++ return err;
++}
++
++/* must be called under vz_quota_sem */
++static int __vz_set_dqinfo(struct vz_quota_master *qmblk,
++ int type, struct if_dqinfo *ii)
++{
++ if (ii->dqi_valid & IIF_FLAGS)
++ if (ii->dqi_flags & DQF_MASK)
++ return -EINVAL;
++
++ if (ii->dqi_valid & IIF_BGRACE)
++ qmblk->dq_ugid_info[type].bexpire = ii->dqi_bgrace;
++ if (ii->dqi_valid & IIF_IGRACE)
++ qmblk->dq_ugid_info[type].iexpire = ii->dqi_igrace;
++ return 0;
++}
++
++static int vz_set_dqinfo(struct super_block *sb, int type,
++ struct if_dqinfo *ii)
++{
++ struct vz_quota_master *qmblk;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ down(&vz_quota_sem);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++ err = __vz_set_dqinfo(qmblk, type, ii);
++out:
++ up(&vz_quota_sem);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++ return err;
++}
++
++#ifdef CONFIG_QUOTA_COMPAT
++
++#define Q_GETQUOTI_SIZE 1024
++
++#define UGID2DQBLK(dst, src) \
++ do { \
++ (dst)->dqb_ihardlimit = (src)->qugid_stat.ihardlimit; \
++ (dst)->dqb_isoftlimit = (src)->qugid_stat.isoftlimit; \
++ (dst)->dqb_curinodes = (src)->qugid_stat.icurrent; \
++ /* in 1K blocks */ \
++ (dst)->dqb_bhardlimit = (src)->qugid_stat.bhardlimit >> 10; \
++ /* in 1K blocks */ \
++ (dst)->dqb_bsoftlimit = (src)->qugid_stat.bsoftlimit >> 10; \
++ /* in bytes, 64 bit */ \
++ (dst)->dqb_curspace = (src)->qugid_stat.bcurrent; \
++ (dst)->dqb_btime = (src)->qugid_stat.btime; \
++ (dst)->dqb_itime = (src)->qugid_stat.itime; \
++ } while (0)
++
++static int vz_get_quoti(struct super_block *sb, int type, qid_t idx,
++ struct v2_disk_dqblk __user *dqblk)
++{
++ struct vz_quota_master *qmblk;
++ struct v2_disk_dqblk *data, *kbuf;
++ struct vz_quota_ugid *ugid;
++ int count;
++ int err;
++
++ qmblk = vzquota_find_qmblk(sb);
++ err = -ESRCH;
++ if (qmblk == NULL)
++ goto out;
++ err = -EIO;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out;
++
++ err = -ENOMEM;
++ kbuf = vmalloc(Q_GETQUOTI_SIZE * sizeof(*kbuf));
++ if (!kbuf)
++ goto out;
++
++ down(&vz_quota_sem);
++ down(&qmblk->dq_sem);
++ for (ugid = vzquota_get_byindex(qmblk, idx, type), count = 0;
++ ugid != NULL && count < Q_GETQUOTI_SIZE;
++ count++)
++ {
++ data = kbuf + count;
++ qmblk_data_read_lock(qmblk);
++ UGID2DQBLK(data, ugid);
++ qmblk_data_read_unlock(qmblk);
++ data->dqb_id = ugid->qugid_id;
++
++ /* Find next entry */
++ ugid = vzquota_get_next(qmblk, ugid);
++ BUG_ON(ugid != NULL && ugid->qugid_type != type);
++ }
++ up(&qmblk->dq_sem);
++ up(&vz_quota_sem);
++
++ err = count;
++ if (copy_to_user(dqblk, kbuf, count * sizeof(*kbuf)))
++ err = -EFAULT;
++
++ vfree(kbuf);
++out:
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qmblk);
++
++ return err;
++}
++
++#endif
++
++struct quotactl_ops vz_quotactl_operations = {
++ .quota_on = vz_quota_on,
++ .quota_off = vz_quota_off,
++ .quota_sync = vz_quota_sync,
++ .get_info = vz_get_dqinfo,
++ .set_info = vz_set_dqinfo,
++ .get_dqblk = vz_get_dqblk,
++ .set_dqblk = vz_set_dqblk,
++#ifdef CONFIG_QUOTA_COMPAT
++ .get_quoti = vz_get_quoti,
++#endif
++};
++
++
++/* ----------------------------------------------------------------------
++ * Management interface for host system admins.
++ * --------------------------------------------------------------------- */
++
++static int quota_ugid_addstat(unsigned int quota_id, unsigned int ugid_size,
++ struct vz_quota_iface __user *u_ugid_buf, int compat)
++{
++ struct vz_quota_master *qmblk;
++ int ret;
++
++ down(&vz_quota_sem);
++
++ ret = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ ret = -EBUSY;
++ if (qmblk->dq_state != VZDQ_STARTING)
++ goto out; /* working quota doesn't accept new ugids */
++
++ ret = 0;
++ /* start to add ugids */
++ for (ret = 0; ret < ugid_size; ret++) {
++ struct vz_quota_iface ugid_buf;
++ struct vz_quota_ugid *ugid;
++
++ if (!compat) {
++ if (copy_from_user(&ugid_buf, u_ugid_buf,
++ sizeof(ugid_buf)))
++ break;
++ u_ugid_buf++; /* next user buffer */
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_vz_quota_iface oqif;
++ if (copy_from_user(&oqif, u_ugid_buf,
++ sizeof(oqif)))
++ break;
++ ugid_buf.qi_id = oqif.qi_id;
++ ugid_buf.qi_type = oqif.qi_type;
++ compat_dqstat2dqstat(&oqif.qi_stat, &ugid_buf.qi_stat);
++ u_ugid_buf = (struct vz_quota_iface __user *)
++ (((void *)u_ugid_buf) + sizeof(oqif));
++#endif
++ }
++
++ if (ugid_buf.qi_type >= MAXQUOTAS)
++ break; /* bad quota type - this is the only check */
++
++ ugid = vzquota_find_ugid(qmblk,
++ ugid_buf.qi_id, ugid_buf.qi_type, 0);
++ if (ugid == VZ_QUOTA_UGBAD) {
++ qmblk->dq_flags |= VZDQUG_FIXED_SET;
++ break; /* limit reached */
++ }
++
++ /* update usage/limits
++ * we can copy the data without the lock, because the data
++ * cannot be modified in VZDQ_STARTING state */
++ ugid->qugid_stat = ugid_buf.qi_stat;
++
++ vzquota_put_ugid(qmblk, ugid);
++ }
++out:
++ up(&vz_quota_sem);
++
++ return ret;
++}
++
++static int quota_ugid_setgrace(unsigned int quota_id,
++ struct dq_info __user u_dq_info[], int compat)
++{
++ struct vz_quota_master *qmblk;
++ struct dq_info dq_info[MAXQUOTAS];
++ struct dq_info *target;
++ int err, type;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EBUSY;
++ if (qmblk->dq_state != VZDQ_STARTING)
++ goto out; /* working quota doesn't accept changing options */
++
++ err = -EFAULT;
++ if (!compat) {
++ if (copy_from_user(dq_info, u_dq_info, sizeof(dq_info)))
++ goto out;
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_dq_info odqi[MAXQUOTAS];
++ if (copy_from_user(odqi, u_dq_info, sizeof(odqi)))
++ goto out;
++ for (type = 0; type < MAXQUOTAS; type++)
++ compat_dqinfo2dqinfo(&odqi[type], &dq_info[type]);
++#endif
++ }
++
++ err = 0;
++
++ /* update in qmblk */
++ for (type = 0; type < MAXQUOTAS; type++) {
++ target = &qmblk->dq_ugid_info[type];
++ target->bexpire = dq_info[type].bexpire;
++ target->iexpire = dq_info[type].iexpire;
++ }
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++static int do_quota_ugid_getstat(struct vz_quota_master *qmblk, int index, int size,
++ struct vz_quota_iface *u_ugid_buf)
++{
++ int type, count;
++ struct vz_quota_ugid *ugid;
++
++ if (QTREE_LEAFNUM(qmblk->dq_uid_tree) +
++ QTREE_LEAFNUM(qmblk->dq_gid_tree)
++ <= index)
++ return 0;
++
++ count = 0;
++
++ type = index < QTREE_LEAFNUM(qmblk->dq_uid_tree) ? USRQUOTA : GRPQUOTA;
++ if (type == GRPQUOTA)
++ index -= QTREE_LEAFNUM(qmblk->dq_uid_tree);
++
++ /* loop through ugid and then qgid quota */
++repeat:
++ for (ugid = vzquota_get_byindex(qmblk, index, type);
++ ugid != NULL && count < size;
++ ugid = vzquota_get_next(qmblk, ugid), count++)
++ {
++ struct vz_quota_iface ugid_buf;
++
++ /* form interface buffer and send in to user-level */
++ qmblk_data_read_lock(qmblk);
++ memcpy(&ugid_buf.qi_stat, &ugid->qugid_stat,
++ sizeof(ugid_buf.qi_stat));
++ qmblk_data_read_unlock(qmblk);
++ ugid_buf.qi_id = ugid->qugid_id;
++ ugid_buf.qi_type = ugid->qugid_type;
++
++ memcpy(u_ugid_buf, &ugid_buf, sizeof(ugid_buf));
++ u_ugid_buf++; /* next portion of user buffer */
++ }
++
++ if (type == USRQUOTA && count < size) {
++ type = GRPQUOTA;
++ index = 0;
++ goto repeat;
++ }
++
++ return count;
++}
++
++static int quota_ugid_getstat(unsigned int quota_id,
++ int index, int size, struct vz_quota_iface __user *u_ugid_buf,
++ int compat)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_iface *k_ugid_buf;
++ int err;
++
++ if (index < 0 || size < 0)
++ return -EINVAL;
++
++ if (size > INT_MAX / sizeof(struct vz_quota_iface))
++ return -EINVAL;
++
++ k_ugid_buf = vmalloc(size * sizeof(struct vz_quota_iface));
++ if (k_ugid_buf == NULL)
++ return -ENOMEM;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ down(&qmblk->dq_sem);
++ err = do_quota_ugid_getstat(qmblk, index, size, k_ugid_buf);
++ up(&qmblk->dq_sem);
++ if (err < 0)
++ goto out;
++
++ if (!compat) {
++ if (copy_to_user(u_ugid_buf, k_ugid_buf,
++ err * sizeof(struct vz_quota_iface)))
++ err = -EFAULT;
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_vz_quota_iface oqif;
++ int i;
++ for (i = 0; i < err; i++) {
++ oqif.qi_id = k_ugid_buf[i].qi_id;
++ oqif.qi_type = k_ugid_buf[i].qi_type;
++ dqstat2compat_dqstat(&k_ugid_buf[i].qi_stat,
++ &oqif.qi_stat);
++ if (copy_to_user(u_ugid_buf, &oqif, sizeof(oqif)))
++ err = -EFAULT;
++ u_ugid_buf = (struct vz_quota_iface __user *)
++ (((void *)u_ugid_buf) + sizeof(oqif));
++ }
++#endif
++ }
++
++out:
++ up(&vz_quota_sem);
++ vfree(k_ugid_buf);
++ return err;
++}
++
++static int quota_ugid_getgrace(unsigned int quota_id,
++ struct dq_info __user u_dq_info[], int compat)
++{
++ struct vz_quota_master *qmblk;
++ struct dq_info dq_info[MAXQUOTAS];
++ struct dq_info *target;
++ int err, type;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = 0;
++ /* update from qmblk */
++ for (type = 0; type < MAXQUOTAS; type ++) {
++ target = &qmblk->dq_ugid_info[type];
++ dq_info[type].bexpire = target->bexpire;
++ dq_info[type].iexpire = target->iexpire;
++ dq_info[type].flags = target->flags;
++ }
++
++ if (!compat) {
++ if (copy_to_user(u_dq_info, dq_info, sizeof(dq_info)))
++ err = -EFAULT;
++ } else {
++#ifdef CONFIG_COMPAT
++ struct compat_dq_info odqi[MAXQUOTAS];
++ for (type = 0; type < MAXQUOTAS; type ++)
++ dqinfo2compat_dqinfo(&dq_info[type], &odqi[type]);
++ if (copy_to_user(u_dq_info, odqi, sizeof(odqi)))
++ err = -EFAULT;
++#endif
++ }
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++static int quota_ugid_getconfig(unsigned int quota_id,
++ struct vz_quota_ugid_stat __user *info)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid_stat kinfo;
++ int err;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = 0;
++ kinfo.limit = qmblk->dq_ugid_max;
++ kinfo.count = qmblk->dq_ugid_count;
++ kinfo.flags = qmblk->dq_flags;
++
++ if (copy_to_user(info, &kinfo, sizeof(kinfo)))
++ err = -EFAULT;
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++static int quota_ugid_setconfig(unsigned int quota_id,
++ struct vz_quota_ugid_stat __user *info)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid_stat kinfo;
++ int err;
++
++ down(&vz_quota_sem);
++
++ err = -ENOENT;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EFAULT;
++ if (copy_from_user(&kinfo, info, sizeof(kinfo)))
++ goto out;
++
++ err = 0;
++ qmblk->dq_ugid_max = kinfo.limit;
++ if (qmblk->dq_state == VZDQ_STARTING) {
++ qmblk->dq_flags = kinfo.flags;
++ if (qmblk->dq_flags & VZDQUG_ON)
++ qmblk->dq_flags |= VZDQ_USRQUOTA | VZDQ_GRPQUOTA;
++ }
++
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++static int quota_ugid_setlimit(unsigned int quota_id,
++ struct vz_quota_ugid_setlimit __user *u_lim)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid_setlimit lim;
++ int err;
++
++ down(&vz_quota_sem);
++
++ err = -ESRCH;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EFAULT;
++ if (copy_from_user(&lim, u_lim, sizeof(lim)))
++ goto out;
++
++ err = __vz_set_dqblk(qmblk, lim.type, lim.id, &lim.dqb);
++
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++static int quota_ugid_setinfo(unsigned int quota_id,
++ struct vz_quota_ugid_setinfo __user *u_info)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid_setinfo info;
++ int err;
++
++ down(&vz_quota_sem);
++
++ err = -ESRCH;
++ qmblk = vzquota_find_master(quota_id);
++ if (qmblk == NULL)
++ goto out;
++
++ err = -EFAULT;
++ if (copy_from_user(&info, u_info, sizeof(info)))
++ goto out;
++
++ err = __vz_set_dqinfo(qmblk, info.type, &info.dqi);
++
++out:
++ up(&vz_quota_sem);
++
++ return err;
++}
++
++/*
++ * This is a system call to maintain UGID quotas
++ * Note this call is allowed to run ONLY from VE0
++ */
++long do_vzquotaugidctl(int cmd, unsigned int quota_id,
++ unsigned int ugid_index, unsigned int ugid_size,
++ void *addr, int compat)
++{
++ int ret;
++
++ ret = -EPERM;
++ /* access allowed only from root of VE0 */
++ if (!capable(CAP_SYS_RESOURCE) ||
++ !capable(CAP_SYS_ADMIN))
++ goto out;
++
++ switch (cmd) {
++ case VZ_DQ_UGID_GETSTAT:
++ ret = quota_ugid_getstat(quota_id,
++ ugid_index, ugid_size,
++ (struct vz_quota_iface __user *)addr,
++ compat);
++ break;
++ case VZ_DQ_UGID_ADDSTAT:
++ ret = quota_ugid_addstat(quota_id, ugid_size,
++ (struct vz_quota_iface __user *) addr,
++ compat);
++ break;
++ case VZ_DQ_UGID_GETGRACE:
++ ret = quota_ugid_getgrace(quota_id,
++ (struct dq_info __user *)addr, compat);
++ break;
++ case VZ_DQ_UGID_SETGRACE:
++ ret = quota_ugid_setgrace(quota_id,
++ (struct dq_info __user *)addr, compat);
++ break;
++ case VZ_DQ_UGID_GETCONFIG:
++ ret = quota_ugid_getconfig(quota_id,
++ (struct vz_quota_ugid_stat __user *)
++ addr);
++ break;
++ case VZ_DQ_UGID_SETCONFIG:
++ ret = quota_ugid_setconfig(quota_id,
++ (struct vz_quota_ugid_stat __user *)
++ addr);
++ break;
++ case VZ_DQ_UGID_SETLIMIT:
++ ret = quota_ugid_setlimit(quota_id,
++ (struct vz_quota_ugid_setlimit __user *)
++ addr);
++ break;
++ case VZ_DQ_UGID_SETINFO:
++ ret = quota_ugid_setinfo(quota_id,
++ (struct vz_quota_ugid_setinfo __user *)
++ addr);
++ break;
++ default:
++ ret = -EINVAL;
++ goto out;
++ }
++out:
++ return ret;
++}
++
++static void ugid_quota_on_sb(struct super_block *sb)
++{
++ struct super_block *real_sb;
++ struct vz_quota_master *qmblk;
++
++ if (!sb->s_op->get_quota_root)
++ return;
++
++ real_sb = sb->s_op->get_quota_root(sb)->i_sb;
++ if (real_sb->dq_op != &vz_quota_operations)
++ return;
++
++ sb->dq_op = &vz_quota_operations2;
++ sb->s_qcop = &vz_quotactl_operations;
++ INIT_LIST_HEAD(&sb->s_dquot.info[USRQUOTA].dqi_dirty_list);
++ INIT_LIST_HEAD(&sb->s_dquot.info[GRPQUOTA].dqi_dirty_list);
++ sb->s_dquot.info[USRQUOTA].dqi_format = &vz_quota_empty_v2_format;
++ sb->s_dquot.info[GRPQUOTA].dqi_format = &vz_quota_empty_v2_format;
++
++ qmblk = vzquota_find_qmblk(sb);
++ if ((qmblk == NULL) || (qmblk == VZ_QUOTA_BAD))
++ return;
++ down(&vz_quota_sem);
++ if (qmblk->dq_flags & VZDQ_USRQUOTA)
++ sb->s_dquot.flags |= DQUOT_USR_ENABLED;
++ if (qmblk->dq_flags & VZDQ_GRPQUOTA)
++ sb->s_dquot.flags |= DQUOT_GRP_ENABLED;
++ up(&vz_quota_sem);
++ qmblk_put(qmblk);
++}
++
++static void ugid_quota_off_sb(struct super_block *sb)
++{
++ /* can't make quota off on mounted super block */
++ BUG_ON(sb->s_root != NULL);
++}
++
++static int ugid_notifier_call(struct vnotifier_block *self,
++ unsigned long n, void *data, int old_ret)
++{
++ struct virt_info_quota *viq;
++
++ viq = (struct virt_info_quota *)data;
++
++ switch (n) {
++ case VIRTINFO_QUOTA_ON:
++ ugid_quota_on_sb(viq->super);
++ break;
++ case VIRTINFO_QUOTA_OFF:
++ ugid_quota_off_sb(viq->super);
++ break;
++ case VIRTINFO_QUOTA_GETSTAT:
++ break;
++ default:
++ return old_ret;
++ }
++ return NOTIFY_OK;
++}
++
++static struct vnotifier_block ugid_notifier_block = {
++ .notifier_call = ugid_notifier_call,
++};
++
++/* ----------------------------------------------------------------------
++ * Init/exit.
++ * --------------------------------------------------------------------- */
++
++int vzquota_ugid_init(void)
++{
++ int err;
++
++ vz_quota_ugid_cachep = kmem_cache_create("vz_quota_ugid",
++ sizeof(struct vz_quota_ugid),
++ 0, SLAB_HWCACHE_ALIGN, NULL);
++ if (vz_quota_ugid_cachep == NULL)
++ goto err_slab;
++
++ err = register_quota_format(&vz_quota_empty_v2_format);
++ if (err)
++ goto err_reg;
++
++ virtinfo_notifier_register(VITYPE_QUOTA, &ugid_notifier_block);
++ return 0;
++
++err_reg:
++ kmem_cache_destroy(vz_quota_ugid_cachep);
++ return err;
++
++err_slab:
++ printk(KERN_ERR "Cannot create VZ_QUOTA SLAB cache\n");
++ return -ENOMEM;
++}
++
++void vzquota_ugid_release(void)
++{
++ virtinfo_notifier_unregister(VITYPE_QUOTA, &ugid_notifier_block);
++ unregister_quota_format(&vz_quota_empty_v2_format);
++
++ kmem_cache_destroy(vz_quota_ugid_cachep);
++}
+diff --git a/fs/vzdquot.c b/fs/vzdquot.c
+new file mode 100644
+index 0000000..a6605dd
+--- /dev/null
++++ b/fs/vzdquot.c
+@@ -0,0 +1,1954 @@
++/*
++ * Copyright (C) 2001, 2002, 2004, 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains the core of Virtuozzo disk quota implementation:
++ * maintenance of VZDQ information in inodes,
++ * external interfaces,
++ * module entry.
++ */
++
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/list.h>
++#include <asm/atomic.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/dcache.h>
++#include <linux/quota.h>
++#include <linux/rcupdate.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <asm/uaccess.h>
++#include <linux/vzctl.h>
++#include <linux/vzctl_quota.h>
++#include <linux/vzquota.h>
++#include <linux/virtinfo.h>
++#include <linux/vzdq_tree.h>
++#include <linux/mount.h>
++
++/* ----------------------------------------------------------------------
++ *
++ * Locking
++ *
++ * ---------------------------------------------------------------------- */
++
++/*
++ * Serializes on/off and all other do_vzquotactl operations.
++ * Protects qmblk hash.
++ */
++struct semaphore vz_quota_sem;
++
++/*
++ * Data access locks
++ * inode_qmblk
++ * protects qmblk pointers in all inodes and qlnk content in general
++ * (but not qmblk content);
++ * also protects related qmblk invalidation procedures;
++ * can't be per-inode because of vzquota_dtree_qmblk complications
++ * and problems with serialization with quota_on,
++ * but can be per-superblock;
++ * qmblk_data
++ * protects qmblk fields (such as current usage)
++ * quota_data
++ * protects charge/uncharge operations, thus, implies
++ * qmblk_data lock and, if CONFIG_VZ_QUOTA_UGID, inode_qmblk lock
++ * (to protect ugid pointers).
++ *
++ * Lock order:
++ * inode_qmblk_lock -> dcache_lock
++ * inode_qmblk_lock -> qmblk_data
++ */
++static DEFINE_SPINLOCK(vzdq_qmblk_lock);
++
++inline void inode_qmblk_lock(struct super_block *sb)
++{
++ spin_lock(&vzdq_qmblk_lock);
++}
++
++inline void inode_qmblk_unlock(struct super_block *sb)
++{
++ spin_unlock(&vzdq_qmblk_lock);
++}
++
++inline void qmblk_data_read_lock(struct vz_quota_master *qmblk)
++{
++ spin_lock(&qmblk->dq_data_lock);
++}
++
++inline void qmblk_data_read_unlock(struct vz_quota_master *qmblk)
++{
++ spin_unlock(&qmblk->dq_data_lock);
++}
++
++inline void qmblk_data_write_lock(struct vz_quota_master *qmblk)
++{
++ spin_lock(&qmblk->dq_data_lock);
++}
++
++inline void qmblk_data_write_unlock(struct vz_quota_master *qmblk)
++{
++ spin_unlock(&qmblk->dq_data_lock);
++}
++
++struct quota_format_type vz_quota_empty_v2_format = {
++ .qf_fmt_id = QFMT_VFS_V0,
++ .qf_ops = NULL,
++ .qf_owner = THIS_MODULE,
++};
++
++/* ----------------------------------------------------------------------
++ *
++ * Master hash table handling.
++ *
++ * SMP not safe, serialied by vz_quota_sem within quota syscalls
++ *
++ * --------------------------------------------------------------------- */
++
++static struct kmem_cache *vzquota_cachep;
++
++/*
++ * Hash function.
++ */
++#define QHASH_BITS 6
++#define VZ_QUOTA_HASH_SIZE (1 << QHASH_BITS)
++#define QHASH_MASK (VZ_QUOTA_HASH_SIZE - 1)
++
++struct list_head vzquota_hash_table[VZ_QUOTA_HASH_SIZE];
++int vzquota_hash_size = VZ_QUOTA_HASH_SIZE;
++
++static inline int vzquota_hash_func(unsigned int qid)
++{
++ return (((qid >> QHASH_BITS) ^ qid) & QHASH_MASK);
++}
++
++/**
++ * vzquota_alloc_master - alloc and instantiate master quota record
++ *
++ * Returns:
++ * pointer to newly created record if SUCCESS
++ * -ENOMEM if out of memory
++ * -EEXIST if record with given quota_id already exist
++ */
++struct vz_quota_master *vzquota_alloc_master(unsigned int quota_id,
++ struct vz_quota_stat *qstat)
++{
++ int err;
++ struct vz_quota_master *qmblk;
++
++ err = -EEXIST;
++ if (vzquota_find_master(quota_id) != NULL)
++ goto out;
++
++ err = -ENOMEM;
++ qmblk = kmem_cache_alloc(vzquota_cachep, GFP_KERNEL);
++ if (qmblk == NULL)
++ goto out;
++#ifdef CONFIG_VZ_QUOTA_UGID
++ qmblk->dq_uid_tree = quotatree_alloc();
++ if (!qmblk->dq_uid_tree)
++ goto out_free;
++
++ qmblk->dq_gid_tree = quotatree_alloc();
++ if (!qmblk->dq_gid_tree)
++ goto out_free_tree;
++#endif
++
++ qmblk->dq_state = VZDQ_STARTING;
++ init_MUTEX(&qmblk->dq_sem);
++ spin_lock_init(&qmblk->dq_data_lock);
++
++ qmblk->dq_id = quota_id;
++ qmblk->dq_stat = qstat->dq_stat;
++ qmblk->dq_info = qstat->dq_info;
++ qmblk->dq_root_path.dentry = NULL;
++ qmblk->dq_root_path.mnt = NULL;
++ qmblk->dq_sb = NULL;
++ qmblk->dq_ugid_count = 0;
++ qmblk->dq_ugid_max = 0;
++ qmblk->dq_flags = 0;
++ memset(qmblk->dq_ugid_info, 0, sizeof(qmblk->dq_ugid_info));
++ INIT_LIST_HEAD(&qmblk->dq_ilink_list);
++
++ atomic_set(&qmblk->dq_count, 1);
++
++ /* insert in hash chain */
++ list_add(&qmblk->dq_hash,
++ &vzquota_hash_table[vzquota_hash_func(quota_id)]);
++
++ /* success */
++ return qmblk;
++
++#ifdef CONFIG_VZ_QUOTA_UGID
++out_free_tree:
++ quotatree_free(qmblk->dq_uid_tree, NULL);
++out_free:
++ kmem_cache_free(vzquota_cachep, qmblk);
++#endif
++out:
++ return ERR_PTR(err);
++}
++
++static struct vz_quota_master *vzquota_alloc_fake(void)
++{
++ struct vz_quota_master *qmblk;
++
++ qmblk = kmem_cache_alloc(vzquota_cachep, GFP_KERNEL);
++ if (qmblk == NULL)
++ return NULL;
++ memset(qmblk, 0, sizeof(*qmblk));
++ qmblk->dq_state = VZDQ_STOPING;
++ qmblk->dq_flags = VZDQ_NOQUOT;
++ spin_lock_init(&qmblk->dq_data_lock);
++ INIT_LIST_HEAD(&qmblk->dq_ilink_list);
++ atomic_set(&qmblk->dq_count, 1);
++ return qmblk;
++}
++
++/**
++ * vzquota_find_master - find master record with given id
++ *
++ * Returns qmblk without touching its refcounter.
++ * Called under vz_quota_sem.
++ */
++struct vz_quota_master *vzquota_find_master(unsigned int quota_id)
++{
++ int i;
++ struct vz_quota_master *qp;
++
++ i = vzquota_hash_func(quota_id);
++ list_for_each_entry(qp, &vzquota_hash_table[i], dq_hash) {
++ if (qp->dq_id == quota_id)
++ return qp;
++ }
++ return NULL;
++}
++
++/**
++ * vzquota_free_master - release resources taken by qmblk, freeing memory
++ *
++ * qmblk is assumed to be already taken out from the hash.
++ * Should be called outside vz_quota_sem.
++ */
++void vzquota_free_master(struct vz_quota_master *qmblk)
++{
++#ifdef CONFIG_VZ_QUOTA_UGID
++ vzquota_kill_ugid(qmblk);
++#endif
++ BUG_ON(!list_empty(&qmblk->dq_ilink_list));
++ kmem_cache_free(vzquota_cachep, qmblk);
++}
++
++
++/* ----------------------------------------------------------------------
++ *
++ * Passing quota information through current
++ *
++ * Used in inode -> qmblk lookup at inode creation stage (since at that
++ * time there are no links between the inode being created and its parent
++ * directory).
++ *
++ * --------------------------------------------------------------------- */
++
++#define VZDQ_CUR_MAGIC 0x57d0fee2
++
++static inline int vzquota_cur_qmblk_check(void)
++{
++ return current->magic == VZDQ_CUR_MAGIC;
++}
++
++static inline struct inode *vzquota_cur_qmblk_fetch(void)
++{
++ return current->ino;
++}
++
++static inline void vzquota_cur_qmblk_set(struct inode *data)
++{
++ struct task_struct *tsk;
++
++ tsk = current;
++ tsk->magic = VZDQ_CUR_MAGIC;
++ tsk->ino = data;
++}
++
++#if 0
++static inline void vzquota_cur_qmblk_reset(void)
++{
++ current->magic = 0;
++}
++#endif
++
++
++/* ----------------------------------------------------------------------
++ *
++ * Superblock quota operations
++ *
++ * --------------------------------------------------------------------- */
++
++/*
++ * Kernel structure abuse.
++ * We use files[0] pointer as an int variable:
++ * reference counter of how many quota blocks uses this superblock.
++ * files[1] is used for generations structure which helps us to track
++ * when traversing of dentries is really required.
++ */
++#define __VZ_QUOTA_NOQUOTA(sb) sb->s_dquot.vzdq_master
++#define __VZ_QUOTA_TSTAMP(sb) ((struct timeval *)\
++ &sb->s_dquot.dqio_mutex)
++
++#if defined(VZ_QUOTA_UNLOAD)
++
++#define __VZ_QUOTA_SBREF(sb) sb->s_dquot.vzdq_count
++
++struct dquot_operations *orig_dq_op;
++struct quotactl_ops *orig_dq_cop;
++
++/**
++ * quota_get_super - account for new a quoted tree under the superblock
++ *
++ * One superblock can have multiple directory subtrees with different VZ
++ * quotas. We keep a counter of such subtrees and set VZ quota operations or
++ * reset the default ones.
++ *
++ * Called under vz_quota_sem (from quota_on).
++ */
++int vzquota_get_super(struct super_block *sb)
++{
++ if (sb->dq_op != &vz_quota_operations) {
++ down(&sb->s_dquot.dqonoff_sem);
++ if (sb->s_dquot.flags & (DQUOT_USR_ENABLED|DQUOT_GRP_ENABLED)) {
++ up(&sb->s_dquot.dqonoff_sem);
++ return -EEXIST;
++ }
++ if (orig_dq_op == NULL && sb->dq_op != NULL)
++ orig_dq_op = sb->dq_op;
++ sb->dq_op = &vz_quota_operations;
++ if (orig_dq_cop == NULL && sb->s_qcop != NULL)
++ orig_dq_cop = sb->s_qcop;
++ /* XXX this may race with sys_quotactl */
++#ifdef CONFIG_VZ_QUOTA_UGID
++ sb->s_qcop = &vz_quotactl_operations;
++#else
++ sb->s_qcop = NULL;
++#endif
++ do_gettimeofday(__VZ_QUOTA_TSTAMP(sb));
++ memset(&sb->s_dquot.info, 0, sizeof(sb->s_dquot.info));
++
++ INIT_LIST_HEAD(&sb->s_dquot.info[USRQUOTA].dqi_dirty_list);
++ INIT_LIST_HEAD(&sb->s_dquot.info[GRPQUOTA].dqi_dirty_list);
++ sb->s_dquot.info[USRQUOTA].dqi_format = &vz_quota_empty_v2_format;
++ sb->s_dquot.info[GRPQUOTA].dqi_format = &vz_quota_empty_v2_format;
++ /*
++ * To get quotaops.h call us we need to mark superblock
++ * as having quota. These flags mark the moment when
++ * our dq_op start to be called.
++ *
++ * The ordering of dq_op and s_dquot.flags assignment
++ * needs to be enforced, but other CPUs do not do rmb()
++ * between s_dquot.flags and dq_op accesses.
++ */
++ wmb(); synchronize_sched();
++ sb->s_dquot.flags = DQUOT_USR_ENABLED|DQUOT_GRP_ENABLED;
++ __module_get(THIS_MODULE);
++ up(&sb->s_dquot.dqonoff_sem);
++ }
++ /* protected by vz_quota_sem */
++ __VZ_QUOTA_SBREF(sb)++;
++ return 0;
++}
++
++/**
++ * quota_put_super - release superblock when one quota tree goes away
++ *
++ * Called under vz_quota_sem.
++ */
++void vzquota_put_super(struct super_block *sb)
++{
++ int count;
++
++ count = --__VZ_QUOTA_SBREF(sb);
++ if (count == 0) {
++ down(&sb->s_dquot.dqonoff_sem);
++ sb->s_dquot.flags = 0;
++ wmb(); synchronize_sched();
++ sema_init(&sb->s_dquot.dqio_sem, 1);
++ sb->s_qcop = orig_dq_cop;
++ sb->dq_op = orig_dq_op;
++ inode_qmblk_lock(sb);
++ quota_gen_put(SB_QGEN(sb));
++ SB_QGEN(sb) = NULL;
++ /* release qlnk's without qmblk */
++ remove_inode_quota_links_list(&non_vzquota_inodes_lh,
++ sb, NULL);
++ /*
++ * Races with quota initialization:
++ * after this inode_qmblk_unlock all inode's generations are
++ * invalidated, quota_inode_qmblk checks superblock operations.
++ */
++ inode_qmblk_unlock(sb);
++ /*
++ * Module refcounting: in theory, this is the best place
++ * to call module_put(THIS_MODULE).
++ * In reality, it can't be done because we can't be sure that
++ * other CPUs do not enter our code segment through dq_op
++ * cached long time ago. Quotaops interface isn't supposed to
++ * go into modules currently (that is, into unloadable
++ * modules). By omitting module_put, our module isn't
++ * unloadable.
++ */
++ up(&sb->s_dquot.dqonoff_sem);
++ }
++}
++
++#else
++
++struct vzquota_new_sop {
++ struct super_operations new_op;
++ const struct super_operations *old_op;
++};
++
++/**
++ * vzquota_shutdown_super - callback on umount
++ */
++void vzquota_shutdown_super(struct super_block *sb)
++{
++ struct vz_quota_master *qmblk;
++ struct vzquota_new_sop *sop;
++
++ qmblk = __VZ_QUOTA_NOQUOTA(sb);
++ __VZ_QUOTA_NOQUOTA(sb) = NULL;
++ if (qmblk != NULL)
++ qmblk_put(qmblk);
++ sop = container_of(sb->s_op, struct vzquota_new_sop, new_op);
++ sb->s_op = sop->old_op;
++ kfree(sop);
++ if (sb->s_op->put_super != NULL)
++ (*sb->s_op->put_super)(sb);
++}
++
++/**
++ * vzquota_get_super - account for new a quoted tree under the superblock
++ *
++ * One superblock can have multiple directory subtrees with different VZ
++ * quotas.
++ *
++ * Called under vz_quota_sem (from vzquota_on).
++ */
++int vzquota_get_super(struct super_block *sb)
++{
++ struct vz_quota_master *qnew;
++ struct vzquota_new_sop *sop;
++ int err;
++
++ mutex_lock(&sb->s_dquot.dqonoff_mutex);
++ err = -EEXIST;
++ if ((sb->s_dquot.flags & (DQUOT_USR_ENABLED|DQUOT_GRP_ENABLED)) &&
++ sb->dq_op != &vz_quota_operations)
++ goto out_up;
++
++ /*
++ * This allocation code should be under sb->dq_op check below, but
++ * it doesn't really matter...
++ */
++ if (__VZ_QUOTA_NOQUOTA(sb) == NULL) {
++ qnew = vzquota_alloc_fake();
++ if (qnew == NULL)
++ goto out_up;
++ __VZ_QUOTA_NOQUOTA(sb) = qnew;
++ }
++
++ if (sb->dq_op != &vz_quota_operations) {
++ sop = kmalloc(sizeof(*sop), GFP_KERNEL);
++ if (sop == NULL) {
++ vzquota_free_master(__VZ_QUOTA_NOQUOTA(sb));
++ __VZ_QUOTA_NOQUOTA(sb) = NULL;
++ goto out_up;
++ }
++ memcpy(&sop->new_op, sb->s_op, sizeof(sop->new_op));
++ sop->new_op.put_super = &vzquota_shutdown_super;
++ sop->old_op = sb->s_op;
++ sb->s_op = &sop->new_op;
++
++ sb->dq_op = &vz_quota_operations;
++#ifdef CONFIG_VZ_QUOTA_UGID
++ sb->s_qcop = &vz_quotactl_operations;
++#else
++ sb->s_qcop = NULL;
++#endif
++ do_gettimeofday(__VZ_QUOTA_TSTAMP(sb));
++
++ memset(&sb->s_dquot.info, 0, sizeof(sb->s_dquot.info));
++ /* these 2 list heads are checked in sync_dquots() */
++ INIT_LIST_HEAD(&sb->s_dquot.info[USRQUOTA].dqi_dirty_list);
++ INIT_LIST_HEAD(&sb->s_dquot.info[GRPQUOTA].dqi_dirty_list);
++ sb->s_dquot.info[USRQUOTA].dqi_format =
++ &vz_quota_empty_v2_format;
++ sb->s_dquot.info[GRPQUOTA].dqi_format =
++ &vz_quota_empty_v2_format;
++
++ /*
++ * To get quotaops.h to call us we need to mark superblock
++ * as having quota. These flags mark the moment when
++ * our dq_op start to be called.
++ *
++ * The ordering of dq_op and s_dquot.flags assignment
++ * needs to be enforced, but other CPUs do not do rmb()
++ * between s_dquot.flags and dq_op accesses.
++ */
++ wmb(); synchronize_sched();
++ sb->s_dquot.flags = DQUOT_USR_ENABLED|DQUOT_GRP_ENABLED;
++ }
++ err = 0;
++
++out_up:
++ mutex_unlock(&sb->s_dquot.dqonoff_mutex);
++ return err;
++}
++
++/**
++ * vzquota_put_super - one quota tree less on this superblock
++ *
++ * Called under vz_quota_sem.
++ */
++void vzquota_put_super(struct super_block *sb)
++{
++ /*
++ * Even if this put is the last one,
++ * sb->s_dquot.flags can't be cleared, because otherwise vzquota_drop
++ * won't be called and the remaining qmblk references won't be put.
++ */
++}
++
++#endif
++
++
++/* ----------------------------------------------------------------------
++ *
++ * Helpers for inode -> qmblk link maintenance
++ *
++ * --------------------------------------------------------------------- */
++
++#define __VZ_QUOTA_EMPTY ((void *)0xbdbdbdbd)
++#define VZ_QUOTA_IS_NOQUOTA(qm, sb) ((qm)->dq_flags & VZDQ_NOQUOT)
++#define VZ_QUOTA_EMPTY_IOPS (&vfs_empty_iops)
++extern struct inode_operations vfs_empty_iops;
++
++static int VZ_QUOTA_IS_ACTUAL(struct inode *inode)
++{
++ struct vz_quota_master *qmblk;
++
++ qmblk = INODE_QLNK(inode)->qmblk;
++ if (qmblk == VZ_QUOTA_BAD)
++ return 1;
++ if (qmblk == __VZ_QUOTA_EMPTY)
++ return 0;
++ if (qmblk->dq_flags & VZDQ_NOACT)
++ /* not actual (invalidated) qmblk */
++ return 0;
++ return 1;
++}
++
++static inline int vzquota_qlnk_is_empty(struct vz_quota_ilink *qlnk)
++{
++ return qlnk->qmblk == __VZ_QUOTA_EMPTY;
++}
++
++static inline void set_qlnk_origin(struct vz_quota_ilink *qlnk,
++ unsigned char origin)
++{
++ qlnk->origin[0] = qlnk->origin[1];
++ qlnk->origin[1] = origin;
++}
++
++static inline void vzquota_qlnk_set_empty(struct vz_quota_ilink *qlnk)
++{
++ qlnk->qmblk = __VZ_QUOTA_EMPTY;
++ set_qlnk_origin(qlnk, VZ_QUOTAO_SETE);
++}
++
++void vzquota_qlnk_init(struct vz_quota_ilink *qlnk)
++{
++ memset(qlnk, 0, sizeof(*qlnk));
++ INIT_LIST_HEAD(&qlnk->list);
++ vzquota_qlnk_set_empty(qlnk);
++ set_qlnk_origin(qlnk, VZ_QUOTAO_INIT);
++}
++
++void vzquota_qlnk_destroy(struct vz_quota_ilink *qlnk)
++{
++ might_sleep();
++ if (vzquota_qlnk_is_empty(qlnk))
++ return;
++#if defined(CONFIG_VZ_QUOTA_UGID)
++ if (qlnk->qmblk != NULL && qlnk->qmblk != VZ_QUOTA_BAD) {
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid *quid, *qgid;
++ qmblk = qlnk->qmblk;
++ quid = qlnk->qugid[USRQUOTA];
++ qgid = qlnk->qugid[GRPQUOTA];
++ if (quid != NULL || qgid != NULL) {
++ down(&qmblk->dq_sem);
++ if (qgid != NULL)
++ vzquota_put_ugid(qmblk, qgid);
++ if (quid != NULL)
++ vzquota_put_ugid(qmblk, quid);
++ up(&qmblk->dq_sem);
++ }
++ }
++#endif
++ if (qlnk->qmblk != NULL && qlnk->qmblk != VZ_QUOTA_BAD)
++ qmblk_put(qlnk->qmblk);
++ set_qlnk_origin(qlnk, VZ_QUOTAO_DESTR);
++}
++
++/**
++ * vzquota_qlnk_swap - swap inode's and temporary vz_quota_ilink contents
++ * @qlt: temporary
++ * @qli: inode's
++ *
++ * Locking is provided by the caller (depending on the context).
++ * After swap, @qli is inserted into the corresponding dq_ilink_list,
++ * @qlt list is reinitialized.
++ */
++static void vzquota_qlnk_swap(struct vz_quota_ilink *qlt,
++ struct vz_quota_ilink *qli)
++{
++ struct vz_quota_master *qb;
++ struct vz_quota_ugid *qu;
++ int i;
++
++ qb = qlt->qmblk;
++ qlt->qmblk = qli->qmblk;
++ qli->qmblk = qb;
++ list_del_init(&qli->list);
++ if (qb != __VZ_QUOTA_EMPTY && qb != VZ_QUOTA_BAD)
++ list_add(&qli->list, &qb->dq_ilink_list);
++ INIT_LIST_HEAD(&qlt->list);
++ set_qlnk_origin(qli, VZ_QUOTAO_SWAP);
++
++ for (i = 0; i < MAXQUOTAS; i++) {
++ qu = qlt->qugid[i];
++ qlt->qugid[i] = qli->qugid[i];
++ qli->qugid[i] = qu;
++ }
++}
++
++/**
++ * vzquota_qlnk_reinit_locked - destroy qlnk content, called under locks
++ *
++ * Called under dcache_lock and inode_qmblk locks.
++ * Returns 1 if locks were dropped inside, 0 if atomic.
++ */
++static int vzquota_qlnk_reinit_locked(struct vz_quota_ilink *qlnk,
++ struct inode *inode)
++{
++ if (vzquota_qlnk_is_empty(qlnk))
++ return 0;
++ if (qlnk->qmblk == VZ_QUOTA_BAD) {
++ vzquota_qlnk_set_empty(qlnk);
++ set_qlnk_origin(qlnk, VZ_QUOTAO_RE_LOCK);
++ return 0;
++ }
++ spin_unlock(&dcache_lock);
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(qlnk);
++ vzquota_qlnk_init(qlnk);
++ inode_qmblk_lock(inode->i_sb);
++ spin_lock(&dcache_lock);
++ return 1;
++}
++
++#if defined(CONFIG_VZ_QUOTA_UGID)
++/**
++ * vzquota_qlnk_reinit_attr - destroy and reinit qlnk content
++ *
++ * Similar to vzquota_qlnk_reinit_locked, called under different locks.
++ */
++static int vzquota_qlnk_reinit_attr(struct vz_quota_ilink *qlnk,
++ struct inode *inode,
++ struct vz_quota_master *qmblk)
++{
++ if (vzquota_qlnk_is_empty(qlnk))
++ return 0;
++ /* may be optimized if qlnk->qugid all NULLs */
++ qmblk_data_write_unlock(qmblk);
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(qlnk);
++ vzquota_qlnk_init(qlnk);
++ inode_qmblk_lock(inode->i_sb);
++ qmblk_data_write_lock(qmblk);
++ return 1;
++}
++#endif
++
++/**
++ * vzquota_qlnk_fill - fill vz_quota_ilink content
++ * @qlnk: vz_quota_ilink to fill
++ * @inode: inode for which @qlnk is filled (i_sb, i_uid, i_gid)
++ * @qmblk: qmblk to which this @qlnk will belong
++ *
++ * Called under dcache_lock and inode_qmblk locks.
++ * Returns 1 if locks were dropped inside, 0 if atomic.
++ * @qlnk is expected to be empty.
++ */
++static int vzquota_qlnk_fill(struct vz_quota_ilink *qlnk,
++ struct inode *inode,
++ struct vz_quota_master *qmblk)
++{
++ if (qmblk != VZ_QUOTA_BAD)
++ qmblk_get(qmblk);
++ qlnk->qmblk = qmblk;
++
++#if defined(CONFIG_VZ_QUOTA_UGID)
++ if (qmblk != VZ_QUOTA_BAD &&
++ !VZ_QUOTA_IS_NOQUOTA(qmblk, inode->i_sb) &&
++ (qmblk->dq_flags & VZDQUG_ON)) {
++ struct vz_quota_ugid *quid, *qgid;
++
++ spin_unlock(&dcache_lock);
++ inode_qmblk_unlock(inode->i_sb);
++
++ down(&qmblk->dq_sem);
++ quid = __vzquota_find_ugid(qmblk, inode->i_uid, USRQUOTA, 0);
++ qgid = __vzquota_find_ugid(qmblk, inode->i_gid, GRPQUOTA, 0);
++ up(&qmblk->dq_sem);
++
++ inode_qmblk_lock(inode->i_sb);
++ spin_lock(&dcache_lock);
++ qlnk->qugid[USRQUOTA] = quid;
++ qlnk->qugid[GRPQUOTA] = qgid;
++ return 1;
++ }
++#endif
++
++ return 0;
++}
++
++#if defined(CONFIG_VZ_QUOTA_UGID)
++/**
++ * vzquota_qlnk_fill_attr - fill vz_quota_ilink content for uid, gid
++ *
++ * This function is a helper for vzquota_transfer, and differs from
++ * vzquota_qlnk_fill only by locking.
++ */
++static int vzquota_qlnk_fill_attr(struct vz_quota_ilink *qlnk,
++ struct inode *inode,
++ struct iattr *iattr,
++ int mask,
++ struct vz_quota_master *qmblk)
++{
++ qmblk_get(qmblk);
++ qlnk->qmblk = qmblk;
++
++ if (mask) {
++ struct vz_quota_ugid *quid, *qgid;
++
++ quid = qgid = NULL; /* to make gcc happy */
++ if (!(mask & (1 << USRQUOTA)))
++ quid = vzquota_get_ugid(INODE_QLNK(inode)->
++ qugid[USRQUOTA]);
++ if (!(mask & (1 << GRPQUOTA)))
++ qgid = vzquota_get_ugid(INODE_QLNK(inode)->
++ qugid[GRPQUOTA]);
++
++ qmblk_data_write_unlock(qmblk);
++ inode_qmblk_unlock(inode->i_sb);
++
++ down(&qmblk->dq_sem);
++ if (mask & (1 << USRQUOTA))
++ quid = __vzquota_find_ugid(qmblk, iattr->ia_uid,
++ USRQUOTA, 0);
++ if (mask & (1 << GRPQUOTA))
++ qgid = __vzquota_find_ugid(qmblk, iattr->ia_gid,
++ GRPQUOTA, 0);
++ up(&qmblk->dq_sem);
++
++ inode_qmblk_lock(inode->i_sb);
++ qmblk_data_write_lock(qmblk);
++ qlnk->qugid[USRQUOTA] = quid;
++ qlnk->qugid[GRPQUOTA] = qgid;
++ return 1;
++ }
++
++ return 0;
++}
++#endif
++
++/**
++ * __vzquota_inode_init - make sure inode's qlnk is initialized
++ *
++ * May be called if qlnk is already initialized, detects this situation itself.
++ * Called under inode_qmblk_lock.
++ */
++static void __vzquota_inode_init(struct inode *inode, unsigned char origin)
++{
++ if (inode->i_dquot[USRQUOTA] == NODQUOT) {
++ vzquota_qlnk_init(INODE_QLNK(inode));
++ inode->i_dquot[USRQUOTA] = (void *)~(unsigned long)NODQUOT;
++ }
++ set_qlnk_origin(INODE_QLNK(inode), origin);
++}
++
++/**
++ * vzquota_inode_drop - destroy VZ quota information in the inode
++ *
++ * Inode must not be externally accessible or dirty.
++ */
++static void vzquota_inode_drop(struct inode *inode)
++{
++ struct vz_quota_ilink qlnk;
++
++ vzquota_qlnk_init(&qlnk);
++ inode_qmblk_lock(inode->i_sb);
++ vzquota_qlnk_swap(&qlnk, INODE_QLNK(inode));
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_DRCAL);
++ inode->i_dquot[USRQUOTA] = NODQUOT;
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(&qlnk);
++}
++
++/**
++ * vzquota_inode_qmblk_set - initialize inode's qlnk
++ * @inode: inode to be initialized
++ * @qmblk: quota master block to which this inode should belong (may be BAD)
++ * @qlnk: placeholder to store data to resolve locking issues
++ *
++ * Returns 1 if locks were dropped and rechecks possibly needed, 0 otherwise.
++ * Called under dcache_lock and inode_qmblk locks.
++ * @qlnk will be destroyed in the caller chain.
++ *
++ * It is not mandatory to restart parent checks since quota on/off currently
++ * shrinks dentry tree and checks that there are not outside references.
++ * But if at some time that shink is removed, restarts will be required.
++ * Additionally, the restarts prevent inconsistencies if the dentry tree
++ * changes (inode is moved). This is not a big deal, but anyway...
++ */
++static int vzquota_inode_qmblk_set(struct inode *inode,
++ struct vz_quota_master *qmblk,
++ struct vz_quota_ilink *qlnk)
++{
++ if (qmblk == NULL) {
++ printk(KERN_ERR "VZDQ: NULL in set, orig {%u, %u}, "
++ "dev %s, inode %lu, fs %s\n",
++ INODE_QLNK(inode)->origin[0],
++ INODE_QLNK(inode)->origin[1],
++ inode->i_sb->s_id, inode->i_ino,
++ inode->i_sb->s_type->name);
++ printk(KERN_ERR "current %d (%s), VE %d\n",
++ current->pid, current->comm,
++ VEID(get_exec_env()));
++ dump_stack();
++ qmblk = VZ_QUOTA_BAD;
++ }
++ while (1) {
++ if (vzquota_qlnk_is_empty(qlnk) &&
++ vzquota_qlnk_fill(qlnk, inode, qmblk))
++ return 1;
++ if (qlnk->qmblk == qmblk)
++ break;
++ if (vzquota_qlnk_reinit_locked(qlnk, inode))
++ return 1;
++ }
++ vzquota_qlnk_swap(qlnk, INODE_QLNK(inode));
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_QSET);
++ return 0;
++}
++
++
++/* ----------------------------------------------------------------------
++ *
++ * vzquota_inode_qmblk (inode -> qmblk lookup) parts
++ *
++ * --------------------------------------------------------------------- */
++
++static int vzquota_dparents_check_attach(struct inode *inode)
++{
++ if (!list_empty(&inode->i_dentry))
++ return 0;
++ printk(KERN_ERR "VZDQ: no parent for "
++ "dev %s, inode %lu, fs %s\n",
++ inode->i_sb->s_id,
++ inode->i_ino,
++ inode->i_sb->s_type->name);
++ return -1;
++}
++
++static struct inode *vzquota_dparents_check_actual(struct inode *inode)
++{
++ struct dentry *de;
++
++ list_for_each_entry(de, &inode->i_dentry, d_alias) {
++ if (de->d_parent == de) /* detached dentry, perhaps */
++ continue;
++ /* first access to parent, make sure its qlnk initialized */
++ __vzquota_inode_init(de->d_parent->d_inode, VZ_QUOTAO_ACT);
++ if (!VZ_QUOTA_IS_ACTUAL(de->d_parent->d_inode))
++ return de->d_parent->d_inode;
++ }
++ return NULL;
++}
++
++static struct vz_quota_master *vzquota_dparents_check_same(struct inode *inode)
++{
++ struct dentry *de;
++ struct vz_quota_master *qmblk;
++
++ qmblk = NULL;
++ list_for_each_entry(de, &inode->i_dentry, d_alias) {
++ if (de->d_parent == de) /* detached dentry, perhaps */
++ continue;
++ if (qmblk == NULL) {
++ qmblk = INODE_QLNK(de->d_parent->d_inode)->qmblk;
++ continue;
++ }
++ if (INODE_QLNK(de->d_parent->d_inode)->qmblk != qmblk) {
++ printk(KERN_WARNING "VZDQ: multiple quotas for "
++ "dev %s, inode %lu, fs %s\n",
++ inode->i_sb->s_id,
++ inode->i_ino,
++ inode->i_sb->s_type->name);
++ qmblk = VZ_QUOTA_BAD;
++ break;
++ }
++ }
++ if (qmblk == NULL) {
++ printk(KERN_WARNING "VZDQ: not attached to tree, "
++ "dev %s, inode %lu, fs %s\n",
++ inode->i_sb->s_id,
++ inode->i_ino,
++ inode->i_sb->s_type->name);
++ qmblk = VZ_QUOTA_BAD;
++ }
++ return qmblk;
++}
++
++static void vzquota_dbranch_actualize(struct inode *inode,
++ struct inode *refinode)
++{
++ struct inode *pinode;
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ilink qlnk;
++
++ vzquota_qlnk_init(&qlnk);
++
++start:
++ if (inode == inode->i_sb->s_root->d_inode) {
++ /* filesystem root */
++ atomic_inc(&inode->i_count);
++ do {
++ qmblk = __VZ_QUOTA_NOQUOTA(inode->i_sb);
++ } while (vzquota_inode_qmblk_set(inode, qmblk, &qlnk));
++ goto out;
++ }
++
++ if (!vzquota_dparents_check_attach(inode)) {
++ pinode = vzquota_dparents_check_actual(inode);
++ if (pinode != NULL) {
++ inode = pinode;
++ goto start;
++ }
++ }
++
++ atomic_inc(&inode->i_count);
++ while (1) {
++ if (VZ_QUOTA_IS_ACTUAL(inode)) /* actualized without us */
++ break;
++ /*
++ * Need to check parents again if we have slept inside
++ * vzquota_inode_qmblk_set() in the loop.
++ * If the state of parents is different, just return and repeat
++ * the actualizing process again from the inode passed to
++ * vzquota_inode_qmblk_recalc().
++ */
++ if (!vzquota_dparents_check_attach(inode)) {
++ if (vzquota_dparents_check_actual(inode) != NULL)
++ break;
++ qmblk = vzquota_dparents_check_same(inode);
++ } else
++ qmblk = VZ_QUOTA_BAD;
++ if (!vzquota_inode_qmblk_set(inode, qmblk, &qlnk)){/* success */
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_ACT);
++ break;
++ }
++ }
++
++out:
++ spin_unlock(&dcache_lock);
++ inode_qmblk_unlock(refinode->i_sb);
++ vzquota_qlnk_destroy(&qlnk);
++ iput(inode);
++ inode_qmblk_lock(refinode->i_sb);
++ spin_lock(&dcache_lock);
++}
++
++static void vzquota_dtree_qmblk_recalc(struct inode *inode,
++ struct vz_quota_ilink *qlnk)
++{
++ struct inode *pinode;
++ struct vz_quota_master *qmblk;
++
++ if (inode == inode->i_sb->s_root->d_inode) {
++ /* filesystem root */
++ do {
++ qmblk = __VZ_QUOTA_NOQUOTA(inode->i_sb);
++ } while (vzquota_inode_qmblk_set(inode, qmblk, qlnk));
++ return;
++ }
++
++start:
++ if (VZ_QUOTA_IS_ACTUAL(inode))
++ return;
++ /*
++ * Here qmblk is (re-)initialized for all ancestors.
++ * This is not a very efficient procedure, but it guarantees that
++ * the quota tree is consistent (that is, the inode doesn't have two
++ * ancestors with different qmblk).
++ */
++ if (!vzquota_dparents_check_attach(inode)) {
++ pinode = vzquota_dparents_check_actual(inode);
++ if (pinode != NULL) {
++ vzquota_dbranch_actualize(pinode, inode);
++ goto start;
++ }
++ qmblk = vzquota_dparents_check_same(inode);
++ } else
++ qmblk = VZ_QUOTA_BAD;
++
++ if (vzquota_inode_qmblk_set(inode, qmblk, qlnk))
++ goto start;
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_DTREE);
++}
++
++static void vzquota_det_qmblk_recalc(struct inode *inode,
++ struct vz_quota_ilink *qlnk)
++{
++ struct inode *parent;
++ struct vz_quota_master *qmblk;
++ char *msg;
++ int cnt;
++ time_t timeout;
++
++ cnt = 0;
++ parent = NULL;
++start:
++ /*
++ * qmblk of detached inodes shouldn't be considered as not actual.
++ * They are not in any dentry tree, so quota on/off shouldn't affect
++ * them.
++ */
++ if (!vzquota_qlnk_is_empty(INODE_QLNK(inode)))
++ return;
++
++ timeout = 3;
++ qmblk = __VZ_QUOTA_NOQUOTA(inode->i_sb);
++ /*
++ * Scenario:
++ * open
++ * unlink
++ * quotaon
++ * generic_delete_inode
++ *
++ * This is the first time vzquota sees inode. inode is outside of
++ * vzquota area of interest, otherwise quotaon would have got -EBUSY
++ * due to shrink_dcache_parent().
++ * inode is almost completely destroyed, so don't intervene.
++ *
++ * dev@:
++ * However, there is a small race here...
++ * dput() first removes itself from all the lists,
++ * so shrink_dcache_parent() can succeed while dentry_iput is not
++ * done yet.
++ */
++ if (inode->i_state & I_FREEING)
++ goto set;
++
++ msg = "detached inode not in creation";
++ if (inode->i_op != VZ_QUOTA_EMPTY_IOPS)
++ goto fail;
++ qmblk = VZ_QUOTA_BAD;
++ msg = "unexpected creation context";
++ if (!vzquota_cur_qmblk_check())
++ goto fail;
++ timeout = 0;
++ parent = vzquota_cur_qmblk_fetch();
++ msg = "uninitialized parent";
++ if (vzquota_qlnk_is_empty(INODE_QLNK(parent)))
++ goto fail;
++ msg = "parent not in tree";
++ if (list_empty(&parent->i_dentry))
++ goto fail;
++ msg = "parent has 0 refcount";
++ if (!atomic_read(&parent->i_count))
++ goto fail;
++ msg = "parent has different sb";
++ if (parent->i_sb != inode->i_sb)
++ goto fail;
++ if (!VZ_QUOTA_IS_ACTUAL(parent)) {
++ vzquota_dbranch_actualize(parent, inode);
++ goto start;
++ }
++
++ qmblk = INODE_QLNK(parent)->qmblk;
++set:
++ if (vzquota_inode_qmblk_set(inode, qmblk, qlnk))
++ goto start;
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_DET);
++ return;
++
++fail:
++ {
++ struct timeval tv, tvo;
++ do_gettimeofday(&tv);
++ memcpy(&tvo, __VZ_QUOTA_TSTAMP(inode->i_sb), sizeof(tvo));
++ tv.tv_sec -= tvo.tv_sec;
++ if (tv.tv_usec < tvo.tv_usec) {
++ tv.tv_sec--;
++ tv.tv_usec += USEC_PER_SEC - tvo.tv_usec;
++ } else
++ tv.tv_usec -= tvo.tv_usec;
++ if (tv.tv_sec < timeout)
++ goto set;
++ printk(KERN_ERR "VZDQ: %s, orig {%u, %u},"
++ " dev %s, inode %lu, fs %s\n",
++ msg,
++ INODE_QLNK(inode)->origin[0],
++ INODE_QLNK(inode)->origin[1],
++ inode->i_sb->s_id, inode->i_ino,
++ inode->i_sb->s_type->name);
++ printk(KERN_ERR "i_count %u, ", atomic_read(&inode->i_count));
++ printk(KERN_ERR "i_mode %o, ", inode->i_mode);
++ printk(KERN_ERR "i_state %lx, ", inode->i_state);
++ printk(KERN_ERR "i_flags %x\n", inode->i_flags);
++ printk(KERN_ERR "i_op %p, vfs_empty_iops %p, "
++ "i_fop %p, i_mapping %p\n",
++ inode->i_op, &vfs_empty_iops,
++ inode->i_fop, inode->i_mapping);
++ if (!cnt++) {
++ printk(KERN_ERR "current %d (%s), VE %d,"
++ " time %ld.%06ld\n",
++ current->pid, current->comm,
++ VEID(get_exec_env()),
++ tv.tv_sec, (long)tv.tv_usec);
++ dump_stack();
++ }
++ if (parent != NULL)
++ printk(KERN_ERR "VZDQ: parent of %lu is %lu\n",
++ inode->i_ino, parent->i_ino);
++ }
++ goto set;
++}
++
++static void vzquota_inode_qmblk_recalc(struct inode *inode,
++ struct vz_quota_ilink *qlnk)
++{
++ spin_lock(&dcache_lock);
++ if (!list_empty(&inode->i_dentry))
++ vzquota_dtree_qmblk_recalc(inode, qlnk);
++ else
++ vzquota_det_qmblk_recalc(inode, qlnk);
++ spin_unlock(&dcache_lock);
++}
++
++/**
++ * vzquota_inode_qmblk - obtain inode's qmblk
++ *
++ * Returns qmblk with refcounter taken, %NULL if not under
++ * VZ quota or %VZ_QUOTA_BAD.
++ *
++ * FIXME: This function should be removed when vzquota_find_qmblk /
++ * get_quota_root / vzquota_dstat code is cleaned up.
++ */
++struct vz_quota_master *vzquota_inode_qmblk(struct inode *inode)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ilink qlnk;
++
++ might_sleep();
++
++ if (inode->i_sb->dq_op != &vz_quota_operations)
++ return NULL;
++#if defined(VZ_QUOTA_UNLOAD)
++#error Make sure qmblk does not disappear
++#endif
++
++ vzquota_qlnk_init(&qlnk);
++ inode_qmblk_lock(inode->i_sb);
++ __vzquota_inode_init(inode, VZ_QUOTAO_INICAL);
++
++ if (vzquota_qlnk_is_empty(INODE_QLNK(inode)) ||
++ !VZ_QUOTA_IS_ACTUAL(inode))
++ vzquota_inode_qmblk_recalc(inode, &qlnk);
++
++ qmblk = INODE_QLNK(inode)->qmblk;
++ if (qmblk != VZ_QUOTA_BAD) {
++ if (!VZ_QUOTA_IS_NOQUOTA(qmblk, inode->i_sb))
++ qmblk_get(qmblk);
++ else
++ qmblk = NULL;
++ }
++
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(&qlnk);
++ return qmblk;
++}
++
++/**
++ * vzquota_find_qmblk - helper to emulate quota on virtual filesystems
++ *
++ * This function finds a quota master block corresponding to the root of
++ * a virtual filesystem.
++ * Returns a quota master block with reference taken, or %NULL if not under
++ * quota, or %VZ_QUOTA_BAD if quota inconsistency is found (and all allocation
++ * operations will fail).
++ *
++ * Note: this function uses vzquota_inode_qmblk().
++ * The latter is a rather confusing function: it returns qmblk that used to be
++ * on the inode some time ago (without guarantee that it still has any
++ * relations to the inode). So, vzquota_find_qmblk() leaves it up to the
++ * caller to think whether the inode could have changed its qmblk and what to
++ * do in that case.
++ * Currently, the callers appear to not care :(
++ */
++struct vz_quota_master *vzquota_find_qmblk(struct super_block *sb)
++{
++ struct inode *qrinode;
++ struct vz_quota_master *qmblk;
++
++ qmblk = NULL;
++ qrinode = NULL;
++ if (sb->s_op->get_quota_root != NULL)
++ qrinode = sb->s_op->get_quota_root(sb);
++ if (qrinode != NULL)
++ qmblk = vzquota_inode_qmblk(qrinode);
++ return qmblk;
++}
++
++/* ----------------------------------------------------------------------
++ *
++ * Calls from quota operations
++ *
++ * --------------------------------------------------------------------- */
++
++/**
++ * vzquota_inode_init_call - call from DQUOT_INIT
++ */
++void vzquota_inode_init_call(struct inode *inode)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++
++ /* initializes inode's quota inside */
++ qmblk = vzquota_inode_data(inode, &data);
++ if (qmblk != NULL && qmblk != VZ_QUOTA_BAD)
++ vzquota_data_unlock(inode, &data);
++
++ /*
++ * The check is needed for repeated new_inode() calls from a single
++ * ext3 call like create or mkdir in case of -ENOSPC.
++ */
++ spin_lock(&dcache_lock);
++ if (!list_empty(&inode->i_dentry))
++ vzquota_cur_qmblk_set(inode);
++ spin_unlock(&dcache_lock);
++}
++
++/**
++ * vzquota_inode_drop_call - call from DQUOT_DROP
++ */
++void vzquota_inode_drop_call(struct inode *inode)
++{
++ vzquota_inode_drop(inode);
++}
++
++/**
++ * vzquota_inode_data - initialize (if nec.) and lock inode quota ptrs
++ * @inode: the inode
++ * @data: storage space
++ *
++ * Returns: qmblk is NULL or VZ_QUOTA_BAD or actualized qmblk.
++ * On return if qmblk is neither NULL nor VZ_QUOTA_BAD:
++ * qmblk in inode's qlnk is the same as returned,
++ * ugid pointers inside inode's qlnk are valid,
++ * some locks are taken (and should be released by vzquota_data_unlock).
++ * If qmblk is NULL or VZ_QUOTA_BAD, locks are NOT taken.
++ */
++struct vz_quota_master *vzquota_inode_data(struct inode *inode,
++ struct vz_quota_datast *data)
++{
++ struct vz_quota_master *qmblk;
++
++ might_sleep();
++
++ vzquota_qlnk_init(&data->qlnk);
++ inode_qmblk_lock(inode->i_sb);
++ if (unlikely(inode->i_flags & S_NOQUOTA)) {
++ inode_qmblk_unlock(inode->i_sb);
++ return NULL;
++ }
++ __vzquota_inode_init(inode, VZ_QUOTAO_INICAL);
++
++ if (vzquota_qlnk_is_empty(INODE_QLNK(inode)) ||
++ !VZ_QUOTA_IS_ACTUAL(inode))
++ vzquota_inode_qmblk_recalc(inode, &data->qlnk);
++
++ qmblk = INODE_QLNK(inode)->qmblk;
++ if (qmblk != VZ_QUOTA_BAD) {
++ if (!VZ_QUOTA_IS_NOQUOTA(qmblk, inode->i_sb)) {
++ /*
++ * Note that in the current implementation,
++ * inode_qmblk_lock can theoretically be dropped here.
++ * This place is serialized with quota_off because
++ * quota_off fails when there are extra dentry
++ * references and syncs inodes before removing quota
++ * information from them.
++ * However, quota usage information should stop being
++ * updated immediately after vzquota_off.
++ */
++ qmblk_data_write_lock(qmblk);
++ } else {
++ inode_qmblk_unlock(inode->i_sb);
++ qmblk = NULL;
++ }
++ } else {
++ inode_qmblk_unlock(inode->i_sb);
++ }
++ return qmblk;
++}
++
++void vzquota_data_unlock(struct inode *inode,
++ struct vz_quota_datast *data)
++{
++ qmblk_data_write_unlock(INODE_QLNK(inode)->qmblk);
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(&data->qlnk);
++}
++
++#if defined(CONFIG_VZ_QUOTA_UGID)
++/**
++ * vzquota_inode_transfer_call - call from vzquota_transfer
++ */
++int vzquota_inode_transfer_call(struct inode *inode, struct iattr *iattr)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_datast data;
++ struct vz_quota_ilink qlnew;
++ int mask;
++ int ret;
++
++ might_sleep();
++ vzquota_qlnk_init(&qlnew);
++start:
++ qmblk = vzquota_inode_data(inode, &data);
++ ret = NO_QUOTA;
++ if (qmblk == VZ_QUOTA_BAD)
++ goto out_destr;
++ ret = QUOTA_OK;
++ if (qmblk == NULL)
++ goto out_destr;
++ qmblk_get(qmblk);
++
++ ret = QUOTA_OK;
++ if (!(qmblk->dq_flags & VZDQUG_ON))
++ /* no ugid quotas */
++ goto out_unlock;
++
++ mask = 0;
++ if ((iattr->ia_valid & ATTR_UID) && iattr->ia_uid != inode->i_uid)
++ mask |= 1 << USRQUOTA;
++ if ((iattr->ia_valid & ATTR_GID) && iattr->ia_gid != inode->i_gid)
++ mask |= 1 << GRPQUOTA;
++ while (1) {
++ if (vzquota_qlnk_is_empty(&qlnew) &&
++ vzquota_qlnk_fill_attr(&qlnew, inode, iattr, mask, qmblk))
++ break;
++ if (qlnew.qmblk == INODE_QLNK(inode)->qmblk &&
++ qlnew.qmblk == qmblk)
++ goto finish;
++ if (vzquota_qlnk_reinit_attr(&qlnew, inode, qmblk))
++ break;
++ }
++
++ /* prepare for restart */
++ vzquota_data_unlock(inode, &data);
++ qmblk_put(qmblk);
++ goto start;
++
++finish:
++ /* all references obtained successfully */
++ ret = vzquota_transfer_usage(inode, mask, &qlnew);
++ if (!ret) {
++ vzquota_qlnk_swap(&qlnew, INODE_QLNK(inode));
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_TRANS);
++ }
++out_unlock:
++ vzquota_data_unlock(inode, &data);
++ qmblk_put(qmblk);
++out_destr:
++ vzquota_qlnk_destroy(&qlnew);
++ return ret;
++}
++#endif
++
++int vzquota_rename_check(struct inode *inode,
++ struct inode *old_dir, struct inode *new_dir)
++{
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ilink qlnk1, qlnk2, qlnk3;
++ int c, ret;
++
++ if (inode->i_sb != old_dir->i_sb || inode->i_sb != new_dir->i_sb)
++ return -1;
++
++ might_sleep();
++
++ vzquota_qlnk_init(&qlnk1);
++ vzquota_qlnk_init(&qlnk2);
++ vzquota_qlnk_init(&qlnk3);
++ inode_qmblk_lock(inode->i_sb);
++ __vzquota_inode_init(inode, VZ_QUOTAO_INICAL);
++ __vzquota_inode_init(old_dir, VZ_QUOTAO_INICAL);
++ __vzquota_inode_init(new_dir, VZ_QUOTAO_INICAL);
++
++ do {
++ c = 0;
++ if (vzquota_qlnk_is_empty(INODE_QLNK(inode)) ||
++ !VZ_QUOTA_IS_ACTUAL(inode)) {
++ vzquota_inode_qmblk_recalc(inode, &qlnk1);
++ c++;
++ }
++ if (vzquota_qlnk_is_empty(INODE_QLNK(new_dir)) ||
++ !VZ_QUOTA_IS_ACTUAL(new_dir)) {
++ vzquota_inode_qmblk_recalc(new_dir, &qlnk2);
++ c++;
++ }
++ } while (c);
++
++ ret = 0;
++ qmblk = INODE_QLNK(inode)->qmblk;
++ if (qmblk != INODE_QLNK(new_dir)->qmblk) {
++ ret = -1;
++ while (vzquota_qlnk_is_empty(INODE_QLNK(old_dir)) ||
++ !VZ_QUOTA_IS_ACTUAL(old_dir))
++ vzquota_inode_qmblk_recalc(old_dir, &qlnk3);
++ if (qmblk != VZ_QUOTA_BAD &&
++ !VZ_QUOTA_IS_NOQUOTA(qmblk, inode->i_sb) &&
++ qmblk->dq_root_path.dentry->d_inode == inode &&
++ VZ_QUOTA_IS_NOQUOTA(INODE_QLNK(new_dir)->qmblk,
++ inode->i_sb) &&
++ VZ_QUOTA_IS_NOQUOTA(INODE_QLNK(old_dir)->qmblk,
++ inode->i_sb))
++ /* quota root rename is allowed */
++ ret = 0;
++ }
++
++ inode_qmblk_unlock(inode->i_sb);
++ vzquota_qlnk_destroy(&qlnk3);
++ vzquota_qlnk_destroy(&qlnk2);
++ vzquota_qlnk_destroy(&qlnk1);
++ return ret;
++}
++
++/*
++ * Scan parent subdirs and find busy dentries names/path
++ * @parent: parent dentry
++ * @buf: buffer to store path.
++ */
++static void vzdquota_read_busy_dentries(struct path *parent,
++ char *buf, int buflen)
++{
++ struct dentry *this_parent = parent->dentry;
++ struct list_head *next;
++ char *res, *end, *start;
++ struct path root, path;
++ int len;
++
++ if (!buf || buflen <= 0)
++ return;
++
++ path.mnt = parent->mnt;
++ /* From d_path() ... */
++ read_lock(&current->fs->lock);
++ path_get(&current->fs->root);
++ root = current->fs->root;
++ read_unlock(&current->fs->lock);
++
++ spin_lock(&dcache_lock);
++
++ end = buf + buflen;
++ start = buf;
++repeat:
++ next = this_parent->d_subdirs.next;
++resume:
++ while (next != &this_parent->d_subdirs) {
++ struct list_head *tmp = next;
++ struct dentry *dentry;
++ int subdirs;
++
++ dentry = list_entry(tmp, struct dentry, d_u.d_child);
++ next = tmp->next;
++ subdirs = !list_empty(&dentry->d_subdirs);
++
++ if (atomic_read(&dentry->d_count) && !subdirs) {
++ if (!buflen)
++ goto out;
++ /*
++ * Note: __d_path will store filename at the
++ * end of buf.
++ */
++ path.dentry = dentry;
++ res = __d_path(&path, &root, buf, buflen);
++ /* Exit if name is too long */
++ if (IS_ERR(res))
++ goto out;
++
++ /*
++ * Move the string obtained by __d_path,
++ * behind the last dentry path in buf.
++ */
++ len = end - res;
++ BUG_ON(len <= 0);
++
++ memmove(buf, res, len);
++
++ /* Trick: replace \0 by \n */
++ if (buf != start)
++ *(char *)(buf - 1) = '\n';
++
++ buf += len;
++ buflen -= len;
++ }
++
++ /*
++ * Descend a level if the d_subdirs list is non-empty.
++ */
++ if (subdirs) {
++ this_parent = dentry;
++ goto repeat;
++ }
++ }
++ /*
++ * All done at this level ... ascend and resume the search.
++ */
++ if (this_parent != parent->dentry) {
++ next = this_parent->d_u.d_child.next;
++ this_parent = this_parent->d_parent;
++ goto resume;
++ }
++out:
++ /* From d_path() ... */
++ spin_unlock(&dcache_lock);
++ path_put(&root);
++}
++
++/* ----------------------------------------------------------------------
++ *
++ * qmblk-related parts of on/off operations
++ *
++ * --------------------------------------------------------------------- */
++
++/**
++ * vzquota_check_dtree - check dentry tree if quota on/off is allowed
++ *
++ * This function doesn't allow quota to be turned on/off if some dentries in
++ * the tree have external references.
++ * In addition to technical reasons, it enforces user-space correctness:
++ * current usage (taken from or reported to the user space) can be meaningful
++ * and accurate only if the tree is not being modified.
++ * Side effect: additional vfsmount structures referencing the tree (bind
++ * mounts of tree nodes to some other places) are not allowed at on/off time.
++ *
++ * Store busy dentries path to the buf (if passed) in case of vzquota_off
++ * ioctl fail.
++ */
++int vzquota_check_dtree(struct vz_quota_master *qmblk, int off,
++ char *buf, int buflen)
++{
++ struct dentry *dentry;
++ int err, count;
++
++ err = -EBUSY;
++ dentry = qmblk->dq_root_path.dentry;
++
++ if (d_unhashed(dentry) && dentry != dentry->d_sb->s_root)
++ goto unhashed;
++
++ /* attempt to shrink */
++ if (!list_empty(&dentry->d_subdirs)) {
++ spin_unlock(&dcache_lock);
++ inode_qmblk_unlock(dentry->d_sb);
++ shrink_dcache_parent(dentry);
++ inode_qmblk_lock(dentry->d_sb);
++ spin_lock(&dcache_lock);
++ if (!list_empty(&dentry->d_subdirs)) {
++ spin_unlock(&dcache_lock);
++ vzdquota_read_busy_dentries(&qmblk->dq_root_path,
++ buf, buflen);
++ spin_lock(&dcache_lock);
++ goto out;
++ }
++
++ count = 1;
++ if (dentry == dentry->d_sb->s_root)
++ count += 2; /* sb and mnt refs */
++ if (atomic_read(&dentry->d_count) < count) {
++ printk(KERN_ERR "%s: too small count %d vs %d.\n",
++ __FUNCTION__,
++ atomic_read(&dentry->d_count), count);
++ goto out;
++ }
++ if (atomic_read(&dentry->d_count) > count)
++ goto out;
++ }
++
++ err = 0;
++out:
++ return err;
++
++unhashed:
++ /*
++ * Quota root is removed.
++ * Allow to turn quota off, but not on.
++ */
++ if (off)
++ err = 0;
++ goto out;
++}
++
++int vzquota_on_qmblk(struct super_block *sb, struct inode *inode,
++ struct vz_quota_master *qmblk, char __user *ubuf)
++{
++ struct vz_quota_ilink qlnk;
++ struct vz_quota_master *qold, *qnew;
++ int err;
++ char *buf;
++
++ buf = (ubuf != NULL) ? (char *)__get_free_page(GFP_KERNEL) : NULL;
++
++ might_sleep();
++
++ qold = NULL;
++ qnew = vzquota_alloc_fake();
++ if (qnew == NULL) {
++ free_page((unsigned long)buf);
++ return -ENOMEM;
++ }
++
++ vzquota_qlnk_init(&qlnk);
++ inode_qmblk_lock(sb);
++ __vzquota_inode_init(inode, VZ_QUOTAO_INICAL);
++
++ spin_lock(&dcache_lock);
++ while (1) {
++ err = vzquota_check_dtree(qmblk, 0, buf, PAGE_SIZE);
++ if (err)
++ break;
++ if (!vzquota_inode_qmblk_set(inode, qmblk, &qlnk))
++ break;
++ }
++ set_qlnk_origin(INODE_QLNK(inode), VZ_QUOTAO_ON);
++ spin_unlock(&dcache_lock);
++
++ if (!err) {
++ qold = __VZ_QUOTA_NOQUOTA(sb);
++ qold->dq_flags |= VZDQ_NOACT;
++ __VZ_QUOTA_NOQUOTA(sb) = qnew;
++ }
++
++ inode_qmblk_unlock(sb);
++ vzquota_qlnk_destroy(&qlnk);
++ if (qold != NULL)
++ qmblk_put(qold);
++
++ if (buf) {
++ if (copy_to_user(ubuf, buf, PAGE_SIZE))
++ ;
++ free_page((unsigned long)buf);
++ }
++ return err;
++}
++
++int vzquota_off_qmblk(struct super_block *sb, struct vz_quota_master *qmblk,
++ char __user *ubuf, int force)
++{
++ int ret;
++ char *buf;
++
++ buf = (ubuf != NULL) ? (char *)__get_free_page(GFP_KERNEL) : NULL;
++
++ ret = 0;
++ inode_qmblk_lock(sb);
++
++ spin_lock(&dcache_lock);
++ if (vzquota_check_dtree(qmblk, 1, buf, PAGE_SIZE) && !force)
++ ret = -EBUSY;
++ spin_unlock(&dcache_lock);
++
++ if (!ret)
++ qmblk->dq_flags |= VZDQ_NOACT | VZDQ_NOQUOT;
++ inode_qmblk_unlock(sb);
++
++ if (buf) {
++ if (copy_to_user(ubuf, buf, PAGE_SIZE))
++ ;
++ free_page((unsigned long)buf);
++ }
++ return ret;
++}
++
++
++/* ----------------------------------------------------------------------
++ *
++ * External interfaces
++ *
++ * ---------------------------------------------------------------------*/
++
++static int vzquota_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ switch (cmd) {
++ case VZCTL_QUOTA_NEW_CTL: {
++ struct vzctl_quotactl qb;
++
++ err = -EFAULT;
++ if (copy_from_user(&qb, (void __user *)arg, sizeof(qb)))
++ break;
++ err = do_vzquotactl(qb.cmd, qb.quota_id,
++ qb.qstat, qb.ve_root, 0);
++ break;
++ }
++#ifdef CONFIG_VZ_QUOTA_UGID
++ case VZCTL_QUOTA_UGID_CTL: {
++ struct vzctl_quotaugidctl qub;
++
++ err = -EFAULT;
++ if (copy_from_user(&qub, (void __user *)arg, sizeof(qub)))
++ break;
++ err = do_vzquotaugidctl(qub.cmd, qub.quota_id,
++ qub.ugid_index, qub.ugid_size, qub.addr, 0);
++ break;
++ }
++#endif
++ default:
++ err = -ENOTTY;
++ }
++ return err;
++}
++
++#ifdef CONFIG_COMPAT
++static int compat_vzquota_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ switch (cmd) {
++ case VZCTL_COMPAT_QUOTA_CTL: {
++ struct compat_vzctl_quotactl cs;
++
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++ err = do_vzquotactl(cs.cmd, cs.quota_id,
++ compat_ptr(cs.qstat),
++ compat_ptr(cs.ve_root), 1);
++ break;
++ }
++#ifdef CONFIG_VZ_QUOTA_UGID
++ case VZCTL_COMPAT_QUOTA_UGID_CTL: {
++ struct compat_vzctl_quotaugidctl cs;
++
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++
++ err = do_vzquotaugidctl(cs.cmd, cs.quota_id, cs.ugid_index,
++ cs.ugid_size, compat_ptr(cs.addr), 1);
++ break;
++ }
++#endif
++ default:
++ err = -ENOIOCTLCMD;
++ }
++ return err;
++}
++#endif
++
++static struct vzioctlinfo vzdqcalls = {
++ .type = VZDQCTLTYPE,
++ .ioctl = vzquota_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = compat_vzquota_ioctl,
++#endif
++ .owner = THIS_MODULE,
++};
++
++/**
++ * vzquota_dstat - get quota usage info for virtual superblock
++ */
++static int vzquota_dstat(struct super_block *super, struct dq_stat *qstat)
++{
++ struct vz_quota_master *qmblk;
++
++ qmblk = vzquota_find_qmblk(super);
++ if (qmblk == NULL)
++ return -ENOENT;
++ if (qmblk == VZ_QUOTA_BAD) {
++ memset(qstat, 0, sizeof(*qstat));
++ return 0;
++ }
++
++ qmblk_data_read_lock(qmblk);
++ memcpy(qstat, &qmblk->dq_stat, sizeof(*qstat));
++ qmblk_data_read_unlock(qmblk);
++ qmblk_put(qmblk);
++ return 0;
++}
++
++
++/* ----------------------------------------------------------------------
++ *
++ * Init/exit helpers
++ *
++ * ---------------------------------------------------------------------*/
++
++static int vzquota_cache_init(void)
++{
++ int i;
++
++ vzquota_cachep = kmem_cache_create("vz_quota_master",
++ sizeof(struct vz_quota_master),
++ 0, SLAB_HWCACHE_ALIGN, NULL);
++ if (vzquota_cachep == NULL) {
++ printk(KERN_ERR "Cannot create VZ_QUOTA SLAB cache\n");
++ goto nomem2;
++ }
++ for (i = 0; i < VZ_QUOTA_HASH_SIZE; i++)
++ INIT_LIST_HEAD(&vzquota_hash_table[i]);
++
++ return 0;
++
++nomem2:
++ return -ENOMEM;
++}
++
++static void vzquota_cache_release(void)
++{
++ int i;
++
++ /* sanity check */
++ for (i = 0; i < VZ_QUOTA_HASH_SIZE; i++)
++ if (!list_empty(&vzquota_hash_table[i]))
++ BUG();
++
++ /* release caches */
++ kmem_cache_destroy(vzquota_cachep);
++ vzquota_cachep = NULL;
++}
++
++static int quota_notifier_call(struct vnotifier_block *self,
++ unsigned long n, void *data, int err)
++{
++ struct virt_info_quota *viq;
++ struct super_block *sb;
++
++ viq = (struct virt_info_quota *)data;
++ switch (n) {
++ case VIRTINFO_QUOTA_ON:
++ err = NOTIFY_BAD;
++ if (!try_module_get(THIS_MODULE))
++ break;
++ sb = viq->super;
++ memset(&sb->s_dquot.info, 0, sizeof(sb->s_dquot.info));
++ INIT_LIST_HEAD(&sb->s_dquot.info[USRQUOTA].dqi_dirty_list);
++ INIT_LIST_HEAD(&sb->s_dquot.info[GRPQUOTA].dqi_dirty_list);
++ err = NOTIFY_OK;
++ break;
++ case VIRTINFO_QUOTA_OFF:
++ module_put(THIS_MODULE);
++ err = NOTIFY_OK;
++ break;
++ case VIRTINFO_QUOTA_GETSTAT:
++ err = NOTIFY_BAD;
++ if (vzquota_dstat(viq->super, viq->qstat))
++ break;
++ err = NOTIFY_OK;
++ break;
++ case VIRTINFO_QUOTA_DISABLE:
++ err = NOTIFY_OK;
++ vzquota_inode_off((struct inode *)data);
++ break;
++ }
++ return err;
++}
++
++struct vnotifier_block quota_notifier_block = {
++ .notifier_call = quota_notifier_call,
++ .priority = INT_MAX,
++};
++
++/* ----------------------------------------------------------------------
++ *
++ * Init/exit procedures
++ *
++ * ---------------------------------------------------------------------*/
++
++static int __init vzquota_init(void)
++{
++ int err;
++
++ if ((err = vzquota_cache_init()) != 0)
++ goto out_cache;
++
++ if ((err = vzquota_proc_init()) != 0)
++ goto out_proc;
++
++#ifdef CONFIG_VZ_QUOTA_UGID
++ if ((err = vzquota_ugid_init()) != 0)
++ goto out_ugid;
++#endif
++
++ init_MUTEX(&vz_quota_sem);
++ vzioctl_register(&vzdqcalls);
++ virtinfo_notifier_register(VITYPE_QUOTA, &quota_notifier_block);
++#if defined(CONFIG_VZ_QUOTA_UGID) && defined(CONFIG_PROC_FS)
++ vzaquota_init();
++#endif
++
++ return 0;
++
++#ifdef CONFIG_VZ_QUOTA_UGID
++out_ugid:
++ vzquota_proc_release();
++#endif
++out_proc:
++ vzquota_cache_release();
++out_cache:
++ return err;
++}
++
++#if defined(VZ_QUOTA_UNLOAD)
++static void __exit vzquota_release(void)
++{
++ virtinfo_notifier_unregister(VITYPE_QUOTA, &quota_notifier_block);
++ vzioctl_unregister(&vzdqcalls);
++#ifdef CONFIG_VZ_QUOTA_UGID
++#ifdef CONFIG_PROC_FS
++ vzaquota_fini();
++#endif
++ vzquota_ugid_release();
++#endif
++ vzquota_proc_release();
++ vzquota_cache_release();
++}
++#endif
++
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Virtuozzo Disk Quota");
++MODULE_LICENSE("GPL v2");
++
++module_init(vzquota_init)
++#if defined(VZ_QUOTA_UNLOAD)
++module_exit(vzquota_release)
++#endif
+diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h
+index 7be4733..a68dc33 100644
+--- a/include/asm-x86/elf.h
++++ b/include/asm-x86/elf.h
+@@ -279,7 +279,7 @@ struct task_struct;
+
+ #define ARCH_DLINFO_IA32(vdso_enabled) \
+ do { \
+- if (vdso_enabled) { \
++ if (vdso_enabled && sysctl_at_vsyscall) { \
+ NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
+ } \
+@@ -324,9 +324,11 @@ struct linux_binprm;
+
+ #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+ extern int arch_setup_additional_pages(struct linux_binprm *bprm,
+- int executable_stack);
++ int executable_stack,
++ unsigned long map_address);
+
+-extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
++extern int syscall32_setup_pages(struct linux_binprm *, int exstack,
++ unsigned long map_address);
+ #define compat_arch_setup_additional_pages syscall32_setup_pages
+
+ extern unsigned long arch_randomize_brk(struct mm_struct *mm);
+diff --git a/include/asm-x86/mman.h b/include/asm-x86/mman.h
+index 90bc410..e370cc3 100644
+--- a/include/asm-x86/mman.h
++++ b/include/asm-x86/mman.h
+@@ -13,6 +13,7 @@
+ #define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
+ #define MAP_NONBLOCK 0x10000 /* do not block on IO */
+ #define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
++#define MAP_EXECPRIO 0x40000 /* soft ubc charge */
+
+ #define MCL_CURRENT 1 /* lock all current mappings */
+ #define MCL_FUTURE 2 /* lock all future mappings */
+diff --git a/include/asm-x86/nmi.h b/include/asm-x86/nmi.h
+index 21f8d02..34c101d 100644
+--- a/include/asm-x86/nmi.h
++++ b/include/asm-x86/nmi.h
+@@ -29,6 +29,10 @@ extern void release_perfctr_nmi(unsigned int);
+ extern int reserve_evntsel_nmi(unsigned int);
+ extern void release_evntsel_nmi(unsigned int);
+
++typedef int (*nmi_callback_t)(struct pt_regs *regs, int cpu);
++void set_nmi_ipi_callback(nmi_callback_t callback);
++void unset_nmi_ipi_callback(void);
++
+ extern void setup_apic_nmi_watchdog(void *);
+ extern void stop_apic_nmi_watchdog(void *);
+ extern void disable_timer_nmi_watchdog(void);
+diff --git a/include/asm-x86/pgalloc.h b/include/asm-x86/pgalloc.h
+index d63ea43..e80d924 100644
+--- a/include/asm-x86/pgalloc.h
++++ b/include/asm-x86/pgalloc.h
+@@ -68,7 +68,7 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
+ #if PAGETABLE_LEVELS > 2
+ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+- return (pmd_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT);
++ return (pmd_t *)get_zeroed_page(GFP_KERNEL_UBC|__GFP_REPEAT);
+ }
+
+ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+@@ -98,7 +98,7 @@ static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)
+
+ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
+ {
+- return (pud_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT);
++ return (pud_t *)get_zeroed_page(GFP_KERNEL_UBC|__GFP_REPEAT);
+ }
+
+ static inline void pud_free(struct mm_struct *mm, pud_t *pud)
+diff --git a/include/asm-x86/processor.h b/include/asm-x86/processor.h
+index 4df3e2f..42aaa5b 100644
+--- a/include/asm-x86/processor.h
++++ b/include/asm-x86/processor.h
+@@ -896,8 +896,7 @@ extern unsigned long thread_saved_pc(struct task_struct *tsk);
+ /* This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+-#define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? \
+- 0xc0000000 : 0xFFFFe000)
++#define IA32_PAGE_OFFSET 0xc0000000
+
+ #define TASK_SIZE (test_thread_flag(TIF_IA32) ? \
+ IA32_PAGE_OFFSET : TASK_SIZE64)
+diff --git a/include/asm-x86/thread_info.h b/include/asm-x86/thread_info.h
+index da0a675..e9f7812 100644
+--- a/include/asm-x86/thread_info.h
++++ b/include/asm-x86/thread_info.h
+@@ -91,6 +91,7 @@ struct thread_info {
+ #define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
+ #define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
+ #define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */
++#define TIF_RESUME 29
+
+ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
+ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+@@ -112,6 +113,7 @@ struct thread_info {
+ #define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
+ #define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
+ #define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
++#define _TIF_RESUME (1<<TIF_RESUME)
+
+ /* work to do in syscall_trace_enter() */
+ #define _TIF_WORK_SYSCALL_ENTRY \
+@@ -155,7 +157,8 @@ struct thread_info {
+ #define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
+
+ #define alloc_thread_info(tsk) \
+- ((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))
++ ((struct thread_info *)__get_free_pages(THREAD_FLAGS | __GFP_UBC,\
++ THREAD_ORDER))
+
+ #ifdef CONFIG_X86_32
+
+diff --git a/include/asm-x86/tsc.h b/include/asm-x86/tsc.h
+index cb6f6ee..5fe18ef 100644
+--- a/include/asm-x86/tsc.h
++++ b/include/asm-x86/tsc.h
+@@ -24,7 +24,7 @@ static inline cycles_t get_cycles(void)
+ unsigned long long ret = 0;
+
+ #ifndef CONFIG_X86_TSC
+- if (!cpu_has_tsc)
++ if (WARN_ON_ONCE(!cpu_has_tsc))
+ return 0;
+ #endif
+ rdtscll(ret);
+diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h
+index d739467..a71689a 100644
+--- a/include/asm-x86/unistd_32.h
++++ b/include/asm-x86/unistd_32.h
+@@ -338,6 +338,16 @@
+ #define __NR_dup3 330
+ #define __NR_pipe2 331
+ #define __NR_inotify_init1 332
++#define __NR_fairsched_mknod 500 /* FairScheduler syscalls */
++#define __NR_fairsched_rmnod 501
++#define __NR_fairsched_chwt 502
++#define __NR_fairsched_mvpr 503
++#define __NR_fairsched_rate 504
++#define __NR_fairsched_vcpus 505
++#define __NR_getluid 510
++#define __NR_setluid 511
++#define __NR_setublimit 512
++#define __NR_ubstat 513
+
+ #ifdef __KERNEL__
+
+diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h
+index 3a341d7..aa5f1cc 100644
+--- a/include/asm-x86/unistd_64.h
++++ b/include/asm-x86/unistd_64.h
+@@ -653,6 +653,26 @@ __SYSCALL(__NR_dup3, sys_dup3)
+ __SYSCALL(__NR_pipe2, sys_pipe2)
+ #define __NR_inotify_init1 294
+ __SYSCALL(__NR_inotify_init1, sys_inotify_init1)
++#define __NR_fairsched_vcpus 499
++__SYSCALL(__NR_fairsched_vcpus, sys_fairsched_vcpus)
++#define __NR_getluid 500
++__SYSCALL(__NR_getluid, sys_getluid)
++#define __NR_setluid 501
++__SYSCALL(__NR_setluid, sys_setluid)
++#define __NR_setublimit 502
++__SYSCALL(__NR_setublimit, sys_setublimit)
++#define __NR_ubstat 503
++__SYSCALL(__NR_ubstat, sys_ubstat)
++#define __NR_fairsched_mknod 504 /* FairScheduler syscalls */
++__SYSCALL(__NR_fairsched_mknod, sys_fairsched_mknod)
++#define __NR_fairsched_rmnod 505
++__SYSCALL(__NR_fairsched_rmnod, sys_fairsched_rmnod)
++#define __NR_fairsched_chwt 506
++__SYSCALL(__NR_fairsched_chwt, sys_fairsched_chwt)
++#define __NR_fairsched_mvpr 507
++__SYSCALL(__NR_fairsched_mvpr, sys_fairsched_mvpr)
++#define __NR_fairsched_rate 508
++__SYSCALL(__NR_fairsched_rate, sys_fairsched_rate)
+
+
+ #ifndef __NO_STUBS
+@@ -678,6 +698,7 @@ __SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+ #define __ARCH_WANT_SYS_RT_SIGSUSPEND
+ #define __ARCH_WANT_SYS_TIME
+ #define __ARCH_WANT_COMPAT_SYS_TIME
++#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+ #endif /* __NO_STUBS */
+
+ #ifdef __KERNEL__
+diff --git a/include/asm-x86/vdso.h b/include/asm-x86/vdso.h
+index 8e18fb8..79b9cbf 100644
+--- a/include/asm-x86/vdso.h
++++ b/include/asm-x86/vdso.h
+@@ -18,6 +18,7 @@ extern const char VDSO64_PRELINK[];
+ #if defined CONFIG_X86_32 || defined CONFIG_COMPAT
+ extern const char VDSO32_PRELINK[];
+
++extern const char VDSO32_SYSENTER_RETURN[];
+ /*
+ * Given a pointer to the vDSO image, find the pointer to VDSO32_name
+ * as that symbol is defined in the vDSO sources or linker script.
+diff --git a/include/bc/beancounter.h b/include/bc/beancounter.h
+new file mode 100644
+index 0000000..7327bcb
+--- /dev/null
++++ b/include/bc/beancounter.h
+@@ -0,0 +1,451 @@
++/*
++ * include/bc/beancounter.h
++ *
++ * Copyright (C) 1999-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * Andrey Savochkin saw@sw-soft.com
++ *
++ */
++
++#ifndef _LINUX_BEANCOUNTER_H
++#define _LINUX_BEANCOUNTER_H
++
++/*
++ * Generic ratelimiting stuff.
++ */
++
++struct ub_rate_info {
++ int burst;
++ int interval; /* jiffy_t per event */
++ int bucket; /* kind of leaky bucket */
++ unsigned long last; /* last event */
++};
++
++/* Return true if rate limit permits. */
++int ub_ratelimit(struct ub_rate_info *);
++
++
++/*
++ * This magic is used to distinuish user beancounter and pages beancounter
++ * in struct page. page_ub and page_bc are placed in union and MAGIC
++ * ensures us that we don't use pbc as ubc in ub_page_uncharge().
++ */
++#define UB_MAGIC 0x62756275
++
++/*
++ * Resource list.
++ */
++
++#define UB_KMEMSIZE 0 /* Unswappable kernel memory size including
++ * struct task, page directories, etc.
++ */
++#define UB_LOCKEDPAGES 1 /* Mlock()ed pages. */
++#define UB_PRIVVMPAGES 2 /* Total number of pages, counting potentially
++ * private pages as private and used.
++ */
++#define UB_SHMPAGES 3 /* IPC SHM segment size. */
++#define UB_DUMMY 4 /* Dummy resource (compatibility) */
++#define UB_NUMPROC 5 /* Number of processes. */
++#define UB_PHYSPAGES 6 /* All resident pages, for swapout guarantee. */
++#define UB_VMGUARPAGES 7 /* Guarantee for memory allocation,
++ * checked against PRIVVMPAGES.
++ */
++#define UB_OOMGUARPAGES 8 /* Guarantees against OOM kill.
++ * Only limit is used, no accounting.
++ */
++#define UB_NUMTCPSOCK 9 /* Number of TCP sockets. */
++#define UB_NUMFLOCK 10 /* Number of file locks. */
++#define UB_NUMPTY 11 /* Number of PTYs. */
++#define UB_NUMSIGINFO 12 /* Number of siginfos. */
++#define UB_TCPSNDBUF 13 /* Total size of tcp send buffers. */
++#define UB_TCPRCVBUF 14 /* Total size of tcp receive buffers. */
++#define UB_OTHERSOCKBUF 15 /* Total size of other socket
++ * send buffers (all buffers for PF_UNIX).
++ */
++#define UB_DGRAMRCVBUF 16 /* Total size of other socket
++ * receive buffers.
++ */
++#define UB_NUMOTHERSOCK 17 /* Number of other sockets. */
++#define UB_DCACHESIZE 18 /* Size of busy dentry/inode cache. */
++#define UB_NUMFILE 19 /* Number of open files. */
++
++#define UB_RESOURCES_COMPAT 24
++
++/* Add new resources here */
++
++#define UB_NUMXTENT 23
++#define UB_RESOURCES 24
++
++#define UB_UNUSEDPRIVVM (UB_RESOURCES + 0)
++#define UB_TMPFSPAGES (UB_RESOURCES + 1)
++#define UB_SWAPPAGES (UB_RESOURCES + 2)
++#define UB_HELDPAGES (UB_RESOURCES + 3)
++
++struct ubparm {
++ /*
++ * A barrier over which resource allocations are failed gracefully.
++ * If the amount of consumed memory is over the barrier further sbrk()
++ * or mmap() calls fail, the existing processes are not killed.
++ */
++ unsigned long barrier;
++ /* hard resource limit */
++ unsigned long limit;
++ /* consumed resources */
++ unsigned long held;
++ /* maximum amount of consumed resources through the last period */
++ unsigned long maxheld;
++ /* minimum amount of consumed resources through the last period */
++ unsigned long minheld;
++ /* count of failed charges */
++ unsigned long failcnt;
++};
++
++/*
++ * Kernel internal part.
++ */
++
++#ifdef __KERNEL__
++
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/cache.h>
++#include <linux/threads.h>
++#include <linux/percpu.h>
++#include <bc/debug.h>
++#include <bc/decl.h>
++#include <asm/atomic.h>
++#include <bc/io_prio.h>
++
++/*
++ * UB_MAXVALUE is essentially LONG_MAX declared in a cross-compiling safe form.
++ */
++#define UB_MAXVALUE ( (1UL << (sizeof(unsigned long)*8-1)) - 1)
++
++
++/*
++ * Resource management structures
++ * Serialization issues:
++ * beancounter list management is protected via ub_hash_lock
++ * task pointers are set only for current task and only once
++ * refcount is managed atomically
++ * value and limit comparison and change are protected by per-ub spinlock
++ */
++
++struct page_beancounter;
++struct task_beancounter;
++struct sock_beancounter;
++
++struct page_private {
++ unsigned long ubp_unused_privvmpages;
++ unsigned long ubp_tmpfs_respages;
++ unsigned long ubp_swap_pages;
++ unsigned long long ubp_held_pages;
++};
++
++struct sock_private {
++ unsigned long ubp_rmem_thres;
++ unsigned long ubp_wmem_pressure;
++ unsigned long ubp_maxadvmss;
++ unsigned long ubp_rmem_pressure;
++ int ubp_tw_count;
++#define UB_RMEM_EXPAND 0
++#define UB_RMEM_KEEP 1
++#define UB_RMEM_SHRINK 2
++ struct list_head ubp_other_socks;
++ struct list_head ubp_tcp_socks;
++ atomic_t ubp_orphan_count;
++};
++
++struct ub_percpu_struct {
++ unsigned long unmap;
++ unsigned long swapin;
++#ifdef CONFIG_BC_IO_ACCOUNTING
++ unsigned long long bytes_wrote;
++ unsigned long long bytes_read;
++ unsigned long long bytes_cancelled;
++#endif
++#ifdef CONFIG_BC_DEBUG_KMEM
++ long pages_charged;
++ long vmalloc_charged;
++ long pbcs;
++#endif
++ unsigned long sync;
++ unsigned long sync_done;
++
++ unsigned long fsync;
++ unsigned long fsync_done;
++
++ unsigned long fdsync;
++ unsigned long fdsync_done;
++
++ unsigned long frsync;
++ unsigned long frsync_done;
++
++ unsigned long write;
++ unsigned long read;
++ unsigned long long wchar;
++ unsigned long long rchar;
++};
++
++struct user_beancounter
++{
++ unsigned long ub_magic;
++ atomic_t ub_refcount;
++ struct list_head ub_list;
++ struct hlist_node ub_hash;
++
++ union {
++ struct rcu_head rcu;
++ struct execute_work cleanup;
++ };
++
++ spinlock_t ub_lock;
++ uid_t ub_uid;
++
++ struct ub_rate_info ub_limit_rl;
++ int ub_oom_noproc;
++
++ struct page_private ppriv;
++#define ub_unused_privvmpages ppriv.ubp_unused_privvmpages
++#define ub_tmpfs_respages ppriv.ubp_tmpfs_respages
++#define ub_swap_pages ppriv.ubp_swap_pages
++#define ub_held_pages ppriv.ubp_held_pages
++ struct sock_private spriv;
++#define ub_rmem_thres spriv.ubp_rmem_thres
++#define ub_maxadvmss spriv.ubp_maxadvmss
++#define ub_rmem_pressure spriv.ubp_rmem_pressure
++#define ub_wmem_pressure spriv.ubp_wmem_pressure
++#define ub_tcp_sk_list spriv.ubp_tcp_socks
++#define ub_other_sk_list spriv.ubp_other_socks
++#define ub_orphan_count spriv.ubp_orphan_count
++#define ub_tw_count spriv.ubp_tw_count
++ struct ub_iopriv iopriv;
++
++ struct user_beancounter *parent;
++ void *private_data;
++ unsigned long ub_aflags;
++
++#ifdef CONFIG_PROC_FS
++ struct proc_dir_entry *proc;
++#endif
++
++ /* resources statistic and settings */
++ struct ubparm ub_parms[UB_RESOURCES];
++ /* resources statistic for last interval */
++ struct ubparm ub_store[UB_RESOURCES];
++
++ struct ub_percpu_struct *ub_percpu;
++#ifdef CONFIG_BC_IO_ACCOUNTING
++ /* these are protected with pb_lock */
++ unsigned long long bytes_wrote;
++ unsigned long long bytes_dirtied;
++ unsigned long long bytes_dirty_missed;
++ unsigned long io_pb_held;
++#endif
++#ifdef CONFIG_BC_DEBUG_KMEM
++ struct list_head ub_cclist;
++#endif
++};
++
++enum ub_severity { UB_HARD, UB_SOFT, UB_FORCE };
++
++#define UB_AFLAG_NOTIF_PAGEIN 0
++
++static inline
++struct user_beancounter *top_beancounter(struct user_beancounter *ub)
++{
++ while (ub->parent != NULL)
++ ub = ub->parent;
++ return ub;
++}
++
++static inline int ub_barrier_hit(struct user_beancounter *ub, int resource)
++{
++ return ub->ub_parms[resource].held > ub->ub_parms[resource].barrier;
++}
++
++static inline int ub_hfbarrier_hit(struct user_beancounter *ub, int resource)
++{
++ return (ub->ub_parms[resource].held >
++ ((ub->ub_parms[resource].barrier) >> 1));
++}
++
++static inline int ub_barrier_farnr(struct user_beancounter *ub, int resource)
++{
++ struct ubparm *p;
++ p = ub->ub_parms + resource;
++ return p->held <= (p->barrier >> 3);
++}
++
++static inline int ub_barrier_farsz(struct user_beancounter *ub, int resource)
++{
++ struct ubparm *p;
++ p = ub->ub_parms + resource;
++ return p->held <= (p->barrier >> 3) && p->barrier >= 1024 * 1024;
++}
++
++#ifndef CONFIG_BEANCOUNTERS
++
++#define ub_percpu_add(ub, f, v) do { } while (0)
++#define ub_percpu_sub(ub, f, v) do { } while (0)
++#define ub_percpu_inc(ub, f) do { } while (0)
++#define ub_percpu_dec(ub, f) do { } while (0)
++
++#define mm_ub(mm) (NULL)
++
++extern inline struct user_beancounter *get_beancounter_byuid
++ (uid_t uid, int create) { return NULL; }
++extern inline struct user_beancounter *get_beancounter
++ (struct user_beancounter *ub) { return NULL; }
++extern inline void put_beancounter(struct user_beancounter *ub) { }
++
++static inline void ub_init_late(void) { };
++static inline void ub_init_early(void) { };
++
++static inline int charge_beancounter(struct user_beancounter *ub,
++ int resource, unsigned long val,
++ enum ub_severity strict) { return 0; }
++static inline void uncharge_beancounter(struct user_beancounter *ub,
++ int resource, unsigned long val) { }
++
++#else /* CONFIG_BEANCOUNTERS */
++
++#define ub_percpu_add(ub, field, v) do { \
++ per_cpu_ptr(ub->ub_percpu, get_cpu())->field += (v); \
++ put_cpu(); \
++ } while (0)
++#define ub_percpu_inc(ub, field) ub_percpu_add(ub, field, 1)
++
++#define ub_percpu_sub(ub, field, v) do { \
++ per_cpu_ptr(ub->ub_percpu, get_cpu())->field -= (v); \
++ put_cpu(); \
++ } while (0)
++#define ub_percpu_dec(ub, field) ub_percpu_sub(ub, field, 1)
++
++#define mm_ub(mm) ((mm)->mm_ub)
++/*
++ * Charge/uncharge operations
++ */
++
++extern int __charge_beancounter_locked(struct user_beancounter *ub,
++ int resource, unsigned long val, enum ub_severity strict);
++
++extern void __uncharge_beancounter_locked(struct user_beancounter *ub,
++ int resource, unsigned long val);
++
++extern void put_beancounter_safe(struct user_beancounter *ub);
++extern void __put_beancounter(struct user_beancounter *ub);
++
++extern void uncharge_warn(struct user_beancounter *ub, int resource,
++ unsigned long val, unsigned long held);
++
++extern const char *ub_rnames[];
++/*
++ * Put a beancounter reference
++ */
++
++static inline void put_beancounter(struct user_beancounter *ub)
++{
++ if (unlikely(ub == NULL))
++ return;
++
++ /* FIXME - optimize not to disable interrupts and make call */
++ __put_beancounter(ub);
++}
++
++/* fast put, refcount can't reach zero */
++static inline void __put_beancounter_batch(struct user_beancounter *ub, int n)
++{
++ atomic_sub(n, &ub->ub_refcount);
++}
++
++static inline void put_beancounter_batch(struct user_beancounter *ub, int n)
++{
++ if (n > 1)
++ __put_beancounter_batch(ub, n - 1);
++ __put_beancounter(ub);
++}
++
++/*
++ * Create a new beancounter reference
++ */
++extern struct user_beancounter *get_beancounter_byuid(uid_t uid, int create);
++
++static inline
++struct user_beancounter *get_beancounter(struct user_beancounter *ub)
++{
++ if (unlikely(ub == NULL))
++ return NULL;
++
++ atomic_inc(&ub->ub_refcount);
++ return ub;
++}
++
++static inline
++struct user_beancounter *get_beancounter_rcu(struct user_beancounter *ub)
++{
++ return atomic_inc_not_zero(&ub->ub_refcount) ? ub : NULL;
++}
++
++static inline void get_beancounter_batch(struct user_beancounter *ub, int n)
++{
++ atomic_add(n, &ub->ub_refcount);
++}
++
++extern struct user_beancounter *get_subbeancounter_byid(
++ struct user_beancounter *,
++ int id, int create);
++
++extern void ub_init_late(void);
++extern void ub_init_early(void);
++
++extern int print_ub_uid(struct user_beancounter *ub, char *buf, int size);
++
++/*
++ * Resource charging
++ * Change user's account and compare against limits
++ */
++
++static inline void ub_adjust_maxheld(struct user_beancounter *ub, int resource)
++{
++ if (ub->ub_parms[resource].maxheld < ub->ub_parms[resource].held)
++ ub->ub_parms[resource].maxheld = ub->ub_parms[resource].held;
++ if (ub->ub_parms[resource].minheld > ub->ub_parms[resource].held)
++ ub->ub_parms[resource].minheld = ub->ub_parms[resource].held;
++}
++
++int charge_beancounter(struct user_beancounter *ub, int resource,
++ unsigned long val, enum ub_severity strict);
++void uncharge_beancounter(struct user_beancounter *ub, int resource,
++ unsigned long val);
++void __charge_beancounter_notop(struct user_beancounter *ub, int resource,
++ unsigned long val);
++void __uncharge_beancounter_notop(struct user_beancounter *ub, int resource,
++ unsigned long val);
++
++static inline void charge_beancounter_notop(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ if (ub->parent != NULL)
++ __charge_beancounter_notop(ub, resource, val);
++}
++
++static inline void uncharge_beancounter_notop(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ if (ub->parent != NULL)
++ __uncharge_beancounter_notop(ub, resource, val);
++}
++
++#endif /* CONFIG_BEANCOUNTERS */
++
++#ifndef CONFIG_BC_RSS_ACCOUNTING
++static inline void ub_ini_pbc(void) { }
++#else
++extern void ub_init_pbc(void);
++#endif
++#endif /* __KERNEL__ */
++#endif /* _LINUX_BEANCOUNTER_H */
+diff --git a/include/bc/dcache.h b/include/bc/dcache.h
+new file mode 100644
+index 0000000..5ebefff
+--- /dev/null
++++ b/include/bc/dcache.h
+@@ -0,0 +1,47 @@
++/*
++ * include/bc/dcache.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_DCACHE_H_
++#define __BC_DCACHE_H_
++
++#include <bc/decl.h>
++
++/*
++ * UB_DCACHESIZE accounting
++ */
++
++struct dentry_beancounter
++{
++ /*
++ * d_inuse =
++ * <number of external refs> +
++ * <number of 'used' childs>
++ *
++ * d_inuse == -1 means that dentry is unused
++ * state change -1 => 0 causes charge
++ * state change 0 => -1 causes uncharge
++ */
++ atomic_t d_inuse;
++ /* charged size, including name length if name is not inline */
++ unsigned long d_ubsize;
++ struct user_beancounter *d_ub;
++};
++
++#ifdef CONFIG_BEANCOUNTERS
++#define ub_dget_testone(d) (atomic_inc_and_test(&(d)->dentry_bc.d_inuse))
++#define ub_dput_testzero(d) (atomic_add_negative(-1, &(d)->dentry_bc.d_inuse))
++#define INUSE_INIT 0
++
++extern int ub_dentry_on;
++#else
++#define ub_dget_testone(d) (0)
++#define ub_dput_testzero(d) (0)
++#endif
++#endif
+diff --git a/include/bc/dcache_op.h b/include/bc/dcache_op.h
+new file mode 100644
+index 0000000..23306e9
+--- /dev/null
++++ b/include/bc/dcache_op.h
+@@ -0,0 +1,102 @@
++/*
++ * include/bc/dcache_op.h
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_DCACHE_OP_H_
++#define __BC_DCACHE_OP_H_
++
++struct dentry;
++
++#ifdef CONFIG_BEANCOUNTERS
++
++#include <linux/spinlock.h>
++#include <bc/dcache.h>
++#include <bc/task.h>
++
++extern int ub_dentry_alloc_barrier;
++extern spinlock_t dcache_lock;
++
++static inline int ub_dentry_alloc(struct dentry *d)
++{
++ extern int __ub_dentry_alloc(struct dentry *);
++
++ if (!ub_dentry_on)
++ return 0;
++ return __ub_dentry_alloc(d);
++}
++
++static inline void ub_dentry_alloc_start(void)
++{
++ extern void __ub_dentry_alloc_start(void);
++
++ if (ub_dentry_alloc_barrier)
++ __ub_dentry_alloc_start();
++}
++
++static inline void ub_dentry_alloc_end(void)
++{
++ extern void __ub_dentry_alloc_end(void);
++
++ if (current->task_bc.dentry_alloc)
++ __ub_dentry_alloc_end();
++}
++
++static inline int ub_dentry_charge(struct dentry *d)
++{
++ extern int __ub_dentry_charge(struct dentry *);
++
++ if (!ub_dentry_on)
++ return 0;
++ return __ub_dentry_charge(d);
++}
++
++static inline void ub_dentry_charge_nofail(struct dentry *d)
++{
++ extern void __ub_dentry_charge_nofail(struct dentry *);
++
++ if (!ub_dentry_on)
++ return;
++ __ub_dentry_charge_nofail(d);
++}
++
++static inline void ub_dentry_uncharge_locked(struct dentry *d)
++{
++ extern void __ub_dentry_uncharge(struct dentry *);
++
++ if (!ub_dentry_on)
++ return;
++ __ub_dentry_uncharge(d);
++}
++
++static inline void ub_dentry_uncharge(struct dentry *d)
++{
++ extern void __ub_dentry_uncharge(struct dentry *);
++
++ if (!ub_dentry_on)
++ return;
++ spin_lock(&dcache_lock);
++ __ub_dentry_uncharge(d);
++ spin_unlock(&dcache_lock);
++}
++
++void uncharge_dcache(struct user_beancounter *ub, unsigned long size);
++#else /* CONFIG_BEANCOUNTERS */
++
++static inline int ub_dentry_alloc(struct dentry *d) { return 0; }
++static inline void ub_dentry_alloc_start(void) { }
++static inline void ub_dentry_alloc_end(void) { }
++static inline int ub_dentry_charge(struct dentry *d) { return 0; }
++static inline void ub_dentry_charge_nofail(struct dentry *d) { }
++static inline void ub_dentry_uncharge_locked(struct dentry *d) { }
++static inline void ub_dentry_uncharge(struct dentry *d) { }
++static inline void uncharge_dcache(struct user_beancounter *ub, unsigned long size) { }
++
++#endif /* CONFIG_BEANCOUNTERS */
++
++#endif /* __dcache_op.h_ */
+diff --git a/include/bc/debug.h b/include/bc/debug.h
+new file mode 100644
+index 0000000..7b1feb6
+--- /dev/null
++++ b/include/bc/debug.h
+@@ -0,0 +1,109 @@
++/*
++ * include/bc/debug.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_DEBUG_H_
++#define __BC_DEBUG_H_
++
++/*
++ * general debugging
++ */
++
++#define UBD_ALLOC 0x1
++#define UBD_CHARGE 0x2
++#define UBD_LIMIT 0x4
++#define UBD_TRACE 0x8
++
++/*
++ * ub_net debugging
++ */
++
++#define UBD_NET_SOCKET 0x10
++#define UBD_NET_SLEEP 0x20
++#define UBD_NET_SEND 0x40
++#define UBD_NET_RECV 0x80
++
++/*
++ * Main routines
++ */
++
++#define UB_DEBUG (0)
++#define DEBUG_RESOURCE (0ULL)
++
++#define ub_dbg_cond(__cond, __str, args...) \
++ do { \
++ if ((__cond) != 0) \
++ printk(__str, ##args); \
++ } while(0)
++
++#define ub_debug(__section, __str, args...) \
++ ub_dbg_cond(UB_DEBUG & (__section), __str, ##args)
++
++#define ub_debug_resource(__resource, __str, args...) \
++ ub_dbg_cond((UB_DEBUG & UBD_CHARGE) && \
++ (DEBUG_RESOURCE & (1 << (__resource))), \
++ __str, ##args)
++
++#if UB_DEBUG & UBD_TRACE
++#define ub_debug_trace(__cond, __b, __r) \
++ do { \
++ static struct ub_rate_info ri = { __b, __r }; \
++ if ((__cond) != 0 && ub_ratelimit(&ri)) \
++ dump_stack(); \
++ } while(0)
++#else
++#define ub_debug_trace(__cond, __burst, __rate)
++#endif
++
++#ifdef CONFIG_BC_DEBUG_KMEM
++#include <linux/list.h>
++
++struct user_beancounter;
++struct ub_cache_counter {
++ struct list_head ulist;
++ struct ub_cache_counter *next;
++ struct user_beancounter *ub;
++ struct kmem_cache *cachep;
++ unsigned long counter;
++};
++
++extern spinlock_t cc_lock;
++extern void init_cache_counters(void);
++extern void ub_free_counters(struct user_beancounter *);
++extern void ub_kmemcache_free(struct kmem_cache *cachep);
++
++struct vm_struct;
++#define inc_vmalloc_charged(vm, flags) do { \
++ if (flags & __GFP_UBC) \
++ ub_percpu_add(get_exec_ub(), vmalloc_charged, \
++ vm->nr_pages); \
++ } while (0)
++#define dec_vmalloc_charged(vm) do { \
++ struct user_beancounter *ub; \
++ ub = page_ub(vm->pages[0]); \
++ if (ub != NULL) \
++ ub_percpu_sub(ub, vmalloc_charged, \
++ vm->nr_pages); \
++ } while (0)
++
++#define inc_pbc_count(ub) ub_percpu_inc(ub, pbcs)
++#define dec_pbc_count(ub) ub_percpu_dec(ub, pbcs)
++#else
++#define init_cache_counters() do { } while (0)
++#define inc_vmalloc_charged(vm, f) do { } while (0)
++#define dec_vmalloc_charged(vm) do { } while (0)
++
++#define inc_pbc_count(ub) do { } while (0)
++#define dec_pbc_count(ub) do { } while (0)
++
++#define ub_free_counters(ub) do { } while (0)
++#define ub_kmemcache_free(cachep) do { } while (0)
++#endif
++
++#endif
+diff --git a/include/bc/decl.h b/include/bc/decl.h
+new file mode 100644
+index 0000000..6dd4cb9
+--- /dev/null
++++ b/include/bc/decl.h
+@@ -0,0 +1,41 @@
++/*
++ * include/bc/decl.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_DECL_H_
++#define __BC_DECL_H_
++
++#ifdef __KERNEL__
++
++/*
++ * Naming convension:
++ * ub_<section|object>_<operation>
++ */
++
++#ifdef CONFIG_BEANCOUNTERS
++
++#define UB_DECLARE_FUNC(ret_type, decl) extern ret_type decl;
++#define UB_DECLARE_VOID_FUNC(decl) extern void decl;
++
++#else /* CONFIG_BEANCOUNTERS */
++
++#define UB_DECLARE_FUNC(ret_type, decl) \
++ static inline ret_type decl \
++ { \
++ return (ret_type)0; \
++ }
++#define UB_DECLARE_VOID_FUNC(decl) \
++ static inline void decl \
++ { \
++ }
++
++#endif /* CONFIG_BEANCOUNTERS */
++#endif
++
++#endif
+diff --git a/include/bc/hash.h b/include/bc/hash.h
+new file mode 100644
+index 0000000..b2afb69
+--- /dev/null
++++ b/include/bc/hash.h
+@@ -0,0 +1,36 @@
++/*
++ * include/bc/hash.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _LINUX_UBHASH_H
++#define _LINUX_UBHASH_H
++
++#ifdef __KERNEL__
++
++#define UB_HASH_SIZE 256
++
++extern struct hlist_head ub_hash[];
++extern spinlock_t ub_hash_lock;
++extern struct list_head ub_list_head;
++
++#ifdef CONFIG_BEANCOUNTERS
++
++/*
++ * Iterate over beancounters
++ * @__ubp - beancounter ptr
++ * Can use break :)
++ */
++#define for_each_beancounter(__ubp) \
++ list_for_each_entry_rcu(__ubp, &ub_list_head, ub_list) \
++
++#define bc_hash_entry(ptr) hlist_entry(ptr, struct user_beancounter, ub_hash)
++
++#endif /* CONFIG_BEANCOUNTERS */
++#endif /* __KERNEL__ */
++#endif /* _LINUX_UBHASH_H */
+diff --git a/include/bc/io_acct.h b/include/bc/io_acct.h
+new file mode 100644
+index 0000000..d84bf5a
+--- /dev/null
++++ b/include/bc/io_acct.h
+@@ -0,0 +1,113 @@
++/*
++ * include/bc/io_acct.h
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * Pavel Emelianov <xemul@openvz.org>
++ *
++ */
++
++#ifndef __UB_IO_ACCT_H_
++#define __UB_IO_ACCT_H_
++
++#ifdef CONFIG_BC_IO_ACCOUNTING
++#include <bc/beancounter.h>
++#include <bc/rss_pages.h>
++
++#define page_iopb(page) ({ \
++ struct page_beancounter *pb; \
++ pb = page_pbc(page); \
++ rmb(); \
++ pb; \
++ })
++
++/*
++ * IO ub is required in task context only, so if exec_ub is set
++ * to NULL this means that uses doesn't need to charge some
++ * resources. nevertheless IO activity must be accounted, so we
++ * account it to current's task beancounter.
++ */
++
++static inline struct user_beancounter *get_io_ub(void)
++{
++ struct user_beancounter *ub;
++
++ ub = get_exec_ub();
++ if (unlikely(ub == NULL))
++ ub = get_task_ub(current);
++
++ return top_beancounter(ub);
++}
++
++extern struct page_beancounter **page_pblist(struct page *);
++
++extern void ub_io_save_context(struct page *, size_t);
++extern void ub_io_release_context(struct page *pg, size_t size);
++
++#define PAGE_IO_MARK (0x1UL)
++
++static inline struct page_beancounter *iopb_to_pb(struct page_beancounter *pb)
++{
++ if (!((unsigned long)pb & PAGE_IO_MARK))
++ return NULL;
++
++ return (struct page_beancounter *)((unsigned long)pb & ~PAGE_IO_MARK);
++}
++
++static inline void ub_io_account_read(size_t bytes)
++{
++ ub_percpu_add(get_io_ub(), bytes_read, bytes);
++}
++
++static inline void ub_io_account_write(size_t bytes)
++{
++ ub_percpu_add(get_io_ub(), bytes_wrote, bytes);
++}
++
++static inline void ub_io_account_dirty(struct page *page, size_t bytes)
++{
++ ub_io_save_context(page, bytes);
++}
++
++static inline void ub_io_account_write_cancelled(size_t bytes)
++{
++ ub_percpu_add(get_io_ub(), bytes_cancelled, bytes);
++}
++
++void ub_init_io(struct kmem_cache *);
++#else /* BC_IO_ACCOUNTING */
++#define page_iopb(page) (NULL)
++#define page_pblist(page) (&page_pbc(page))
++
++static inline void ub_io_release_context(struct page *pg, size_t bytes)
++{
++}
++
++static inline void ub_io_account_dirty(struct page *p, size_t bytes)
++{
++}
++
++static inline void ub_io_account_read(size_t bytes)
++{
++}
++
++static inline void ub_io_account_write(size_t bytes)
++{
++}
++
++static inline void ub_io_account_write_cancelled(size_t bytes)
++{
++}
++
++static inline void ub_init_io(struct kmem_cache *pb_cachep) { };
++#endif
++
++#ifdef CONFIG_BC_DEBUG_IO
++extern void ub_io_release_debug(struct page *pg);
++#else
++#define ub_io_release_debug(pg) do { } while (0)
++#endif
++#endif
+diff --git a/include/bc/io_prio.h b/include/bc/io_prio.h
+new file mode 100644
+index 0000000..8c1d1e3
+--- /dev/null
++++ b/include/bc/io_prio.h
+@@ -0,0 +1,82 @@
++/*
++ * include/bc/io_prio.h
++ *
++ * Copyright (C) 2007 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * Vasily Tarasov <vtaras@openvz.org>
++ *
++ */
++
++#ifndef _UB_IO_PRIO_H
++#define _UB_IO_PRIO_H
++
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/cfq-iosched.h>
++
++#define UB_IOPRIO_MIN 0
++#define UB_IOPRIO_MAX IOPRIO_BE_NR
++#define UB_IOPRIO_BASE 4
++
++struct ub_iopriv {
++ struct list_head cfq_bc_head;
++ rwlock_t cfq_bc_list_lock;
++
++ unsigned int ioprio;
++};
++
++struct cfq_data;
++struct cfq_queue;
++
++#ifdef CONFIG_BC_IO_SCHED
++extern void bc_init_ioprio(struct ub_iopriv *);
++extern void bc_fini_ioprio(struct ub_iopriv *);
++extern struct cfq_bc_data * bc_find_cfq_bc(struct ub_iopriv *,
++ struct cfq_data *);
++extern struct cfq_bc_data * bc_findcreate_cfq_bc(struct ub_iopriv *,
++ struct cfq_data *, gfp_t gfp_mask);
++extern void bc_cfq_exit_queue(struct cfq_data *);
++extern int bc_expired(struct cfq_data *);
++extern void bc_schedule_active(struct cfq_data *);
++extern void bc_inc_rqnum(struct cfq_queue *);
++extern void bc_dec_rqnum(struct cfq_queue *);
++extern unsigned long bc_set_ioprio(int, int);
++extern struct cfq_bc_data *
++__find_cfq_bc(struct ub_iopriv *iopriv, struct cfq_data *cfqd);
++extern struct user_beancounter *bc_io_switch_context(struct page *);
++extern void bc_io_restore_context(struct user_beancounter *);
++#else
++#include <linux/cfq-iosched.h>
++static inline void bc_init_ioprio(struct ub_iopriv *iopriv) { ; }
++static inline void bc_fini_ioprio(struct ub_iopriv *iopriv) { ; }
++static inline struct cfq_bc_data *
++bc_findcreate_cfq_bc(struct ub_iopriv *iopriv,
++ struct cfq_data *cfqd, gfp_t mask)
++{
++ return &cfqd->cfq_bc;
++}
++static inline void bc_cfq_exit_queue(struct cfq_data *cfqd) { ; }
++static inline int bc_expired(struct cfq_data *cfqd) { return 0; }
++static inline void bc_schedule_active(struct cfq_data *cfqd)
++{
++ cfqd->active_cfq_bc = &cfqd->cfq_bc;
++}
++static inline void bc_inc_rqnum(struct cfq_queue *cfqq) { ; }
++static inline void bc_dec_rqnum(struct cfq_queue *cfqq) { ; }
++static inline unsigned long bc_set_ioprio(int ubid, int ioprio)
++{
++ return -EINVAL;
++}
++static inline struct cfq_bc_data *
++__find_cfq_bc(struct ub_iopriv *iopriv, struct cfq_data *cfqd)
++{
++ return &cfqd->cfq_bc;
++}
++static inline struct user_beancounter *
++bc_io_switch_context(struct page *page) { return NULL; }
++static inline void bc_io_restore_context(struct user_beancounter *ub) { ; }
++#endif /* CONFIG_BC_IO_SCHED */
++#endif /* _UB_IO_PRIO_H */
+diff --git a/include/bc/kmem.h b/include/bc/kmem.h
+new file mode 100644
+index 0000000..c0ea26a
+--- /dev/null
++++ b/include/bc/kmem.h
+@@ -0,0 +1,69 @@
++/*
++ * include/bc/kmem.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __UB_SLAB_H_
++#define __UB_SLAB_H_
++
++#include <bc/beancounter.h>
++#include <bc/decl.h>
++
++/*
++ * UB_KMEMSIZE accounting
++ */
++
++#ifdef CONFIG_BC_DEBUG_ITEMS
++#define CHARGE_ORDER(__o) (1 << (__o))
++#define CHARGE_SIZE(__s) 1
++#else
++#define CHARGE_ORDER(__o) (PAGE_SIZE << (__o))
++#define CHARGE_SIZE(__s) (__s)
++#endif
++
++#ifdef CONFIG_BEANCOUNTERS
++#define page_ub(__page) ((__page)->bc.page_ub)
++#else
++#define page_ub(__page) NULL
++#endif
++
++struct mm_struct;
++struct page;
++struct kmem_cache;
++
++UB_DECLARE_FUNC(struct user_beancounter *, vmalloc_ub(void *obj))
++UB_DECLARE_FUNC(struct user_beancounter *, mem_ub(void *obj))
++
++UB_DECLARE_FUNC(int, ub_kmemsize_charge(struct user_beancounter *ub,
++ unsigned long size, enum ub_severity strict))
++UB_DECLARE_VOID_FUNC(ub_kmemsize_uncharge(struct user_beancounter *ub,
++ unsigned long size))
++
++UB_DECLARE_FUNC(int, ub_page_charge(struct page *page, int order, gfp_t mask))
++UB_DECLARE_VOID_FUNC(ub_page_uncharge(struct page *page, int order))
++UB_DECLARE_FUNC(int, ub_slab_charge(struct kmem_cache *cachep,
++ void *objp, gfp_t flags))
++UB_DECLARE_VOID_FUNC(ub_slab_uncharge(struct kmem_cache *cachep, void *obj))
++
++#ifdef CONFIG_BEANCOUNTERS
++static inline int should_charge(struct kmem_cache *cachep, gfp_t flags)
++{
++ if (!(cachep->flags & SLAB_UBC))
++ return 0;
++ if ((cachep->flags & SLAB_NO_CHARGE) && !(flags & __GFP_UBC))
++ return 0;
++ return 1;
++}
++
++#define should_uncharge(cachep) should_charge(cachep, __GFP_UBC)
++#else
++#define should_charge(cache, f) 0
++#define should_uncharge(cache) 0
++#endif
++
++#endif /* __UB_SLAB_H_ */
+diff --git a/include/bc/misc.h b/include/bc/misc.h
+new file mode 100644
+index 0000000..84082b2
+--- /dev/null
++++ b/include/bc/misc.h
+@@ -0,0 +1,55 @@
++/*
++ * include/bc/misc.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_MISC_H_
++#define __BC_MISC_H_
++
++#include <bc/decl.h>
++
++struct tty_struct;
++struct file;
++struct file_lock;
++struct sigqueue;
++
++UB_DECLARE_FUNC(int, ub_file_charge(struct file *f))
++UB_DECLARE_VOID_FUNC(ub_file_uncharge(struct file *f))
++UB_DECLARE_FUNC(int, ub_flock_charge(struct file_lock *fl, int hard))
++UB_DECLARE_VOID_FUNC(ub_flock_uncharge(struct file_lock *fl))
++UB_DECLARE_FUNC(int, ub_siginfo_charge(struct sigqueue *q,
++ struct user_beancounter *ub))
++UB_DECLARE_VOID_FUNC(ub_siginfo_uncharge(struct sigqueue *q))
++UB_DECLARE_FUNC(int, ub_task_charge(struct task_struct *parent,
++ struct task_struct *task))
++UB_DECLARE_VOID_FUNC(ub_task_uncharge(struct task_struct *task))
++UB_DECLARE_VOID_FUNC(ub_task_put(struct task_struct *task))
++UB_DECLARE_FUNC(int, ub_pty_charge(struct tty_struct *tty))
++UB_DECLARE_VOID_FUNC(ub_pty_uncharge(struct tty_struct *tty))
++
++#ifdef CONFIG_BEANCOUNTERS
++#define set_flock_charged(fl) do { (fl)->fl_charged = 1; } while (0)
++#define unset_flock_charged(fl) do { \
++ WARN_ON((fl)->fl_charged == 0); \
++ (fl)->fl_charged = 0; \
++ } while (0)
++#define set_mm_ub(mm, tsk) do { \
++ (mm)->mm_ub = get_beancounter(tsk != current ? \
++ tsk->task_bc.task_ub : get_exec_ub()); \
++ } while (0)
++#define put_mm_ub(mm) do { \
++ put_beancounter((mm)->mm_ub); \
++ (mm)->mm_ub = NULL; \
++ } while (0)
++#else
++#define set_flock_charged(fl) do { } while (0)
++#define unset_flock_charged(fl) do { } while (0)
++#define set_mm_ub(mm, tsk) do { } while (0)
++#define put_mm_ub(mm) do { } while (0)
++#endif
++#endif
+diff --git a/include/bc/net.h b/include/bc/net.h
+new file mode 100644
+index 0000000..7c4c894
+--- /dev/null
++++ b/include/bc/net.h
+@@ -0,0 +1,213 @@
++/*
++ * include/bc/net.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_NET_H_
++#define __BC_NET_H_
++
++/*
++ * UB_NUMXXXSOCK, UB_XXXBUF accounting
++ */
++
++#include <bc/decl.h>
++#include <bc/sock.h>
++#include <bc/beancounter.h>
++
++#define bid2sid(__bufid) \
++ ((__bufid) == UB_TCPSNDBUF ? UB_NUMTCPSOCK : UB_NUMOTHERSOCK)
++
++#define SOCK_MIN_UBCSPACE ((int)((2048 - sizeof(struct skb_shared_info)) & \
++ ~(SMP_CACHE_BYTES-1)))
++#define SOCK_MIN_UBCSPACE_CH skb_charge_size(SOCK_MIN_UBCSPACE)
++
++static inline int ub_skb_alloc_bc(struct sk_buff *skb, gfp_t gfp_mask)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ memset(skb_bc(skb), 0, sizeof(struct skb_beancounter));
++#endif
++ return 0;
++}
++
++static inline void ub_skb_free_bc(struct sk_buff *skb)
++{
++}
++
++#define IS_TCP_SOCK(__family, __type) \
++ (((__family) == PF_INET || (__family) == PF_INET6) && (__type) == SOCK_STREAM)
++
++/* number of sockets */
++UB_DECLARE_FUNC(int, ub_sock_charge(struct sock *sk, int family, int type))
++UB_DECLARE_FUNC(int, ub_tcp_sock_charge(struct sock *sk))
++UB_DECLARE_FUNC(int, ub_other_sock_charge(struct sock *sk))
++UB_DECLARE_VOID_FUNC(ub_sock_uncharge(struct sock *sk))
++
++/* management of queue for send space */
++UB_DECLARE_FUNC(long, ub_sock_wait_for_space(struct sock *sk, long timeo,
++ unsigned long size))
++UB_DECLARE_VOID_FUNC(ub_sock_snd_queue_add(struct sock *sk, int resource,
++ unsigned long size))
++UB_DECLARE_VOID_FUNC(ub_sock_sndqueuedel(struct sock *sk))
++
++/* send space */
++UB_DECLARE_FUNC(int, ub_sock_make_wreserv(struct sock *sk, int bufid,
++ unsigned long size))
++UB_DECLARE_FUNC(int, ub_sock_get_wreserv(struct sock *sk, int bufid,
++ unsigned long size))
++UB_DECLARE_VOID_FUNC(ub_sock_ret_wreserv(struct sock *sk, int bufid,
++ unsigned long size, unsigned long ressize))
++UB_DECLARE_FUNC(int, ub_sock_tcp_chargesend(struct sock *sk,
++ struct sk_buff *skb, enum ub_severity strict))
++UB_DECLARE_FUNC(int, ub_sock_tcp_chargepage(struct sock *sk))
++UB_DECLARE_VOID_FUNC(ub_sock_tcp_detachpage(struct sock *sk))
++
++UB_DECLARE_FUNC(int, ub_nlrcvbuf_charge(struct sk_buff *skb, struct sock *sk))
++
++/* receive space */
++UB_DECLARE_FUNC(int, ub_sockrcvbuf_charge(struct sock *sk, struct sk_buff *skb))
++UB_DECLARE_FUNC(int, ub_sock_tcp_chargerecv(struct sock *sk,
++ struct sk_buff *skb, enum ub_severity strict))
++
++/* skb destructor */
++UB_DECLARE_VOID_FUNC(ub_skb_uncharge(struct sk_buff *skb))
++
++static inline int ub_sock_makewres_other(struct sock *sk, unsigned long size)
++{
++ return ub_sock_make_wreserv(sk, UB_OTHERSOCKBUF, size);
++}
++
++static inline int ub_sock_makewres_tcp(struct sock *sk, unsigned long size)
++{
++ return ub_sock_make_wreserv(sk, UB_TCPSNDBUF, size);
++}
++
++UB_DECLARE_FUNC(int, ub_sock_getwres_other(struct sock *sk,
++ unsigned long size))
++
++static inline int ub_sock_getwres_tcp(struct sock *sk, unsigned long size)
++{
++ return ub_sock_get_wreserv(sk, UB_TCPSNDBUF, size);
++}
++
++UB_DECLARE_VOID_FUNC(ub_sock_retwres_other(struct sock *sk,
++ unsigned long size, unsigned long ressize))
++
++static inline void ub_sock_retwres_tcp(struct sock *sk, unsigned long size,
++ unsigned long ressize)
++{
++ ub_sock_ret_wreserv(sk, UB_TCPSNDBUF, size, ressize);
++}
++
++static inline void ub_sock_sndqueueadd_other(struct sock *sk, unsigned long sz)
++{
++ ub_sock_snd_queue_add(sk, UB_OTHERSOCKBUF, sz);
++}
++
++static inline void ub_sock_sndqueueadd_tcp(struct sock *sk, unsigned long sz)
++{
++ ub_sock_snd_queue_add(sk, UB_TCPSNDBUF, sz);
++}
++
++static inline int ub_tcpsndbuf_charge(struct sock *sk,
++ struct sk_buff *skb)
++{
++ return ub_sock_tcp_chargesend(sk, skb, UB_HARD);
++}
++
++static inline int ub_tcpsndbuf_charge_forced(struct sock *sk,
++ struct sk_buff *skb)
++{
++ return ub_sock_tcp_chargesend(sk, skb, UB_FORCE);
++}
++
++static inline int ub_tcprcvbuf_charge(struct sock *sk, struct sk_buff *skb)
++{
++ return ub_sock_tcp_chargerecv(sk, skb, UB_SOFT);
++}
++
++static inline int ub_tcprcvbuf_charge_forced(struct sock *sk,
++ struct sk_buff *skb)
++{
++ return ub_sock_tcp_chargerecv(sk, skb, UB_FORCE);
++}
++
++/* Charge size */
++static inline unsigned long skb_charge_datalen(unsigned long chargesize)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ unsigned long slabsize;
++
++ chargesize -= sizeof(struct sk_buff);
++ slabsize = 64;
++ do {
++ slabsize <<= 1;
++ } while (slabsize <= chargesize);
++
++ slabsize >>= 1;
++ return (slabsize - sizeof(struct skb_shared_info)) &
++ ~(SMP_CACHE_BYTES-1);
++#else
++ return 0;
++#endif
++}
++
++static inline unsigned long skb_charge_size_gen(unsigned long size)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ unsigned int slabsize;
++
++ size = SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info);
++ slabsize = 32; /* min size is 64 because of skb_shared_info */
++ do {
++ slabsize <<= 1;
++ } while (slabsize < size);
++
++ return slabsize + sizeof(struct sk_buff);
++#else
++ return 0;
++#endif
++
++}
++
++static inline unsigned long skb_charge_size_const(unsigned long size)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ unsigned int ret;
++ if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 64)
++ ret = 64 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 128)
++ ret = 128 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 256)
++ ret = 256 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 512)
++ ret = 512 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 1024)
++ ret = 1024 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 2048)
++ ret = 2048 + sizeof(struct sk_buff);
++ else if (SKB_DATA_ALIGN(size) + sizeof(struct skb_shared_info) <= 4096)
++ ret = 4096 + sizeof(struct sk_buff);
++ else
++ ret = skb_charge_size_gen(size);
++ return ret;
++#else
++ return 0;
++#endif
++}
++
++
++#define skb_charge_size(__size) \
++ (__builtin_constant_p(__size) ? \
++ skb_charge_size_const(__size) : \
++ skb_charge_size_gen(__size))
++
++UB_DECLARE_FUNC(int, skb_charge_fullsize(struct sk_buff *skb))
++UB_DECLARE_VOID_FUNC(ub_skb_set_charge(struct sk_buff *skb,
++ struct sock *sk, unsigned long size, int res))
++
++#endif
+diff --git a/include/bc/oom_kill.h b/include/bc/oom_kill.h
+new file mode 100644
+index 0000000..c07608f
+--- /dev/null
++++ b/include/bc/oom_kill.h
+@@ -0,0 +1,26 @@
++#include <bc/decl.h>
++#include <bc/task.h>
++
++UB_DECLARE_FUNC(int, ub_oom_lock(void))
++UB_DECLARE_FUNC(struct user_beancounter *, ub_oom_select_worst(void))
++UB_DECLARE_VOID_FUNC(ub_oom_mm_killed(struct user_beancounter *ub))
++UB_DECLARE_VOID_FUNC(ub_oom_unlock(void))
++UB_DECLARE_VOID_FUNC(ub_out_of_memory(struct user_beancounter *ub))
++UB_DECLARE_VOID_FUNC(ub_oom_task_dead(struct task_struct *tsk))
++UB_DECLARE_FUNC(int, ub_oom_task_skip(struct user_beancounter *ub,
++ struct task_struct *tsk))
++
++#ifdef CONFIG_BEANCOUNTERS
++extern int oom_generation;
++extern int oom_kill_counter;
++#define ub_oom_start() do { \
++ current->task_bc.oom_generation = oom_generation; \
++ } while (0)
++#define ub_oom_task_killed(p) do { \
++ oom_kill_counter++; \
++ wake_up_process(p); \
++ } while (0)
++#else
++#define ub_oom_start() do { } while (0)
++#define ub_oom_task_killed(p) do { } while (0)
++#endif
+diff --git a/include/bc/proc.h b/include/bc/proc.h
+new file mode 100644
+index 0000000..f244523
+--- /dev/null
++++ b/include/bc/proc.h
+@@ -0,0 +1,40 @@
++/*
++ * include/bc/proc.h
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __UB_PROC_H_
++#define __UB_PROC_H_
++
++#include <linux/seq_file.h>
++
++struct bc_proc_entry {
++ char *name;
++ union {
++ int (*show)(struct seq_file *, void *);
++ struct file_operations *fops;
++ } u;
++ struct bc_proc_entry *next;
++ int cookie;
++};
++
++struct user_beancounter;
++
++void bc_register_proc_entry(struct bc_proc_entry *);
++void bc_register_proc_root_entry(struct bc_proc_entry *);
++
++static inline struct user_beancounter *seq_beancounter(struct seq_file *f)
++{
++ return (struct user_beancounter *)(f->private);
++}
++
++extern const char *bc_proc_lu_fmt;
++extern const char *bc_proc_lu_lfmt;
++extern const char *bc_proc_llu_fmt;
++extern const char *bc_proc_lu_lu_fmt;
++#endif
+diff --git a/include/bc/rss_pages.h b/include/bc/rss_pages.h
+new file mode 100644
+index 0000000..b195961
+--- /dev/null
++++ b/include/bc/rss_pages.h
+@@ -0,0 +1,57 @@
++/*
++ * include/bc/rss_pages.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __RSS_PAGES_H_
++#define __RSS_PAGES_H_
++
++/*
++ * Page_beancounters
++ */
++
++struct page;
++struct user_beancounter;
++
++#define PB_MAGIC 0x62700001UL
++
++struct page_beancounter {
++ unsigned long pb_magic;
++ struct page *page;
++ struct user_beancounter *ub;
++ union {
++ struct page_beancounter *next_hash;
++ struct page_beancounter *page_pb_list;
++ };
++ union {
++ unsigned refcount;
++ unsigned io_debug;
++ };
++ union {
++ struct list_head page_list;
++ struct list_head io_list;
++ };
++};
++
++#define PB_REFCOUNT_BITS 24
++#define PB_SHIFT_GET(c) ((c) >> PB_REFCOUNT_BITS)
++#define PB_SHIFT_INC(c) ((c) += (1 << PB_REFCOUNT_BITS))
++#define PB_SHIFT_DEC(c) ((c) -= (1 << PB_REFCOUNT_BITS))
++#define PB_COUNT_GET(c) ((c) & ((1 << PB_REFCOUNT_BITS) - 1))
++#define PB_COUNT_INC(c) ((c)++)
++#define PB_COUNT_DEC(c) ((c)--)
++#define PB_REFCOUNT_MAKE(s, c) (((s) << PB_REFCOUNT_BITS) + (c))
++
++#define page_pbc(__page) ((__page)->bc.page_pb)
++
++extern spinlock_t pb_lock;
++
++struct address_space;
++extern int is_shmem_mapping(struct address_space *);
++
++#endif
+diff --git a/include/bc/sock.h b/include/bc/sock.h
+new file mode 100644
+index 0000000..b314c9b
+--- /dev/null
++++ b/include/bc/sock.h
+@@ -0,0 +1,47 @@
++/*
++ * include/bc/sock.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_SOCK_H_
++#define __BC_SOCK_H_
++
++#include <bc/task.h>
++
++struct sock;
++struct sk_buff;
++
++struct skb_beancounter {
++ struct user_beancounter *ub;
++ unsigned long charged:27, resource:5;
++};
++
++struct sock_beancounter {
++ struct user_beancounter *ub;
++ /*
++ * poll_reserv accounts space already charged for future sends.
++ * It is required to make poll agree with sendmsg.
++ * Additionally, it makes real charges (with taking bc spinlock)
++ * in the send path rarer, speeding networking up.
++ * For TCP (only): changes are protected by socket lock (not bc!)
++ * For all proto: may be read without serialization in poll.
++ */
++ unsigned long poll_reserv;
++ unsigned long forw_space;
++ /* fields below are protected by bc spinlock */
++ unsigned long ub_waitspc; /* space waiting for */
++ unsigned long ub_wcharged;
++ struct list_head ub_sock_list;
++};
++
++#define sock_bc(__sk) (&(__sk)->sk_bc)
++#define skb_bc(__skb) (&(__skb)->skb_bc)
++#define skbc_sock(__skbc) (container_of(__skbc, struct sock, sk_bc))
++#define sock_has_ubc(__sk) (sock_bc(__sk)->ub != NULL)
++
++#endif
+diff --git a/include/bc/sock_orphan.h b/include/bc/sock_orphan.h
+new file mode 100644
+index 0000000..038d52b
+--- /dev/null
++++ b/include/bc/sock_orphan.h
+@@ -0,0 +1,106 @@
++/*
++ * include/bc/sock_orphan.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_SOCK_ORPHAN_H_
++#define __BC_SOCK_ORPHAN_H_
++
++#include <net/tcp.h>
++
++#include "bc/beancounter.h"
++#include "bc/net.h"
++
++
++static inline atomic_t *__ub_get_orphan_count_ptr(struct sock *sk)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ if (sock_has_ubc(sk))
++ return &sock_bc(sk)->ub->ub_orphan_count;
++#endif
++ return sk->sk_prot->orphan_count;
++}
++
++static inline void ub_inc_orphan_count(struct sock *sk)
++{
++ atomic_inc(__ub_get_orphan_count_ptr(sk));
++}
++
++static inline void ub_dec_orphan_count(struct sock *sk)
++{
++ atomic_dec(__ub_get_orphan_count_ptr(sk));
++}
++
++static inline int ub_get_orphan_count(struct sock *sk)
++{
++ return atomic_read(__ub_get_orphan_count_ptr(sk));
++}
++
++extern int __ub_too_many_orphans(struct sock *sk, int count);
++static inline int ub_too_many_orphans(struct sock *sk, int count)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ if (__ub_too_many_orphans(sk, count))
++ return 1;
++#endif
++ return (ub_get_orphan_count(sk) > sysctl_tcp_max_orphans ||
++ (sk->sk_wmem_queued > SOCK_MIN_SNDBUF &&
++ atomic_read(&tcp_memory_allocated) > sysctl_tcp_mem[2]));
++}
++
++#include <bc/kmem.h>
++
++struct inet_timewait_sock;
++
++static inline void ub_timewait_mod(struct inet_timewait_sock *tw, int incdec)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *ub;
++
++ ub = slab_ub(tw);
++ if (ub != NULL)
++ ub->ub_tw_count += incdec;
++#endif
++}
++
++static inline int __ub_timewait_check(struct sock *sk)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *ub;
++ unsigned long mem_max, mem;
++ int tw_count;
++
++ ub = sock_bc(sk)->ub;
++ if (ub == NULL)
++ return 1;
++
++ tw_count = ub->ub_tw_count;
++ mem_max = sysctl_tcp_max_tw_kmem_fraction *
++ ((ub->ub_parms[UB_KMEMSIZE].limit >> 10) + 1);
++ mem = kmem_cache_objuse(sk->sk_prot_creator->twsk_prot->twsk_slab);
++ mem *= tw_count;
++ return tw_count < sysctl_tcp_max_tw_buckets_ub && mem < mem_max;
++#else
++ return 1;
++#endif
++}
++
++#define ub_timewait_inc(tw, twdr) do { \
++ if ((twdr)->ub_managed) \
++ ub_timewait_mod(tw, 1); \
++ } while (0)
++
++#define ub_timewait_dec(tw, twdr) do { \
++ if ((twdr)->ub_managed) \
++ ub_timewait_mod(tw, -1); \
++ } while (0)
++
++#define ub_timewait_check(sk, twdr) ((!(twdr)->ub_managed) || \
++ __ub_timewait_check(sk))
++
++#endif
+diff --git a/include/bc/statd.h b/include/bc/statd.h
+new file mode 100644
+index 0000000..9dafc5e
+--- /dev/null
++++ b/include/bc/statd.h
+@@ -0,0 +1,70 @@
++/*
++ * include/bc/statd.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_STATD_H_
++#define __BC_STATD_H_
++
++/* sys_ubstat commands list */
++#define UBSTAT_READ_ONE 0x010000
++#define UBSTAT_READ_ALL 0x020000
++#define UBSTAT_READ_FULL 0x030000
++#define UBSTAT_UBLIST 0x040000
++#define UBSTAT_UBPARMNUM 0x050000
++#define UBSTAT_GETTIME 0x060000
++
++#define UBSTAT_CMD(func) ((func) & 0xF0000)
++#define UBSTAT_PARMID(func) ((func) & 0x0FFFF)
++
++#define TIME_MAX_SEC (LONG_MAX / HZ)
++#define TIME_MAX_JIF (TIME_MAX_SEC * HZ)
++
++typedef unsigned long ubstattime_t;
++
++typedef struct {
++ ubstattime_t start_time;
++ ubstattime_t end_time;
++ ubstattime_t cur_time;
++} ubgettime_t;
++
++typedef struct {
++ long maxinterval;
++ int signum;
++} ubnotifrq_t;
++
++typedef struct {
++ unsigned long maxheld;
++ unsigned long failcnt;
++} ubstatparm_t;
++
++typedef struct {
++ unsigned long barrier;
++ unsigned long limit;
++ unsigned long held;
++ unsigned long maxheld;
++ unsigned long minheld;
++ unsigned long failcnt;
++ unsigned long __unused1;
++ unsigned long __unused2;
++} ubstatparmf_t;
++
++typedef struct {
++ ubstattime_t start_time;
++ ubstattime_t end_time;
++ ubstatparmf_t param[0];
++} ubstatfull_t;
++
++#ifdef __KERNEL__
++struct ub_stat_notify {
++ struct list_head list;
++ struct task_struct *task;
++ int signum;
++};
++#endif
++#endif
+diff --git a/include/bc/task.h b/include/bc/task.h
+new file mode 100644
+index 0000000..f5a2915
+--- /dev/null
++++ b/include/bc/task.h
+@@ -0,0 +1,69 @@
++/*
++ * include/bc/task.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_TASK_H_
++#define __BC_TASK_H_
++
++struct user_beancounter;
++
++
++#ifdef CONFIG_BEANCOUNTERS
++struct task_beancounter {
++ struct user_beancounter *exec_ub;
++ struct user_beancounter *saved_ub;
++ struct user_beancounter *task_ub;
++ struct user_beancounter *fork_sub;
++ unsigned long file_precharged, file_quant, file_count;
++ unsigned long kmem_precharged;
++ char dentry_alloc, pgfault_handle;
++ void *task_fnode, *task_freserv;
++ unsigned long oom_generation;
++ unsigned long task_data[4];
++ unsigned long pgfault_allot;
++};
++
++#define get_task_ub(__task) ((__task)->task_bc.task_ub)
++
++extern struct user_beancounter ub0;
++#define get_ub0() (&ub0)
++
++#define ub_save_context(t) do { \
++ t->task_bc.saved_ub = t->task_bc.exec_ub; \
++ t->task_bc.exec_ub = get_ub0(); \
++ } while (0)
++#define ub_restore_context(t) do { \
++ t->task_bc.exec_ub = t->task_bc.saved_ub; \
++ } while (0)
++
++#define get_exec_ub() (current->task_bc.exec_ub)
++#define set_exec_ub(__newub) \
++({ \
++ struct user_beancounter *old; \
++ struct task_beancounter *tbc; \
++ \
++ tbc = &current->task_bc; \
++ old = tbc->exec_ub; \
++ tbc->exec_ub = __newub; \
++ old; \
++})
++
++void ub_init_task_bc(struct task_beancounter *);
++
++#else /* CONFIG_BEANCOUNTERS */
++
++#define get_ub0() (NULL)
++#define get_exec_ub() (NULL)
++#define get_task_ub(task) (NULL)
++#define set_exec_ub(__ub) (NULL)
++#define ub_save_context(t) do { } while (0)
++#define ub_restore_context(t) do { } while (0)
++
++#endif /* CONFIG_BEANCOUNTERS */
++#endif /* __task.h_ */
+diff --git a/include/bc/tcp.h b/include/bc/tcp.h
+new file mode 100644
+index 0000000..d2bf748
+--- /dev/null
++++ b/include/bc/tcp.h
+@@ -0,0 +1,76 @@
++/*
++ * include/bc/tcp.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __BC_TCP_H_
++#define __BC_TCP_H_
++
++/*
++ * UB_NUMXXXSOCK, UB_XXXBUF accounting
++ */
++
++#include <bc/sock.h>
++#include <bc/beancounter.h>
++
++static inline void ub_tcp_update_maxadvmss(struct sock *sk)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ if (!sock_has_ubc(sk))
++ return;
++ if (sock_bc(sk)->ub->ub_maxadvmss >= tcp_sk(sk)->advmss)
++ return;
++
++ sock_bc(sk)->ub->ub_maxadvmss =
++ skb_charge_size(MAX_HEADER + sizeof(struct iphdr)
++ + sizeof(struct tcphdr) + tcp_sk(sk)->advmss);
++#endif
++}
++
++static inline int ub_tcp_rmem_allows_expand(struct sock *sk)
++{
++ if (tcp_memory_pressure)
++ return 0;
++#ifdef CONFIG_BEANCOUNTERS
++ if (sock_has_ubc(sk)) {
++ struct user_beancounter *ub;
++
++ ub = sock_bc(sk)->ub;
++ if (ub->ub_rmem_pressure == UB_RMEM_EXPAND)
++ return 1;
++ if (ub->ub_rmem_pressure == UB_RMEM_SHRINK)
++ return 0;
++ return sk->sk_rcvbuf <= ub->ub_rmem_thres;
++ }
++#endif
++ return 1;
++}
++
++static inline int ub_tcp_memory_pressure(struct sock *sk)
++{
++ if (tcp_memory_pressure)
++ return 1;
++#ifdef CONFIG_BEANCOUNTERS
++ if (sock_has_ubc(sk))
++ return sock_bc(sk)->ub->ub_rmem_pressure != UB_RMEM_EXPAND;
++#endif
++ return 0;
++}
++
++static inline int ub_tcp_shrink_rcvbuf(struct sock *sk)
++{
++ if (tcp_memory_pressure)
++ return 1;
++#ifdef CONFIG_BEANCOUNTERS
++ if (sock_has_ubc(sk))
++ return sock_bc(sk)->ub->ub_rmem_pressure == UB_RMEM_SHRINK;
++#endif
++ return 0;
++}
++
++#endif
+diff --git a/include/bc/vmpages.h b/include/bc/vmpages.h
+new file mode 100644
+index 0000000..09642e3
+--- /dev/null
++++ b/include/bc/vmpages.h
+@@ -0,0 +1,152 @@
++/*
++ * include/bc/vmpages.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __UB_PAGES_H_
++#define __UB_PAGES_H_
++
++#include <linux/linkage.h>
++#include <bc/beancounter.h>
++#include <bc/decl.h>
++
++/*
++ * Check whether vma has private or copy-on-write mapping.
++ * Should match checks in ub_protected_charge().
++ */
++#define VM_UB_PRIVATE(__flags, __file) \
++ ( ((__flags) & VM_WRITE) ? \
++ (__file) == NULL || !((__flags) & VM_SHARED) : \
++ 0 \
++ )
++
++/* Mprotect charging result */
++#define PRIVVM_ERROR -1
++#define PRIVVM_NO_CHARGE 0 /* UB_DECLARE_FUNC retval with ubc off */
++#define PRIVVM_TO_PRIVATE 1
++#define PRIVVM_TO_SHARED 2
++
++UB_DECLARE_FUNC(int, ub_protected_charge(struct mm_struct *mm,
++ unsigned long size,
++ unsigned long newflags,
++ struct vm_area_struct *vma))
++
++UB_DECLARE_VOID_FUNC(ub_unused_privvm_add(struct mm_struct *mm,
++ struct vm_area_struct *vma,
++ unsigned long num))
++#define ub_unused_privvm_inc(mm, vma) ub_unused_privvm_add(mm, vma, 1)
++UB_DECLARE_VOID_FUNC(ub_unused_privvm_sub(struct mm_struct *mm,
++ struct vm_area_struct *vma,
++ unsigned long num))
++#define ub_unused_privvm_dec(mm, vma) ub_unused_privvm_sub(mm, vma, 1)
++
++UB_DECLARE_VOID_FUNC(__ub_unused_privvm_dec(struct mm_struct *mm,
++ long sz))
++
++UB_DECLARE_FUNC(int, ub_memory_charge(struct mm_struct *mm,
++ unsigned long size,
++ unsigned vm_flags,
++ struct file *vm_file,
++ int strict))
++UB_DECLARE_VOID_FUNC(ub_memory_uncharge(struct mm_struct *mm,
++ unsigned long size,
++ unsigned vm_flags,
++ struct file *vm_file))
++
++struct shmem_inode_info;
++UB_DECLARE_FUNC(int, ub_shmpages_charge(struct shmem_inode_info *i,
++ unsigned long sz))
++UB_DECLARE_VOID_FUNC(ub_shmpages_uncharge(struct shmem_inode_info *i,
++ unsigned long sz))
++UB_DECLARE_VOID_FUNC(ub_tmpfs_respages_inc(struct shmem_inode_info *shi))
++UB_DECLARE_VOID_FUNC(ub_tmpfs_respages_sub(struct shmem_inode_info *shi,
++ unsigned long size))
++#define ub_tmpfs_respages_dec(shi) ub_tmpfs_respages_sub(shi, 1)
++
++#ifdef CONFIG_BEANCOUNTERS
++#define shmi_ub_set(shi, ub) do { \
++ (shi)->shmi_ub = get_beancounter(ub); \
++ } while (0)
++#define shmi_ub_put(shi) do { \
++ put_beancounter((shi)->shmi_ub); \
++ (shi)->shmi_ub = NULL; \
++ } while (0)
++#else
++#define shmi_ub_set(shi, ub) do { } while (0)
++#define shmi_ub_put(shi) do { } while (0)
++#endif
++
++UB_DECLARE_FUNC(int, ub_locked_charge(struct mm_struct *mm,
++ unsigned long size))
++UB_DECLARE_VOID_FUNC(ub_locked_uncharge(struct mm_struct *mm,
++ unsigned long size))
++UB_DECLARE_FUNC(int, ub_lockedshm_charge(struct shmem_inode_info *shi,
++ unsigned long size))
++UB_DECLARE_VOID_FUNC(ub_lockedshm_uncharge(struct shmem_inode_info *shi,
++ unsigned long size))
++
++UB_DECLARE_FUNC(unsigned long, pages_in_vma_range(struct vm_area_struct *vma,
++ unsigned long addr, unsigned long end))
++#define pages_in_vma(vma) (pages_in_vma_range(vma, \
++ vma->vm_start, vma->vm_end))
++
++#define UB_PAGE_WEIGHT_SHIFT 24
++#define UB_PAGE_WEIGHT (1 << UB_PAGE_WEIGHT_SHIFT)
++
++struct page_beancounter;
++#define PBC_COPY_SAME ((struct page_beancounter *) 1)
++
++/* Mprotect charging result */
++#define PRIVVM_ERROR -1
++#define PRIVVM_NO_CHARGE 0
++#define PRIVVM_TO_PRIVATE 1
++#define PRIVVM_TO_SHARED 2
++
++extern void __ub_update_physpages(struct user_beancounter *ub);
++extern void __ub_update_oomguarpages(struct user_beancounter *ub);
++extern void __ub_update_privvm(struct user_beancounter *ub);
++
++#ifdef CONFIG_BC_RSS_ACCOUNTING
++#define PB_DECLARE_FUNC(ret, decl) UB_DECLARE_FUNC(ret, decl)
++#define PB_DECLARE_VOID_FUNC(decl) UB_DECLARE_VOID_FUNC(decl)
++#else
++#define PB_DECLARE_FUNC(ret, decl) static inline ret decl {return (ret)0;}
++#define PB_DECLARE_VOID_FUNC(decl) static inline void decl { }
++#endif
++
++PB_DECLARE_FUNC(int, pb_alloc(struct page_beancounter **pbc))
++PB_DECLARE_FUNC(int, pb_alloc_list(struct page_beancounter **pbc, int num))
++PB_DECLARE_FUNC(int, pb_alloc_all(struct page_beancounter **pbc))
++PB_DECLARE_VOID_FUNC(pb_add_ref(struct page *page,
++ struct mm_struct *mm,
++ struct page_beancounter **pbc))
++PB_DECLARE_VOID_FUNC(pb_dup_ref(struct page *page,
++ struct mm_struct *mm,
++ struct page_beancounter **pbc))
++PB_DECLARE_VOID_FUNC(pb_free_list(struct page_beancounter **pb))
++PB_DECLARE_VOID_FUNC(pb_free(struct page_beancounter **pb))
++PB_DECLARE_VOID_FUNC(pb_remove_ref(struct page *page,
++ struct mm_struct *mm))
++
++PB_DECLARE_FUNC(struct user_beancounter *, pb_grab_page_ub(struct page *page))
++#endif
++
++#ifdef CONFIG_BC_SWAP_ACCOUNTING
++#define SWP_DECLARE_FUNC(ret, decl) UB_DECLARE_FUNC(ret, decl)
++#define SWP_DECLARE_VOID_FUNC(decl) UB_DECLARE_VOID_FUNC(decl)
++#else
++#define SWP_DECLARE_FUNC(ret, decl) static inline ret decl {return (ret)0;}
++#define SWP_DECLARE_VOID_FUNC(decl) static inline void decl { }
++#endif
++
++struct swap_info_struct;
++SWP_DECLARE_FUNC(int, ub_swap_init(struct swap_info_struct *si, pgoff_t n))
++SWP_DECLARE_VOID_FUNC(ub_swap_fini(struct swap_info_struct *si))
++SWP_DECLARE_VOID_FUNC(ub_swapentry_inc(struct swap_info_struct *si, pgoff_t n,
++ struct user_beancounter *ub))
++SWP_DECLARE_VOID_FUNC(ub_swapentry_dec(struct swap_info_struct *si, pgoff_t n))
+diff --git a/include/linux/aio.h b/include/linux/aio.h
+index 09b276c..bd4b515 100644
+--- a/include/linux/aio.h
++++ b/include/linux/aio.h
+@@ -224,4 +224,8 @@ static inline struct kiocb *list_kiocb(struct list_head *h)
+ extern unsigned long aio_nr;
+ extern unsigned long aio_max_nr;
+
++void wait_for_all_aios(struct kioctx *ctx);
++extern struct kmem_cache *kioctx_cachep;
++extern void aio_kick_handler(struct work_struct *);
++
+ #endif /* __LINUX__AIO_H */
+diff --git a/include/linux/capability.h b/include/linux/capability.h
+index 9d1fe30..8a90af2 100644
+--- a/include/linux/capability.h
++++ b/include/linux/capability.h
+@@ -186,12 +186,9 @@ typedef struct kernel_cap_struct {
+
+ #define CAP_NET_BROADCAST 11
+
+-/* Allow interface configuration */
+ /* Allow administration of IP firewall, masquerading and accounting */
+ /* Allow setting debug option on sockets */
+ /* Allow modification of routing tables */
+-/* Allow setting arbitrary process / process group ownership on
+- sockets */
+ /* Allow binding to any address for transparent proxying */
+ /* Allow setting TOS (type of service) */
+ /* Allow setting promiscuous mode */
+@@ -221,6 +218,7 @@ typedef struct kernel_cap_struct {
+ #define CAP_SYS_MODULE 16
+
+ /* Allow ioperm/iopl access */
++/* Allow O_DIRECT access */
+ /* Allow sending USB messages to any device via /proc/bus/usb */
+
+ #define CAP_SYS_RAWIO 17
+@@ -239,24 +237,19 @@ typedef struct kernel_cap_struct {
+
+ /* Allow configuration of the secure attention key */
+ /* Allow administration of the random device */
+-/* Allow examination and configuration of disk quotas */
+ /* Allow configuring the kernel's syslog (printk behaviour) */
+ /* Allow setting the domainname */
+ /* Allow setting the hostname */
+ /* Allow calling bdflush() */
+-/* Allow mount() and umount(), setting up new smb connection */
++/* Allow setting up new smb connection */
+ /* Allow some autofs root ioctls */
+ /* Allow nfsservctl */
+ /* Allow VM86_REQUEST_IRQ */
+ /* Allow to read/write pci config on alpha */
+ /* Allow irix_prctl on mips (setstacksize) */
+ /* Allow flushing all cache on m68k (sys_cacheflush) */
+-/* Allow removing semaphores */
+-/* Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
+- and shared memory */
+ /* Allow locking/unlocking of shared memory segment */
+ /* Allow turning swap on/off */
+-/* Allow forged pids on socket credentials passing */
+ /* Allow setting readahead and flushing buffers on block devices */
+ /* Allow setting geometry in floppy driver */
+ /* Allow turning DMA on/off in xd driver */
+@@ -329,6 +322,50 @@ typedef struct kernel_cap_struct {
+
+ #define CAP_SETFCAP 31
+
++#ifdef __KERNEL__
++/*
++ * Important note: VZ capabilities do intersect with CAP_AUDIT
++ * this is due to compatibility reasons. Nothing bad.
++ * Both VZ and Audit/SELinux caps are disabled in VPSs.
++ */
++
++/* Allow access to all information. In the other case some structures will be
++ hiding to ensure different Virtual Environment non-interaction on the same
++ node */
++#define CAP_SETVEID 29
++
++#define CAP_VE_ADMIN 30
++
++#ifdef CONFIG_VE
++
++/* Replacement for CAP_NET_ADMIN:
++ delegated rights to the Virtual environment of its network administration.
++ For now the following rights have been delegated:
++
++ Allow setting arbitrary process / process group ownership on sockets
++ Allow interface configuration
++ */
++#define CAP_VE_NET_ADMIN CAP_VE_ADMIN
++
++/* Replacement for CAP_SYS_ADMIN:
++ delegated rights to the Virtual environment of its administration.
++ For now the following rights have been delegated:
++ */
++/* Allow mount/umount/remount */
++/* Allow examination and configuration of disk quotas */
++/* Allow removing semaphores */
++/* Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
++ and shared memory */
++/* Allow locking/unlocking of shared memory segment */
++/* Allow forged pids on socket credentials passing */
++
++#define CAP_VE_SYS_ADMIN CAP_VE_ADMIN
++#else
++#define CAP_VE_NET_ADMIN CAP_NET_ADMIN
++#define CAP_VE_SYS_ADMIN CAP_SYS_ADMIN
++#endif
++#endif
++
+ /* Override MAC access.
+ The base kernel enforces no MAC policy.
+ An LSM may enforce a MAC policy, and if it does and it chooses
+@@ -390,7 +427,16 @@ typedef struct kernel_cap_struct {
+ #define CAP_INIT_INH_SET CAP_EMPTY_SET
+
+ # define cap_clear(c) do { (c) = __cap_empty_set; } while (0)
++#ifndef CONFIG_VE
+ # define cap_set_full(c) do { (c) = __cap_full_set; } while (0)
++#else
++# define cap_set_full(c) do { \
++ if (ve_is_super(get_exec_env())) \
++ (c) = __cap_full_set; \
++ else \
++ (c) = get_exec_env()->ve_cap_bset;\
++ } while (0)
++#endif
+ # define cap_set_init_eff(c) do { (c) = __cap_init_eff_set; } while (0)
+
+ #define cap_raise(c, flag) ((c).cap[CAP_TO_INDEX(flag)] |= CAP_TO_MASK(flag))
+@@ -503,6 +549,10 @@ extern const kernel_cap_t __cap_init_eff_set;
+
+ kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
+
++#include <linux/spinlock_types.h>
++
++extern spinlock_t task_capability_lock;
++
+ /**
+ * has_capability - Determine if a task has a superior capability available
+ * @t: The task in question
+diff --git a/include/linux/cfq-iosched.h b/include/linux/cfq-iosched.h
+new file mode 100644
+index 0000000..4e2afed
+--- /dev/null
++++ b/include/linux/cfq-iosched.h
+@@ -0,0 +1,149 @@
++#ifndef _LINUX_CFQ_IOSCHED_H
++#define _LINUX_CFQ_IOSCHED_H
++
++#include <linux/ioprio.h>
++#include <linux/rbtree.h>
++#include <linux/blkdev.h>
++
++extern struct kmem_cache *cfq_pool;
++
++#define CFQ_PRIO_LISTS IOPRIO_BE_NR
++
++/*
++ * Most of our rbtree usage is for sorting with min extraction, so
++ * if we cache the leftmost node we don't have to walk down the tree
++ * to find it. Idea borrowed from Ingo Molnars CFS scheduler. We should
++ * move this into the elevator for the rq sorting as well.
++ */
++struct cfq_rb_root {
++ struct rb_root rb;
++ struct rb_node *left;
++};
++#define CFQ_RB_ROOT (struct cfq_rb_root) { RB_ROOT, NULL, }
++
++/*
++ * Per (Device, UBC) queue data
++ */
++struct cfq_bc_data {
++ /* for ub.iopriv->cfq_bc_head */
++ struct list_head cfq_bc_list;
++ /* for cfqd->act_cfq_bc_head */
++ struct list_head act_cfq_bc_list;
++
++ struct cfq_data *cfqd;
++ struct ub_iopriv *ub_iopriv;
++
++ /*
++ * rr list of queues with requests and the count of them
++ */
++ struct cfq_rb_root service_tree;
++
++ int cur_prio;
++ int cur_end_prio;
++
++ unsigned long rqnum;
++ unsigned long on_dispatch;
++
++ /*
++ * async queue for each priority case
++ */
++ struct cfq_queue *async_cfqq[2][CFQ_PRIO_LISTS];
++ struct cfq_queue *async_idle_cfqq;
++};
++
++/*
++ * Per block device queue structure
++ */
++struct cfq_data {
++ struct request_queue *queue;
++
++#ifndef CONFIG_BC_IO_SCHED
++ struct cfq_bc_data cfq_bc;
++#endif
++ unsigned int busy_queues;
++
++ int rq_in_driver;
++ int sync_flight;
++ int hw_tag;
++
++ /*
++ * idle window management
++ */
++ struct timer_list idle_slice_timer;
++ struct work_struct unplug_work;
++
++ struct cfq_queue *active_queue;
++ struct cfq_io_context *active_cic;
++
++ sector_t last_position;
++ unsigned long last_end_request;
++
++ /*
++ * tunables, see top of file
++ */
++ unsigned int cfq_quantum;
++ unsigned int cfq_fifo_expire[2];
++ unsigned int cfq_back_penalty;
++ unsigned int cfq_back_max;
++ unsigned int cfq_slice[2];
++ unsigned int cfq_slice_async_rq;
++ unsigned int cfq_slice_idle;
++
++ struct list_head cic_list;
++
++ /* list of ub that have requests */
++ struct list_head act_cfq_bc_head;
++ /* ub that owns a timeslice at the moment */
++ struct cfq_bc_data *active_cfq_bc;
++ unsigned int cfq_ub_slice;
++ unsigned long slice_end;
++ int virt_mode;
++ int write_virt_mode;
++};
++
++/*
++ * Per process-grouping structure
++ */
++struct cfq_queue {
++ /* reference count */
++ atomic_t ref;
++ /* various state flags, see below */
++ unsigned int flags;
++ /* parent cfq_data */
++ struct cfq_data *cfqd;
++ /* service_tree member */
++ struct rb_node rb_node;
++ /* service_tree key */
++ unsigned long rb_key;
++ /* sorted list of pending requests */
++ struct rb_root sort_list;
++ /* if fifo isn't expired, next request to serve */
++ struct request *next_rq;
++ /* requests queued in sort_list */
++ int queued[2];
++ /* currently allocated requests */
++ int allocated[2];
++ /* fifo list of requests in sort_list */
++ struct list_head fifo;
++
++ unsigned long slice_end;
++ long slice_resid;
++
++ /* pending metadata requests */
++ int meta_pending;
++ /* number of requests that are on the dispatch list or inside driver */
++ int dispatched;
++
++ /* io prio of this group */
++ unsigned short ioprio, org_ioprio;
++ unsigned short ioprio_class, org_ioprio_class;
++
++ pid_t pid;
++ struct cfq_bc_data *cfq_bc;
++};
++
++static void inline cfq_init_cfq_bc(struct cfq_bc_data *cfq_bc)
++{
++ cfq_bc->service_tree = CFQ_RB_ROOT;
++}
++#endif /* _LINUX_CFQ_IOSCHED_H */
+diff --git a/include/linux/compat.h b/include/linux/compat.h
+index cf8d11c..3c778e2 100644
+--- a/include/linux/compat.h
++++ b/include/linux/compat.h
+@@ -238,6 +238,7 @@ extern int put_compat_itimerspec(struct compat_itimerspec __user *dst,
+ asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp);
+
+ extern int compat_printk(const char *fmt, ...);
++extern int ve_compat_printk(int dst, const char *fmt, ...);
+ extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat);
+
+ asmlinkage long compat_sys_migrate_pages(compat_pid_t pid,
+diff --git a/include/linux/cpt_image.h b/include/linux/cpt_image.h
+new file mode 100644
+index 0000000..6a39f32
+--- /dev/null
++++ b/include/linux/cpt_image.h
+@@ -0,0 +1,1763 @@
++/*
++ *
++ * include/linux/cpt_image.h
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __CPT_IMAGE_H_
++#define __CPT_IMAGE_H_ 1
++
++#define CPT_NULL (~0ULL)
++#define CPT_NOINDEX (~0U)
++
++/*
++ * Image file layout.
++ *
++ * - major header
++ * - sections[]
++ *
++ * Each section is:
++ * - section header
++ * - array of objects
++ *
++ * All data records are arch independent, 64 bit aligned.
++ */
++
++enum _cpt_object_type
++{
++ CPT_OBJ_TASK = 0,
++ CPT_OBJ_MM,
++ CPT_OBJ_FS,
++ CPT_OBJ_FILES,
++ CPT_OBJ_FILE,
++ CPT_OBJ_SIGHAND_STRUCT,
++ CPT_OBJ_SIGNAL_STRUCT,
++ CPT_OBJ_TTY,
++ CPT_OBJ_SOCKET,
++ CPT_OBJ_SYSVSEM_UNDO,
++ CPT_OBJ_NAMESPACE,
++ CPT_OBJ_SYSV_SHM,
++ CPT_OBJ_INODE,
++ CPT_OBJ_UBC,
++ CPT_OBJ_SLM_SGREG,
++ CPT_OBJ_SLM_REGOBJ,
++ CPT_OBJ_SLM_MM,
++ CPT_OBJ_MAX,
++ /* The objects above are stored in memory while checkpointing */
++
++ CPT_OBJ_VMA = 1024,
++ CPT_OBJ_FILEDESC,
++ CPT_OBJ_SIGHANDLER,
++ CPT_OBJ_SIGINFO,
++ CPT_OBJ_LASTSIGINFO,
++ CPT_OBJ_SYSV_SEM,
++ CPT_OBJ_SKB,
++ CPT_OBJ_FLOCK,
++ CPT_OBJ_OPENREQ,
++ CPT_OBJ_VFSMOUNT,
++ CPT_OBJ_TRAILER,
++ CPT_OBJ_SYSVSEM_UNDO_REC,
++ CPT_OBJ_NET_DEVICE,
++ CPT_OBJ_NET_IFADDR,
++ CPT_OBJ_NET_ROUTE,
++ CPT_OBJ_NET_CONNTRACK,
++ CPT_OBJ_NET_CONNTRACK_EXPECT,
++ CPT_OBJ_AIO_CONTEXT,
++ CPT_OBJ_VEINFO,
++ CPT_OBJ_EPOLL,
++ CPT_OBJ_EPOLL_FILE,
++ CPT_OBJ_SKFILTER,
++ CPT_OBJ_SIGALTSTACK,
++ CPT_OBJ_SOCK_MCADDR,
++ CPT_OBJ_BIND_MNT,
++ CPT_OBJ_SYSVMSG,
++ CPT_OBJ_SYSVMSG_MSG,
++
++ CPT_OBJ_X86_REGS = 4096,
++ CPT_OBJ_X86_64_REGS,
++ CPT_OBJ_PAGES,
++ CPT_OBJ_COPYPAGES,
++ CPT_OBJ_REMAPPAGES,
++ CPT_OBJ_LAZYPAGES,
++ CPT_OBJ_NAME,
++ CPT_OBJ_BITS,
++ CPT_OBJ_REF,
++ CPT_OBJ_ITERPAGES,
++ CPT_OBJ_ITERYOUNGPAGES,
++ CPT_OBJ_VSYSCALL,
++ CPT_OBJ_IA64_REGS,
++ CPT_OBJ_INOTIFY,
++ CPT_OBJ_INOTIFY_WATCH,
++ CPT_OBJ_INOTIFY_EVENT,
++ CPT_OBJ_TASK_AUX,
++ CPT_OBJ_NET_TUNTAP,
++ CPT_OBJ_NET_HWADDR,
++ CPT_OBJ_NET_VETH,
++ CPT_OBJ_NET_STATS,
++};
++
++#define CPT_ALIGN(n) (((n)+7)&~7)
++
++struct cpt_major_hdr
++{
++ __u8 cpt_signature[4]; /* Magic number */
++ __u16 cpt_hdrlen; /* Length of this header */
++ __u16 cpt_image_version; /* Format of this file */
++#define CPT_VERSION_MINOR(a) ((a) & 0xf)
++#define CPT_VERSION_8 0
++#define CPT_VERSION_9 0x100
++#define CPT_VERSION_9_1 0x101
++#define CPT_VERSION_9_2 0x102
++#define CPT_VERSION_16 0x200
++#define CPT_VERSION_18 0x300
++#define CPT_VERSION_18_1 0x301
++#define CPT_VERSION_20 0x400
++#define CPT_VERSION_24 0x500
++#define CPT_VERSION_26 0x600
++#define CPT_VERSION_27 0x700
++ __u16 cpt_os_arch; /* Architecture */
++#define CPT_OS_ARCH_I386 0
++#define CPT_OS_ARCH_EMT64 1
++#define CPT_OS_ARCH_IA64 2
++ __u16 __cpt_pad1;
++ __u32 cpt_ve_features; /* VE features */
++ __u32 cpt_ve_features2; /* VE features */
++ __u16 cpt_pagesize; /* Page size used by OS */
++ __u16 cpt_hz; /* HZ used by OS */
++ __u64 cpt_start_jiffies64; /* Jiffies */
++ __u32 cpt_start_sec; /* Seconds */
++ __u32 cpt_start_nsec; /* Nanoseconds */
++ __u32 cpt_cpu_caps[4]; /* CPU capabilities */
++ __u32 cpt_kernel_config[4]; /* Kernel config */
++ __u64 cpt_iptables_mask; /* Used netfilter modules */
++} __attribute__ ((aligned (8)));
++
++#define CPT_SIGNATURE0 0x79
++#define CPT_SIGNATURE1 0x1c
++#define CPT_SIGNATURE2 0x01
++#define CPT_SIGNATURE3 0x63
++
++/* CPU capabilities */
++#define CPT_CPU_X86_CMOV 0
++#define CPT_CPU_X86_FXSR 1
++#define CPT_CPU_X86_SSE 2
++#define CPT_CPU_X86_SSE2 3
++#define CPT_CPU_X86_MMX 4
++#define CPT_CPU_X86_3DNOW 5
++#define CPT_CPU_X86_3DNOW2 6
++#define CPT_CPU_X86_SEP 7
++#define CPT_CPU_X86_EMT64 8
++#define CPT_CPU_X86_IA64 9
++#define CPT_CPU_X86_SYSCALL 10
++#define CPT_CPU_X86_SYSCALL32 11
++#define CPT_CPU_X86_SEP32 12
++
++/* Unsupported features */
++#define CPT_EXTERNAL_PROCESS 16
++#define CPT_NAMESPACES 17
++#define CPT_SCHEDULER_POLICY 18
++#define CPT_PTRACED_FROM_VE0 19
++#define CPT_UNSUPPORTED_FSTYPE 20
++#define CPT_BIND_MOUNT 21
++#define CPT_UNSUPPORTED_NETDEV 22
++#define CPT_UNSUPPORTED_MISC 23
++
++/* This mask is used to determine whether VE
++ has some unsupported features or not */
++#define CPT_UNSUPPORTED_MASK 0xffff0000UL
++
++#define CPT_KERNEL_CONFIG_PAE 0
++
++struct cpt_section_hdr
++{
++ __u64 cpt_next;
++ __u32 cpt_section;
++ __u16 cpt_hdrlen;
++ __u16 cpt_align;
++} __attribute__ ((aligned (8)));
++
++enum
++{
++ CPT_SECT_ERROR, /* Error section, content is string */
++ CPT_SECT_VEINFO,
++ CPT_SECT_FILES, /* Files. Content is array of file objects */
++ CPT_SECT_TASKS,
++ CPT_SECT_MM,
++ CPT_SECT_FILES_STRUCT,
++ CPT_SECT_FS,
++ CPT_SECT_SIGHAND_STRUCT,
++ CPT_SECT_TTY,
++ CPT_SECT_SOCKET,
++ CPT_SECT_NAMESPACE,
++ CPT_SECT_SYSVSEM_UNDO,
++ CPT_SECT_INODE, /* Inodes with i->i_nlink==0 and
++ * deleted dentires with inodes not
++ * referenced inside dumped process.
++ */
++ CPT_SECT_SYSV_SHM,
++ CPT_SECT_SYSV_SEM,
++ CPT_SECT_ORPHANS,
++ CPT_SECT_NET_DEVICE,
++ CPT_SECT_NET_IFADDR,
++ CPT_SECT_NET_ROUTE,
++ CPT_SECT_NET_IPTABLES,
++ CPT_SECT_NET_CONNTRACK,
++ CPT_SECT_NET_CONNTRACK_VE0,
++ CPT_SECT_UTSNAME,
++ CPT_SECT_TRAILER,
++ CPT_SECT_UBC,
++ CPT_SECT_SLM_SGREGS,
++ CPT_SECT_SLM_REGOBJS,
++/* Due to silly mistake we cannot index sections beyond this value */
++#define CPT_SECT_MAX_INDEX (CPT_SECT_SLM_REGOBJS+1)
++ CPT_SECT_EPOLL,
++ CPT_SECT_VSYSCALL,
++ CPT_SECT_INOTIFY,
++ CPT_SECT_SYSV_MSG,
++ CPT_SECT_MAX
++};
++
++struct cpt_major_tail
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_lazypages;
++ __u32 cpt_64bit;
++ __u64 cpt_sections[CPT_SECT_MAX_INDEX];
++ __u32 cpt_nsect;
++ __u8 cpt_signature[4]; /* Magic number */
++} __attribute__ ((aligned (8)));
++
++
++/* Common object header. */
++struct cpt_object_hdr
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++} __attribute__ ((aligned (8)));
++
++enum _cpt_content_type {
++ CPT_CONTENT_VOID,
++ CPT_CONTENT_ARRAY,
++ CPT_CONTENT_DATA,
++ CPT_CONTENT_NAME,
++
++ CPT_CONTENT_STACK,
++ CPT_CONTENT_X86_FPUSTATE_OLD,
++ CPT_CONTENT_X86_FPUSTATE,
++ CPT_CONTENT_MM_CONTEXT,
++ CPT_CONTENT_SEMARRAY,
++ CPT_CONTENT_SEMUNDO,
++ CPT_CONTENT_NLMARRAY,
++ CPT_CONTENT_MAX
++};
++
++/* CPT_OBJ_BITS: encode array of bytes */
++struct cpt_obj_bits
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_size;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++
++/* CPT_OBJ_REF: a reference to another object */
++struct cpt_obj_ref
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_pos;
++} __attribute__ ((aligned (8)));
++
++/* CPT_OBJ_VEINFO: various ve specific data */
++struct cpt_veinfo_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ /* ipc ctls */
++ __u32 shm_ctl_max;
++ __u32 shm_ctl_all;
++ __u32 shm_ctl_mni;
++ __u32 msg_ctl_max;
++ __u32 msg_ctl_mni;
++ __u32 msg_ctl_mnb;
++ __u32 sem_ctl_arr[4];
++
++ /* start time */
++ __u64 start_timespec_delta;
++ __u64 start_jiffies_delta;
++
++ /* later extension */
++ __u32 last_pid;
++ __u32 pad1;
++ __u64 reserved[8];
++} __attribute__ ((aligned (8)));
++
++/* CPT_OBJ_FILE: one struct file */
++struct cpt_file_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_flags;
++ __u32 cpt_mode;
++ __u64 cpt_pos;
++ __u32 cpt_uid;
++ __u32 cpt_gid;
++
++ __u32 cpt_i_mode;
++ __u32 cpt_lflags;
++#define CPT_DENTRY_DELETED 1
++#define CPT_DENTRY_ROOT 2
++#define CPT_DENTRY_CLONING 4
++#define CPT_DENTRY_PROC 8
++#define CPT_DENTRY_EPOLL 0x10
++#define CPT_DENTRY_REPLACED 0x20
++#define CPT_DENTRY_INOTIFY 0x40
++#define CPT_DENTRY_FUTEX 0x80
++#define CPT_DENTRY_TUNTAP 0x100
++ __u64 cpt_inode;
++ __u64 cpt_priv;
++
++ __u32 cpt_fown_fd;
++ __u32 cpt_fown_pid;
++#define CPT_FOWN_STRAY_PID 0
++ __u32 cpt_fown_uid;
++ __u32 cpt_fown_euid;
++ __u32 cpt_fown_signo;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++/* Followed by file name, encoded as CPT_OBJ_NAME */
++
++struct cpt_epoll_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_file;
++} __attribute__ ((aligned (8)));
++/* Followed by array of struct cpt_epoll_file */
++
++struct cpt_epoll_file_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_file;
++ __u32 cpt_fd;
++ __u32 cpt_events;
++ __u64 cpt_data;
++ __u32 cpt_revents;
++ __u32 cpt_ready;
++} __attribute__ ((aligned (8)));
++
++struct cpt_inotify_wd_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_wd;
++ __u32 cpt_mask;
++} __attribute__ ((aligned (8)));
++/* Followed by cpt_file_image of inode to watch */
++
++struct cpt_inotify_ev_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_wd;
++ __u32 cpt_mask;
++ __u32 cpt_cookie;
++ __u32 cpt_namelen;
++} __attribute__ ((aligned (8)));
++/* Followed by name */
++
++struct cpt_inotify_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_file;
++ __u32 cpt_user;
++ __u32 cpt_max_events;
++ __u32 cpt_last_wd;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++/* Followed by array of struct cpt_inotify_wd_image and cpt_inotify_ev_image */
++
++
++/* CPT_OBJ_FILEDESC: one file descriptor */
++struct cpt_fd_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_fd;
++ __u32 cpt_flags;
++#define CPT_FD_FLAG_CLOSEEXEC 1
++ __u64 cpt_file;
++} __attribute__ ((aligned (8)));
++
++/* CPT_OBJ_FILES: one files_struct */
++struct cpt_files_struct_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_index;
++ __u32 cpt_max_fds;
++ __u32 cpt_next_fd;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++/* Followed by array of cpt_fd_image */
++
++/* CPT_OBJ_FS: one fs_struct */
++struct cpt_fs_struct_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_umask;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++/* Followed by two/three CPT_OBJ_FILENAME for root, pwd and, optionally, altroot */
++
++/* CPT_OBJ_INODE: one struct inode */
++struct cpt_inode_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_dev;
++ __u64 cpt_ino;
++ __u32 cpt_mode;
++ __u32 cpt_nlink;
++ __u32 cpt_uid;
++ __u32 cpt_gid;
++ __u64 cpt_rdev;
++ __u64 cpt_size;
++ __u64 cpt_blksize;
++ __u64 cpt_atime;
++ __u64 cpt_mtime;
++ __u64 cpt_ctime;
++ __u64 cpt_blocks;
++ __u32 cpt_sb;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++
++/* CPT_OBJ_VFSMOUNT: one vfsmount */
++struct cpt_vfsmount_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_mntflags;
++#define CPT_MNT_BIND 0x80000000
++#define CPT_MNT_EXT 0x40000000
++ __u32 cpt_flags;
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_flock_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_owner;
++ __u32 cpt_pid;
++ __u64 cpt_start;
++ __u64 cpt_end;
++ __u32 cpt_flags;
++ __u32 cpt_type;
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_tty_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_flags;
++ __u32 cpt_link;
++ __u32 cpt_index;
++ __u32 cpt_drv_type;
++ __u32 cpt_drv_subtype;
++ __u32 cpt_drv_flags;
++ __u8 cpt_packet;
++ __u8 cpt_stopped;
++ __u8 cpt_hw_stopped;
++ __u8 cpt_flow_stopped;
++
++ __u32 cpt_canon_data;
++ __u32 cpt_canon_head;
++ __u32 cpt_canon_column;
++ __u32 cpt_column;
++ __u8 cpt_ctrl_status;
++ __u8 cpt_erasing;
++ __u8 cpt_lnext;
++ __u8 cpt_icanon;
++ __u8 cpt_raw;
++ __u8 cpt_real_raw;
++ __u8 cpt_closing;
++ __u8 __cpt_pad1;
++ __u16 cpt_minimum_to_wake;
++ __u16 __cpt_pad2;
++ __u32 cpt_pgrp;
++ __u32 cpt_session;
++ __u32 cpt_c_line;
++ __u8 cpt_name[64];
++ __u16 cpt_ws_row;
++ __u16 cpt_ws_col;
++ __u16 cpt_ws_prow;
++ __u16 cpt_ws_pcol;
++ __u8 cpt_c_cc[32];
++ __u32 cpt_c_iflag;
++ __u32 cpt_c_oflag;
++ __u32 cpt_c_cflag;
++ __u32 cpt_c_lflag;
++ __u32 cpt_read_flags[4096/32];
++} __attribute__ ((aligned (8)));
++
++struct cpt_sock_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_file;
++ __u32 cpt_parent;
++ __u32 cpt_index;
++
++ __u64 cpt_ssflags;
++ __u16 cpt_type;
++ __u16 cpt_family;
++ __u8 cpt_sstate;
++ __u8 cpt_passcred;
++ __u8 cpt_state;
++ __u8 cpt_reuse;
++
++ __u8 cpt_zapped;
++ __u8 cpt_shutdown;
++ __u8 cpt_userlocks;
++ __u8 cpt_no_check;
++ __u8 cpt_debug;
++ __u8 cpt_rcvtstamp;
++ __u8 cpt_localroute;
++ __u8 cpt_protocol;
++
++ __u32 cpt_err;
++ __u32 cpt_err_soft;
++
++ __u16 cpt_max_ack_backlog;
++ __u16 __cpt_pad1;
++ __u32 cpt_priority;
++
++ __u32 cpt_rcvlowat;
++ __u32 cpt_bound_dev_if;
++
++ __u64 cpt_rcvtimeo;
++ __u64 cpt_sndtimeo;
++ __u32 cpt_rcvbuf;
++ __u32 cpt_sndbuf;
++ __u64 cpt_flags;
++ __u64 cpt_lingertime;
++ __u32 cpt_peer_pid;
++ __u32 cpt_peer_uid;
++
++ __u32 cpt_peer_gid;
++ __u32 cpt_laddrlen;
++ __u32 cpt_laddr[128/4];
++ __u32 cpt_raddrlen;
++ __u32 cpt_raddr[128/4];
++ /* AF_UNIX */
++ __u32 cpt_peer;
++
++ __u8 cpt_socketpair;
++ __u8 cpt_deleted;
++ __u16 __cpt_pad4;
++ __u32 __cpt_pad5;
++/*
++ struct sk_filter *sk_filter;
++ */
++
++ __u64 cpt_stamp;
++ __u32 cpt_daddr;
++ __u16 cpt_dport;
++ __u16 cpt_sport;
++
++ __u32 cpt_saddr;
++ __u32 cpt_rcv_saddr;
++
++ __u32 cpt_uc_ttl;
++ __u32 cpt_tos;
++
++ __u32 cpt_cmsg_flags;
++ __u32 cpt_mc_index;
++
++ __u32 cpt_mc_addr;
++/*
++ struct ip_options *opt;
++ */
++ __u8 cpt_hdrincl;
++ __u8 cpt_mc_ttl;
++ __u8 cpt_mc_loop;
++ __u8 cpt_pmtudisc;
++
++ __u8 cpt_recverr;
++ __u8 cpt_freebind;
++ __u16 cpt_idcounter;
++ __u32 cpt_cork_flags;
++
++ __u32 cpt_cork_fragsize;
++ __u32 cpt_cork_length;
++ __u32 cpt_cork_addr;
++ __u32 cpt_cork_saddr;
++ __u32 cpt_cork_daddr;
++ __u32 cpt_cork_oif;
++
++ __u32 cpt_udp_pending;
++ __u32 cpt_udp_corkflag;
++ __u16 cpt_udp_encap;
++ __u16 cpt_udp_len;
++ __u32 __cpt_pad7;
++
++ __u64 cpt_saddr6[2];
++ __u64 cpt_rcv_saddr6[2];
++ __u64 cpt_daddr6[2];
++ __u32 cpt_flow_label6;
++ __u32 cpt_frag_size6;
++ __u32 cpt_hop_limit6;
++ __u32 cpt_mcast_hops6;
++
++ __u32 cpt_mcast_oif6;
++ __u8 cpt_rxopt6;
++ __u8 cpt_mc_loop6;
++ __u8 cpt_recverr6;
++ __u8 cpt_sndflow6;
++
++ __u8 cpt_pmtudisc6;
++ __u8 cpt_ipv6only6;
++ __u8 cpt_mapped;
++ __u8 __cpt_pad8;
++ __u32 cpt_pred_flags;
++
++ __u32 cpt_rcv_nxt;
++ __u32 cpt_snd_nxt;
++
++ __u32 cpt_snd_una;
++ __u32 cpt_snd_sml;
++
++ __u32 cpt_rcv_tstamp;
++ __u32 cpt_lsndtime;
++
++ __u8 cpt_tcp_header_len;
++ __u8 cpt_ack_pending;
++ __u8 cpt_quick;
++ __u8 cpt_pingpong;
++ __u8 cpt_blocked;
++ __u8 __cpt_pad9;
++ __u16 __cpt_pad10;
++
++ __u32 cpt_ato;
++ __u32 cpt_ack_timeout;
++
++ __u32 cpt_lrcvtime;
++ __u16 cpt_last_seg_size;
++ __u16 cpt_rcv_mss;
++
++ __u32 cpt_snd_wl1;
++ __u32 cpt_snd_wnd;
++
++ __u32 cpt_max_window;
++ __u32 cpt_pmtu_cookie;
++
++ __u32 cpt_mss_cache;
++ __u16 cpt_mss_cache_std;
++ __u16 cpt_mss_clamp;
++
++ __u16 cpt_ext_header_len;
++ __u16 cpt_ext2_header_len;
++ __u8 cpt_ca_state;
++ __u8 cpt_retransmits;
++ __u8 cpt_reordering;
++ __u8 cpt_frto_counter;
++
++ __u32 cpt_frto_highmark;
++ __u8 cpt_adv_cong;
++ __u8 cpt_defer_accept;
++ __u8 cpt_backoff;
++ __u8 __cpt_pad11;
++
++ __u32 cpt_srtt;
++ __u32 cpt_mdev;
++
++ __u32 cpt_mdev_max;
++ __u32 cpt_rttvar;
++
++ __u32 cpt_rtt_seq;
++ __u32 cpt_rto;
++
++ __u32 cpt_packets_out;
++ __u32 cpt_left_out;
++
++ __u32 cpt_retrans_out;
++ __u32 cpt_snd_ssthresh;
++
++ __u32 cpt_snd_cwnd;
++ __u16 cpt_snd_cwnd_cnt;
++ __u16 cpt_snd_cwnd_clamp;
++
++ __u32 cpt_snd_cwnd_used;
++ __u32 cpt_snd_cwnd_stamp;
++
++ __u32 cpt_timeout;
++ __u32 cpt_ka_timeout;
++
++ __u32 cpt_rcv_wnd;
++ __u32 cpt_rcv_wup;
++
++ __u32 cpt_write_seq;
++ __u32 cpt_pushed_seq;
++
++ __u32 cpt_copied_seq;
++ __u8 cpt_tstamp_ok;
++ __u8 cpt_wscale_ok;
++ __u8 cpt_sack_ok;
++ __u8 cpt_saw_tstamp;
++
++ __u8 cpt_snd_wscale;
++ __u8 cpt_rcv_wscale;
++ __u8 cpt_nonagle;
++ __u8 cpt_keepalive_probes;
++ __u32 cpt_rcv_tsval;
++
++ __u32 cpt_rcv_tsecr;
++ __u32 cpt_ts_recent;
++
++ __u64 cpt_ts_recent_stamp;
++ __u16 cpt_user_mss;
++ __u8 cpt_dsack;
++ __u8 cpt_eff_sacks;
++ __u32 cpt_sack_array[2*5];
++ __u32 cpt_window_clamp;
++
++ __u32 cpt_rcv_ssthresh;
++ __u8 cpt_probes_out;
++ __u8 cpt_num_sacks;
++ __u16 cpt_advmss;
++
++ __u8 cpt_syn_retries;
++ __u8 cpt_ecn_flags;
++ __u16 cpt_prior_ssthresh;
++ __u32 cpt_lost_out;
++
++ __u32 cpt_sacked_out;
++ __u32 cpt_fackets_out;
++
++ __u32 cpt_high_seq;
++ __u32 cpt_retrans_stamp;
++
++ __u32 cpt_undo_marker;
++ __u32 cpt_undo_retrans;
++
++ __u32 cpt_urg_seq;
++ __u16 cpt_urg_data;
++ __u8 cpt_pending;
++ __u8 cpt_urg_mode;
++
++ __u32 cpt_snd_up;
++ __u32 cpt_keepalive_time;
++
++ __u32 cpt_keepalive_intvl;
++ __u32 cpt_linger2;
++
++ __u32 cpt_rcvrtt_rtt;
++ __u32 cpt_rcvrtt_seq;
++
++ __u32 cpt_rcvrtt_time;
++ __u32 __cpt_pad12;
++} __attribute__ ((aligned (8)));
++
++struct cpt_sockmc_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u16 cpt_family;
++ __u16 cpt_mode;
++ __u32 cpt_ifindex;
++ __u32 cpt_mcaddr[4];
++} __attribute__ ((aligned (8)));
++/* Followed by array of source addresses, each zero padded to 16 bytes */
++
++struct cpt_openreq_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_rcv_isn;
++ __u32 cpt_snt_isn;
++
++ __u16 cpt_rmt_port;
++ __u16 cpt_mss;
++ __u8 cpt_family;
++ __u8 cpt_retrans;
++ __u8 cpt_snd_wscale;
++ __u8 cpt_rcv_wscale;
++
++ __u8 cpt_tstamp_ok;
++ __u8 cpt_sack_ok;
++ __u8 cpt_wscale_ok;
++ __u8 cpt_ecn_ok;
++ __u8 cpt_acked;
++ __u8 __cpt_pad1;
++ __u16 __cpt_pad2;
++
++ __u32 cpt_window_clamp;
++ __u32 cpt_rcv_wnd;
++ __u32 cpt_ts_recent;
++ __u32 cpt_iif;
++ __u64 cpt_expires;
++
++ __u64 cpt_loc_addr[2];
++ __u64 cpt_rmt_addr[2];
++/*
++ struct ip_options *opt;
++ */
++
++} __attribute__ ((aligned (8)));
++
++struct cpt_skb_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_owner;
++ __u32 cpt_queue;
++#define CPT_SKB_NQ 0
++#define CPT_SKB_RQ 1
++#define CPT_SKB_WQ 2
++#define CPT_SKB_OFOQ 3
++
++ __u64 cpt_stamp;
++ __u32 cpt_len;
++ __u32 cpt_hspace;
++ __u32 cpt_tspace;
++ __u32 cpt_h;
++ __u32 cpt_nh;
++ __u32 cpt_mac;
++
++ __u64 cpt_cb[5];
++ __u32 cpt_mac_len;
++ __u32 cpt_csum;
++ __u8 cpt_local_df;
++ __u8 cpt_pkt_type;
++ __u8 cpt_ip_summed;
++ __u8 __cpt_pad1;
++ __u32 cpt_priority;
++ __u16 cpt_protocol;
++ __u16 cpt_security;
++ __u16 cpt_gso_segs;
++ __u16 cpt_gso_size;
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_sysvshm_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_key;
++ __u64 cpt_uid;
++ __u64 cpt_gid;
++ __u64 cpt_cuid;
++ __u64 cpt_cgid;
++ __u64 cpt_mode;
++ __u64 cpt_seq;
++
++ __u32 cpt_id;
++ __u32 cpt_mlockuser;
++ __u64 cpt_segsz;
++ __u64 cpt_atime;
++ __u64 cpt_ctime;
++ __u64 cpt_dtime;
++ __u64 cpt_creator;
++ __u64 cpt_last;
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_sysvsem_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_key;
++ __u64 cpt_uid;
++ __u64 cpt_gid;
++ __u64 cpt_cuid;
++ __u64 cpt_cgid;
++ __u64 cpt_mode;
++ __u64 cpt_seq;
++ __u32 cpt_id;
++ __u32 __cpt_pad1;
++
++ __u64 cpt_otime;
++ __u64 cpt_ctime;
++} __attribute__ ((aligned (8)));
++/* Content is array of pairs semval/sempid */
++
++struct cpt_sysvsem_undo_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_id;
++ __u32 cpt_nsem;
++} __attribute__ ((aligned (8)));
++
++struct cpt_sysvmsg_msg_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_type;
++ __u64 cpt_size;
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_sysvmsg_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_key;
++ __u64 cpt_uid;
++ __u64 cpt_gid;
++ __u64 cpt_cuid;
++ __u64 cpt_cgid;
++ __u64 cpt_mode;
++ __u64 cpt_seq;
++ __u32 cpt_id;
++ __u32 __cpt_pad1;
++
++ __u64 cpt_stime;
++ __u64 cpt_rtime;
++ __u64 cpt_ctime;
++ __u64 cpt_last_sender;
++ __u64 cpt_last_receiver;
++ __u64 cpt_qbytes;
++} __attribute__ ((aligned (8)));
++/* Content is array of sysv msg */
++
++
++struct cpt_mm_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start_code;
++ __u64 cpt_end_code;
++ __u64 cpt_start_data;
++ __u64 cpt_end_data;
++ __u64 cpt_start_brk;
++ __u64 cpt_brk;
++ __u64 cpt_start_stack;
++ __u64 cpt_start_arg;
++ __u64 cpt_end_arg;
++ __u64 cpt_start_env;
++ __u64 cpt_end_env;
++ __u64 cpt_def_flags;
++ __u64 cpt_mmub;
++ __u8 cpt_dumpable;
++ __u8 cpt_vps_dumpable;
++ __u8 cpt_used_hugetlb;
++ __u8 __cpt_pad;
++ __u32 cpt_vdso;
++} __attribute__ ((aligned (8)));
++
++struct cpt_page_block
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++} __attribute__ ((aligned (8)));
++
++struct cpt_remappage_block
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++ __u64 cpt_pgoff;
++} __attribute__ ((aligned (8)));
++
++struct cpt_copypage_block
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++ __u64 cpt_source;
++} __attribute__ ((aligned (8)));
++
++struct cpt_lazypage_block
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++ __u64 cpt_index;
++} __attribute__ ((aligned (8)));
++
++struct cpt_iterpage_block
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++} __attribute__ ((aligned (8)));
++/* Followed by array of PFNs */
++
++struct cpt_vma_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_file;
++ __u32 cpt_type;
++#define CPT_VMA_TYPE_0 0
++#define CPT_VMA_TYPE_SHM 1
++#define CPT_VMA_VDSO 2
++ __u32 cpt_anonvma;
++ __u64 cpt_anonvmaid;
++
++ __u64 cpt_start;
++ __u64 cpt_end;
++ __u64 cpt_flags;
++ __u64 cpt_pgprot;
++ __u64 cpt_pgoff;
++} __attribute__ ((aligned (8)));
++
++struct cpt_aio_ctx_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_max_reqs;
++ __u32 cpt_ring_pages;
++ __u32 cpt_tail;
++ __u32 cpt_nr;
++ __u64 cpt_mmap_base;
++ /* Data (io_event's) and struct aio_ring are stored in user space VM */
++} __attribute__ ((aligned (8)));
++
++
++/* Format of MM section.
++ *
++ * It is array of MM objects (mm_struct). Each MM object is
++ * header, encoding mm_struct, followed by array of VMA objects.
++ * Each VMA consists of VMA header, encoding vm_area_struct, and
++ * if the VMA contains copied pages, the header is followed by
++ * array of tuples start-end each followed by data.
++ *
++ * ATTN: no block/page alignment. Only 64bit alignment. This might be not good?
++ */
++
++struct cpt_restart_block {
++ __u64 fn;
++#define CPT_RBL_0 0
++#define CPT_RBL_NANOSLEEP 1
++#define CPT_RBL_COMPAT_NANOSLEEP 2
++#define CPT_RBL_POLL 3
++#define CPT_RBL_FUTEX_WAIT 4
++ __u64 arg0;
++ __u64 arg1;
++ __u64 arg2;
++ __u64 arg3;
++} __attribute__ ((aligned (8)));
++
++struct cpt_siginfo_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_qflags;
++ __u32 cpt_signo;
++ __u32 cpt_errno;
++ __u32 cpt_code;
++
++ __u64 cpt_sigval;
++ __u32 cpt_pid;
++ __u32 cpt_uid;
++ __u64 cpt_utime;
++ __u64 cpt_stime;
++
++ __u64 cpt_user;
++} __attribute__ ((aligned (8)));
++
++/* Portable presentaions for segment registers */
++
++#define CPT_SEG_ZERO 0
++#define CPT_SEG_TLS1 1
++#define CPT_SEG_TLS2 2
++#define CPT_SEG_TLS3 3
++#define CPT_SEG_USER32_DS 4
++#define CPT_SEG_USER32_CS 5
++#define CPT_SEG_USER64_DS 6
++#define CPT_SEG_USER64_CS 7
++#define CPT_SEG_LDT 256
++
++struct cpt_x86_regs
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_debugreg[8];
++ __u32 cpt_fs;
++ __u32 cpt_gs;
++
++ __u32 cpt_ebx;
++ __u32 cpt_ecx;
++ __u32 cpt_edx;
++ __u32 cpt_esi;
++ __u32 cpt_edi;
++ __u32 cpt_ebp;
++ __u32 cpt_eax;
++ __u32 cpt_xds;
++ __u32 cpt_xes;
++ __u32 cpt_orig_eax;
++ __u32 cpt_eip;
++ __u32 cpt_xcs;
++ __u32 cpt_eflags;
++ __u32 cpt_esp;
++ __u32 cpt_xss;
++ __u32 pad;
++};
++
++struct cpt_x86_64_regs
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_debugreg[8];
++
++ __u64 cpt_fsbase;
++ __u64 cpt_gsbase;
++ __u32 cpt_fsindex;
++ __u32 cpt_gsindex;
++ __u32 cpt_ds;
++ __u32 cpt_es;
++
++ __u64 cpt_r15;
++ __u64 cpt_r14;
++ __u64 cpt_r13;
++ __u64 cpt_r12;
++ __u64 cpt_rbp;
++ __u64 cpt_rbx;
++ __u64 cpt_r11;
++ __u64 cpt_r10;
++ __u64 cpt_r9;
++ __u64 cpt_r8;
++ __u64 cpt_rax;
++ __u64 cpt_rcx;
++ __u64 cpt_rdx;
++ __u64 cpt_rsi;
++ __u64 cpt_rdi;
++ __u64 cpt_orig_rax;
++ __u64 cpt_rip;
++ __u64 cpt_cs;
++ __u64 cpt_eflags;
++ __u64 cpt_rsp;
++ __u64 cpt_ss;
++};
++
++struct cpt_ia64_regs
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 gr[128];
++ __u64 fr[256];
++ __u64 br[8];
++ __u64 nat[2];
++
++ __u64 ar_bspstore;
++ __u64 num_regs;
++ __u64 loadrs;
++ __u64 ar_bsp;
++ __u64 ar_unat;
++ __u64 ar_pfs;
++ __u64 ar_ccv;
++ __u64 ar_fpsr;
++ __u64 ar_csd;
++ __u64 ar_ssd;
++ __u64 ar_ec;
++ __u64 ar_lc;
++ __u64 ar_rsc;
++ __u64 ar_rnat;
++
++ __u64 cr_iip;
++ __u64 cr_ipsr;
++
++ __u64 cfm;
++ __u64 pr;
++
++ __u64 ibr[8];
++ __u64 dbr[8];
++};
++
++
++struct cpt_task_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_state;
++ __u64 cpt_flags;
++ __u64 cpt_ptrace;
++ __u32 cpt_prio;
++ __u32 cpt_static_prio;
++ __u32 cpt_policy;
++ __u32 cpt_rt_priority;
++
++ /* struct thread_info */
++ __u64 cpt_exec_domain;
++ __u64 cpt_thrflags;
++ __u64 cpt_thrstatus;
++ __u64 cpt_addr_limit;
++
++ __u64 cpt_personality;
++
++ __u64 cpt_mm;
++ __u64 cpt_files;
++ __u64 cpt_fs;
++ __u64 cpt_signal;
++ __u64 cpt_sighand;
++ __u64 cpt_sigblocked;
++ __u64 cpt_sigrblocked;
++ __u64 cpt_sigpending;
++ __u64 cpt_namespace;
++ __u64 cpt_sysvsem_undo;
++ __u32 cpt_pid;
++ __u32 cpt_tgid;
++ __u32 cpt_ppid;
++ __u32 cpt_rppid;
++ __u32 cpt_pgrp;
++ __u32 cpt_session;
++ __u32 cpt_old_pgrp;
++ __u32 __cpt_pad;
++ __u32 cpt_leader;
++ __u8 cpt_pn_state;
++ __u8 cpt_stopped_state;
++ __u8 cpt_sigsuspend_state;
++ __u8 cpt_64bit;
++ __u64 cpt_set_tid;
++ __u64 cpt_clear_tid;
++ __u32 cpt_exit_code;
++ __u32 cpt_exit_signal;
++ __u32 cpt_pdeath_signal;
++ __u32 cpt_user;
++ __u32 cpt_uid;
++ __u32 cpt_euid;
++ __u32 cpt_suid;
++ __u32 cpt_fsuid;
++ __u32 cpt_gid;
++ __u32 cpt_egid;
++ __u32 cpt_sgid;
++ __u32 cpt_fsgid;
++ __u32 cpt_ngids;
++ __u32 cpt_gids[32];
++ __u8 cpt_prctl_uac;
++ __u8 cpt_prctl_fpemu;
++ __u16 __cpt_pad1;
++ __u64 cpt_ecap;
++ __u64 cpt_icap;
++ __u64 cpt_pcap;
++ __u8 cpt_comm[16];
++ __u64 cpt_tls[3];
++ struct cpt_restart_block cpt_restart;
++ __u64 cpt_it_real_value; /* V8: jiffies, V9..: nsec */
++ __u64 cpt_it_real_incr; /* V8: jiffies, V9..: nsec */
++ __u64 cpt_it_prof_value;
++ __u64 cpt_it_prof_incr;
++ __u64 cpt_it_virt_value;
++ __u64 cpt_it_virt_incr;
++
++ __u16 cpt_used_math;
++ __u8 cpt_keepcap;
++ __u8 cpt_did_exec;
++ __u32 cpt_ptrace_message;
++
++ __u64 cpt_utime;
++ __u64 cpt_stime;
++ __u64 cpt_starttime; /* V8: jiffies, V9...: timespec */
++ __u64 cpt_nvcsw;
++ __u64 cpt_nivcsw;
++ __u64 cpt_min_flt;
++ __u64 cpt_maj_flt;
++
++ __u64 cpt_sigsuspend_blocked;
++ __u64 cpt_cutime, cpt_cstime;
++ __u64 cpt_cnvcsw, cpt_cnivcsw;
++ __u64 cpt_cmin_flt, cpt_cmaj_flt;
++
++#define CPT_RLIM_NLIMITS 16
++ __u64 cpt_rlim_cur[CPT_RLIM_NLIMITS];
++ __u64 cpt_rlim_max[CPT_RLIM_NLIMITS];
++
++ __u64 cpt_task_ub;
++ __u64 cpt_exec_ub;
++ __u64 cpt_mm_ub;
++ __u64 cpt_fork_sub;
++} __attribute__ ((aligned (8)));
++
++struct cpt_sigaltstack_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_stack;
++ __u32 cpt_stacksize;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++
++struct cpt_task_aux_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_robust_list;
++ __u64 __cpt_future[16];
++} __attribute__ ((aligned (8)));
++
++
++struct cpt_signal_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_leader;
++ __u8 cpt_pgrp_type;
++ __u8 cpt_old_pgrp_type;
++ __u8 cpt_session_type;
++#define CPT_PGRP_NORMAL 0
++#define CPT_PGRP_ORPHAN 1
++#define CPT_PGRP_STRAY 2
++ __u8 __cpt_pad1;
++ __u64 cpt_pgrp;
++ __u64 cpt_old_pgrp;
++ __u64 cpt_session;
++ __u64 cpt_sigpending;
++ __u64 cpt_ctty;
++
++ __u32 cpt_curr_target;
++ __u32 cpt_group_exit;
++ __u32 cpt_group_exit_code;
++ __u32 cpt_group_exit_task;
++ __u32 cpt_notify_count;
++ __u32 cpt_group_stop_count;
++ __u32 cpt_stop_state;
++ __u32 __cpt_pad2;
++
++ __u64 cpt_utime, cpt_stime, cpt_cutime, cpt_cstime;
++ __u64 cpt_nvcsw, cpt_nivcsw, cpt_cnvcsw, cpt_cnivcsw;
++ __u64 cpt_min_flt, cpt_maj_flt, cpt_cmin_flt, cpt_cmaj_flt;
++
++ __u64 cpt_rlim_cur[CPT_RLIM_NLIMITS];
++ __u64 cpt_rlim_max[CPT_RLIM_NLIMITS];
++} __attribute__ ((aligned (8)));
++/* Followed by list of posix timers. */
++
++struct cpt_sighand_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++} __attribute__ ((aligned (8)));
++/* Followed by list of sighandles. */
++
++struct cpt_sighandler_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_signo;
++ __u32 __cpt_pad1;
++ __u64 cpt_handler;
++ __u64 cpt_restorer;
++ __u64 cpt_flags;
++ __u64 cpt_mask;
++} __attribute__ ((aligned (8)));
++
++struct cpt_netdev_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_index;
++ __u32 cpt_flags;
++ __u8 cpt_name[16];
++} __attribute__ ((aligned (8)));
++
++struct cpt_tuntap_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_owner;
++ __u32 cpt_attached;
++ __u64 cpt_flags;
++ __u64 cpt_bindfile;
++ __u64 cpt_if_flags;
++ __u8 cpt_dev_addr[6];
++ __u16 cpt_pad;
++ __u32 cpt_chr_filter[2];
++ __u32 cpt_net_filter[2];
++} __attribute__ ((aligned (8)));
++
++struct cpt_veth_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_allow_mac_change;
++ __u32 __cpt_pad;
++} __attribute__ ((aligned (8)));
++
++struct cpt_hwaddr_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u8 cpt_dev_addr[32];
++} __attribute__ ((aligned (8)));
++
++struct cpt_netstats_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_rx_packets;
++ __u64 cpt_tx_packets;
++ __u64 cpt_rx_bytes;
++ __u64 cpt_tx_bytes;
++ __u64 cpt_rx_errors;
++ __u64 cpt_tx_errors;
++ __u64 cpt_rx_dropped;
++ __u64 cpt_tx_dropped;
++ __u64 cpt_multicast;
++ __u64 cpt_collisions;
++ __u64 cpt_rx_length_errors;
++ __u64 cpt_rx_over_errors;
++ __u64 cpt_rx_crc_errors;
++ __u64 cpt_rx_frame_errors;
++ __u64 cpt_rx_fifo_errors;
++ __u64 cpt_rx_missed_errors;
++ __u64 cpt_tx_aborted_errors;
++ __u64 cpt_tx_carrier_errors;
++ __u64 cpt_tx_fifo_errors;
++ __u64 cpt_tx_heartbeat_errors;
++ __u64 cpt_tx_window_errors;
++ __u64 cpt_rx_compressed;
++ __u64 cpt_tx_compressed;
++ __u64 pad[4];
++} __attribute__ ((aligned (8)));
++
++struct cpt_ifaddr_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_index;
++ __u8 cpt_family;
++ __u8 cpt_masklen;
++ __u8 cpt_flags;
++ __u8 cpt_scope;
++ __u32 cpt_address[4];
++ __u32 cpt_peer[4];
++ __u32 cpt_broadcast[4];
++ __u8 cpt_label[16];
++ __u32 cpt_valid_lft;
++ __u32 cpt_prefered_lft;
++} __attribute__ ((aligned (8)));
++
++struct cpt_ipct_tuple
++{
++ __u32 cpt_src;
++ __u16 cpt_srcport;
++ __u16 __cpt_pad1;
++
++ __u32 cpt_dst;
++ __u16 cpt_dstport;
++ __u8 cpt_protonum;
++ __u8 cpt_dir; /* TEMPORARY HACK TO VALIDATE CODE */
++} __attribute__ ((aligned (8)));
++
++struct cpt_nat_manip
++{
++ __u8 cpt_direction;
++ __u8 cpt_hooknum;
++ __u8 cpt_maniptype;
++ __u8 __cpt_pad1;
++
++ __u32 cpt_manip_addr;
++ __u16 cpt_manip_port;
++ __u16 __cpt_pad2;
++ __u32 __cpt_pad3;
++} __attribute__ ((aligned (8)));
++
++struct cpt_nat_seq
++{
++ __u32 cpt_correction_pos;
++ __u32 cpt_offset_before;
++ __u32 cpt_offset_after;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++
++struct cpt_ip_connexpect_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_timeout;
++ __u32 cpt_sibling_conntrack; /* Index of child conntrack */
++ __u32 cpt_seq; /* id in 2.6.15 */
++
++ struct cpt_ipct_tuple cpt_ct_tuple; /* NU 2.6.15 */
++ struct cpt_ipct_tuple cpt_tuple;
++ struct cpt_ipct_tuple cpt_mask;
++
++ /* union ip_conntrack_expect_help. Used by ftp, irc, amanda */
++ __u32 cpt_help[3]; /* NU 2.6.15 */
++ __u16 cpt_manip_proto;
++ __u8 cpt_dir;
++ __u8 cpt_flags;
++} __attribute__ ((aligned (8)));
++
++struct cpt_ip_conntrack_image
++{
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ struct cpt_ipct_tuple cpt_tuple[2];
++ __u64 cpt_status;
++ __u64 cpt_timeout;
++ __u32 cpt_index;
++ __u8 cpt_ct_helper;
++ __u8 cpt_nat_helper;
++ __u16 cpt_pad1;
++
++ /* union ip_conntrack_proto. Used by tcp and icmp. */
++ __u32 cpt_proto_data[12];
++
++ /* union ip_conntrack_help. Used by ftp and pptp helper.
++ * We do not support pptp...
++ */
++ __u32 cpt_help_data[6];
++
++ /* nat info */
++ __u32 cpt_initialized; /* NU 2.6.15 */
++ __u32 cpt_num_manips; /* NU 2.6.15 */
++ struct cpt_nat_manip cpt_nat_manips[6]; /* NU 2.6.15 */
++
++ struct cpt_nat_seq cpt_nat_seq[2];
++
++ __u32 cpt_masq_index;
++ __u32 cpt_id;
++ __u32 cpt_mark;
++} __attribute__ ((aligned (8)));
++
++struct cpt_ubparm
++{
++ __u64 barrier;
++ __u64 limit;
++ __u64 held;
++ __u64 maxheld;
++ __u64 minheld;
++ __u64 failcnt;
++} __attribute__ ((aligned (8)));
++
++struct cpt_beancounter_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u64 cpt_parent;
++ __u32 cpt_id;
++ __u32 __cpt_pad;
++ struct cpt_ubparm cpt_parms[32 * 2];
++} __attribute__ ((aligned (8)));
++
++struct cpt_slm_sgreg_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_size;
++ __u32 __cpt_pad1;
++ __u32 cpt_id;
++ __u16 cpt_resource;
++ __u8 cpt_regname[32];
++ __u8 __cpt_pad2[2];
++} __attribute__ ((aligned (8)));
++
++struct cpt_slm_obj_image {
++ __u64 cpt_next;
++ __u32 cpt_object;
++ __u16 cpt_hdrlen;
++ __u16 cpt_content;
++
++ __u32 cpt_size;
++ __u32 __cpt_pad1;
++} __attribute__ ((aligned (8)));
++
++#ifdef __KERNEL__
++
++static inline void __user * cpt_ptr_import(__u64 ptr)
++{
++ return (void*)(unsigned long)ptr;
++}
++
++static inline __u64 cpt_ptr_export(void __user *ptr)
++{
++ return (__u64)(unsigned long)ptr;
++}
++
++static inline void cpt_sigset_import(sigset_t *sig, __u64 ptr)
++{
++ memcpy(sig, &ptr, sizeof(*sig));
++}
++
++static inline __u64 cpt_sigset_export(sigset_t *sig)
++{
++ return *(__u64*)sig;
++}
++
++static inline __u64 cpt_timespec_export(struct timespec *tv)
++{
++ return (((u64)tv->tv_sec) << 32) + tv->tv_nsec;
++}
++
++static inline void cpt_timespec_import(struct timespec *tv, __u64 val)
++{
++ tv->tv_sec = val>>32;
++ tv->tv_nsec = (val&0xFFFFFFFF);
++}
++
++static inline __u64 cpt_timeval_export(struct timeval *tv)
++{
++ return (((u64)tv->tv_sec) << 32) + tv->tv_usec;
++}
++
++static inline void cpt_timeval_import(struct timeval *tv, __u64 val)
++{
++ tv->tv_sec = val>>32;
++ tv->tv_usec = (val&0xFFFFFFFF);
++}
++
++#endif
++
++#endif /* __CPT_IMAGE_H_ */
+diff --git a/include/linux/cpt_ioctl.h b/include/linux/cpt_ioctl.h
+new file mode 100644
+index 0000000..b8e83cc
+--- /dev/null
++++ b/include/linux/cpt_ioctl.h
+@@ -0,0 +1,43 @@
++/*
++ *
++ * include/linux/cpt_ioctl.h
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _CPT_IOCTL_H_
++#define _CPT_IOCTL_H_ 1
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++
++#define CPTCTLTYPE '-'
++#define CPT_SET_DUMPFD _IOW(CPTCTLTYPE, 1, int)
++#define CPT_SET_STATUSFD _IOW(CPTCTLTYPE, 2, int)
++#define CPT_SET_LOCKFD _IOW(CPTCTLTYPE, 3, int)
++#define CPT_SET_VEID _IOW(CPTCTLTYPE, 4, int)
++#define CPT_SUSPEND _IO(CPTCTLTYPE, 5)
++#define CPT_DUMP _IO(CPTCTLTYPE, 6)
++#define CPT_UNDUMP _IO(CPTCTLTYPE, 7)
++#define CPT_RESUME _IO(CPTCTLTYPE, 8)
++#define CPT_KILL _IO(CPTCTLTYPE, 9)
++#define CPT_JOIN_CONTEXT _IO(CPTCTLTYPE, 10)
++#define CPT_GET_CONTEXT _IOW(CPTCTLTYPE, 11, unsigned int)
++#define CPT_PUT_CONTEXT _IO(CPTCTLTYPE, 12)
++#define CPT_SET_PAGEINFDIN _IOW(CPTCTLTYPE, 13, int)
++#define CPT_SET_PAGEINFDOUT _IOW(CPTCTLTYPE, 14, int)
++#define CPT_PAGEIND _IO(CPTCTLTYPE, 15)
++#define CPT_VMPREP _IOW(CPTCTLTYPE, 16, int)
++#define CPT_SET_LAZY _IOW(CPTCTLTYPE, 17, int)
++#define CPT_SET_CPU_FLAGS _IOW(CPTCTLTYPE, 18, unsigned int)
++#define CPT_TEST_CAPS _IOW(CPTCTLTYPE, 19, unsigned int)
++#define CPT_TEST_VECAPS _IOW(CPTCTLTYPE, 20, unsigned int)
++#define CPT_SET_ERRORFD _IOW(CPTCTLTYPE, 21, int)
++
++#define CPT_ITER _IOW(CPTCTLTYPE, 23, int)
++
++#endif
+diff --git a/include/linux/dcache.h b/include/linux/dcache.h
+index efba1de..d66ceed 100644
+--- a/include/linux/dcache.h
++++ b/include/linux/dcache.h
+@@ -8,6 +8,8 @@
+ #include <linux/cache.h>
+ #include <linux/rcupdate.h>
+
++#include <bc/dcache.h>
++
+ struct nameidata;
+ struct path;
+ struct vfsmount;
+@@ -111,6 +113,9 @@ struct dentry {
+ struct dcookie_struct *d_cookie; /* cookie, if any */
+ #endif
+ int d_mounted;
++#ifdef CONFIG_BEANCOUNTERS
++ struct dentry_beancounter dentry_bc;
++#endif
+ unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
+ };
+
+@@ -174,9 +179,13 @@ d_iput: no no no yes
+
+ #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */
+ #define DCACHE_UNHASHED 0x0010
++#define DCACHE_VIRTUAL 0x0100 /* ve accessible */
++
++extern void mark_tree_virtual(struct path *path);
+
+ #define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */
+
++extern struct kmem_cache *dentry_cache;
+ extern spinlock_t dcache_lock;
+ extern seqlock_t rename_lock;
+
+@@ -304,6 +313,7 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
+ extern char *__d_path(const struct path *path, struct path *root, char *, int);
+ extern char *d_path(const struct path *, char *, int);
+ extern char *dentry_path(struct dentry *, char *, int);
++extern int d_root_check(struct path *path);
+
+ /* Allocation counts.. */
+
+@@ -323,6 +333,12 @@ extern char *dentry_path(struct dentry *, char *, int);
+ static inline struct dentry *dget(struct dentry *dentry)
+ {
+ if (dentry) {
++#ifdef CONFIG_BEANCOUNTERS
++ preempt_disable();
++ if (ub_dentry_on && ub_dget_testone(dentry))
++ BUG();
++ preempt_enable_no_resched();
++#endif
+ BUG_ON(!atomic_read(&dentry->d_count));
+ atomic_inc(&dentry->d_count);
+ }
+@@ -365,4 +381,5 @@ extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
+
+ extern int sysctl_vfs_cache_pressure;
+
++extern int check_area_access_ve(struct path *);
+ #endif /* __LINUX_DCACHE_H */
+diff --git a/include/linux/device.h b/include/linux/device.h
+index 4d8372d..08a186d 100644
+--- a/include/linux/device.h
++++ b/include/linux/device.h
+@@ -199,8 +199,16 @@ struct class {
+ struct class_private *p;
+ };
+
++#ifndef CONFIG_VE
+ extern struct kobject *sysfs_dev_block_kobj;
+ extern struct kobject *sysfs_dev_char_kobj;
++#define ve_sysfs_dev_block_kobj sysfs_dev_block_kobj
++#define ve_sysfs_dev_char_kobj sysfs_dev_char_kobj
++#else
++#define ve_sysfs_dev_block_kobj (get_exec_env()->dev_block_kobj)
++#define ve_sysfs_dev_char_kobj (get_exec_env()->dev_char_kobj)
++#endif
++
+ extern int __must_check __class_register(struct class *class,
+ struct lock_class_key *key);
+ extern void class_unregister(struct class *class);
+@@ -250,6 +258,15 @@ extern struct class * __must_check __class_create(struct module *owner,
+ struct lock_class_key *key);
+ extern void class_destroy(struct class *cls);
+
++extern struct class net_class;
++extern struct kset *class_kset;
++
++int classes_init(void);
++void classes_fini(void);
++
++int devices_init(void);
++void devices_fini(void);
++
+ /* This is a #define to keep the compiler from merging different
+ * instances of the __key variable */
+ #define class_create(owner, name) \
+diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h
+index 154769c..ee767ed 100644
+--- a/include/linux/devpts_fs.h
++++ b/include/linux/devpts_fs.h
+@@ -23,6 +23,16 @@ int devpts_pty_new(struct tty_struct *tty); /* mknod in devpts */
+ struct tty_struct *devpts_get_tty(int number); /* get tty structure */
+ void devpts_pty_kill(int number); /* unlink */
+
++struct devpts_config {
++ int setuid;
++ int setgid;
++ uid_t uid;
++ gid_t gid;
++ umode_t mode;
++};
++
++extern struct devpts_config devpts_config;
++extern struct file_system_type devpts_fs_type;
+ #else
+
+ /* Dummy stubs in the no-pty case */
+diff --git a/include/linux/elevator.h b/include/linux/elevator.h
+index 639624b..be231eb 100644
+--- a/include/linux/elevator.h
++++ b/include/linux/elevator.h
+@@ -56,6 +56,11 @@ struct elevator_ops
+ elevator_init_fn *elevator_init_fn;
+ elevator_exit_fn *elevator_exit_fn;
+ void (*trim)(struct io_context *);
++ /* In original cfq design task holds a cfqq refcount and puts it
++ * on exit via io context. Now async cfqqs are hold by UB,
++ * so we need somehow to put these queues. Use this function.
++ */
++ void (*put_queue)(struct cfq_queue *);
+ };
+
+ #define ELV_NAME_MAX (16)
+diff --git a/include/linux/elf.h b/include/linux/elf.h
+index edc3dac..079ffc7 100644
+--- a/include/linux/elf.h
++++ b/include/linux/elf.h
+@@ -404,4 +404,6 @@ extern int elf_coredump_extra_notes_size(void);
+ extern int elf_coredump_extra_notes_write(struct file *file, loff_t *foffset);
+ #endif
+
++extern int sysctl_at_vsyscall;
++
+ #endif /* _LINUX_ELF_H */
+diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h
+index f1e1d3c..9ac8dd6 100644
+--- a/include/linux/eventpoll.h
++++ b/include/linux/eventpoll.h
+@@ -17,6 +17,7 @@
+ /* For O_CLOEXEC */
+ #include <linux/fcntl.h>
+ #include <linux/types.h>
++#include <linux/fs.h>
+
+ /* Flags for epoll_create1. */
+ #define EPOLL_CLOEXEC O_CLOEXEC
+@@ -64,6 +65,88 @@ static inline void eventpoll_init_file(struct file *file)
+ spin_lock_init(&file->f_ep_lock);
+ }
+
++struct epoll_filefd {
++ struct file *file;
++ int fd;
++};
++
++/*
++ * This structure is stored inside the "private_data" member of the file
++ * structure and rapresent the main data sructure for the eventpoll
++ * interface.
++ */
++struct eventpoll {
++ /* Protect the this structure access */
++ spinlock_t lock;
++
++ /*
++ * This mutex is used to ensure that files are not removed
++ * while epoll is using them. This is held during the event
++ * collection loop, the file cleanup path, the epoll file exit
++ * code and the ctl operations.
++ */
++ struct mutex mtx;
++
++ /* Wait queue used by sys_epoll_wait() */
++ wait_queue_head_t wq;
++
++ /* Wait queue used by file->poll() */
++ wait_queue_head_t poll_wait;
++
++ /* List of ready file descriptors */
++ struct list_head rdllist;
++
++ /* RB tree root used to store monitored fd structs */
++ struct rb_root rbr;
++
++ /*
++ * This is a single linked list that chains all the "struct epitem" that
++ * happened while transfering ready events to userspace w/out
++ * holding ->lock.
++ */
++ struct epitem *ovflist;
++};
++
++/*
++ * Each file descriptor added to the eventpoll interface will
++ * have an entry of this type linked to the "rbr" RB tree.
++ */
++struct epitem {
++ /* RB tree node used to link this structure to the eventpoll RB tree */
++ struct rb_node rbn;
++
++ /* List header used to link this structure to the eventpoll ready list */
++ struct list_head rdllink;
++
++ /*
++ * Works together "struct eventpoll"->ovflist in keeping the
++ * single linked chain of items.
++ */
++ struct epitem *next;
++
++ /* The file descriptor information this item refers to */
++ struct epoll_filefd ffd;
++
++ /* Number of active wait queue attached to poll operations */
++ int nwait;
++
++ /* List containing poll wait queues */
++ struct list_head pwqlist;
++
++ /* The "container" of this item */
++ struct eventpoll *ep;
++
++ /* List header used to link this item to the "struct file" items list */
++ struct list_head fllink;
++
++ /* The structure that describe the interested events and the source fd */
++ struct epoll_event event;
++};
++
++extern struct semaphore epsem;
++struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd);
++int ep_insert(struct eventpoll *ep, struct epoll_event *event,
++ struct file *tfile, int fd);
+
+ /* Used to release the epoll bits inside the "struct file" */
+ void eventpoll_release_file(struct file *file);
+@@ -96,6 +179,8 @@ static inline void eventpoll_release(struct file *file)
+ eventpoll_release_file(file);
+ }
+
++extern struct mutex epmutex;
++
+ #else
+
+ static inline void eventpoll_init_file(struct file *file) {}
+diff --git a/include/linux/fairsched.h b/include/linux/fairsched.h
+new file mode 100644
+index 0000000..e08c84d
+--- /dev/null
++++ b/include/linux/fairsched.h
+@@ -0,0 +1,86 @@
++/*
++ * Fair Scheduler
++ *
++ * Copyright (C) 2000-2008 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __LINUX_FAIRSCHED_H__
++#define __LINUX_FAIRSCHED_H__
++
++#define FAIRSCHED_SET_RATE 0
++#define FAIRSCHED_DROP_RATE 1
++#define FAIRSCHED_GET_RATE 2
++
++#ifdef __KERNEL__
++
++/* refcnt change protected with tasklist write lock */
++struct fairsched_node {
++ struct task_group *tg;
++ int refcnt;
++ unsigned id;
++ struct list_head nodelist;
++
++ unsigned weight;
++ unsigned char rate_limited;
++ unsigned rate;
++#ifdef CONFIG_VE
++ struct ve_struct *owner_env;
++#endif
++};
++
++#ifdef CONFIG_VZ_FAIRSCHED
++
++#define FAIRSCHED_INIT_NODE_ID INT_MAX
++
++extern struct fairsched_node fairsched_init_node;
++
++void fairsched_init_early(void);
++void fairsched_init_late(void);
++
++static inline int task_fairsched_node_id(struct task_struct *p)
++{
++ return p->fsched_node->id;
++}
++
++/* must called with tasklist write locked */
++static inline void get_task_fairsched_node(struct task_struct *p)
++{
++ p->fsched_node->refcnt++;
++}
++static inline void put_task_fairsched_node(struct task_struct *p)
++{
++ p->fsched_node->refcnt--;
++}
++
++#define INIT_VZ_FAIRSCHED .fsched_node = &fairsched_init_node,
++
++#define FSCHWEIGHT_MAX ((1 << 16) - 1)
++#define FSCHRATE_SHIFT 10
++#define FSCH_TIMESLICE 16
++
++asmlinkage int sys_fairsched_mknod(unsigned int parent, unsigned int weight,
++ unsigned int newid);
++asmlinkage int sys_fairsched_rmnod(unsigned int id);
++asmlinkage int sys_fairsched_mvpr(pid_t pid, unsigned int nodeid);
++asmlinkage int sys_fairsched_vcpus(unsigned int id, unsigned int vcpus);
++asmlinkage int sys_fairsched_chwt(unsigned int id, unsigned int weight);
++asmlinkage int sys_fairsched_rate(unsigned int id, int op, unsigned rate);
++
++#else /* CONFIG_VZ_FAIRSCHED */
++
++static inline void fairsched_init_early(void) { }
++static inline void fairsched_init_late(void) { }
++static inline int task_fairsched_node_id(struct task_struct *p) { return 0; }
++static inline void get_task_fairsched_node(struct task_struct *p) { }
++static inline void put_task_fairsched_node(struct task_struct *p) { }
++
++#define INIT_VZ_FAIRSCHED
++
++#endif /* CONFIG_VZ_FAIRSCHED */
++#endif /* __KERNEL__ */
++
++#endif /* __LINUX_FAIRSCHED_H__ */
+diff --git a/include/linux/faudit.h b/include/linux/faudit.h
+new file mode 100644
+index 0000000..631c42e
+--- /dev/null
++++ b/include/linux/faudit.h
+@@ -0,0 +1,45 @@
++/*
++ * include/linux/faudit.h
++ *
++ * Copyright (C) 2005 SWSoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __FAUDIT_H_
++#define __FAUDIT_H_
++
++#include <linux/virtinfo.h>
++
++struct vfsmount;
++struct dentry;
++struct super_block;
++struct kstatfs;
++struct kstat;
++struct pt_regs;
++
++struct faudit_regs_arg {
++ int err;
++ struct pt_regs *regs;
++};
++
++struct faudit_stat_arg {
++ int err;
++ struct vfsmount *mnt;
++ struct dentry *dentry;
++ struct kstat *stat;
++};
++
++struct faudit_statfs_arg {
++ int err;
++ struct super_block *sb;
++ struct kstatfs *stat;
++};
++
++#define VIRTINFO_FAUDIT (0)
++#define VIRTINFO_FAUDIT_STAT (VIRTINFO_FAUDIT + 0)
++#define VIRTINFO_FAUDIT_STATFS (VIRTINFO_FAUDIT + 1)
++
++#endif
+diff --git a/include/linux/fs.h b/include/linux/fs.h
+index 580b513..a612846 100644
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -50,6 +50,7 @@ extern struct inodes_stat_t inodes_stat;
+
+ extern int leases_enable, lease_break_time;
+
++extern int odirect_enable;
+ #ifdef CONFIG_DNOTIFY
+ extern int dir_notify_enable;
+ #endif
+@@ -62,6 +63,7 @@ extern int dir_notify_enable;
+ #define MAY_APPEND 8
+ #define MAY_ACCESS 16
+ #define MAY_OPEN 32
++#define MAY_QUOTACTL 16 /* for devgroup-vs-openvz only */
+
+ #define FMODE_READ 1
+ #define FMODE_WRITE 2
+@@ -70,6 +72,7 @@ extern int dir_notify_enable;
+ #define FMODE_LSEEK 4
+ #define FMODE_PREAD 8
+ #define FMODE_PWRITE FMODE_PREAD /* These go hand in hand */
++#define FMODE_QUOTACTL 4
+
+ /* File is being opened for execution. Primary users of this flag are
+ distributed filesystems that can use it to achieve correct ETXTBUSY
+@@ -96,6 +99,8 @@ extern int dir_notify_enable;
+ #define FS_REQUIRES_DEV 1
+ #define FS_BINARY_MOUNTDATA 2
+ #define FS_HAS_SUBTYPE 4
++#define FS_VIRTUALIZED 64 /* Can mount this fstype inside ve */
++#define FS_MANGLE_PROC 128 /* hide some /proc/mounts info inside VE */
+ #define FS_REVAL_DOT 16384 /* Check the paths ".", ".." for staleness */
+ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move()
+ * during rename() internally.
+@@ -369,6 +374,9 @@ struct iattr {
+ * Includes for diskquotas.
+ */
+ #include <linux/quota.h>
++#if defined(CONFIG_VZ_QUOTA) || defined(CONFIG_VZ_QUOTA_MODULE)
++#include <linux/vzquota_qlnk.h>
++#endif
+
+ /**
+ * enum positive_aop_returns - aop return codes with specific semantics
+@@ -651,6 +659,9 @@ struct inode {
+ #ifdef CONFIG_QUOTA
+ struct dquot *i_dquot[MAXQUOTAS];
+ #endif
++#if defined(CONFIG_VZ_QUOTA) || defined(CONFIG_VZ_QUOTA_MODULE)
++ struct vz_quota_ilink i_qlnk;
++#endif
+ struct list_head i_devices;
+ union {
+ struct pipe_inode_info *i_pipe;
+@@ -706,6 +717,8 @@ enum inode_i_mutex_lock_class
+ extern void inode_double_lock(struct inode *inode1, struct inode *inode2);
+ extern void inode_double_unlock(struct inode *inode1, struct inode *inode2);
+
++extern struct kmem_cache *inode_cachep;
++
+ /*
+ * NOTE: in a 32bit arch with a preemptable kernel and
+ * an UP compile the i_size_read/write must be atomic
+@@ -825,6 +838,7 @@ struct file {
+ struct fown_struct f_owner;
+ unsigned int f_uid, f_gid;
+ struct file_ra_state f_ra;
++ struct user_beancounter *f_ub;
+
+ u64 f_version;
+ #ifdef CONFIG_SECURITY
+@@ -842,6 +856,7 @@ struct file {
+ #ifdef CONFIG_DEBUG_WRITECOUNT
+ unsigned long f_mnt_write_state;
+ #endif
++ struct ve_struct *owner_env;
+ };
+ extern spinlock_t files_lock;
+ #define file_list_lock() spin_lock(&files_lock);
+@@ -952,6 +967,9 @@ struct file_lock {
+ fl_owner_t fl_owner;
+ unsigned char fl_flags;
+ unsigned char fl_type;
++#ifdef CONFIG_BEANCOUNTERS
++ unsigned char fl_charged;
++#endif
+ unsigned int fl_pid;
+ struct pid *fl_nspid;
+ wait_queue_head_t fl_wait;
+@@ -1260,6 +1278,7 @@ struct file_operations {
+ ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
+ ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
+ int (*setlease)(struct file *, long, struct file_lock **);
++ struct file * (*get_host)(struct file *);
+ };
+
+ struct inode_operations {
+@@ -1326,6 +1345,7 @@ struct super_operations {
+ #ifdef CONFIG_QUOTA
+ ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
+ ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
++ struct inode *(*get_quota_root)(struct super_block *);
+ #endif
+ };
+
+@@ -1502,8 +1522,14 @@ struct file_system_type {
+ struct lock_class_key i_mutex_key;
+ struct lock_class_key i_mutex_dir_key;
+ struct lock_class_key i_alloc_sem_key;
++
++ struct file_system_type *proto;
++ struct ve_struct *owner_env;
+ };
+
++void get_filesystem(struct file_system_type *fs);
++void put_filesystem(struct file_system_type *fs);
++
+ extern int get_sb_bdev(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ int (*fill_super)(struct super_block *, void *, int),
+@@ -1543,6 +1569,11 @@ extern int register_filesystem(struct file_system_type *);
+ extern int unregister_filesystem(struct file_system_type *);
+ extern struct vfsmount *kern_mount_data(struct file_system_type *, void *data);
+ #define kern_mount(type) kern_mount_data(type, NULL)
++extern int register_ve_fs_type(struct ve_struct *, struct file_system_type *,
++ struct file_system_type **, struct vfsmount **);
++extern void unregister_ve_fs_type(struct file_system_type *, struct vfsmount *);
++extern void umount_ve_fs_type(struct file_system_type *local_fs_type);
++#define kern_umount mntput
+ extern int may_umount_tree(struct vfsmount *);
+ extern int may_umount(struct vfsmount *);
+ extern long do_mount(char *, char *, char *, unsigned long, void *);
+@@ -1550,6 +1581,7 @@ extern struct vfsmount *collect_mounts(struct vfsmount *, struct dentry *);
+ extern void drop_collected_mounts(struct vfsmount *);
+
+ extern int vfs_statfs(struct dentry *, struct kstatfs *);
++extern int faudit_statfs(struct super_block *, struct kstatfs *);
+
+ /* /sys/fs */
+ extern struct kobject *fs_kobj;
+@@ -1722,7 +1754,8 @@ extern int check_disk_change(struct block_device *);
+ extern int __invalidate_device(struct block_device *);
+ extern int invalidate_partition(struct gendisk *, int);
+ #endif
+-extern int invalidate_inodes(struct super_block *);
++extern int invalidate_inodes_check(struct super_block *, int check);
++#define invalidate_inodes(sb) invalidate_inodes_check(sb, 0)
+ unsigned long __invalidate_mapping_pages(struct address_space *mapping,
+ pgoff_t start, pgoff_t end,
+ bool be_atomic);
+@@ -2147,6 +2180,17 @@ static inline void free_secdata(void *secdata)
+ { }
+ #endif /* CONFIG_SECURITY */
+
++static inline void *file_private(struct file *file)
++{
++ struct file *host = file;
++
++ while (host->f_op->get_host) {
++ host = host->f_op->get_host(host);
++ BUG_ON(host->f_mapping != file->f_mapping);
++ }
++ return host->private_data;
++}
++
+ struct ctl_table;
+ int proc_nr_files(struct ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
+diff --git a/include/linux/futex.h b/include/linux/futex.h
+index 586ab56..9bf4c37 100644
+--- a/include/linux/futex.h
++++ b/include/linux/futex.h
+@@ -124,7 +124,7 @@ struct robust_list_head {
+ #ifdef __KERNEL__
+ long do_futex(u32 __user *uaddr, int op, u32 val, union ktime *timeout,
+ u32 __user *uaddr2, u32 val2, u32 val3);
+-
++long futex_wait_restart(struct restart_block *restart);
+ extern int
+ handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi);
+
+diff --git a/include/linux/gfp.h b/include/linux/gfp.h
+index e8003af..4302d3b 100644
+--- a/include/linux/gfp.h
++++ b/include/linux/gfp.h
+@@ -50,20 +50,25 @@ struct vm_area_struct;
+ #define __GFP_THISNODE ((__force gfp_t)0x40000u)/* No fallback, no policies */
+ #define __GFP_RECLAIMABLE ((__force gfp_t)0x80000u) /* Page is reclaimable */
+ #define __GFP_MOVABLE ((__force gfp_t)0x100000u) /* Page is movable */
++#define __GFP_UBC ((__force gfp_t)0x200000u)/* charge kmem in buddy and slab */
++#define __GFP_SOFT_UBC ((__force gfp_t)0x400000u)/* use soft charging */
+
+-#define __GFP_BITS_SHIFT 21 /* Room for 21 __GFP_FOO bits */
++#define __GFP_BITS_SHIFT 23 /* Room for __GFP_FOO bits */
+ #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
+
+ /* This equals 0, but use constants in case they ever change */
+ #define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH)
+ /* GFP_ATOMIC means both !wait (__GFP_WAIT not set) and use emergency pool */
+ #define GFP_ATOMIC (__GFP_HIGH)
++#define GFP_ATOMIC_UBC (__GFP_HIGH | __GFP_UBC)
+ #define GFP_NOIO (__GFP_WAIT)
+ #define GFP_NOFS (__GFP_WAIT | __GFP_IO)
+ #define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
++#define GFP_KERNEL_UBC (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_UBC)
+ #define GFP_TEMPORARY (__GFP_WAIT | __GFP_IO | __GFP_FS | \
+ __GFP_RECLAIMABLE)
+ #define GFP_USER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
++#define GFP_USER_UBC (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | __GFP_UBC)
+ #define GFP_HIGHUSER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \
+ __GFP_HIGHMEM)
+ #define GFP_HIGHUSER_MOVABLE (__GFP_WAIT | __GFP_IO | __GFP_FS | \
+diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
+index 181006c..5d48dcb 100644
+--- a/include/linux/hardirq.h
++++ b/include/linux/hardirq.h
+@@ -7,6 +7,9 @@
+ #include <asm/hardirq.h>
+ #include <asm/system.h>
+
++#include <bc/task.h>
++#include <linux/ve_task.h>
++
+ /*
+ * We put the hardirq and softirq counter into the preemption
+ * counter. The bitmask has the following meaning:
+@@ -126,6 +129,24 @@ extern void rcu_irq_exit(void);
+ # define rcu_irq_exit() do { } while (0)
+ #endif /* CONFIG_PREEMPT_RCU */
+
++#define save_context() do { \
++ struct task_struct *tsk; \
++ if (hardirq_count() == HARDIRQ_OFFSET) { \
++ tsk = current; \
++ ve_save_context(tsk); \
++ ub_save_context(tsk); \
++ } \
++ } while (0)
++
++#define restore_context() do { \
++ struct task_struct *tsk; \
++ if (hardirq_count() == HARDIRQ_OFFSET) { \
++ tsk = current; \
++ ve_restore_context(tsk); \
++ ub_restore_context(tsk); \
++ } \
++ } while (0)
++
+ /*
+ * It is safe to do non-atomic ops on ->hardirq_context,
+ * because NMI handlers may not preempt and the ops are
+@@ -137,6 +158,7 @@ extern void rcu_irq_exit(void);
+ rcu_irq_enter(); \
+ account_system_vtime(current); \
+ add_preempt_count(HARDIRQ_OFFSET); \
++ save_context(); \
+ trace_hardirq_enter(); \
+ } while (0)
+
+@@ -152,6 +174,7 @@ extern void irq_enter(void);
+ do { \
+ trace_hardirq_exit(); \
+ account_system_vtime(current); \
++ restore_context(); \
+ sub_preempt_count(HARDIRQ_OFFSET); \
+ rcu_irq_exit(); \
+ } while (0)
+diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
+index 2f245fe..84a4828 100644
+--- a/include/linux/hrtimer.h
++++ b/include/linux/hrtimer.h
+@@ -352,6 +352,9 @@ extern long hrtimer_nanosleep(struct timespec *rqtp,
+ const enum hrtimer_mode mode,
+ const clockid_t clockid);
+ extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);
++#ifdef CONFIG_COMPAT
++long compat_nanosleep_restart(struct restart_block *restart);
++#endif
+
+ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
+ struct task_struct *tsk);
+diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
+index 6badb3e..50c628d 100644
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -42,6 +42,7 @@
+ #define BRCTL_SET_PORT_PRIORITY 16
+ #define BRCTL_SET_PATH_COST 17
+ #define BRCTL_GET_FDB_ENTRIES 18
++#define BRCTL_SET_VIA_ORIG_DEV 19
+
+ #define BR_STATE_DISABLED 0
+ #define BR_STATE_LISTENING 1
+@@ -70,6 +71,7 @@ struct __bridge_info
+ __u32 tcn_timer_value;
+ __u32 topology_change_timer_value;
+ __u32 gc_timer_value;
++ __u8 via_phys_dev;
+ };
+
+ struct __port_info
+@@ -104,9 +106,12 @@ struct __fdb_entry
+
+ #include <linux/netdevice.h>
+
++#define BR_ALREADY_SEEN 1
++
+ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
+ extern struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
+ struct sk_buff *skb);
++extern int (*br_hard_xmit_hook)(struct sk_buff *skb, struct net_bridge_port *port);
+ extern int (*br_should_route_hook)(struct sk_buff *skb);
+
+ #endif
+diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
+index 8529f57..68d3962 100644
+--- a/include/linux/if_tun.h
++++ b/include/linux/if_tun.h
+@@ -82,4 +82,44 @@ struct tun_filter {
+ __u8 addr[0][ETH_ALEN];
+ };
+
++struct sk_buff_head;
++
++#define FLT_EXACT_COUNT 8
++struct tap_filter {
++ unsigned int count; /* Number of addrs. Zero means disabled */
++ u32 mask[2]; /* Mask of the hashed addrs */
++ unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN];
++};
++
++struct tun_struct {
++ struct list_head list;
++ unsigned int flags;
++ int attached;
++ uid_t owner;
++ gid_t group;
++
++ wait_queue_head_t read_wait;
++ struct sk_buff_head readq;
++
++ struct net_device *dev;
++ struct fasync_struct *fasync;
++ struct file *bind_file;
++
++ struct tap_filter txflt;
++
++#ifdef TUN_DEBUG
++ int debug;
++#endif
++};
++
++struct tun_net {
++ struct list_head dev_list;
++};
++
++extern int tun_net_open(struct net_device *dev);
++extern int tun_chr_open(struct inode *inode, struct file * file);
++extern void tun_net_init(struct net_device *dev);
++extern void tun_setup(struct net_device *dev);
++extern struct list_head tun_dev_list;
++
+ #endif /* __IF_TUN_H */
+diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
+index 9e7b49b..3dbff65 100644
+--- a/include/linux/if_vlan.h
++++ b/include/linux/if_vlan.h
+@@ -84,6 +84,9 @@ struct vlan_group {
+ struct hlist_node hlist; /* linked list */
+ struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS];
+ struct rcu_head rcu;
++#ifdef CONFIG_VE
++ struct ve_struct *owner;
++#endif
+ };
+
+ static inline struct net_device *vlan_group_get_device(struct vlan_group *vg,
+diff --git a/include/linux/init_task.h b/include/linux/init_task.h
+index 021d8e7..76babc9 100644
+--- a/include/linux/init_task.h
++++ b/include/linux/init_task.h
+@@ -10,6 +10,7 @@
+ #include <linux/user_namespace.h>
+ #include <linux/securebits.h>
+ #include <net/net_namespace.h>
++#include <linux/fairsched.h>
+
+ extern struct files_struct init_files;
+
+@@ -49,10 +50,17 @@ extern struct files_struct init_files;
+ .rlim = INIT_RLIMITS, \
+ }
+
++#ifdef CONFIG_VE
++/* one for ve0, one for init_task */
++#define INIT_NSPROXY_COUNT ATOMIC_INIT(2)
++#else
++#define INIT_NSPROXY_COUNT ATOMIC_INIT(1)
++#endif
++
+ extern struct nsproxy init_nsproxy;
+ #define INIT_NSPROXY(nsproxy) { \
+ .pid_ns = &init_pid_ns, \
+- .count = ATOMIC_INIT(1), \
++ .count = INIT_NSPROXY_COUNT, \
+ .uts_ns = &init_uts_ns, \
+ .mnt_ns = NULL, \
+ INIT_NET_NS(net_ns) \
+@@ -179,6 +187,7 @@ extern struct group_info init_groups;
+ INIT_IDS \
+ INIT_TRACE_IRQFLAGS \
+ INIT_LOCKDEP \
++ INIT_VZ_FAIRSCHED \
+ }
+
+
+diff --git a/include/linux/inotify.h b/include/linux/inotify.h
+index bd57857..8833215 100644
+--- a/include/linux/inotify.h
++++ b/include/linux/inotify.h
+@@ -73,6 +73,7 @@ struct inotify_event {
+
+ #include <linux/dcache.h>
+ #include <linux/fs.h>
++#include <linux/idr.h>
+
+ /*
+ * struct inotify_watch - represents a watch request on a specific inode
+@@ -90,6 +91,7 @@ struct inotify_watch {
+ struct list_head i_list; /* entry in inode's list */
+ atomic_t count; /* reference count */
+ struct inotify_handle *ih; /* associated inotify handle */
++ struct path path;
+ struct inode *inode; /* associated inode */
+ __s32 wd; /* watch descriptor */
+ __u32 mask; /* event mask for this watch */
+@@ -126,6 +128,8 @@ extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *,
+ u32);
+ extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *,
+ struct inode *, __u32);
++extern __s32 inotify_add_watch_dget(struct inotify_handle *, struct inotify_watch *,
++ struct path *, __u32);
+ extern __s32 inotify_clone_watch(struct inotify_watch *, struct inotify_watch *);
+ extern void inotify_evict_watch(struct inotify_watch *);
+ extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *);
+@@ -135,6 +139,66 @@ extern void inotify_remove_watch_locked(struct inotify_handle *,
+ extern void get_inotify_watch(struct inotify_watch *);
+ extern void put_inotify_watch(struct inotify_watch *);
+
++/*
++ * struct inotify_handle - represents an inotify instance
++ *
++ * This structure is protected by the mutex 'mutex'.
++ */
++struct inotify_handle {
++ struct idr idr; /* idr mapping wd -> watch */
++ struct mutex mutex; /* protects this bad boy */
++ struct list_head watches; /* list of watches */
++ atomic_t count; /* reference count */
++ u32 last_wd; /* the last wd allocated */
++ const struct inotify_operations *in_ops; /* inotify caller operations */
++};
++
++
++/*
++ * struct inotify_device - represents an inotify instance
++ *
++ * This structure is protected by the mutex 'mutex'.
++ */
++struct inotify_device {
++ wait_queue_head_t wq; /* wait queue for i/o */
++ struct mutex ev_mutex; /* protects event queue */
++ struct mutex up_mutex; /* synchronizes watch updates */
++ struct list_head events; /* list of queued events */
++ atomic_t count; /* reference count */
++ struct user_struct *user; /* user who opened this dev */
++ struct inotify_handle *ih; /* inotify handle */
++ struct fasync_struct *fa; /* async notification */
++ unsigned int queue_size; /* size of the queue (bytes) */
++ unsigned int event_count; /* number of pending events */
++ unsigned int max_events; /* maximum number of events */
++};
++
++/*
++ * struct inotify_kernel_event - An inotify event, originating from a watch and
++ * queued for user-space. A list of these is attached to each instance of the
++ * device. In read(), this list is walked and all events that can fit in the
++ * buffer are returned.
++ *
++ * Protected by dev->ev_mutex of the device in which we are queued.
++ */
++struct inotify_kernel_event {
++ struct inotify_event event; /* the user-space event */
++ struct list_head list; /* entry in inotify_device's list */
++ char *name; /* filename, if any */
++};
++
++/*
++ * struct inotify_user_watch - our version of an inotify_watch, we add
++ * a reference to the associated inotify_device.
++ */
++struct inotify_user_watch {
++ struct inotify_device *dev; /* associated device */
++ struct inotify_watch wdata; /* inotify watch data */
++};
++
++int inotify_create_watch(struct inotify_device *dev, struct path *p, u32 mask);
++
++
+ #else
+
+ static inline void inotify_d_instantiate(struct dentry *dentry,
+@@ -204,6 +268,13 @@ static inline __s32 inotify_add_watch(struct inotify_handle *ih,
+ return -EOPNOTSUPP;
+ }
+
++static inline __s32 inotify_add_watch_dget(struct inotify_handle *h,
++ struct inotify_watch *w,
++ struct path *p, __u32 mask)
++{
++ return -EOPNOTSUPP;
++}
++
+ static inline int inotify_rm_watch(struct inotify_handle *ih,
+ struct inotify_watch *watch)
+ {
+diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h
+index f98a656..2d86ade 100644
+--- a/include/linux/ioprio.h
++++ b/include/linux/ioprio.h
+@@ -39,6 +39,7 @@ enum {
+ IOPRIO_WHO_PROCESS = 1,
+ IOPRIO_WHO_PGRP,
+ IOPRIO_WHO_USER,
++ IOPRIO_WHO_UBC = 1000,
+ };
+
+ /*
+diff --git a/include/linux/ipc.h b/include/linux/ipc.h
+index b882610..67d186c 100644
+--- a/include/linux/ipc.h
++++ b/include/linux/ipc.h
+@@ -81,6 +81,7 @@ struct ipc_kludge {
+
+ #include <linux/kref.h>
+ #include <linux/spinlock.h>
++#include <linux/rcupdate.h>
+
+ #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */
+
+@@ -100,6 +101,15 @@ struct kern_ipc_perm
+ void *security;
+ };
+
++struct ipc_ids;
++
++struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
++static inline void ipc_unlock(struct kern_ipc_perm *perm)
++{
++ spin_unlock(&perm->lock);
++ rcu_read_unlock();
++}
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_IPC_H */
+diff --git a/include/linux/kdev_t.h b/include/linux/kdev_t.h
+index 2dacab8..91783a7 100644
+--- a/include/linux/kdev_t.h
++++ b/include/linux/kdev_t.h
+@@ -87,6 +87,57 @@ static inline unsigned sysv_minor(u32 dev)
+ return dev & 0x3ffff;
+ }
+
++#define UNNAMED_MAJOR_COUNT 16
++
++#if UNNAMED_MAJOR_COUNT > 1
++
++extern int unnamed_dev_majors[UNNAMED_MAJOR_COUNT];
++
++static inline dev_t make_unnamed_dev(int idx)
++{
++ /*
++ * Here we transfer bits from 8 to 8+log2(UNNAMED_MAJOR_COUNT) of the
++ * unnamed device index into major number.
++ */
++ return MKDEV(unnamed_dev_majors[(idx >> 8) & (UNNAMED_MAJOR_COUNT - 1)],
++ idx & ~((UNNAMED_MAJOR_COUNT - 1) << 8));
++}
++
++static inline int unnamed_dev_idx(dev_t dev)
++{
++ int i;
++ for (i = 0; i < UNNAMED_MAJOR_COUNT &&
++ MAJOR(dev) != unnamed_dev_majors[i]; i++);
++ return MINOR(dev) | (i << 8);
++}
++
++static inline int is_unnamed_dev(dev_t dev)
++{
++ int i;
++ for (i = 0; i < UNNAMED_MAJOR_COUNT &&
++ MAJOR(dev) != unnamed_dev_majors[i]; i++);
++ return i < UNNAMED_MAJOR_COUNT;
++}
++
++#else /* UNNAMED_MAJOR_COUNT */
++
++static inline dev_t make_unnamed_dev(int idx)
++{
++ return MKDEV(0, idx);
++}
++
++static inline int unnamed_dev_idx(dev_t dev)
++{
++ return MINOR(dev);
++}
++
++static inline int is_unnamed_dev(dev_t dev)
++{
++ return MAJOR(dev) == 0;
++}
++
++#endif /* UNNAMED_MAJOR_COUNT */
++
+ #else /* __KERNEL__ */
+
+ /*
+diff --git a/include/linux/kernel.h b/include/linux/kernel.h
+index 2651f80..80cad52 100644
+--- a/include/linux/kernel.h
++++ b/include/linux/kernel.h
+@@ -200,6 +200,12 @@ extern struct ratelimit_state printk_ratelimit_state;
+ extern int printk_ratelimit(void);
+ extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
+ unsigned int interval_msec);
++asmlinkage int ve_vprintk(int dst, const char *fmt, va_list args)
++ __attribute__ ((format (printf, 2, 0)));
++asmlinkage int ve_printk(int, const char * fmt, ...)
++ __attribute__ ((format (printf, 2, 3)));
++void prepare_printk(void);
++
+ #else
+ static inline int vprintk(const char *s, va_list args)
+ __attribute__ ((format (printf, 1, 0)));
+@@ -211,6 +217,15 @@ static inline int printk_ratelimit(void) { return 0; }
+ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \
+ unsigned int interval_msec) \
+ { return false; }
++static inline int ve_printk(int d, const char *s, ...)
++ __attribute__ ((format (printf, 2, 3)));
++static inline int ve_printk(int d, const char *s, ...)
++{
++ return 0;
++}
++static inline void prepare_printk(void)
++{
++}
+ #endif
+
+ extern void asmlinkage __attribute__((format(printf, 1, 2)))
+@@ -218,9 +233,17 @@ extern void asmlinkage __attribute__((format(printf, 1, 2)))
+
+ unsigned long int_sqrt(unsigned long);
+
++#define VE0_LOG 1
++#define VE_LOG 2
++#define VE_LOG_BOTH (VE0_LOG | VE_LOG)
++extern int console_silence_loglevel;
++
+ static inline void console_silent(void)
+ {
+- console_loglevel = 0;
++ if (console_loglevel > console_silence_loglevel) {
++ printk(KERN_EMERG "console shuts up ...\n");
++ console_loglevel = 0;
++ }
+ }
+
+ static inline void console_verbose(void)
+@@ -234,6 +257,7 @@ extern void wake_up_klogd(void);
+ extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
+ extern int panic_timeout;
+ extern int panic_on_oops;
++extern int decode_call_traces;
+ extern int panic_on_unrecovered_nmi;
+ extern int tainted;
+ extern const char *print_tainted(void);
+diff --git a/include/linux/kobject.h b/include/linux/kobject.h
+index 5437ac0..2592187 100644
+--- a/include/linux/kobject.h
++++ b/include/linux/kobject.h
+@@ -51,6 +51,8 @@ enum kobject_action {
+ KOBJ_REMOVE,
+ KOBJ_CHANGE,
+ KOBJ_MOVE,
++ KOBJ_START,
++ KOBJ_STOP,
+ KOBJ_ONLINE,
+ KOBJ_OFFLINE,
+ KOBJ_MAX
+diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
+index dbb87ab..734aafe 100644
+--- a/include/linux/lockd/lockd.h
++++ b/include/linux/lockd/lockd.h
+@@ -61,6 +61,7 @@ struct nlm_host {
+ struct list_head h_granted; /* Locks in GRANTED state */
+ struct list_head h_reclaim; /* Locks in RECLAIM state */
+ struct nsm_handle * h_nsmhandle; /* NSM status handle */
++ struct ve_struct * owner_env; /* VE owning the host */
+ };
+
+ struct nsm_handle {
+@@ -152,8 +153,11 @@ extern struct svc_procedure nlmsvc_procedures[];
+ #ifdef CONFIG_LOCKD_V4
+ extern struct svc_procedure nlmsvc_procedures4[];
+ #endif
+-extern int nlmsvc_grace_period;
+-extern unsigned long nlmsvc_timeout;
++
++#include <linux/ve_nfs.h>
++extern int _nlmsvc_grace_period;
++extern unsigned long _nlmsvc_timeout;
++
+ extern int nsm_use_hostnames;
+
+ /*
+diff --git a/include/linux/major.h b/include/linux/major.h
+index 53d5faf..4cd77c4 100644
+--- a/include/linux/major.h
++++ b/include/linux/major.h
+@@ -170,4 +170,7 @@
+
+ #define VIOTAPE_MAJOR 230
+
++#define UNNAMED_EXTRA_MAJOR 130
++#define UNNAMED_EXTRA_MAJOR_COUNT 120
++
+ #endif
+diff --git a/include/linux/mm.h b/include/linux/mm.h
+index 72a15dc..f97db27 100644
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -704,15 +704,7 @@ static inline int page_mapped(struct page *page)
+
+ extern void show_free_areas(void);
+
+-#ifdef CONFIG_SHMEM
+-int shmem_lock(struct file *file, int lock, struct user_struct *user);
+-#else
+-static inline int shmem_lock(struct file *file, int lock,
+- struct user_struct *user)
+-{
+- return 0;
+-}
+-#endif
++#define shmem_nopage filemap_nopage
+ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags);
+
+ int shmem_zero_setup(struct vm_area_struct *);
+@@ -778,7 +770,9 @@ int walk_page_range(unsigned long addr, unsigned long end,
+ void free_pgd_range(struct mmu_gather *tlb, unsigned long addr,
+ unsigned long end, unsigned long floor, unsigned long ceiling);
+ int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
+- struct vm_area_struct *vma);
++ struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma);
++int __copy_page_range(struct vm_area_struct *dst_vma, struct vm_area_struct *vma,
++ unsigned long addr, size_t size);
+ void unmap_mapping_range(struct address_space *mapping,
+ loff_t const holebegin, loff_t const holelen, int even_cows);
+ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
+diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
+index bf33413..604871b 100644
+--- a/include/linux/mm_types.h
++++ b/include/linux/mm_types.h
+@@ -95,6 +95,14 @@ struct page {
+ #ifdef CONFIG_CGROUP_MEM_RES_CTLR
+ unsigned long page_cgroup;
+ #endif
++#ifdef CONFIG_BEANCOUNTERS
++ /* FIXME: switch to mainline memcgroup */
++ union {
++ struct user_beancounter *page_ub;
++ struct page_beancounter *page_pb;
++ struct user_beancounter **slub_ubs;
++ } bc;
++#endif
+ };
+
+ /*
+@@ -230,11 +238,17 @@ struct mm_struct {
+
+ unsigned long flags; /* Must use atomic bitops to access the bits */
+
++ unsigned int vps_dumpable:2;
++ unsigned int oom_killed:1;
++
+ struct core_state *core_state; /* coredumping support */
+
+ /* aio bits */
+ rwlock_t ioctx_list_lock; /* aio lock */
+ struct kioctx *ioctx_list;
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *mm_ub;
++#endif
+ #ifdef CONFIG_MM_OWNER
+ /*
+ * "owner" points to a task that is regarded as the canonical
+diff --git a/include/linux/mman.h b/include/linux/mman.h
+index 30d1073..787f2a4 100644
+--- a/include/linux/mman.h
++++ b/include/linux/mman.h
+@@ -88,6 +88,9 @@ static inline unsigned long
+ calc_vm_flag_bits(unsigned long flags)
+ {
+ return _calc_vm_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN ) |
++#ifdef MAP_GROWSUP
++ _calc_vm_trans(flags, MAP_GROWSUP, VM_GROWSUP ) |
++#endif
+ _calc_vm_trans(flags, MAP_DENYWRITE, VM_DENYWRITE ) |
+ _calc_vm_trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE) |
+ _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED );
+diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
+index 830bbcd..fdc1225 100644
+--- a/include/linux/mnt_namespace.h
++++ b/include/linux/mnt_namespace.h
+@@ -24,6 +24,8 @@ struct proc_mounts {
+
+ extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
+ struct fs_struct *);
++extern struct rw_semaphore namespace_sem;
++
+ extern void __put_mnt_ns(struct mnt_namespace *ns);
+
+ static inline void put_mnt_ns(struct mnt_namespace *ns)
+diff --git a/include/linux/mount.h b/include/linux/mount.h
+index 30a1d63..803b390 100644
+--- a/include/linux/mount.h
++++ b/include/linux/mount.h
+@@ -71,6 +71,7 @@ struct vfsmount {
+ * are held, and all mnt_writer[]s on this mount have 0 as their ->count
+ */
+ atomic_t __mnt_writers;
++ unsigned owner;
+ };
+
+ static inline struct vfsmount *mntget(struct vfsmount *mnt)
+diff --git a/include/linux/msg.h b/include/linux/msg.h
+index 56abf15..050f740 100644
+--- a/include/linux/msg.h
++++ b/include/linux/msg.h
+@@ -107,6 +107,14 @@ extern long do_msgsnd(int msqid, long mtype, void __user *mtext,
+ extern long do_msgrcv(int msqid, long *pmtype, void __user *mtext,
+ size_t msgsz, long msgtyp, int msgflg);
+
++int sysvipc_walk_msg(int (*func)(int, struct msg_queue*, void *), void *arg);
++int sysvipc_setup_msg(key_t key, int msqid, int msgflg);
++int sysv_msg_store(struct msg_msg *msg,
++ int (*store)(void * src, int len, int offset, void * data),
++ int len, void * data);
++struct msg_msg *sysv_msg_load(int (*load)(void * dst, int len, int offset,
++ void * data), int len, void * data);
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_MSG_H */
+diff --git a/include/linux/namei.h b/include/linux/namei.h
+index 68f8c32..16cd273 100644
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -53,6 +53,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
+ */
+ #define LOOKUP_OPEN (0x0100)
+ #define LOOKUP_CREATE (0x0200)
++#define LOOKUP_NOAREACHECK (0x1000) /* no area check on lookup */
++#define LOOKUP_STRICT (0x2000) /* no symlinks or other filesystems */
+
+ extern int user_path_at(int, const char __user *, unsigned, struct path *);
+
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 488c56e..2cadfda 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -278,6 +278,11 @@ enum netdev_state_t
+ __LINK_STATE_DORMANT,
+ };
+
++struct netdev_bc {
++ struct user_beancounter *exec_ub, *owner_ub;
++};
++
++#define netdev_bc(dev) (&(dev)->dev_bc)
+
+ /*
+ * This structure holds at boot time configured netdevice settings. They
+@@ -521,13 +526,17 @@ struct net_device
+ #define NETIF_F_LRO 32768 /* large receive offload */
+
+ /* Segmentation offload features */
+-#define NETIF_F_GSO_SHIFT 16
+-#define NETIF_F_GSO_MASK 0xffff0000
++#define NETIF_F_GSO_SHIFT 20
++#define NETIF_F_GSO_MASK 0xfff00000
+ #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
+ #define NETIF_F_UFO (SKB_GSO_UDP << NETIF_F_GSO_SHIFT)
+ #define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
+ #define NETIF_F_TSO_ECN (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT)
+ #define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
++/* device is venet device */
++#define NETIF_F_VENET (1 << (NETIF_F_GSO_SHIFT - 1))
++/* can be registered inside VE */
++#define NETIF_F_VIRTUAL (1 << (NETIF_F_GSO_SHIFT - 2))
+
+ /* List of features with software fallbacks. */
+ #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
+@@ -735,6 +744,9 @@ struct net_device
+ /* GARP */
+ struct garp_port *garp_port;
+
++ struct ve_struct *owner_env; /* Owner VE of the interface */
++ struct netdev_bc dev_bc;
++
+ /* class/net/name entry */
+ struct device dev;
+ /* space for optional statistics and wireless sysfs groups */
+@@ -752,6 +764,20 @@ struct net_device
+ };
+ #define to_net_dev(d) container_of(d, struct net_device, dev)
+
++#define NETDEV_HASHBITS 8
++#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)
++
++static inline struct hlist_head *dev_name_hash(struct net *net, const char *name)
++{
++ unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ));
++ return &net->dev_name_head[hash & ((1 << NETDEV_HASHBITS) - 1)];
++}
++
++static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
++{
++ return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
++}
++
+ #define NETDEV_ALIGN 32
+ #define NETDEV_ALIGN_CONST (NETDEV_ALIGN - 1)
+
+@@ -1224,6 +1250,8 @@ extern int dev_ethtool(struct net *net, struct ifreq *);
+ extern unsigned dev_get_flags(const struct net_device *);
+ extern int dev_change_flags(struct net_device *, unsigned);
+ extern int dev_change_name(struct net_device *, char *);
++int __dev_change_net_namespace(struct net_device *, struct net *, const char *,
++ struct user_beancounter *exec_ub);
+ extern int dev_change_net_namespace(struct net_device *,
+ struct net *, const char *);
+ extern int dev_set_mtu(struct net_device *, int);
+@@ -1673,6 +1701,18 @@ extern void linkwatch_run_queue(void);
+
+ extern int netdev_compute_features(unsigned long all, unsigned long one);
+
++#if defined(CONFIG_VE) && defined(CONFIG_NET)
++static inline int ve_is_dev_movable(struct net_device *dev)
++{
++ return !(dev->features & (NETIF_F_VIRTUAL | NETIF_F_NETNS_LOCAL));
++}
++#else
++static inline int ve_is_dev_movable(struct net_device *dev)
++{
++ return 0;
++}
++#endif
++
+ static inline int net_gso_ok(int features, int gso_type)
+ {
+ int feature = gso_type << NETIF_F_GSO_SHIFT;
+diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
+index 0c5eb7e..8d41ea4 100644
+--- a/include/linux/netfilter.h
++++ b/include/linux/netfilter.h
+@@ -394,5 +394,24 @@ static inline struct net *nf_post_routing_net(const struct net_device *in,
+ #endif
+ }
+
++#ifdef CONFIG_VE_IPTABLES
++#include <linux/vziptable_defs.h>
++
++#define net_ipt_module_permitted(netns, ipt) \
++ (VE_IPT_CMP((netns)->owner_ve->ipt_mask, ipt) && \
++ VE_IPT_CMP((netns)->owner_ve->_iptables_modules, \
++ (ipt) & ~(ipt##_MOD)))
++
++#define net_ipt_module_set(netns, ipt) ({ \
++ (netns)->owner_ve->_iptables_modules |= ipt##_MOD; \
++ })
++#define net_is_ipt_module_set(netns, ipt) ( \
++ (netns)->owner_ve->_iptables_modules & (ipt##_MOD))
++#else
++#define net_ipt_module_permitted(netns, ipt) (1)
++#define net_ipt_module_set(netns, ipt)
++#define net_is_ipt_module_set(netns, ipt) (1)
++#endif
++
+ #endif /*__KERNEL__*/
+ #endif /*__LINUX_NETFILTER_H*/
+diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
+index 2326296..7a66377 100644
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -302,6 +302,7 @@ struct xt_table_info
+ {
+ /* Size per table */
+ unsigned int size;
++ unsigned int alloc_size;
+ /* Number of entries: FIXME. --RR */
+ unsigned int number;
+ /* Initial number of entries. Needed for module usage count */
+diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h
+index 51b18d8..439da56 100644
+--- a/include/linux/netfilter/xt_hashlimit.h
++++ b/include/linux/netfilter/xt_hashlimit.h
+@@ -63,4 +63,11 @@ struct xt_hashlimit_mtinfo1 {
+ struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
+ };
+
++#ifdef __KERNEL__
++struct ve_xt_hashlimit {
++ struct hlist_head hashlimit_htables;
++ struct proc_dir_entry *hashlimit_procdir4;
++ struct proc_dir_entry *hashlimit_procdir6;
++};
++#endif
+ #endif /*_XT_HASHLIMIT_H*/
+diff --git a/include/linux/netfilter_ipv4/ipt_recent.h b/include/linux/netfilter_ipv4/ipt_recent.h
+index 6508a45..3b9a1e8 100644
+--- a/include/linux/netfilter_ipv4/ipt_recent.h
++++ b/include/linux/netfilter_ipv4/ipt_recent.h
+@@ -24,4 +24,12 @@ struct ipt_recent_info {
+ u_int8_t side;
+ };
+
++#ifdef __KERNEL__
++struct ve_ipt_recent {
++ struct list_head tables;
++#ifdef CONFIG_PROC_FS
++ struct proc_dir_entry *proc_dir;
++#endif
++};
++#endif
+ #endif /*_IPT_RECENT_H*/
+diff --git a/include/linux/nfcalls.h b/include/linux/nfcalls.h
+new file mode 100644
+index 0000000..f968054
+--- /dev/null
++++ b/include/linux/nfcalls.h
+@@ -0,0 +1,172 @@
++/*
++ * include/linux/nfcalls.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _LINUX_NFCALLS_H
++#define _LINUX_NFCALLS_H
++
++#include <linux/rcupdate.h>
++
++#ifdef CONFIG_MODULES
++extern struct module no_module;
++
++#define DECL_KSYM_MODULE(name) \
++ extern struct module *vz_mod_##name
++
++#define INIT_KSYM_MODULE(name) \
++ struct module *vz_mod_##name = &no_module; \
++ EXPORT_SYMBOL(vz_mod_##name)
++
++static inline void __vzksym_modresolve(struct module **modp, struct module *mod)
++{
++ /*
++ * we want to be sure, that pointer updates are visible first:
++ * 1. wmb() is here only for piece of sure
++ * (note, no rmb() in KSYMSAFECALL)
++ * 2. synchronize_sched() guarantees that updates are visible
++ * on all cpus and allows us to remove rmb() in KSYMSAFECALL
++ */
++ wmb(); synchronize_sched();
++ *modp = mod;
++ /* just to be sure, our changes are visible as soon as possible */
++ wmb(); synchronize_sched();
++}
++
++static inline void __vzksym_modunresolve(struct module **modp)
++{
++ /*
++ * try_module_get() in KSYMSAFECALL should fail at this moment since
++ * THIS_MODULE in in unloading state (we should be called from fini),
++ * no need to syncronize pointers/ve_module updates.
++ */
++ *modp = &no_module;
++ /*
++ * synchronize_sched() guarantees here that we see
++ * updated module pointer before the module really gets away
++ */
++ synchronize_sched();
++}
++
++static inline int __vzksym_module_get(struct module *mod)
++{
++ /*
++ * we want to avoid rmb(), so use synchronize_sched() in KSYMUNRESOLVE
++ * and smp_read_barrier_depends() here...
++ */
++ smp_read_barrier_depends(); /* for module loading */
++ if (!try_module_get(mod))
++ return -EBUSY;
++
++ return 0;
++}
++
++static inline void __vzksym_module_put(struct module *mod)
++{
++ module_put(mod);
++}
++#else
++#define DECL_KSYM_MODULE(name)
++#define INIT_KSYM_MODULE(name)
++#define __vzksym_modresolve(modp, mod)
++#define __vzksym_modunresolve(modp)
++#define __vzksym_module_get(mod) 0
++#define __vzksym_module_put(mod)
++#endif
++
++#define __KSYMERRCALL(err, type, mod, name, args) \
++({ \
++ type ret = (type)err; \
++ if (!__vzksym_module_get(vz_mod_##mod)) { \
++ if (vz_##name) \
++ ret = ((*vz_##name)args); \
++ __vzksym_module_put(vz_mod_##mod); \
++ } \
++ ret; \
++})
++
++#define __KSYMSAFECALL_VOID(mod, name, args) \
++ do { \
++ if (!__vzksym_module_get(vz_mod_##mod)) { \
++ if (vz_##name) \
++ ((*vz_##name)args); \
++ __vzksym_module_put(vz_mod_##mod); \
++ } \
++ } while (0)
++
++#define DECL_KSYM_CALL(type, name, args) \
++ extern type (*vz_##name) args
++#define INIT_KSYM_CALL(type, name, args) \
++ type (*vz_##name) args; \
++EXPORT_SYMBOL(vz_##name)
++
++#define KSYMERRCALL(err, mod, name, args) \
++ __KSYMERRCALL(err, int, mod, name, args)
++#define KSYMSAFECALL(type, mod, name, args) \
++ __KSYMERRCALL(0, type, mod, name, args)
++#define KSYMSAFECALL_VOID(mod, name, args) \
++ __KSYMSAFECALL_VOID(mod, name, args)
++#define KSYMREF(name) vz_##name
++
++/* should be called _after_ KSYMRESOLVE's */
++#define KSYMMODRESOLVE(name) \
++ __vzksym_modresolve(&vz_mod_##name, THIS_MODULE)
++#define KSYMMODUNRESOLVE(name) \
++ __vzksym_modunresolve(&vz_mod_##name)
++
++#define KSYMRESOLVE(name) \
++ vz_##name = &name
++#define KSYMUNRESOLVE(name) \
++ vz_##name = NULL
++
++#if defined(CONFIG_VE)
++DECL_KSYM_MODULE(ip_tables);
++DECL_KSYM_MODULE(ip6_tables);
++DECL_KSYM_MODULE(iptable_filter);
++DECL_KSYM_MODULE(ip6table_filter);
++DECL_KSYM_MODULE(iptable_mangle);
++DECL_KSYM_MODULE(ip6table_mangle);
++DECL_KSYM_MODULE(ip_conntrack);
++DECL_KSYM_MODULE(nf_conntrack);
++DECL_KSYM_MODULE(nf_conntrack_ipv4);
++DECL_KSYM_MODULE(nf_conntrack_ipv6);
++DECL_KSYM_MODULE(xt_conntrack);
++DECL_KSYM_MODULE(ip_nat);
++DECL_KSYM_MODULE(nf_nat);
++DECL_KSYM_MODULE(iptable_nat);
++
++struct sk_buff;
++
++DECL_KSYM_CALL(int, init_iptable_conntrack, (void));
++DECL_KSYM_CALL(int, nf_conntrack_init_ve, (void));
++DECL_KSYM_CALL(int, init_nf_ct_l3proto_ipv4, (void));
++DECL_KSYM_CALL(int, init_nf_ct_l3proto_ipv6, (void));
++DECL_KSYM_CALL(int, nf_nat_init, (void));
++DECL_KSYM_CALL(int, init_nftable_nat, (void));
++DECL_KSYM_CALL(int, nf_nat_init, (void));
++DECL_KSYM_CALL(void, fini_nftable_nat, (void));
++DECL_KSYM_CALL(void, nf_nat_cleanup, (void));
++DECL_KSYM_CALL(void, fini_iptable_conntrack, (void));
++DECL_KSYM_CALL(void, nf_conntrack_cleanup_ve, (void));
++DECL_KSYM_CALL(void, fini_nf_ct_l3proto_ipv4, (void));
++DECL_KSYM_CALL(void, fini_nf_ct_l3proto_ipv6, (void));
++
++#include <linux/netfilter/x_tables.h>
++#endif
++
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++DECL_KSYM_MODULE(vzethdev);
++DECL_KSYM_CALL(int, veth_open, (struct net_device *dev));
++#endif
++
++#if defined(CONFIG_VE_CALLS) || defined(CONFIG_VE_CALLS_MODULE)
++DECL_KSYM_MODULE(vzmon);
++DECL_KSYM_CALL(void, real_do_env_free, (struct ve_struct *env));
++#endif
++
++#endif /* _LINUX_NFCALLS_H */
+diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
+index c9beacd..cb87ca2 100644
+--- a/include/linux/nfs_fs_sb.h
++++ b/include/linux/nfs_fs_sb.h
+@@ -70,6 +70,7 @@ struct nfs_client {
+ char cl_ipaddr[48];
+ unsigned char cl_id_uniquifier;
+ #endif
++ struct ve_struct *owner_env;
+ };
+
+ /*
+diff --git a/include/linux/notifier.h b/include/linux/notifier.h
+index da2698b..ae805e0 100644
+--- a/include/linux/notifier.h
++++ b/include/linux/notifier.h
+@@ -153,8 +153,9 @@ extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
+
+ #define NOTIFY_DONE 0x0000 /* Don't care */
+ #define NOTIFY_OK 0x0001 /* Suits me */
++#define NOTIFY_FAIL 0x0002 /* Reject */
+ #define NOTIFY_STOP_MASK 0x8000 /* Don't call further */
+-#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
++#define NOTIFY_BAD (NOTIFY_STOP_MASK|NOTIFY_FAIL)
+ /* Bad/Veto action */
+ /*
+ * Clean way to return from the notifier and stop further calls.
+diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
+index c8a768e..119368b 100644
+--- a/include/linux/nsproxy.h
++++ b/include/linux/nsproxy.h
+@@ -66,6 +66,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk);
+ void exit_task_namespaces(struct task_struct *tsk);
+ void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
+ void free_nsproxy(struct nsproxy *ns);
++struct mnt_namespace * get_task_mnt_ns(struct task_struct *tsk);
+ int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
+ struct fs_struct *);
+
+@@ -76,9 +77,10 @@ static inline void put_nsproxy(struct nsproxy *ns)
+ }
+ }
+
+-static inline void get_nsproxy(struct nsproxy *ns)
++static inline struct nsproxy *get_nsproxy(struct nsproxy *ns)
+ {
+ atomic_inc(&ns->count);
++ return ns;
+ }
+
+ #ifdef CONFIG_CGROUP_NS
+diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
+index c74d3e8..c5f47c1 100644
+--- a/include/linux/page-flags.h
++++ b/include/linux/page-flags.h
+@@ -173,6 +173,7 @@ __PAGEFLAG(Slab, slab)
+ PAGEFLAG(Checked, checked) /* Used by some filesystems */
+ PAGEFLAG(Pinned, pinned) TESTSCFLAG(Pinned, pinned) /* Xen */
+ PAGEFLAG(SavePinned, savepinned); /* Xen */
++PAGEFLAG(Checkpointed, owner_priv_1)
+ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved)
+ PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private)
+ __SETPAGEFLAG(Private, private)
+diff --git a/include/linux/percpu.h b/include/linux/percpu.h
+index fac3337..4c889bf 100644
+--- a/include/linux/percpu.h
++++ b/include/linux/percpu.h
+@@ -74,6 +74,22 @@ struct percpu_data {
+ (__typeof__(ptr))__p->ptrs[(cpu)]; \
+ })
+
++struct percpu_data_static {
++ void *ptrs[NR_CPUS];
++};
++
++#define DEFINE_PER_CPU_STATIC(type, name) \
++ static struct percpu_data_static per_cpu_data__##name; \
++ static __typeof__(type) per_cpu__##name[NR_CPUS]
++
++#define percpu_static_init(name) ({ \
++ int i; \
++ for (i = 0; i < NR_CPUS; i++) \
++ (per_cpu_data__##name).ptrs[i] = &(per_cpu__##name)[i];\
++ (__typeof__(&(per_cpu__##name)[0])) \
++ __percpu_disguise(&(per_cpu_data__##name));\
++ })
++
+ extern void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask);
+ extern void percpu_free(void *__pdata);
+
+@@ -81,6 +97,11 @@ extern void percpu_free(void *__pdata);
+
+ #define percpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); })
+
++#define DEFINE_PER_CPU_STATIC(type, name) \
++ static __typeof__(type) per_cpu__##name[NR_CPUS]
++
++#define percpu_static_init(name) (&(per_cpu__##name)[0])
++
+ static __always_inline void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask)
+ {
+ return kzalloc(size, gfp);
+diff --git a/include/linux/pid.h b/include/linux/pid.h
+index d7e98ff..9ca7db8 100644
+--- a/include/linux/pid.h
++++ b/include/linux/pid.h
+@@ -60,6 +60,9 @@ struct pid
+ unsigned int level;
+ /* lists of tasks that use this pid */
+ struct hlist_head tasks[PIDTYPE_MAX];
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *ub;
++#endif
+ struct rcu_head rcu;
+ struct upid numbers[1];
+ };
+@@ -96,6 +99,11 @@ extern void change_pid(struct task_struct *task, enum pid_type,
+ struct pid *pid);
+ extern void transfer_pid(struct task_struct *old, struct task_struct *new,
+ enum pid_type);
++extern void reattach_pid(struct task_struct *, enum pid_type, struct pid *);
++extern int alloc_pidmap(struct pid_namespace *pid_ns);
++extern int set_pidmap(struct pid_namespace *pid_ns, pid_t pid);
++
++extern spinlock_t pidmap_lock;
+
+ struct pid_namespace;
+ extern struct pid_namespace init_pid_ns;
+@@ -119,8 +127,11 @@ extern struct pid *find_get_pid(int nr);
+ extern struct pid *find_ge_pid(int nr, struct pid_namespace *);
+ int next_pidmap(struct pid_namespace *pid_ns, int last);
+
+-extern struct pid *alloc_pid(struct pid_namespace *ns);
++extern struct pid *alloc_pid(struct pid_namespace *ns, pid_t vpid);
+ extern void free_pid(struct pid *pid);
++extern int pid_ns_attach_init(struct pid_namespace *, struct task_struct *);
++extern int pid_ns_attach_task(struct pid_namespace *, struct task_struct *);
++pid_t pid_to_vpid(pid_t nr);
+
+ /*
+ * the helpers to get the pid's id seen from different namespaces
+@@ -167,7 +178,7 @@ pid_t pid_vnr(struct pid *pid);
+ do {
+
+ #define while_each_pid_thread(pid, type, task) \
+- } while_each_thread(tg___, task); \
++ } while_each_thread_ve(tg___, task); \
+ task = tg___; \
+ } while_each_pid_task(pid, type, task)
+ #endif /* _LINUX_PID_H */
+diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h
+index 1af82c4..d5d638d 100644
+--- a/include/linux/pid_namespace.h
++++ b/include/linux/pid_namespace.h
+@@ -16,6 +16,14 @@ struct pidmap {
+
+ struct bsd_acct_struct;
+
++/* pid namespace flags */
++
++/* if set newly created pid ns got PID_NS_HIDE_CHILD flag */
++#define PID_NS_HIDE_CHILD 0x00000001
++
++/* if set newly created processes invisible from parent ns*/
++#define PID_NS_HIDDEN 0x00000002
++
+ struct pid_namespace {
+ struct kref kref;
+ struct pidmap pidmap[PIDMAP_ENTRIES];
+@@ -24,6 +32,7 @@ struct pid_namespace {
+ struct kmem_cache *pid_cachep;
+ unsigned int level;
+ struct pid_namespace *parent;
++ unsigned flags;
+ #ifdef CONFIG_PROC_FS
+ struct vfsmount *proc_mnt;
+ #endif
+diff --git a/include/linux/poll.h b/include/linux/poll.h
+index ef45382..c1bf82a 100644
+--- a/include/linux/poll.h
++++ b/include/linux/poll.h
+@@ -119,6 +119,7 @@ extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds,
+ s64 *timeout);
+ extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
+ fd_set __user *exp, s64 *timeout);
++long do_restart_poll(struct restart_block *restart_block);
+
+ #endif /* KERNEL */
+
+diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
+index fb61850..fc2a6c7 100644
+--- a/include/linux/proc_fs.h
++++ b/include/linux/proc_fs.h
+@@ -126,7 +126,10 @@ extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
+ extern struct vfsmount *proc_mnt;
+ struct pid_namespace;
+ extern int proc_fill_super(struct super_block *);
+-extern struct inode *proc_get_inode(struct super_block *, unsigned int, struct proc_dir_entry *);
++extern struct inode *proc_get_inode(struct super_block *, unsigned int,
++ struct proc_dir_entry *glob, struct proc_dir_entry *loc);
++
++extern struct file_system_type proc_fs_type;
+
+ /*
+ * These are generic /proc routines that use the internal
+@@ -140,6 +143,7 @@ extern struct dentry *proc_lookup(struct inode *, struct dentry *, struct nameid
+
+ extern const struct file_operations proc_kcore_operations;
+ extern const struct file_operations ppc_htab_operations;
++extern const struct file_operations proc_kmsg_operations;
+
+ extern int pid_ns_prepare_proc(struct pid_namespace *ns);
+ extern void pid_ns_release_proc(struct pid_namespace *ns);
+@@ -174,6 +178,8 @@ extern struct proc_dir_entry *proc_mkdir(const char *,struct proc_dir_entry *);
+ extern struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
+ struct proc_dir_entry *parent);
+
++extern struct proc_dir_entry glob_proc_root;
++
+ static inline struct proc_dir_entry *proc_create(const char *name, mode_t mode,
+ struct proc_dir_entry *parent, const struct file_operations *proc_fops)
+ {
+@@ -292,6 +298,9 @@ struct proc_inode {
+ struct proc_dir_entry *pde;
+ struct ctl_table_header *sysctl;
+ struct ctl_table *sysctl_entry;
++#ifdef CONFIG_VE
++ struct proc_dir_entry *lpde;
++#endif
+ struct inode vfs_inode;
+ };
+
+@@ -305,6 +314,15 @@ static inline struct proc_dir_entry *PDE(const struct inode *inode)
+ return PROC_I(inode)->pde;
+ }
+
++static inline struct proc_dir_entry *LPDE(const struct inode *inode)
++{
++#ifdef CONFIG_VE
++ return PROC_I(inode)->lpde;
++#else
++ return NULL;
++#endif
++}
++
+ static inline struct net *PDE_NET(struct proc_dir_entry *pde)
+ {
+ return pde->parent->data;
+diff --git a/include/linux/quota.h b/include/linux/quota.h
+index 376a050..eb3df9b 100644
+--- a/include/linux/quota.h
++++ b/include/linux/quota.h
+@@ -167,6 +167,10 @@ enum {
+ #include <linux/spinlock.h>
+ #include <linux/wait.h>
+
++#include <linux/spinlock.h>
++
++extern spinlock_t dq_data_lock;
++
+ #include <linux/dqblk_xfs.h>
+ #include <linux/dqblk_v1.h>
+ #include <linux/dqblk_v2.h>
+@@ -284,6 +288,8 @@ struct quota_format_ops {
+ int (*release_dqblk)(struct dquot *dquot); /* Called when last reference to dquot is being dropped */
+ };
+
++struct inode;
++struct iattr;
+ /* Operations working with dquots */
+ struct dquot_operations {
+ int (*initialize) (struct inode *, int);
+@@ -298,9 +304,11 @@ struct dquot_operations {
+ int (*release_dquot) (struct dquot *); /* Quota is going to be deleted from disk */
+ int (*mark_dirty) (struct dquot *); /* Dquot is marked dirty */
+ int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */
++ int (*rename) (struct inode *, struct inode *, struct inode *);
+ };
+
+ /* Operations handling requests from userspace */
++struct v2_disk_dqblk;
+ struct quotactl_ops {
+ int (*quota_on)(struct super_block *, int, int, char *, int);
+ int (*quota_off)(struct super_block *, int, int);
+@@ -313,6 +321,10 @@ struct quotactl_ops {
+ int (*set_xstate)(struct super_block *, unsigned int, int);
+ int (*get_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+ int (*set_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
++#ifdef CONFIG_QUOTA_COMPAT
++ int (*get_quoti)(struct super_block *, int, unsigned int,
++ struct v2_disk_dqblk __user *);
++#endif
+ };
+
+ struct quota_format_type {
+@@ -337,6 +349,10 @@ struct quota_info {
+ struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */
+ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
+ struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */
++#if defined(CONFIG_VZ_QUOTA) || defined(CONFIG_VZ_QUOTA_MODULE)
++ struct vz_quota_master *vzdq_master;
++ int vzdq_count;
++#endif
+ };
+
+ int register_quota_format(struct quota_format_type *fmt);
+diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
+index ca6b9b5..e9cc3f1 100644
+--- a/include/linux/quotaops.h
++++ b/include/linux/quotaops.h
+@@ -183,6 +183,19 @@ static inline void vfs_dq_free_inode(struct inode *inode)
+ inode->i_sb->dq_op->free_inode(inode, 1);
+ }
+
++static __inline__ int vfs_dq_rename(struct inode *inode,
++ struct inode *old_dir, struct inode *new_dir)
++{
++ struct dquot_operations *q_op;
++
++ q_op = inode->i_sb->dq_op;
++ if (q_op && q_op->rename) {
++ if (q_op->rename(inode, old_dir, new_dir) == NO_QUOTA)
++ return 1;
++ }
++ return 0;
++}
++
+ /* The following two functions cannot be called inside a transaction */
+ static inline void vfs_dq_sync(struct super_block *sb)
+ {
+@@ -262,6 +275,12 @@ static inline int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
+ return 0;
+ }
+
++static inline int vfs_dq_rename(struct inode *inode, struct inode *old_dir,
++ struct inode *new_dir)
++{
++ return 0;
++}
++
+ static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr)
+ {
+ inode_add_bytes(inode, nr);
+@@ -363,6 +382,7 @@ static inline void vfs_dq_free_block(struct inode *inode, qsize_t nr)
+ #define DQUOT_FREE_INODE(inode) vfs_dq_free_inode(inode)
+ #define DQUOT_TRANSFER(inode, iattr) vfs_dq_transfer(inode, iattr)
+ #define DQUOT_SYNC(sb) vfs_dq_sync(sb)
++#define DQUOT_RENAME(inode, od, nd) vfs_dq_rename(inode, od, nd)
+ #define DQUOT_OFF(sb, remount) vfs_dq_off(sb, remount)
+ #define DQUOT_ON_REMOUNT(sb) vfs_dq_quota_on_remount(sb)
+
+diff --git a/include/linux/rmap.h b/include/linux/rmap.h
+index fed6f5e..dff17e3 100644
+--- a/include/linux/rmap.h
++++ b/include/linux/rmap.h
+@@ -82,6 +82,8 @@ void page_add_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
+ void page_add_new_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
+ void page_add_file_rmap(struct page *);
+ void page_remove_rmap(struct page *, struct vm_area_struct *);
++struct anon_vma *page_lock_anon_vma(struct page *page);
++void page_unlock_anon_vma(struct anon_vma *anon_vma);
+
+ #ifdef CONFIG_DEBUG_VM
+ void page_dup_rmap(struct page *page, struct vm_area_struct *vma, unsigned long address);
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index 3d9120c..6e47614 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -29,6 +29,10 @@
+ #define CLONE_NEWNET 0x40000000 /* New network namespace */
+ #define CLONE_IO 0x80000000 /* Clone io context */
+
++/* mask of clones which are disabled in OpenVZ VEs */
++#define CLONE_NAMESPACES_MASK (CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | \
++ CLONE_NEWPID | CLONE_NEWNET)
++
+ /*
+ * Scheduling policies
+ */
+@@ -91,6 +95,8 @@ struct sched_param {
+
+ #include <asm/processor.h>
+
++#include <bc/task.h>
++
+ struct mem_cgroup;
+ struct exec_domain;
+ struct futex_pi_state;
+@@ -127,14 +133,37 @@ extern unsigned long avenrun[]; /* Load averages */
+ load += n*(FIXED_1-exp); \
+ load >>= FSHIFT;
+
++#define LOAD_INT(x) ((x) >> FSHIFT)
++#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
++
+ extern unsigned long total_forks;
+ extern int nr_threads;
+ DECLARE_PER_CPU(unsigned long, process_counts);
+ extern int nr_processes(void);
+ extern unsigned long nr_running(void);
++extern unsigned long nr_sleeping(void);
++extern unsigned long nr_stopped(void);
+ extern unsigned long nr_uninterruptible(void);
+ extern unsigned long nr_active(void);
+ extern unsigned long nr_iowait(void);
++extern atomic_t nr_dead;
++extern unsigned long nr_zombie;
++
++#ifdef CONFIG_VE
++struct ve_struct;
++extern unsigned long nr_running_ve(struct ve_struct *);
++extern unsigned long nr_iowait_ve(struct ve_struct *);
++extern unsigned long nr_uninterruptible_ve(struct ve_struct *);
++extern cycles_t ve_sched_get_idle_time(struct ve_struct *ve, int cpu);
++extern cycles_t ve_sched_get_iowait_time(struct ve_struct *ve, int cpu);
++void ve_sched_attach(struct ve_struct *envid);
++#else
++#define nr_running_ve(ve) 0
++#define nr_iowait_ve(ve) 0
++#define nr_uninterruptible_ve(ve) 0
++#define ve_sched_get_idle_time(ve, cpu) 0
++#define ve_sched_get_iowait_time(ve, cpu) 0
++#endif
+
+ struct seq_file;
+ struct cfs_rq;
+@@ -271,6 +300,7 @@ static inline void show_state(void)
+ }
+
+ extern void show_regs(struct pt_regs *);
++extern void smp_show_regs(struct pt_regs *, void *);
+
+ /*
+ * TASK is a pointer to the task whose backtrace we want to see (or NULL for current
+@@ -425,6 +455,9 @@ struct pacct_struct {
+ unsigned long ac_minflt, ac_majflt;
+ };
+
++#include <linux/ve.h>
++#include <linux/ve_task.h>
++
+ /*
+ * NOTE! "signal_struct" does not have it's own
+ * locking, because a shared signal_struct always
+@@ -1088,6 +1121,7 @@ struct task_struct {
+ /* ??? */
+ unsigned int personality;
+ unsigned did_exec:1;
++ unsigned did_ve_enter:1;
+ pid_t pid;
+ pid_t tgid;
+
+@@ -1287,6 +1321,14 @@ struct task_struct {
+ struct rcu_head rcu;
+
+ /*
++ * state tracking for suspend
++ * FIXME - ptrace is completely rewritten in this kernel
++ * so set_pn_state() is not set in many places correctyl
++ */
++ __u8 pn_state;
++ __u8 stopped_state:1;
++
++ /*
+ * cache last used pipe for splice
+ */
+ struct pipe_inode_info *splice_pipe;
+@@ -1301,6 +1343,19 @@ struct task_struct {
+ int latency_record_count;
+ struct latency_record latency_record[LT_SAVECOUNT];
+ #endif
++#ifdef CONFIG_BEANCOUNTERS
++ struct task_beancounter task_bc;
++#endif
++#ifdef CONFIG_VE
++ struct ve_task_info ve_task_info;
++#endif
++#if defined(CONFIG_VZ_QUOTA) || defined(CONFIG_VZ_QUOTA_MODULE)
++ unsigned long magic;
++ struct inode *ino;
++#endif
++#ifdef CONFIG_VZ_FAIRSCHED
++ struct fairsched_node *fsched_node;
++#endif
+ };
+
+ /*
+@@ -1479,6 +1534,43 @@ extern cputime_t task_utime(struct task_struct *p);
+ extern cputime_t task_stime(struct task_struct *p);
+ extern cputime_t task_gtime(struct task_struct *p);
+
++#ifndef CONFIG_VE
++#define set_pn_state(tsk, state) do { } while(0)
++#define clear_pn_state(tsk) do { } while(0)
++#define set_stop_state(tsk) do { } while(0)
++#define clear_stop_state(tsk) do { } while(0)
++#else
++#define PN_STOP_TF 1 /* was not in 2.6.8 */
++#define PN_STOP_TF_RT 2 /* was not in 2.6.8 */
++#define PN_STOP_ENTRY 3
++#define PN_STOP_FORK 4
++#define PN_STOP_VFORK 5
++#define PN_STOP_SIGNAL 6
++#define PN_STOP_EXIT 7
++#define PN_STOP_EXEC 8
++#define PN_STOP_LEAVE 9
++
++static inline void set_pn_state(struct task_struct *tsk, int state)
++{
++ tsk->pn_state = state;
++}
++
++static inline void clear_pn_state(struct task_struct *tsk)
++{
++ tsk->pn_state = 0;
++}
++
++static inline void set_stop_state(struct task_struct *tsk)
++{
++ tsk->stopped_state = 1;
++}
++
++static inline void clear_stop_state(struct task_struct *tsk)
++{
++ tsk->stopped_state = 0;
++}
++#endif
++
+ /*
+ * Per process flags
+ */
+@@ -1495,6 +1587,7 @@ extern cputime_t task_gtime(struct task_struct *p);
+ #define PF_MEMALLOC 0x00000800 /* Allocating memory */
+ #define PF_FLUSHER 0x00001000 /* responsible for disk writeback */
+ #define PF_USED_MATH 0x00002000 /* if unset the fpu must be initialized before use */
++#define PF_EXIT_RESTART 0x00004000 /* do_exit() restarted, see do_exit() */
+ #define PF_NOFREEZE 0x00008000 /* this thread should not be frozen */
+ #define PF_FROZEN 0x00010000 /* frozen for system suspend */
+ #define PF_FSTRANS 0x00020000 /* inside a filesystem transaction */
+@@ -1586,6 +1679,21 @@ extern unsigned long long cpu_clock(int cpu);
+ extern unsigned long long
+ task_sched_runtime(struct task_struct *task);
+
++static inline unsigned long cycles_to_clocks(cycles_t cycles)
++{
++ extern unsigned long cycles_per_clock;
++ do_div(cycles, cycles_per_clock);
++ return cycles;
++}
++
++static inline u64 cycles_to_jiffies(cycles_t cycles)
++{
++ extern unsigned long cycles_per_jiffy;
++ do_div(cycles, cycles_per_jiffy);
++ return cycles;
++}
++
++
+ /* sched_exec is called by processes performing an exec */
+ #ifdef CONFIG_SMP
+ extern void sched_exec(void);
+@@ -1720,6 +1828,7 @@ static inline struct user_struct *get_uid(struct user_struct *u)
+ extern void free_uid(struct user_struct *);
+ extern void switch_uid(struct user_struct *);
+ extern void release_uids(struct user_namespace *ns);
++extern int set_user(uid_t uid, int dumpclear);
+
+ #include <asm/current.h>
+
+@@ -1851,6 +1960,13 @@ extern int disallow_signal(int);
+
+ extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
+ extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
++extern long do_fork_pid(unsigned long clone_flags,
++ unsigned long stack_start,
++ struct pt_regs *regs,
++ unsigned long stack_size,
++ int __user *parent_tidptr,
++ int __user *child_tidptr,
++ long pid0);
+ struct task_struct *fork_idle(int);
+
+ extern void set_task_comm(struct task_struct *tsk, char *from);
+@@ -1866,19 +1982,19 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
+ }
+ #endif
+
+-#define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks)
++#define next_task_all(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks)
+
+-#define for_each_process(p) \
+- for (p = &init_task ; (p = next_task(p)) != &init_task ; )
++#define for_each_process_all(p) \
++ for (p = &init_task ; (p = next_task_all(p)) != &init_task ; )
+
+ /*
+ * Careful: do_each_thread/while_each_thread is a double loop so
+ * 'break' will not work as expected - use goto instead.
+ */
+-#define do_each_thread(g, t) \
+- for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do
++#define do_each_thread_all(g, t) \
++ for (g = t = &init_task ; (g = t = next_task_all(g)) != &init_task ; ) do
+
+-#define while_each_thread(g, t) \
++#define while_each_thread_all(g, t) \
+ while ((t = next_thread(t)) != g)
+
+ /* de_thread depends on thread_group_leader not being a pid based check */
+@@ -1903,8 +2019,15 @@ int same_thread_group(struct task_struct *p1, struct task_struct *p2)
+
+ static inline struct task_struct *next_thread(const struct task_struct *p)
+ {
+- return list_entry(rcu_dereference(p->thread_group.next),
++ struct task_struct *tsk;
++
++ tsk = list_entry(rcu_dereference(p->thread_group.next),
+ struct task_struct, thread_group);
++#ifdef CONFIG_VE
++ /* all threads should belong to ONE ve! */
++ BUG_ON(VE_TASK_INFO(tsk)->owner_env != VE_TASK_INFO(p)->owner_env);
++#endif
++ return tsk;
+ }
+
+ static inline int thread_group_empty(struct task_struct *p)
+@@ -1944,6 +2067,98 @@ static inline void unlock_task_sighand(struct task_struct *tsk,
+ spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
+ }
+
++#ifndef CONFIG_VE
++
++#define for_each_process_ve(p) for_each_process_all(p)
++#define do_each_thread_ve(g, t) do_each_thread_all(g, t)
++#define while_each_thread_ve(g, t) while_each_thread_all(g, t)
++#define first_task_ve() next_task_ve(&init_task)
++#define __first_task_ve(owner) next_task_ve(&init_task)
++#define __next_task_ve(owner, p) next_task_ve(p)
++#define next_task_ve(p) \
++ (next_task_all(p) != &init_task ? next_task_all(p) : NULL)
++
++#define ve_is_super(env) 1
++#define ve_accessible(target, owner) 1
++#define ve_accessible_strict(target, owner) 1
++#define ve_accessible_veid(target, owner) 1
++#define ve_accessible_strict_veid(target, owner) 1
++
++#define VEID(ve) 0
++
++#else /* CONFIG_VE */
++
++#include <linux/ve.h>
++
++#define ve_is_super(env) ((env) == get_ve0())
++
++#define ve_accessible_strict(target, owner) ((target) == (owner))
++static inline int ve_accessible(struct ve_struct *target,
++ struct ve_struct *owner)
++{
++ return ve_is_super(owner) || ve_accessible_strict(target, owner);
++}
++
++#define ve_accessible_strict_veid(target, owner) ((target) == (owner))
++static inline int ve_accessible_veid(envid_t target, envid_t owner)
++{
++ return get_ve0()->veid == owner ||
++ ve_accessible_strict_veid(target, owner);
++}
++
++#define VEID(ve) (ve->veid)
++
++static inline struct task_struct *ve_lh2task(struct ve_struct *ve,
++ struct list_head *lh)
++{
++ return lh == &ve->vetask_lh ? NULL :
++ list_entry(lh, struct task_struct, ve_task_info.vetask_list);
++}
++
++static inline struct task_struct *__first_task_ve(struct ve_struct *ve)
++{
++ struct task_struct *tsk;
++
++ if (unlikely(ve_is_super(ve))) {
++ tsk = next_task_all(&init_task);
++ if (tsk == &init_task)
++ tsk = NULL;
++ } else {
++ tsk = ve_lh2task(ve, rcu_dereference(ve->vetask_lh.next));
++ }
++ return tsk;
++}
++
++static inline struct task_struct *__next_task_ve(struct ve_struct *ve,
++ struct task_struct *tsk)
++{
++ if (unlikely(ve_is_super(ve))) {
++ tsk = next_task_all(tsk);
++ if (tsk == &init_task)
++ tsk = NULL;
++ } else {
++ BUG_ON(tsk->ve_task_info.owner_env != ve);
++ tsk = ve_lh2task(ve, rcu_dereference(tsk->
++ ve_task_info.vetask_list.next));
++ }
++ return tsk;
++}
++
++#define first_task_ve() __first_task_ve(get_exec_env())
++#define next_task_ve(p) __next_task_ve(get_exec_env(), p)
++/* no one uses prev_task_ve(), copy next_task_ve() if needed */
++
++#define for_each_process_ve(p) \
++ for (p = first_task_ve(); p != NULL ; p = next_task_ve(p))
++
++#define do_each_thread_ve(g, t) \
++ for (g = t = first_task_ve() ; g != NULL; g = t = next_task_ve(g)) do
++
++#define while_each_thread_ve(g, t) \
++ while ((t = next_thread(t)) != g)
++
++#endif /* CONFIG_VE */
++
+ #ifndef __HAVE_THREAD_FUNCTIONS
+
+ #define task_thread_info(task) ((struct thread_info *)(task)->stack)
+diff --git a/include/linux/sem.h b/include/linux/sem.h
+index 1b191c1..64f30a9 100644
+--- a/include/linux/sem.h
++++ b/include/linux/sem.h
+@@ -154,6 +154,9 @@ static inline void exit_sem(struct task_struct *tsk)
+ }
+ #endif
+
++int sysvipc_walk_sem(int (*func)(int, struct sem_array*, void *), void *arg);
++int sysvipc_setup_sem(key_t key, int semid, size_t size, int semflg);
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_SEM_H */
+diff --git a/include/linux/shm.h b/include/linux/shm.h
+index eca6235..c2b3bb5 100644
+--- a/include/linux/shm.h
++++ b/include/linux/shm.h
+@@ -83,6 +83,22 @@ struct shm_info {
+ };
+
+ #ifdef __KERNEL__
++
++#include <linux/ipc_namespace.h>
++
++#define IPC_SEM_IDS 0
++#define IPC_MSG_IDS 1
++#define IPC_SHM_IDS 2
++
++struct shm_file_data {
++ int id;
++ struct ipc_namespace *ns;
++ struct file *file;
++ const struct vm_operations_struct *vm_ops;
++};
++#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data))
++#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS])
++
+ struct shmid_kernel /* private to the kernel */
+ {
+ struct kern_ipc_perm shm_perm;
+@@ -97,6 +113,23 @@ struct shmid_kernel /* private to the kernel */
+ struct user_struct *mlock_user;
+ };
+
++/*
++ * shm_lock_(check_) routines are called in the paths where the rw_mutex
++ * is not held.
++ */
++static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
++{
++ struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id);
++
++ if (IS_ERR(ipcp))
++ return (struct shmid_kernel *)ipcp;
++
++ return container_of(ipcp, struct shmid_kernel, shm_perm);
++}
++
++#define shm_unlock(shp) \
++ ipc_unlock(&(shp)->shm_perm)
++
+ /* shm_mode upper byte flags */
+ #define SHM_DEST 01000 /* segment will be destroyed on last detach */
+ #define SHM_LOCKED 02000 /* segment will not be swapped */
+@@ -118,6 +151,12 @@ static inline int is_file_shm_hugepages(struct file *file)
+ }
+ #endif
+
++int sysvipc_walk_shm(int (*func)(struct shmid_kernel*, void *), void *arg);
++struct file * sysvipc_setup_shm(key_t key, int shmid, size_t size, int shmflg);
++extern const struct file_operations shmem_file_operations;
++extern const struct file_operations shm_file_operations;
++
++extern struct file_system_type tmpfs_fs_type;
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_SHM_H_ */
+diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
+index fd83f25..f09735a 100644
+--- a/include/linux/shmem_fs.h
++++ b/include/linux/shmem_fs.h
+@@ -23,6 +23,9 @@ struct shmem_inode_info {
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+ #endif
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *shmi_ub;
++#endif
+ };
+
+ struct shmem_sb_info {
+@@ -62,4 +65,7 @@ static inline void shmem_acl_destroy_inode(struct inode *inode)
+ }
+ #endif /* CONFIG_TMPFS_POSIX_ACL */
+
++int shmem_insertpage(struct inode * inode, unsigned long index,
++ swp_entry_t swap);
++
+ #endif
+diff --git a/include/linux/signal.h b/include/linux/signal.h
+index 84f997f..5adb84b 100644
+--- a/include/linux/signal.h
++++ b/include/linux/signal.h
+@@ -6,6 +6,8 @@
+
+ #ifdef __KERNEL__
+ #include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
+
+ /*
+ * Real Time signals may be queued.
+@@ -16,6 +18,9 @@ struct sigqueue {
+ int flags;
+ siginfo_t info;
+ struct user_struct *user;
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *sig_ub;
++#endif
+ };
+
+ /* flags values. */
+@@ -372,6 +377,8 @@ int unhandled_signal(struct task_struct *tsk, int sig);
+
+ void signals_init(void);
+
++extern struct kmem_cache *sigqueue_cachep;
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_SIGNAL_H */
+diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
+index 9099237..8731b5c 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -250,6 +250,8 @@ typedef unsigned char *sk_buff_data_t;
+ * @vlan_tci: vlan tag control information
+ */
+
++#include <bc/sock.h>
++
+ struct sk_buff {
+ /* These two members must be first. */
+ struct sk_buff *next;
+@@ -296,7 +298,13 @@ struct sk_buff {
+ peeked:1,
+ nf_trace:1;
+ __be16 protocol;
+-
++#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
++ __u8 brmark;
++#endif
++#ifdef CONFIG_VE
++ unsigned int accounted:1;
++ unsigned int redirected:1;
++#endif
+ void (*destructor)(struct sk_buff *skb);
+ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+ struct nf_conntrack *nfct;
+@@ -343,6 +351,8 @@ struct sk_buff {
+ *data;
+ unsigned int truesize;
+ atomic_t users;
++ struct skb_beancounter skb_bc;
++ struct ve_struct *owner_env;
+ };
+
+ #ifdef __KERNEL__
+@@ -350,6 +360,7 @@ struct sk_buff {
+ * Handling routines are only of interest to the kernel
+ */
+ #include <linux/slab.h>
++#include <bc/net.h>
+
+ #include <asm/system.h>
+
+@@ -1176,6 +1187,8 @@ static inline void pskb_trim_unique(struct sk_buff *skb, unsigned int len)
+ */
+ static inline void skb_orphan(struct sk_buff *skb)
+ {
++ ub_skb_uncharge(skb);
++
+ if (skb->destructor)
+ skb->destructor(skb);
+ skb->destructor = NULL;
+@@ -1678,6 +1691,26 @@ static inline void skb_init_secmark(struct sk_buff *skb)
+ { }
+ #endif
+
++#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
++static inline void skb_copy_brmark(struct sk_buff *to, const struct sk_buff *from)
++{
++ to->brmark = from->brmark;
++}
++
++static inline void skb_init_brmark(struct sk_buff *skb)
++{
++ skb->brmark = 0;
++}
++#else
++static inline void skb_copy_brmark(struct sk_buff *to, const struct sk_buff *from)
++{
++}
++
++static inline void skb_init_brmark(struct sk_buff *skb)
++{
++}
++#endif
++
+ static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping)
+ {
+ skb->queue_mapping = queue_mapping;
+diff --git a/include/linux/slab.h b/include/linux/slab.h
+index 5ff9676..9d7cee0 100644
+--- a/include/linux/slab.h
++++ b/include/linux/slab.h
+@@ -51,6 +51,26 @@
+ (unsigned long)ZERO_SIZE_PTR)
+
+ /*
++ * allocation rules: __GFP_UBC 0
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ * cache (SLAB_UBC) charge charge
++ * (usual caches: mm, vma, task_struct, ...)
++ *
++ * cache (SLAB_UBC | SLAB_NO_CHARGE) charge ---
++ * (ub_kmalloc) (kmalloc)
++ *
++ * cache (no UB flags) BUG() ---
++ * (nonub caches, mempools)
++ *
++ * pages charge ---
++ * (ub_vmalloc, (vmalloc,
++ * poll, fdsets, ...) non-ub allocs)
++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++ */
++#define SLAB_UBC 0x10000000UL /* alloc space for ubs ... */
++#define SLAB_NO_CHARGE 0x20000000UL /* ... but don't charge */
++
++/*
+ * struct kmem_cache related prototypes
+ */
+ void __init kmem_cache_init(void);
+@@ -65,7 +85,20 @@ void kmem_cache_free(struct kmem_cache *, void *);
+ unsigned int kmem_cache_size(struct kmem_cache *);
+ const char *kmem_cache_name(struct kmem_cache *);
+ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);
++extern void show_slab_info(void);
++int kmem_cache_objuse(struct kmem_cache *cachep);
++int kmem_obj_objuse(void *obj);
++int kmem_dname_objuse(void *obj);
++unsigned long ub_cache_growth(struct kmem_cache *cachep);
+
++#ifdef CONFIG_BEANCOUNTERS
++void kmem_mark_nocharge(struct kmem_cache *cachep);
++struct user_beancounter **ub_slab_ptr(struct kmem_cache *cachep, void *obj);
++struct user_beancounter *slab_ub(void *obj);
++#else
++static inline void kmem_mark_nocharge(struct kmem_cache *cachep) { }
++static inline struct user_beancounter *slab_ub(void *obj) { return NULL; }
++#endif
+ /*
+ * Please use this macro to create slab caches. Simply specify the
+ * name of the structure and maybe some flags that are listed above.
+diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h
+index 39c3a5e..de03bd0 100644
+--- a/include/linux/slab_def.h
++++ b/include/linux/slab_def.h
+@@ -15,6 +15,26 @@
+ #include <asm/cache.h> /* kmalloc_sizes.h needs L1_CACHE_BYTES */
+ #include <linux/compiler.h>
+
++/*
++ * DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON.
++ * 0 for faster, smaller code (especially in the critical paths).
++ *
++ * STATS - 1 to collect stats for /proc/slabinfo.
++ * 0 for faster, smaller code (especially in the critical paths).
++ *
++ * FORCED_DEBUG - 1 enables SLAB_RED_ZONE and SLAB_POISON (if possible)
++ */
++
++#ifdef CONFIG_DEBUG_SLAB
++#define SLAB_DEBUG 1
++#define SLAB_STATS 1
++#define SLAB_FORCED_DEBUG 1
++#else
++#define SLAB_DEBUG 0
++#define SLAB_STATS 0
++#define SLAB_FORCED_DEBUG 0
++#endif
++
+ /* Size description struct for general caches. */
+ struct cache_sizes {
+ size_t cs_size;
+@@ -24,6 +44,7 @@ struct cache_sizes {
+ #endif
+ };
+ extern struct cache_sizes malloc_sizes[];
++extern int malloc_cache_num;
+
+ void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
+ void *__kmalloc(size_t size, gfp_t flags);
+@@ -48,6 +69,8 @@ static inline void *kmalloc(size_t size, gfp_t flags)
+ __you_cannot_kmalloc_that_much();
+ }
+ found:
++ if (flags & __GFP_UBC)
++ i += malloc_cache_num;
+ #ifdef CONFIG_ZONE_DMA
+ if (flags & GFP_DMA)
+ return kmem_cache_alloc(malloc_sizes[i].cs_dmacachep,
+diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
+index 2f5c16b..9fd7575 100644
+--- a/include/linux/slub_def.h
++++ b/include/linux/slub_def.h
+@@ -95,6 +95,10 @@ struct kmem_cache {
+ struct kobject kobj; /* For sysfs */
+ #endif
+
++#ifdef CONFIG_BEANCOUNTERS
++ atomic_t grown;
++ int objuse;
++#endif
+ #ifdef CONFIG_NUMA
+ /*
+ * Defragmentation by allocating from a remote node.
+@@ -126,6 +130,19 @@ struct kmem_cache {
+ */
+ extern struct kmem_cache kmalloc_caches[PAGE_SHIFT + 1];
+
++#ifdef CONFIG_BEANCOUNTERS
++extern struct kmem_cache ub_kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
++static inline struct kmem_cache *__kmalloc_cache(gfp_t f, int idx)
++{
++ return (f & __GFP_UBC) ? &ub_kmalloc_caches[idx] : &kmalloc_caches[idx];
++}
++#else
++static inline struct kmem_cache *__kmalloc_cache(gfp_t flags, int idx)
++{
++ return &kmalloc_caches[idx];
++}
++#endif
++
+ /*
+ * Sorry that the following has to be that ugly but some versions of GCC
+ * have trouble with constant propagation and loops.
+@@ -184,14 +201,14 @@ static __always_inline int kmalloc_index(size_t size)
+ * This ought to end up with a global pointer to the right cache
+ * in kmalloc_caches.
+ */
+-static __always_inline struct kmem_cache *kmalloc_slab(size_t size)
++static __always_inline struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
+ {
+ int index = kmalloc_index(size);
+
+ if (index == 0)
+ return NULL;
+
+- return &kmalloc_caches[index];
++ return __kmalloc_cache(flags, index);
+ }
+
+ #ifdef CONFIG_ZONE_DMA
+@@ -216,7 +233,7 @@ static __always_inline void *kmalloc(size_t size, gfp_t flags)
+ return kmalloc_large(size, flags);
+
+ if (!(flags & SLUB_DMA)) {
+- struct kmem_cache *s = kmalloc_slab(size);
++ struct kmem_cache *s = kmalloc_slab(size, flags);
+
+ if (!s)
+ return ZERO_SIZE_PTR;
+@@ -235,7 +252,7 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
+ {
+ if (__builtin_constant_p(size) &&
+ size <= PAGE_SIZE && !(flags & SLUB_DMA)) {
+- struct kmem_cache *s = kmalloc_slab(size);
++ struct kmem_cache *s = kmalloc_slab(size, flags);
+
+ if (!s)
+ return ZERO_SIZE_PTR;
+diff --git a/include/linux/smp.h b/include/linux/smp.h
+index 66484d4..ac21923 100644
+--- a/include/linux/smp.h
++++ b/include/linux/smp.h
+@@ -12,6 +12,9 @@
+
+ extern void cpu_idle(void);
+
++struct pt_regs;
++typedef void (*smp_nmi_function)(struct pt_regs *regs, void *info);
++
+ struct call_single_data {
+ struct list_head list;
+ void (*func) (void *info);
+@@ -58,6 +61,8 @@ extern int __cpu_up(unsigned int cpunum);
+ */
+ extern void smp_cpus_done(unsigned int max_cpus);
+
++extern int smp_nmi_call_function(smp_nmi_function func, void *info, int wait);
++
+ /*
+ * Call a function on all other processors
+ */
+@@ -138,6 +143,12 @@ static inline void smp_send_reschedule(int cpu) { }
+ static inline void init_call_single_data(void)
+ {
+ }
++static inline int smp_nmi_call_function(smp_nmi_function func,
++ void *info, int wait)
++{
++ return 0;
++}
++
+ #endif /* !SMP */
+
+ /*
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index dc5086f..8038e33 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -300,6 +300,16 @@ struct ucred {
+ #define IPX_TYPE 1
+
+ #ifdef __KERNEL__
++
++#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
++ 16 for IP, 16 for IPX,
++ 24 for IPv6,
++ about 80 for AX.25
++ must be at least one bigger than
++ the AF_UNIX size (see net/unix/af_unix.c
++ :unix_mkname()).
++ */
++
+ extern int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
+ extern int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov,
+ int offset, int len);
+@@ -313,6 +323,8 @@ extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
+ extern int move_addr_to_user(struct sockaddr *kaddr, int klen, void __user *uaddr, int __user *ulen);
+ extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr);
+ extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
++extern int vz_security_family_check(int family);
++extern int vz_security_protocol_check(int protocol);
+
+ #endif
+ #endif /* not kernel and not glibc */
+diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
+index e5bfe01..8b00c3b 100644
+--- a/include/linux/sunrpc/clnt.h
++++ b/include/linux/sunrpc/clnt.h
+@@ -44,6 +44,7 @@ struct rpc_clnt {
+ cl_discrtry : 1,/* disconnect before retry */
+ cl_autobind : 1,/* use getport() */
+ cl_chatty : 1;/* be verbose */
++ unsigned int cl_broken : 1;/* no responce for too long */
+
+ struct rpc_rtt * cl_rtt; /* RTO estimator data */
+ const struct rpc_timeout *cl_timeout; /* Timeout strategy */
+@@ -57,6 +58,7 @@ struct rpc_clnt {
+ struct rpc_rtt cl_rtt_default;
+ struct rpc_timeout cl_timeout_default;
+ struct rpc_program * cl_program;
++ unsigned long cl_pr_time;
+ char cl_inline_name[32];
+ };
+
+diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
+index 4d80a11..ceee9a3 100644
+--- a/include/linux/sunrpc/xprt.h
++++ b/include/linux/sunrpc/xprt.h
+@@ -24,6 +24,14 @@
+ #define RPC_MAX_SLOT_TABLE (128U)
+
+ /*
++ * Grand abort timeout (stop the client if occures)
++ */
++extern int xprt_abort_timeout;
++
++#define RPC_MIN_ABORT_TIMEOUT 300
++#define RPC_MAX_ABORT_TIMEOUT INT_MAX
++
++/*
+ * This describes a timeout strategy
+ */
+ struct rpc_timeout {
+@@ -123,6 +131,7 @@ struct rpc_xprt_ops {
+ struct rpc_xprt {
+ struct kref kref; /* Reference count */
+ struct rpc_xprt_ops * ops; /* transport methods */
++ struct ve_struct * owner_env; /* VE owner of mount */
+
+ const struct rpc_timeout *timeout; /* timeout parms */
+ struct sockaddr_storage addr; /* server address */
+diff --git a/include/linux/swap.h b/include/linux/swap.h
+index de40f16..74394ee 100644
+--- a/include/linux/swap.h
++++ b/include/linux/swap.h
+@@ -18,6 +18,7 @@ struct bio;
+ #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */
+ #define SWAP_FLAG_PRIO_MASK 0x7fff
+ #define SWAP_FLAG_PRIO_SHIFT 0
++#define SWAP_FLAG_READONLY 0x40000000 /* set if swap is read-only */
+
+ static inline int current_is_kswapd(void)
+ {
+@@ -93,6 +94,7 @@ struct address_space;
+ struct sysinfo;
+ struct writeback_control;
+ struct zone;
++struct user_beancounter;
+
+ /*
+ * A swap extent maps a range of a swapfile's PAGE_SIZE pages onto a range of
+@@ -122,6 +124,7 @@ enum {
+ SWP_ACTIVE = (SWP_USED | SWP_WRITEOK),
+ /* add others here before... */
+ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */
++ SWP_READONLY = (1 << 2),
+ };
+
+ #define SWAP_CLUSTER_MAX 32
+@@ -132,6 +135,7 @@ enum {
+ /*
+ * The in-memory structure used to track swap areas.
+ */
++struct user_beancounter;
+ struct swap_info_struct {
+ unsigned int flags;
+ int prio; /* swap priority */
+@@ -149,6 +153,9 @@ struct swap_info_struct {
+ unsigned int max;
+ unsigned int inuse_pages;
+ int next; /* next entry on swap list */
++#ifdef CONFIG_BC_SWAP_ACCOUNTING
++ struct user_beancounter **swap_ubs;
++#endif
+ };
+
+ struct swap_list_t {
+@@ -156,9 +163,21 @@ struct swap_list_t {
+ int next; /* swapfile to be used next */
+ };
+
++extern struct swap_list_t swap_list;
++extern struct swap_info_struct swap_info[MAX_SWAPFILES];
++
+ /* Swap 50% full? Release swapcache more aggressively.. */
+ #define vm_swap_full() (nr_swap_pages*2 < total_swap_pages)
+
++/* linux/mm/oom_kill.c */
++extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order);
++extern int register_oom_notifier(struct notifier_block *nb);
++extern int unregister_oom_notifier(struct notifier_block *nb);
++extern int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
++ struct mem_cgroup *mem, const char *message);
++extern struct task_struct *select_bad_process(struct user_beancounter *ub,
++ struct mem_cgroup *memcg);
++
+ /* linux/mm/page_alloc.c */
+ extern unsigned long totalram_pages;
+ extern unsigned long totalreserve_pages;
+@@ -226,6 +245,8 @@ extern void show_swap_cache_info(void);
+ extern int add_to_swap(struct page *, gfp_t);
+ extern int add_to_swap_cache(struct page *, swp_entry_t, gfp_t);
+ extern void __delete_from_swap_cache(struct page *);
++extern int __add_to_swap_cache(struct page *page,
++ swp_entry_t entry, gfp_t gfp_mask);
+ extern void delete_from_swap_cache(struct page *);
+ extern void free_page_and_swap_cache(struct page *);
+ extern void free_pages_and_swap_cache(struct page **, int);
+@@ -238,7 +259,7 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t,
+ /* linux/mm/swapfile.c */
+ extern long total_swap_pages;
+ extern void si_swapinfo(struct sysinfo *);
+-extern swp_entry_t get_swap_page(void);
++extern swp_entry_t get_swap_page(struct user_beancounter *);
+ extern swp_entry_t get_swap_page_of_type(int);
+ extern int swap_duplicate(swp_entry_t);
+ extern int valid_swaphandles(swp_entry_t, unsigned long *);
+@@ -251,6 +272,7 @@ extern sector_t swapdev_block(int, pgoff_t);
+ extern struct swap_info_struct *get_swap_info_struct(unsigned);
+ extern int can_share_swap_page(struct page *);
+ extern int remove_exclusive_swap_page(struct page *);
++extern int try_to_remove_exclusive_swap_page(struct page *);
+ struct backing_dev_info;
+
+ /* linux/mm/thrash.c */
+@@ -339,7 +361,7 @@ static inline int remove_exclusive_swap_page(struct page *p)
+ return 0;
+ }
+
+-static inline swp_entry_t get_swap_page(void)
++static inline swp_entry_t get_swap_page(struct user_beancounter *ub)
+ {
+ swp_entry_t entry;
+ entry.val = 0;
+diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
+index d0437f3..28eab78 100644
+--- a/include/linux/sysctl.h
++++ b/include/linux/sysctl.h
+@@ -1102,10 +1102,15 @@ struct ctl_table_header *__register_sysctl_paths(
+ struct ctl_table_header *register_sysctl_table(struct ctl_table * table);
+ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
+ struct ctl_table *table);
++struct ctl_table_header *register_sysctl_glob_table(struct ctl_table *, int);
++struct ctl_table_header *register_sysctl_glob_paths(const struct ctl_path *,
++ struct ctl_table *, int);
+
+ void unregister_sysctl_table(struct ctl_table_header * table);
+ int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table);
+
++extern int ve_allow_kthreads;
++
+ #endif /* __KERNEL__ */
+
+ #endif /* _LINUX_SYSCTL_H */
+diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
+index 37fa241..3075594 100644
+--- a/include/linux/sysfs.h
++++ b/include/linux/sysfs.h
+@@ -19,6 +19,7 @@
+
+ struct kobject;
+ struct module;
++struct sysfs_open_dirent;
+
+ /* FIXME
+ * The *owner field is no longer used, but leave around
+@@ -78,6 +79,66 @@ struct sysfs_ops {
+ ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
+ };
+
++/* type-specific structures for sysfs_dirent->s_* union members */
++struct sysfs_elem_dir {
++ struct kobject *kobj;
++ /* children list starts here and goes through sd->s_sibling */
++ struct sysfs_dirent *children;
++};
++
++struct sysfs_elem_symlink {
++ struct sysfs_dirent *target_sd;
++};
++
++struct sysfs_elem_attr {
++ struct attribute *attr;
++ struct sysfs_open_dirent *open;
++};
++
++struct sysfs_elem_bin_attr {
++ struct bin_attribute *bin_attr;
++};
++
++/*
++ * sysfs_dirent - the building block of sysfs hierarchy. Each and
++ * every sysfs node is represented by single sysfs_dirent.
++ *
++ * As long as s_count reference is held, the sysfs_dirent itself is
++ * accessible. Dereferencing s_elem or any other outer entity
++ * requires s_active reference.
++ */
++struct sysfs_dirent {
++ atomic_t s_count;
++ atomic_t s_active;
++ struct sysfs_dirent *s_parent;
++ struct sysfs_dirent *s_sibling;
++ const char *s_name;
++
++ union {
++ struct sysfs_elem_dir s_dir;
++ struct sysfs_elem_symlink s_symlink;
++ struct sysfs_elem_attr s_attr;
++ struct sysfs_elem_bin_attr s_bin_attr;
++ };
++
++ unsigned int s_flags;
++ ino_t s_ino;
++ umode_t s_mode;
++ struct iattr *s_iattr;
++};
++
++#define SD_DEACTIVATED_BIAS INT_MIN
++
++#define SYSFS_TYPE_MASK 0x00ff
++#define SYSFS_DIR 0x0001
++#define SYSFS_KOBJ_ATTR 0x0002
++#define SYSFS_KOBJ_BIN_ATTR 0x0004
++#define SYSFS_KOBJ_LINK 0x0008
++#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
++
++#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK
++#define SYSFS_FLAG_REMOVED 0x0200
++
+ #ifdef CONFIG_SYSFS
+
+ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
+@@ -121,6 +182,8 @@ void sysfs_notify(struct kobject *kobj, char *dir, char *attr);
+
+ extern int __must_check sysfs_init(void);
+
++extern struct file_system_type sysfs_fs_type;
++
+ #else /* CONFIG_SYSFS */
+
+ static inline int sysfs_schedule_callback(struct kobject *kobj,
+diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h
+index 4d090f9..ba40964 100644
+--- a/include/linux/task_io_accounting_ops.h
++++ b/include/linux/task_io_accounting_ops.h
+@@ -5,10 +5,12 @@
+ #define __TASK_IO_ACCOUNTING_OPS_INCLUDED
+
+ #include <linux/sched.h>
++#include <bc/io_acct.h>
+
+ #ifdef CONFIG_TASK_IO_ACCOUNTING
+ static inline void task_io_account_read(size_t bytes)
+ {
++ ub_io_account_read(bytes);
+ current->ioac.read_bytes += bytes;
+ }
+
+@@ -21,8 +23,14 @@ static inline unsigned long task_io_get_inblock(const struct task_struct *p)
+ return p->ioac.read_bytes >> 9;
+ }
+
+-static inline void task_io_account_write(size_t bytes)
++static inline void task_io_account_write(struct page *page, size_t bytes,
++ int sync)
+ {
++ if (sync)
++ ub_io_account_write(bytes);
++ else
++ ub_io_account_dirty(page, bytes);
++
+ current->ioac.write_bytes += bytes;
+ }
+
+@@ -37,6 +45,7 @@ static inline unsigned long task_io_get_oublock(const struct task_struct *p)
+
+ static inline void task_io_account_cancelled_write(size_t bytes)
+ {
++ ub_io_account_write_cancelled(bytes);
+ current->ioac.cancelled_write_bytes += bytes;
+ }
+
+@@ -64,7 +73,8 @@ static inline unsigned long task_io_get_inblock(const struct task_struct *p)
+ return 0;
+ }
+
+-static inline void task_io_account_write(size_t bytes)
++static inline void task_io_account_write(struct page *page, size_t bytes,
++ int sync)
+ {
+ }
+
+diff --git a/include/linux/tty.h b/include/linux/tty.h
+index 0cbec74..a0db563 100644
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -269,6 +269,7 @@ struct tty_struct {
+ /* If the tty has a pending do_SAK, queue it here - akpm */
+ struct work_struct SAK_work;
+ struct tty_port *port;
++ struct ve_struct *owner_env;
+ };
+
+ /* tty magic number */
+@@ -298,6 +299,7 @@ struct tty_struct {
+ #define TTY_HUPPED 18 /* Post driver->hangup() */
+ #define TTY_FLUSHING 19 /* Flushing to ldisc in progress */
+ #define TTY_FLUSHPENDING 20 /* Queued buffer flush pending */
++#define TTY_CHARGED 21 /* Charged as ub resource */
+
+ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
+
+diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
+index 16d2794..981f9b1 100644
+--- a/include/linux/tty_driver.h
++++ b/include/linux/tty_driver.h
+@@ -260,8 +260,19 @@ struct tty_driver {
+
+ const struct tty_operations *ops;
+ struct list_head tty_drivers;
++ struct ve_struct *owner_env;
+ };
+
++#ifdef CONFIG_UNIX98_PTYS
++extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
++extern struct tty_driver *pts_driver; /* Unix98 pty slaves; for /dev/ptmx */
++#endif
++
++#ifdef CONFIG_LEGACY_PTYS
++extern struct tty_driver *pty_driver;
++extern struct tty_driver *pty_slave_driver;
++#endif
++
+ extern struct list_head tty_drivers;
+
+ struct tty_driver *alloc_tty_driver(int lines);
+@@ -270,6 +281,9 @@ void tty_set_operations(struct tty_driver *driver,
+ const struct tty_operations *op);
+ extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
+
++int init_ve_tty_class(void);
++void fini_ve_tty_class(void);
++
+ /* tty driver magic number */
+ #define TTY_DRIVER_MAGIC 0x5402
+
+diff --git a/include/linux/types.h b/include/linux/types.h
+index d4a9ce6..dcdaf75 100644
+--- a/include/linux/types.h
++++ b/include/linux/types.h
+@@ -29,6 +29,11 @@ typedef __kernel_timer_t timer_t;
+ typedef __kernel_clockid_t clockid_t;
+ typedef __kernel_mqd_t mqd_t;
+
++#ifndef __ENVID_T_DEFINED__
++typedef unsigned envid_t;
++#define __ENVID_T_DEFINED__
++#endif
++
+ #ifdef __KERNEL__
+ typedef _Bool bool;
+
+diff --git a/include/linux/utsname.h b/include/linux/utsname.h
+index 1123267..ec24d89 100644
+--- a/include/linux/utsname.h
++++ b/include/linux/utsname.h
+@@ -43,6 +43,7 @@ struct uts_namespace {
+ struct new_utsname name;
+ };
+ extern struct uts_namespace init_uts_ns;
++extern struct new_utsname virt_utsname;
+
+ #ifdef CONFIG_UTS_NS
+ static inline void get_uts_ns(struct uts_namespace *ns)
+diff --git a/include/linux/ve.h b/include/linux/ve.h
+new file mode 100644
+index 0000000..f1b84d4
+--- /dev/null
++++ b/include/linux/ve.h
+@@ -0,0 +1,353 @@
++/*
++ * include/linux/ve.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _LINUX_VE_H
++#define _LINUX_VE_H
++
++#include <linux/types.h>
++#include <linux/capability.h>
++#include <linux/sysctl.h>
++#include <linux/net.h>
++#include <linux/vzstat.h>
++#include <linux/kobject.h>
++#include <linux/pid.h>
++#include <linux/socket.h>
++#include <net/inet_frag.h>
++
++#ifdef VZMON_DEBUG
++# define VZTRACE(fmt,args...) \
++ printk(KERN_DEBUG fmt, ##args)
++#else
++# define VZTRACE(fmt,args...)
++#endif /* VZMON_DEBUG */
++
++struct tty_driver;
++struct devpts_config;
++struct task_struct;
++struct new_utsname;
++struct file_system_type;
++struct icmp_mib;
++struct ip_mib;
++struct tcp_mib;
++struct udp_mib;
++struct linux_mib;
++struct fib_info;
++struct fib_rule;
++struct veip_struct;
++struct ve_monitor;
++struct nsproxy;
++
++#if defined(CONFIG_VE) && defined(CONFIG_INET)
++struct fib_table;
++#ifdef CONFIG_VE_IPTABLES
++struct xt_table;
++struct nf_conn;
++
++#define FRAG6Q_HASHSZ 64
++
++struct ve_nf_conntrack {
++ struct hlist_head *_bysource;
++ struct nf_nat_protocol **_nf_nat_protos;
++ int _nf_nat_vmalloced;
++ struct xt_table *_nf_nat_table;
++ struct nf_conntrack_l3proto *_nf_nat_l3proto;
++ atomic_t _nf_conntrack_count;
++ int _nf_conntrack_max;
++ struct hlist_head *_nf_conntrack_hash;
++ int _nf_conntrack_checksum;
++ int _nf_conntrack_vmalloc;
++ struct hlist_head _unconfirmed;
++ struct hlist_head *_nf_ct_expect_hash;
++ unsigned int _nf_ct_expect_vmalloc;
++ unsigned int _nf_ct_expect_count;
++ unsigned int _nf_ct_expect_max;
++ struct hlist_head *_nf_ct_helper_hash;
++ unsigned int _nf_ct_helper_vmalloc;
++#ifdef CONFIG_SYSCTL
++ /* l4 stuff: */
++ unsigned long _nf_ct_icmp_timeout;
++ unsigned long _nf_ct_icmpv6_timeout;
++ unsigned int _nf_ct_udp_timeout;
++ unsigned int _nf_ct_udp_timeout_stream;
++ unsigned int _nf_ct_generic_timeout;
++ unsigned int _nf_ct_log_invalid;
++ unsigned int _nf_ct_tcp_timeout_max_retrans;
++ unsigned int _nf_ct_tcp_timeout_unacknowledged;
++ int _nf_ct_tcp_be_liberal;
++ int _nf_ct_tcp_loose;
++ int _nf_ct_tcp_max_retrans;
++ unsigned int _nf_ct_tcp_timeouts[10];
++ struct ctl_table_header *_icmp_sysctl_header;
++ unsigned int _tcp_sysctl_table_users;
++ struct ctl_table_header *_tcp_sysctl_header;
++ unsigned int _udp_sysctl_table_users;
++ struct ctl_table_header *_udp_sysctl_header;
++ struct ctl_table_header *_icmpv6_sysctl_header;
++ struct ctl_table_header *_generic_sysctl_header;
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ struct ctl_table_header *_icmp_compat_sysctl_header;
++ struct ctl_table_header *_tcp_compat_sysctl_header;
++ struct ctl_table_header *_udp_compat_sysctl_header;
++ struct ctl_table_header *_generic_compat_sysctl_header;
++#endif
++ /* l4 protocols sysctl tables: */
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_icmp;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_tcp4;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_icmpv6;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_tcp6;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_udp4;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_udp6;
++ struct nf_conntrack_l4proto *_nf_conntrack_l4proto_generic;
++ struct nf_conntrack_l4proto **_nf_ct_protos[PF_MAX];
++ /* l3 protocols sysctl tables: */
++ struct nf_conntrack_l3proto *_nf_conntrack_l3proto_ipv4;
++ struct nf_conntrack_l3proto *_nf_ct_l3protos[AF_MAX];
++ /* sysctl standalone stuff: */
++ struct ctl_table_header *_nf_ct_sysctl_header;
++ struct ctl_table_header *_nf_ct_netfilter_header;
++ ctl_table *_nf_ct_sysctl_table;
++ ctl_table *_nf_ct_netfilter_table;
++ ctl_table *_nf_ct_net_table;
++ ctl_table *_ip_ct_netfilter_table;
++ struct ctl_table_header *_ip_ct_sysctl_header;
++ int _nf_ct_log_invalid_proto_min;
++ int _nf_ct_log_invalid_proto_max;
++#endif /* CONFIG_SYSCTL */
++};
++#endif
++#endif
++
++struct ve_cpu_stats {
++ cycles_t idle_time;
++ cycles_t iowait_time;
++ cycles_t strt_idle_time;
++ cycles_t used_time;
++ seqcount_t stat_lock;
++ int nr_running;
++ int nr_unint;
++ int nr_iowait;
++ cputime64_t user;
++ cputime64_t nice;
++ cputime64_t system;
++} ____cacheline_aligned;
++
++struct ve_ipt_recent;
++struct ve_xt_hashlimit;
++struct svc_rqst;
++
++struct cgroup;
++struct css_set;
++
++struct ve_struct {
++ struct list_head ve_list;
++
++ envid_t veid;
++ struct list_head vetask_lh;
++ /* capability bounding set */
++ kernel_cap_t ve_cap_bset;
++ atomic_t pcounter;
++ /* ref counter to ve from ipc */
++ atomic_t counter;
++ unsigned int class_id;
++ struct rw_semaphore op_sem;
++ int is_running;
++ int is_locked;
++ atomic_t suspend;
++ /* see vzcalluser.h for VE_FEATURE_XXX definitions */
++ __u64 features;
++
++/* VE's root */
++ struct path root_path;
++
++ struct file_system_type *proc_fstype;
++ struct vfsmount *proc_mnt;
++ struct proc_dir_entry *proc_root;
++
++/* BSD pty's */
++#ifdef CONFIG_LEGACY_PTYS
++ struct tty_driver *pty_driver;
++ struct tty_driver *pty_slave_driver;
++#endif
++#ifdef CONFIG_UNIX98_PTYS
++ struct tty_driver *ptm_driver;
++ struct tty_driver *pts_driver;
++ struct ida *allocated_ptys;
++ struct file_system_type *devpts_fstype;
++ struct vfsmount *devpts_mnt;
++ struct dentry *devpts_root;
++ struct devpts_config *devpts_config;
++#endif
++
++ struct ve_nfs_context *nfs_context;
++
++ struct file_system_type *shmem_fstype;
++ struct vfsmount *shmem_mnt;
++#ifdef CONFIG_SYSFS
++ struct file_system_type *sysfs_fstype;
++ struct vfsmount *sysfs_mnt;
++ struct super_block *sysfs_sb;
++ struct sysfs_dirent *_sysfs_root;
++#endif
++#ifndef CONFIG_SYSFS_DEPRECATED
++ struct kobject *_virtual_dir;
++#endif
++ struct kset *class_kset;
++ struct kset *devices_kset;
++ struct kobject *dev_kobj;
++ struct kobject *dev_char_kobj;
++ struct kobject *dev_block_kobj;
++ struct class *tty_class;
++ struct class *mem_class;
++
++#ifdef CONFIG_NET
++ struct class *net_class;
++#ifdef CONFIG_INET
++ unsigned long rt_flush_required;
++#endif
++#endif
++#if defined(CONFIG_VE_NETDEV) || defined (CONFIG_VE_NETDEV_MODULE)
++ struct veip_struct *veip;
++ struct net_device *_venet_dev;
++#endif
++
++/* per VE CPU stats*/
++ struct timespec start_timespec;
++ u64 start_jiffies; /* Deprecated */
++ cycles_t start_cycles;
++ unsigned long avenrun[3]; /* loadavg data */
++
++ cycles_t cpu_used_ve;
++ struct kstat_lat_pcpu_struct sched_lat_ve;
++
++#ifdef CONFIG_INET
++ struct venet_stat *stat;
++#ifdef CONFIG_VE_IPTABLES
++/* core/netfilter.c virtualization */
++ struct xt_table *_ve_ipt_filter_pf; /* packet_filter struct */
++ struct xt_table *_ve_ip6t_filter_pf;
++ struct xt_table *_ipt_mangle_table;
++ struct xt_table *_ip6t_mangle_table;
++ struct list_head _xt_tables[NPROTO];
++
++ __u64 ipt_mask;
++ __u64 _iptables_modules;
++ struct ve_nf_conntrack *_nf_conntrack;
++ struct ve_ipt_recent *_ipt_recent;
++ struct ve_xt_hashlimit *_xt_hashlimit;
++#endif /* CONFIG_VE_IPTABLES */
++
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ struct ipstats_mib *_ipv6_statistics[2];
++ struct icmpv6_mib *_icmpv6_statistics[2];
++ struct icmpv6msg_mib *_icmpv6msg_statistics[2];
++ struct udp_mib *_udp_stats_in6[2];
++ struct udp_mib *_udplite_stats_in6[2];
++#endif
++#endif
++ wait_queue_head_t *_log_wait;
++ unsigned *_log_start;
++ unsigned *_log_end;
++ unsigned *_logged_chars;
++ char *log_buf;
++#define VE_DEFAULT_LOG_BUF_LEN 4096
++
++ struct ve_cpu_stats *cpu_stats;
++ unsigned long down_at;
++ struct list_head cleanup_list;
++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE)
++ struct list_head _fuse_conn_list;
++ struct super_block *_fuse_control_sb;
++
++ struct file_system_type *fuse_fs_type;
++ struct file_system_type *fuse_ctl_fs_type;
++#endif
++ unsigned long jiffies_fixup;
++ unsigned char disable_net;
++ struct ve_monitor *monitor;
++ struct proc_dir_entry *monitor_proc;
++ unsigned long meminfo_val;
++
++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) \
++ || defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
++ unsigned int _nlmsvc_users;
++ struct task_struct* _nlmsvc_task;
++ int _nlmsvc_grace_period;
++ unsigned long _nlmsvc_timeout;
++ struct svc_rqst* _nlmsvc_rqst;
++#endif
++
++ struct nsproxy *ve_ns;
++ struct net *ve_netns;
++ struct cgroup *ve_cgroup;
++ struct css_set *ve_css_set;
++};
++
++int init_ve_cgroups(struct ve_struct *ve);
++void fini_ve_cgroups(struct ve_struct *ve);
++
++#define VE_CPU_STATS(ve, cpu) (per_cpu_ptr((ve)->cpu_stats, cpu))
++
++extern int nr_ve;
++extern struct proc_dir_entry *proc_vz_dir;
++extern struct proc_dir_entry *glob_proc_vz_dir;
++
++#ifdef CONFIG_VE
++
++void do_update_load_avg_ve(void);
++void do_env_free(struct ve_struct *ptr);
++
++static inline struct ve_struct *get_ve(struct ve_struct *ptr)
++{
++ if (ptr != NULL)
++ atomic_inc(&ptr->counter);
++ return ptr;
++}
++
++static inline void put_ve(struct ve_struct *ptr)
++{
++ if (ptr && atomic_dec_and_test(&ptr->counter)) {
++ BUG_ON(atomic_read(&ptr->pcounter) > 0);
++ BUG_ON(ptr->is_running);
++ do_env_free(ptr);
++ }
++}
++
++static inline void pget_ve(struct ve_struct *ptr)
++{
++ atomic_inc(&ptr->pcounter);
++}
++
++void ve_cleanup_schedule(struct ve_struct *);
++static inline void pput_ve(struct ve_struct *ptr)
++{
++ if (unlikely(atomic_dec_and_test(&ptr->pcounter)))
++ ve_cleanup_schedule(ptr);
++}
++
++extern spinlock_t ve_cleanup_lock;
++extern struct list_head ve_cleanup_list;
++extern struct task_struct *ve_cleanup_thread;
++
++extern unsigned long long ve_relative_clock(struct timespec * ts);
++
++#ifdef CONFIG_FAIRSCHED
++#define ve_cpu_online_map(ve, mask) fairsched_cpu_online_map(ve->veid, mask)
++#else
++#define ve_cpu_online_map(ve, mask) do { *(mask) = cpu_online_map; } while (0)
++#endif
++#else /* CONFIG_VE */
++#define ve_utsname system_utsname
++#define get_ve(ve) (NULL)
++#define put_ve(ve) do { } while (0)
++#define pget_ve(ve) do { } while (0)
++#define pput_ve(ve) do { } while (0)
++#endif /* CONFIG_VE */
++
++#endif /* _LINUX_VE_H */
+diff --git a/include/linux/ve_nfs.h b/include/linux/ve_nfs.h
+new file mode 100644
+index 0000000..8f2e8f8
+--- /dev/null
++++ b/include/linux/ve_nfs.h
+@@ -0,0 +1,30 @@
++/*
++ * linux/include/ve_nfs.h
++ *
++ * VE context for NFS
++ *
++ * Copyright (C) 2007 SWsoft
++ */
++
++#ifndef __VE_NFS_H__
++#define __VE_NFS_H__
++
++#ifdef CONFIG_VE
++
++#include <linux/ve.h>
++
++#define NFS_CTX_FIELD(arg) (get_exec_env()->_##arg)
++
++#else /* CONFIG_VE */
++
++#define NFS_CTX_FIELD(arg) _##arg
++
++#endif /* CONFIG_VE */
++
++#define nlmsvc_grace_period NFS_CTX_FIELD(nlmsvc_grace_period)
++#define nlmsvc_timeout NFS_CTX_FIELD(nlmsvc_timeout)
++#define nlmsvc_users NFS_CTX_FIELD(nlmsvc_users)
++#define nlmsvc_task NFS_CTX_FIELD(nlmsvc_task)
++#define nlmsvc_rqst NFS_CTX_FIELD(nlmsvc_rqst)
++
++#endif
+diff --git a/include/linux/ve_proto.h b/include/linux/ve_proto.h
+new file mode 100644
+index 0000000..26ca897
+--- /dev/null
++++ b/include/linux/ve_proto.h
+@@ -0,0 +1,89 @@
++/*
++ * include/linux/ve_proto.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __VE_H__
++#define __VE_H__
++
++#ifdef CONFIG_VE
++
++struct ve_struct;
++
++#ifdef CONFIG_INET
++void tcp_v4_kill_ve_sockets(struct ve_struct *envid);
++#ifdef CONFIG_VE_NETDEV
++int venet_init(void);
++#endif
++#endif
++
++extern struct list_head ve_list_head;
++#define for_each_ve(ve) list_for_each_entry((ve), &ve_list_head, ve_list)
++extern rwlock_t ve_list_lock;
++extern struct ve_struct *get_ve_by_id(envid_t);
++extern struct ve_struct *__find_ve_by_id(envid_t);
++
++struct env_create_param3;
++extern int real_env_create(envid_t veid, unsigned flags, u32 class_id,
++ struct env_create_param3 *data, int datalen);
++extern void ve_move_task(struct task_struct *, struct ve_struct *);
++
++int set_device_perms_ve(struct ve_struct *, unsigned, dev_t, unsigned);
++int get_device_perms_ve(int dev_type, dev_t dev, int access_mode);
++int devperms_seq_show(struct seq_file *m, void *v);
++
++enum {
++ VE_SS_CHAIN,
++
++ VE_MAX_CHAINS
++};
++
++typedef int ve_hook_init_fn(void *data);
++typedef void ve_hook_fini_fn(void *data);
++
++struct ve_hook
++{
++ ve_hook_init_fn *init;
++ ve_hook_fini_fn *fini;
++ struct module *owner;
++
++ /* Functions are called in ascending priority */
++ int priority;
++
++ /* Private part */
++ struct list_head list;
++};
++
++enum {
++ HOOK_PRIO_DEFAULT = 0,
++
++ HOOK_PRIO_FS = HOOK_PRIO_DEFAULT,
++
++ HOOK_PRIO_NET_PRE,
++ HOOK_PRIO_NET,
++ HOOK_PRIO_NET_POST,
++
++ HOOK_PRIO_AFTERALL = INT_MAX
++};
++
++void *ve_seq_start(struct seq_file *m, loff_t *pos);
++void *ve_seq_next(struct seq_file *m, void *v, loff_t *pos);
++void ve_seq_stop(struct seq_file *m, void *v);
++
++extern int ve_hook_iterate_init(int chain, void *data);
++extern void ve_hook_iterate_fini(int chain, void *data);
++
++extern void ve_hook_register(int chain, struct ve_hook *vh);
++extern void ve_hook_unregister(struct ve_hook *vh);
++#else /* CONFIG_VE */
++#define ve_hook_register(ch, vh) do { } while (0)
++#define ve_hook_unregister(ve) do { } while (0)
++
++#define get_device_perms_ve(t, d, a) (0)
++#endif /* CONFIG_VE */
++#endif
+diff --git a/include/linux/ve_task.h b/include/linux/ve_task.h
+new file mode 100644
+index 0000000..4b7d722
+--- /dev/null
++++ b/include/linux/ve_task.h
+@@ -0,0 +1,73 @@
++/*
++ * include/linux/ve_task.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __VE_TASK_H__
++#define __VE_TASK_H__
++
++#include <linux/seqlock.h>
++#include <asm/timex.h>
++
++struct ve_task_info {
++/* virtualization */
++ struct ve_struct *owner_env;
++ struct ve_struct *exec_env;
++ struct ve_struct *saved_env;
++ struct list_head vetask_list;
++ struct dentry *glob_proc_dentry;
++/* statistics: scheduling latency */
++ cycles_t sleep_time;
++ cycles_t sched_time;
++ cycles_t sleep_stamp;
++ cycles_t wakeup_stamp;
++ seqcount_t wakeup_lock;
++};
++
++#define VE_TASK_INFO(task) (&(task)->ve_task_info)
++#define VE_TASK_LIST_2_TASK(lh) \
++ list_entry(lh, struct task_struct, ve_task_info.vetask_list)
++
++#ifdef CONFIG_VE
++extern struct ve_struct ve0;
++#define get_ve0() (&ve0)
++
++#define ve_save_context(t) do { \
++ t->ve_task_info.saved_env = \
++ t->ve_task_info.exec_env; \
++ t->ve_task_info.exec_env = get_ve0(); \
++ } while (0)
++#define ve_restore_context(t) do { \
++ t->ve_task_info.exec_env = \
++ t->ve_task_info.saved_env; \
++ } while (0)
++
++#define get_exec_env() (current->ve_task_info.exec_env)
++#define set_exec_env(ve) ({ \
++ struct ve_task_info *vi; \
++ struct ve_struct *old, *new; \
++ \
++ vi = &current->ve_task_info; \
++ old = vi->exec_env; \
++ new = ve; \
++ if (unlikely(new == NULL)) { \
++ printk("%s: NULL exec env (%s)\n", __func__, #ve);\
++ new = get_ve0(); \
++ } \
++ vi->exec_env = new; \
++ old; \
++ })
++#else
++#define get_ve0() (NULL)
++#define get_exec_env() (NULL)
++#define set_exec_env(new_env) (NULL)
++#define ve_save_context(t) do { } while (0)
++#define ve_restore_context(t) do { } while (0)
++#endif
++
++#endif /* __VE_TASK_H__ */
+diff --git a/include/linux/veip.h b/include/linux/veip.h
+new file mode 100644
+index 0000000..745f1ec
+--- /dev/null
++++ b/include/linux/veip.h
+@@ -0,0 +1,15 @@
++#ifndef __VE_IP_H_
++#define __VE_IP_H_
++
++struct ve_addr_struct {
++ int family;
++ __u32 key[4];
++};
++
++struct sockaddr;
++
++extern void veaddr_print(char *, int, struct ve_addr_struct *);
++extern int sockaddr_to_veaddr(struct sockaddr __user *uaddr, int addrlen,
++ struct ve_addr_struct *veaddr);
++
++#endif
+diff --git a/include/linux/venet.h b/include/linux/venet.h
+new file mode 100644
+index 0000000..14cf89e
+--- /dev/null
++++ b/include/linux/venet.h
+@@ -0,0 +1,86 @@
++/*
++ * include/linux/venet.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _VENET_H
++#define _VENET_H
++
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/vzcalluser.h>
++#include <linux/veip.h>
++#include <linux/netdevice.h>
++
++#define VEIP_HASH_SZ 512
++
++struct ve_struct;
++struct venet_stat;
++struct venet_stats {
++ struct net_device_stats stats;
++ struct net_device_stats *real_stats;
++};
++
++struct ip_entry_struct
++{
++ struct ve_addr_struct addr;
++ struct ve_struct *active_env;
++ struct venet_stat *stat;
++ struct veip_struct *veip;
++ struct list_head ip_hash;
++ struct list_head ve_list;
++};
++
++struct veip_struct
++{
++ struct list_head src_lh;
++ struct list_head dst_lh;
++ struct list_head ip_lh;
++ struct list_head list;
++ envid_t veid;
++};
++
++static inline struct net_device_stats *
++venet_stats(struct net_device *dev, int cpu)
++{
++ struct venet_stats *stats;
++ stats = (struct venet_stats*)dev->priv;
++ return per_cpu_ptr(stats->real_stats, cpu);
++}
++
++/* veip_hash_lock should be taken for write by caller */
++void ip_entry_hash(struct ip_entry_struct *entry, struct veip_struct *veip);
++/* veip_hash_lock should be taken for write by caller */
++void ip_entry_unhash(struct ip_entry_struct *entry);
++/* veip_hash_lock should be taken for read by caller */
++struct ip_entry_struct *venet_entry_lookup(struct ve_addr_struct *);
++
++/* veip_hash_lock should be taken for read by caller */
++struct veip_struct *veip_find(envid_t veid);
++/* veip_hash_lock should be taken for write by caller */
++struct veip_struct *veip_findcreate(envid_t veid);
++/* veip_hash_lock should be taken for write by caller */
++void veip_put(struct veip_struct *veip);
++
++extern struct list_head veip_lh;
++
++int veip_start(struct ve_struct *ve);
++void veip_stop(struct ve_struct *ve);
++__exit void veip_cleanup(void);
++int veip_entry_add(struct ve_struct *ve, struct ve_addr_struct *addr);
++int veip_entry_del(envid_t veid, struct ve_addr_struct *addr);
++int venet_change_skb_owner(struct sk_buff *skb);
++
++extern struct list_head ip_entry_hash_table[];
++extern rwlock_t veip_hash_lock;
++
++#ifdef CONFIG_PROC_FS
++int veip_seq_show(struct seq_file *m, void *v);
++#endif
++
++#endif
+diff --git a/include/linux/veprintk.h b/include/linux/veprintk.h
+new file mode 100644
+index 0000000..5669d7b
+--- /dev/null
++++ b/include/linux/veprintk.h
+@@ -0,0 +1,38 @@
++/*
++ * include/linux/veprintk.h
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __VE_PRINTK_H__
++#define __VE_PRINTK_H__
++
++#ifdef CONFIG_VE
++
++#define ve_log_wait (*(get_exec_env()->_log_wait))
++#define ve_log_start (*(get_exec_env()->_log_start))
++#define ve_log_end (*(get_exec_env()->_log_end))
++#define ve_logged_chars (*(get_exec_env()->_logged_chars))
++#define ve_log_buf (get_exec_env()->log_buf)
++#define ve_log_buf_len (ve_is_super(get_exec_env()) ? \
++ log_buf_len : VE_DEFAULT_LOG_BUF_LEN)
++#define VE_LOG_BUF_MASK (ve_log_buf_len - 1)
++#define VE_LOG_BUF(idx) (ve_log_buf[(idx) & VE_LOG_BUF_MASK])
++
++#else
++
++#define ve_log_wait log_wait
++#define ve_log_start log_start
++#define ve_log_end log_end
++#define ve_logged_chars logged_chars
++#define ve_log_buf log_buf
++#define ve_log_buf_len log_buf_len
++#define VE_LOG_BUF_MASK LOG_BUF_MASK
++#define VE_LOG_BUF(idx) LOG_BUF(idx)
++
++#endif /* CONFIG_VE */
++#endif /* __VE_PRINTK_H__ */
+diff --git a/include/linux/veth.h b/include/linux/veth.h
+index 3354c1e..34cfe2b 100644
+--- a/include/linux/veth.h
++++ b/include/linux/veth.h
+@@ -1,3 +1,12 @@
++/*
++ * include/linux/veth.h
++ *
++ * Copyright (C) 2007 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
+ #ifndef __NET_VETH_H_
+ #define __NET_VETH_H_
+
+@@ -9,4 +18,28 @@ enum {
+ #define VETH_INFO_MAX (__VETH_INFO_MAX - 1)
+ };
+
++#ifdef __KERNEL__
++struct veth_struct
++{
++ struct net_device_stats stats;
++ struct net_device *pair;
++ struct list_head hwaddr_list;
++ struct net_device_stats *real_stats;
++ int allow_mac_change;
++};
++
++#define veth_from_netdev(dev) \
++ ((struct veth_struct *)(netdev_priv(dev)))
++static inline struct net_device * veth_to_netdev(struct veth_struct *veth)
++{
++ return (struct net_device *)((char *)veth - ((sizeof(struct net_device) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST));
++}
++#endif
++
++static inline struct net_device_stats *
++veth_stats(struct net_device *dev, int cpuid)
++{
++ return per_cpu_ptr(veth_from_netdev(dev)->real_stats, cpuid);
++}
++
+ #endif
+diff --git a/include/linux/virtinfo.h b/include/linux/virtinfo.h
+new file mode 100644
+index 0000000..b0dad07
+--- /dev/null
++++ b/include/linux/virtinfo.h
+@@ -0,0 +1,100 @@
++/*
++ * include/linux/virtinfo.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __LINUX_VIRTINFO_H
++#define __LINUX_VIRTINFO_H
++
++#include <linux/kernel.h>
++#include <linux/page-flags.h>
++#include <linux/notifier.h>
++
++struct vnotifier_block
++{
++ int (*notifier_call)(struct vnotifier_block *self,
++ unsigned long, void *, int);
++ struct vnotifier_block *next;
++ int priority;
++};
++
++extern struct semaphore virtinfo_sem;
++void __virtinfo_notifier_register(int type, struct vnotifier_block *nb);
++void virtinfo_notifier_register(int type, struct vnotifier_block *nb);
++void virtinfo_notifier_unregister(int type, struct vnotifier_block *nb);
++int virtinfo_notifier_call(int type, unsigned long n, void *data);
++
++struct page_info {
++ unsigned long nr_file_dirty;
++ unsigned long nr_writeback;
++ unsigned long nr_anon_pages;
++ unsigned long nr_file_mapped;
++ unsigned long nr_slab_rec;
++ unsigned long nr_slab_unrec;
++ unsigned long nr_pagetable;
++ unsigned long nr_unstable_nfs;
++ unsigned long nr_bounce;
++ unsigned long nr_writeback_temp;
++};
++
++struct meminfo {
++ struct sysinfo si;
++ struct page_info pi;
++ unsigned long active, inactive;
++ unsigned long cache, swapcache;
++ unsigned long committed_space;
++ unsigned long allowed;
++ unsigned long vmalloc_total, vmalloc_used, vmalloc_largest;
++};
++
++#define VIRTINFO_MEMINFO 0
++#define VIRTINFO_ENOUGHMEM 1
++#define VIRTINFO_DOFORK 2
++#define VIRTINFO_DOEXIT 3
++#define VIRTINFO_DOEXECVE 4
++#define VIRTINFO_DOFORKRET 5
++#define VIRTINFO_DOFORKPOST 6
++#define VIRTINFO_EXIT 7
++#define VIRTINFO_EXITMMAP 8
++#define VIRTINFO_EXECMMAP 9
++#define VIRTINFO_OUTOFMEM 10
++#define VIRTINFO_PAGEIN 11
++#define VIRTINFO_SYSINFO 12
++#define VIRTINFO_NEWUBC 13
++#define VIRTINFO_VMSTAT 14
++
++enum virt_info_types {
++ VITYPE_GENERAL,
++ VITYPE_FAUDIT,
++ VITYPE_QUOTA,
++ VITYPE_SCP,
++
++ VIRT_TYPES
++};
++
++#ifdef CONFIG_VZ_GENCALLS
++
++static inline int virtinfo_gencall(unsigned long n, void *data)
++{
++ int r;
++
++ r = virtinfo_notifier_call(VITYPE_GENERAL, n, data);
++ if (r & NOTIFY_FAIL)
++ return -ENOBUFS;
++ if (r & NOTIFY_OK)
++ return -ERESTARTNOINTR;
++ return 0;
++}
++
++#else
++
++#define virtinfo_gencall(n, data) 0
++
++#endif
++
++#endif /* __LINUX_VIRTINFO_H */
+diff --git a/include/linux/virtinfoscp.h b/include/linux/virtinfoscp.h
+new file mode 100644
+index 0000000..9e7584f
+--- /dev/null
++++ b/include/linux/virtinfoscp.h
+@@ -0,0 +1,21 @@
++#ifndef __VIRTINFO_SCP_H__
++#define __VIRTINFO_SCP_H__
++
++/*
++ * Dump and restore operations are non-symmetric.
++ * With respect to finish/fail hooks, 2 dump hooks are called from
++ * different proc operations, but restore hooks are called from a single one.
++ */
++#define VIRTINFO_SCP_COLLECT 0x10
++#define VIRTINFO_SCP_DUMP 0x11
++#define VIRTINFO_SCP_DMPFIN 0x12
++#define VIRTINFO_SCP_RSTCHECK 0x13
++#define VIRTINFO_SCP_RESTORE 0x14
++#define VIRTINFO_SCP_RSTFAIL 0x15
++
++#define VIRTINFO_SCP_RSTTSK 0x20
++#define VIRTINFO_SCP_RSTMM 0x21
++
++#define VIRTNOTIFY_CHANGE 0x100
++
++#endif /* __VIRTINFO_SCP_H__ */
+diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
+index 328eb40..f90025c 100644
+--- a/include/linux/vmalloc.h
++++ b/include/linux/vmalloc.h
+@@ -22,6 +22,10 @@ struct vm_area_struct; /* vma defining user mapping in mm_types.h */
+ #define IOREMAP_MAX_ORDER (7 + PAGE_SHIFT) /* 128 pages */
+ #endif
+
++/* align size to 2^n page boundary */
++#define POWER2_PAGE_ALIGN(size) \
++ ((typeof(size))(1UL << (PAGE_SHIFT + get_order(size))))
++
+ struct vm_struct {
+ /* keep next,addr,size together to speedup lookups */
+ struct vm_struct *next;
+@@ -38,12 +42,16 @@ struct vm_struct {
+ * Highlevel APIs for driver use
+ */
+ extern void *vmalloc(unsigned long size);
++extern void *ub_vmalloc(unsigned long size);
+ extern void *vmalloc_user(unsigned long size);
+ extern void *vmalloc_node(unsigned long size, int node);
++extern void *ub_vmalloc_node(unsigned long size, int node);
+ extern void *vmalloc_exec(unsigned long size);
+ extern void *vmalloc_32(unsigned long size);
+ extern void *vmalloc_32_user(unsigned long size);
+ extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot);
++extern void *vmalloc_best(unsigned long size);
++extern void *ub_vmalloc_best(unsigned long size);
+ extern void *__vmalloc_area(struct vm_struct *area, gfp_t gfp_mask,
+ pgprot_t prot);
+ extern void vfree(const void *addr);
+@@ -71,6 +79,9 @@ extern struct vm_struct *get_vm_area_caller(unsigned long size,
+ unsigned long flags, void *caller);
+ extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
+ unsigned long start, unsigned long end);
++extern struct vm_struct * get_vm_area_best(unsigned long size,
++ unsigned long flags);
++extern void vprintstat(void);
+ extern struct vm_struct *get_vm_area_node(unsigned long size,
+ unsigned long flags, int node,
+ gfp_t gfp_mask);
+diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
+index 58334d4..75ace44 100644
+--- a/include/linux/vmstat.h
++++ b/include/linux/vmstat.h
+@@ -98,6 +98,7 @@ static inline void vm_events_fold_cpu(int cpu)
+ }
+ #endif
+
++extern unsigned long vm_events(enum vm_event_item i);
+ #else
+
+ /* Disable counters */
+@@ -120,6 +121,7 @@ static inline void vm_events_fold_cpu(int cpu)
+ {
+ }
+
++static inline unsigned long vm_events(enum vm_event_item i) { return 0; }
+ #endif /* CONFIG_VM_EVENT_COUNTERS */
+
+ #define __count_zone_vm_events(item, zone, delta) \
+diff --git a/include/linux/vzcalluser.h b/include/linux/vzcalluser.h
+new file mode 100644
+index 0000000..46c04e6
+--- /dev/null
++++ b/include/linux/vzcalluser.h
+@@ -0,0 +1,198 @@
++/*
++ * include/linux/vzcalluser.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _LINUX_VZCALLUSER_H
++#define _LINUX_VZCALLUSER_H
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++#include <linux/vziptable_defs.h>
++
++#define KERN_VZ_PRIV_RANGE 51
++
++#ifndef __ENVID_T_DEFINED__
++typedef unsigned envid_t;
++#define __ENVID_T_DEFINED__
++#endif
++
++#ifndef __KERNEL__
++#define __user
++#endif
++
++/*
++ * VE management ioctls
++ */
++
++struct vzctl_old_env_create {
++ envid_t veid;
++ unsigned flags;
++#define VE_CREATE 1 /* Create VE, VE_ENTER added automatically */
++#define VE_EXCLUSIVE 2 /* Fail if exists */
++#define VE_ENTER 4 /* Enter existing VE */
++#define VE_TEST 8 /* Test if VE exists */
++#define VE_LOCK 16 /* Do not allow entering created VE */
++#define VE_SKIPLOCK 32 /* Allow entering embrion VE */
++ __u32 addr;
++};
++
++struct vzctl_mark_env_to_down {
++ envid_t veid;
++};
++
++struct vzctl_setdevperms {
++ envid_t veid;
++ unsigned type;
++#define VE_USE_MAJOR 010 /* Test MAJOR supplied in rule */
++#define VE_USE_MINOR 030 /* Test MINOR supplied in rule */
++#define VE_USE_MASK 030 /* Testing mask, VE_USE_MAJOR|VE_USE_MINOR */
++ unsigned dev;
++ unsigned mask;
++};
++
++struct vzctl_ve_netdev {
++ envid_t veid;
++ int op;
++#define VE_NETDEV_ADD 1
++#define VE_NETDEV_DEL 2
++ char __user *dev_name;
++};
++
++struct vzctl_ve_meminfo {
++ envid_t veid;
++ unsigned long val;
++};
++
++struct vzctl_env_create_cid {
++ envid_t veid;
++ unsigned flags;
++ __u32 class_id;
++};
++
++struct vzctl_env_create {
++ envid_t veid;
++ unsigned flags;
++ __u32 class_id;
++};
++
++struct env_create_param {
++ __u64 iptables_mask;
++};
++
++#define VZCTL_ENV_CREATE_DATA_MINLEN sizeof(struct env_create_param)
++
++struct env_create_param2 {
++ __u64 iptables_mask;
++ __u64 feature_mask;
++ __u32 total_vcpus; /* 0 - don't care, same as in host */
++};
++
++struct env_create_param3 {
++ __u64 iptables_mask;
++ __u64 feature_mask;
++ __u32 total_vcpus;
++ __u32 pad;
++ __u64 known_features;
++};
++
++#define VE_FEATURE_SYSFS (1ULL << 0)
++#define VE_FEATURE_NFS (1ULL << 1)
++#define VE_FEATURE_DEF_PERMS (1ULL << 2)
++#define VE_FEATURE_SIT (1ULL << 3)
++#define VE_FEATURE_IPIP (1ULL << 4)
++
++#define VE_FEATURES_OLD (VE_FEATURE_SYSFS)
++#define VE_FEATURES_DEF (VE_FEATURE_SYSFS | \
++ VE_FEATURE_DEF_PERMS)
++
++typedef struct env_create_param3 env_create_param_t;
++#define VZCTL_ENV_CREATE_DATA_MAXLEN sizeof(env_create_param_t)
++
++struct vzctl_env_create_data {
++ envid_t veid;
++ unsigned flags;
++ __u32 class_id;
++ env_create_param_t __user *data;
++ int datalen;
++};
++
++struct vz_load_avg {
++ int val_int;
++ int val_frac;
++};
++
++struct vz_cpu_stat {
++ unsigned long user_jif;
++ unsigned long nice_jif;
++ unsigned long system_jif;
++ unsigned long uptime_jif;
++ __u64 idle_clk;
++ __u64 strv_clk;
++ __u64 uptime_clk;
++ struct vz_load_avg avenrun[3]; /* loadavg data */
++};
++
++struct vzctl_cpustatctl {
++ envid_t veid;
++ struct vz_cpu_stat __user *cpustat;
++};
++
++#define VZCTLTYPE '.'
++#define VZCTL_OLD_ENV_CREATE _IOW(VZCTLTYPE, 0, \
++ struct vzctl_old_env_create)
++#define VZCTL_MARK_ENV_TO_DOWN _IOW(VZCTLTYPE, 1, \
++ struct vzctl_mark_env_to_down)
++#define VZCTL_SETDEVPERMS _IOW(VZCTLTYPE, 2, \
++ struct vzctl_setdevperms)
++#define VZCTL_ENV_CREATE_CID _IOW(VZCTLTYPE, 4, \
++ struct vzctl_env_create_cid)
++#define VZCTL_ENV_CREATE _IOW(VZCTLTYPE, 5, \
++ struct vzctl_env_create)
++#define VZCTL_GET_CPU_STAT _IOW(VZCTLTYPE, 6, \
++ struct vzctl_cpustatctl)
++#define VZCTL_ENV_CREATE_DATA _IOW(VZCTLTYPE, 10, \
++ struct vzctl_env_create_data)
++#define VZCTL_VE_NETDEV _IOW(VZCTLTYPE, 11, \
++ struct vzctl_ve_netdev)
++#define VZCTL_VE_MEMINFO _IOW(VZCTLTYPE, 13, \
++ struct vzctl_ve_meminfo)
++
++#ifdef __KERNEL__
++#ifdef CONFIG_COMPAT
++#include <linux/compat.h>
++
++struct compat_vzctl_ve_netdev {
++ envid_t veid;
++ int op;
++ compat_uptr_t dev_name;
++};
++
++struct compat_vzctl_ve_meminfo {
++ envid_t veid;
++ compat_ulong_t val;
++};
++
++struct compat_vzctl_env_create_data {
++ envid_t veid;
++ unsigned flags;
++ __u32 class_id;
++ compat_uptr_t data;
++ int datalen;
++};
++
++#define VZCTL_COMPAT_ENV_CREATE_DATA _IOW(VZCTLTYPE, 10, \
++ struct compat_vzctl_env_create_data)
++#define VZCTL_COMPAT_VE_NETDEV _IOW(VZCTLTYPE, 11, \
++ struct compat_vzctl_ve_netdev)
++#define VZCTL_COMPAT_VE_MEMINFO _IOW(VZCTLTYPE, 13, \
++ struct compat_vzctl_ve_meminfo)
++#endif
++#endif
++
++#endif
+diff --git a/include/linux/vzctl.h b/include/linux/vzctl.h
+new file mode 100644
+index 0000000..ad967ed
+--- /dev/null
++++ b/include/linux/vzctl.h
+@@ -0,0 +1,30 @@
++/*
++ * include/linux/vzctl.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _LINUX_VZCTL_H
++#define _LINUX_VZCTL_H
++
++#include <linux/list.h>
++
++struct module;
++struct inode;
++struct file;
++struct vzioctlinfo {
++ unsigned type;
++ int (*ioctl)(struct file *, unsigned int, unsigned long);
++ int (*compat_ioctl)(struct file *, unsigned int, unsigned long);
++ struct module *owner;
++ struct list_head list;
++};
++
++extern void vzioctl_register(struct vzioctlinfo *inf);
++extern void vzioctl_unregister(struct vzioctlinfo *inf);
++
++#endif
+diff --git a/include/linux/vzctl_quota.h b/include/linux/vzctl_quota.h
+new file mode 100644
+index 0000000..6d36cdd
+--- /dev/null
++++ b/include/linux/vzctl_quota.h
+@@ -0,0 +1,74 @@
++/*
++ * include/linux/vzctl_quota.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __LINUX_VZCTL_QUOTA_H__
++#define __LINUX_VZCTL_QUOTA_H__
++
++#include <linux/compat.h>
++
++#ifndef __KERNEL__
++#define __user
++#endif
++
++/*
++ * Quota management ioctl
++ */
++
++struct vz_quota_stat;
++struct vzctl_quotactl {
++ int cmd;
++ unsigned int quota_id;
++ struct vz_quota_stat __user *qstat;
++ char __user *ve_root;
++};
++
++struct vzctl_quotaugidctl {
++ int cmd; /* subcommand */
++ unsigned int quota_id; /* quota id where it applies to */
++ unsigned int ugid_index;/* for reading statistic. index of first
++ uid/gid record to read */
++ unsigned int ugid_size; /* size of ugid_buf array */
++ void *addr; /* user-level buffer */
++};
++
++#define VZDQCTLTYPE '+'
++#define VZCTL_QUOTA_DEPR_CTL _IOWR(VZDQCTLTYPE, 1, \
++ struct vzctl_quotactl)
++#define VZCTL_QUOTA_NEW_CTL _IOWR(VZDQCTLTYPE, 2, \
++ struct vzctl_quotactl)
++#define VZCTL_QUOTA_UGID_CTL _IOWR(VZDQCTLTYPE, 3, \
++ struct vzctl_quotaugidctl)
++
++#ifdef __KERNEL__
++#ifdef CONFIG_COMPAT
++struct compat_vzctl_quotactl {
++ int cmd;
++ unsigned int quota_id;
++ compat_uptr_t qstat;
++ compat_uptr_t ve_root;
++};
++
++struct compat_vzctl_quotaugidctl {
++ int cmd; /* subcommand */
++ unsigned int quota_id; /* quota id where it applies to */
++ unsigned int ugid_index;/* for reading statistic. index of first
++ uid/gid record to read */
++ unsigned int ugid_size; /* size of ugid_buf array */
++ compat_uptr_t addr; /* user-level buffer */
++};
++
++#define VZCTL_COMPAT_QUOTA_CTL _IOWR(VZDQCTLTYPE, 2, \
++ struct compat_vzctl_quotactl)
++#define VZCTL_COMPAT_QUOTA_UGID_CTL _IOWR(VZDQCTLTYPE, 3, \
++ struct compat_vzctl_quotaugidctl)
++#endif
++#endif
++
++#endif /* __LINUX_VZCTL_QUOTA_H__ */
+diff --git a/include/linux/vzctl_venet.h b/include/linux/vzctl_venet.h
+new file mode 100644
+index 0000000..4797a50
+--- /dev/null
++++ b/include/linux/vzctl_venet.h
+@@ -0,0 +1,51 @@
++/*
++ * include/linux/vzctl_venet.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _VZCTL_VENET_H
++#define _VZCTL_VENET_H
++
++#include <linux/types.h>
++#include <linux/compat.h>
++#include <linux/ioctl.h>
++
++#ifndef __ENVID_T_DEFINED__
++typedef unsigned envid_t;
++#define __ENVID_T_DEFINED__
++#endif
++
++struct vzctl_ve_ip_map {
++ envid_t veid;
++ int op;
++#define VE_IP_ADD 1
++#define VE_IP_DEL 2
++ struct sockaddr *addr;
++ int addrlen;
++};
++
++#define VENETCTLTYPE '('
++
++#define VENETCTL_VE_IP_MAP _IOW(VENETCTLTYPE, 3, \
++ struct vzctl_ve_ip_map)
++
++#ifdef __KERNEL__
++#ifdef CONFIG_COMPAT
++struct compat_vzctl_ve_ip_map {
++ envid_t veid;
++ int op;
++ compat_uptr_t addr;
++ int addrlen;
++};
++
++#define VENETCTL_COMPAT_VE_IP_MAP _IOW(VENETCTLTYPE, 3, \
++ struct compat_vzctl_ve_ip_map)
++#endif
++#endif
++
++#endif
+diff --git a/include/linux/vzctl_veth.h b/include/linux/vzctl_veth.h
+new file mode 100644
+index 0000000..1480c5b
+--- /dev/null
++++ b/include/linux/vzctl_veth.h
+@@ -0,0 +1,42 @@
++/*
++ * include/linux/vzctl_veth.h
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _VZCTL_VETH_H
++#define _VZCTL_VETH_H
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++
++#ifndef __ENVID_T_DEFINED__
++typedef unsigned envid_t;
++#define __ENVID_T_DEFINED__
++#endif
++
++struct vzctl_ve_hwaddr {
++ envid_t veid;
++ int op;
++#define VE_ETH_ADD 1
++#define VE_ETH_DEL 2
++#define VE_ETH_ALLOW_MAC_CHANGE 3
++#define VE_ETH_DENY_MAC_CHANGE 4
++ unsigned char dev_addr[6];
++ int addrlen;
++ char dev_name[16];
++ unsigned char dev_addr_ve[6];
++ int addrlen_ve;
++ char dev_name_ve[16];
++};
++
++#define VETHCTLTYPE '['
++
++#define VETHCTL_VE_HWADDR _IOW(VETHCTLTYPE, 3, \
++ struct vzctl_ve_hwaddr)
++
++#endif
+diff --git a/include/linux/vzdq_tree.h b/include/linux/vzdq_tree.h
+new file mode 100644
+index 0000000..c019e09
+--- /dev/null
++++ b/include/linux/vzdq_tree.h
+@@ -0,0 +1,99 @@
++/*
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains Virtuozzo disk quota tree definition
++ */
++
++#ifndef _VZDQ_TREE_H
++#define _VZDQ_TREE_H
++
++#include <linux/list.h>
++#include <asm/string.h>
++
++typedef unsigned int quotaid_t;
++#define QUOTAID_BITS 32
++#define QUOTAID_BBITS 4
++#define QUOTAID_EBITS 8
++
++#if QUOTAID_EBITS % QUOTAID_BBITS
++#error Quota bit assumption failure
++#endif
++
++#define QUOTATREE_BSIZE (1 << QUOTAID_BBITS)
++#define QUOTATREE_BMASK (QUOTATREE_BSIZE - 1)
++#define QUOTATREE_DEPTH ((QUOTAID_BITS + QUOTAID_BBITS - 1) \
++ / QUOTAID_BBITS)
++#define QUOTATREE_EDEPTH ((QUOTAID_BITS + QUOTAID_EBITS - 1) \
++ / QUOTAID_EBITS)
++#define QUOTATREE_BSHIFT(lvl) ((QUOTATREE_DEPTH - (lvl) - 1) * QUOTAID_BBITS)
++
++/*
++ * Depth of keeping unused node (not inclusive).
++ * 0 means release all nodes including root,
++ * QUOTATREE_DEPTH means never release nodes.
++ * Current value: release all nodes strictly after QUOTATREE_EDEPTH
++ * (measured in external shift units).
++ */
++#define QUOTATREE_CDEPTH (QUOTATREE_DEPTH \
++ - 2 * QUOTATREE_DEPTH / QUOTATREE_EDEPTH \
++ + 1)
++
++/*
++ * Levels 0..(QUOTATREE_DEPTH-1) are tree nodes.
++ * On level i the maximal number of nodes is 2^(i*QUOTAID_BBITS),
++ * and each node contains 2^QUOTAID_BBITS pointers.
++ * Level 0 is a (single) tree root node.
++ *
++ * Nodes of level (QUOTATREE_DEPTH-1) contain pointers to caller's data.
++ * Nodes of lower levels contain pointers to nodes.
++ *
++ * Double pointer in array of i-level node, pointing to a (i+1)-level node
++ * (such as inside quotatree_find_state) are marked by level (i+1), not i.
++ * Level 0 double pointer is a pointer to root inside tree struct.
++ *
++ * The tree is permanent, i.e. all index blocks allocated are keeped alive to
++ * preserve the blocks numbers in the quota file tree to keep its changes
++ * locally.
++ */
++struct quotatree_node {
++ struct list_head list;
++ quotaid_t num;
++ void *blocks[QUOTATREE_BSIZE];
++};
++
++struct quotatree_level {
++ struct list_head usedlh, freelh;
++ quotaid_t freenum;
++};
++
++struct quotatree_tree {
++ struct quotatree_level levels[QUOTATREE_DEPTH];
++ struct quotatree_node *root;
++ unsigned int leaf_num;
++};
++
++struct quotatree_find_state {
++ void **block;
++ int level;
++};
++
++/* number of leafs (objects) and leaf level of the tree */
++#define QTREE_LEAFNUM(tree) ((tree)->leaf_num)
++#define QTREE_LEAFLVL(tree) (&(tree)->levels[QUOTATREE_DEPTH - 1])
++
++struct quotatree_tree *quotatree_alloc(void);
++void *quotatree_find(struct quotatree_tree *tree, quotaid_t id,
++ struct quotatree_find_state *st);
++int quotatree_insert(struct quotatree_tree *tree, quotaid_t id,
++ struct quotatree_find_state *st, void *data);
++void quotatree_remove(struct quotatree_tree *tree, quotaid_t id);
++void quotatree_free(struct quotatree_tree *tree, void (*dtor)(void *));
++void *quotatree_get_next(struct quotatree_tree *tree, quotaid_t id);
++void *quotatree_leaf_byindex(struct quotatree_tree *tree, unsigned int index);
++
++#endif /* _VZDQ_TREE_H */
++
+diff --git a/include/linux/vzevent.h b/include/linux/vzevent.h
+new file mode 100644
+index 0000000..1a67297
+--- /dev/null
++++ b/include/linux/vzevent.h
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_VZ_EVENT_H__
++#define __LINUX_VZ_EVENT_H__
++
++#if defined(CONFIG_VZ_EVENT) || defined(CONFIG_VZ_EVENT_MODULE)
++extern int vzevent_send(int msg, const char *attrs_fmt, ...);
++#else
++static inline int vzevent_send(int msg, const char *attrs_fmt, ...)
++{
++ return 0;
++}
++#endif
++
++#endif /* __LINUX_VZ_EVENT_H__ */
+diff --git a/include/linux/vziptable_defs.h b/include/linux/vziptable_defs.h
+new file mode 100644
+index 0000000..ec7586f
+--- /dev/null
++++ b/include/linux/vziptable_defs.h
+@@ -0,0 +1,51 @@
++#ifndef _LINUX_VZIPTABLE_DEFS_H
++#define _LINUX_VZIPTABLE_DEFS_H
++
++/* these masks represent modules */
++#define VE_IP_IPTABLES_MOD (1U<<0)
++#define VE_IP_FILTER_MOD (1U<<1)
++#define VE_IP_MANGLE_MOD (1U<<2)
++#define VE_IP_CONNTRACK_MOD (1U<<14)
++#define VE_IP_CONNTRACK_FTP_MOD (1U<<15)
++#define VE_IP_CONNTRACK_IRC_MOD (1U<<16)
++#define VE_IP_NAT_MOD (1U<<20)
++#define VE_IP_NAT_FTP_MOD (1U<<21)
++#define VE_IP_NAT_IRC_MOD (1U<<22)
++#define VE_IP_IPTABLES6_MOD (1U<<26)
++#define VE_IP_FILTER6_MOD (1U<<27)
++#define VE_IP_MANGLE6_MOD (1U<<28)
++#define VE_IP_IPTABLE_NAT_MOD (1U<<29)
++#define VE_NF_CONNTRACK_MOD (1U<<30)
++
++/* these masks represent modules with their dependences */
++#define VE_IP_IPTABLES (VE_IP_IPTABLES_MOD)
++#define VE_IP_FILTER (VE_IP_FILTER_MOD \
++ | VE_IP_IPTABLES)
++#define VE_IP_MANGLE (VE_IP_MANGLE_MOD \
++ | VE_IP_IPTABLES)
++#define VE_IP_IPTABLES6 (VE_IP_IPTABLES6_MOD)
++#define VE_IP_FILTER6 (VE_IP_FILTER6_MOD | VE_IP_IPTABLES6)
++#define VE_IP_MANGLE6 (VE_IP_MANGLE6_MOD | VE_IP_IPTABLES6)
++#define VE_NF_CONNTRACK (VE_NF_CONNTRACK_MOD | VE_IP_IPTABLES)
++#define VE_IP_CONNTRACK (VE_IP_CONNTRACK_MOD \
++ | VE_IP_IPTABLES)
++#define VE_IP_CONNTRACK_FTP (VE_IP_CONNTRACK_FTP_MOD \
++ | VE_IP_CONNTRACK)
++#define VE_IP_CONNTRACK_IRC (VE_IP_CONNTRACK_IRC_MOD \
++ | VE_IP_CONNTRACK)
++#define VE_IP_NAT (VE_IP_NAT_MOD \
++ | VE_IP_CONNTRACK)
++#define VE_IP_NAT_FTP (VE_IP_NAT_FTP_MOD \
++ | VE_IP_NAT | VE_IP_CONNTRACK_FTP)
++#define VE_IP_NAT_IRC (VE_IP_NAT_IRC_MOD \
++ | VE_IP_NAT | VE_IP_CONNTRACK_IRC)
++#define VE_IP_IPTABLE_NAT (VE_IP_IPTABLE_NAT_MOD | VE_IP_CONNTRACK)
++
++/* safe iptables mask to be used by default */
++#define VE_IP_DEFAULT \
++ (VE_IP_IPTABLES | \
++ VE_IP_FILTER | VE_IP_MANGLE)
++
++#define VE_IPT_CMP(x, y) (((x) & (y)) == (y))
++
++#endif /* _LINUX_VZIPTABLE_DEFS_H */
+diff --git a/include/linux/vzquota.h b/include/linux/vzquota.h
+new file mode 100644
+index 0000000..e16605e
+--- /dev/null
++++ b/include/linux/vzquota.h
+@@ -0,0 +1,379 @@
++/*
++ *
++ * Copyright (C) 2001-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * This file contains Virtuozzo disk quota implementation
++ */
++
++#ifndef _VZDQUOTA_H
++#define _VZDQUOTA_H
++
++#include <linux/types.h>
++#include <linux/quota.h>
++
++/* vzquotactl syscall commands */
++#define VZ_DQ_CREATE 5 /* create quota master block */
++#define VZ_DQ_DESTROY 6 /* destroy qmblk */
++#define VZ_DQ_ON 7 /* mark dentry with already created qmblk */
++#define VZ_DQ_OFF 8 /* remove mark, don't destroy qmblk */
++#define VZ_DQ_SETLIMIT 9 /* set new limits */
++#define VZ_DQ_GETSTAT 10 /* get usage statistic */
++#define VZ_DQ_OFF_FORCED 11 /* forced off */
++/* set of syscalls to maintain UGID quotas */
++#define VZ_DQ_UGID_GETSTAT 1 /* get usage/limits for ugid(s) */
++#define VZ_DQ_UGID_ADDSTAT 2 /* set usage/limits statistic for ugid(s) */
++#define VZ_DQ_UGID_GETGRACE 3 /* get expire times */
++#define VZ_DQ_UGID_SETGRACE 4 /* set expire times */
++#define VZ_DQ_UGID_GETCONFIG 5 /* get ugid_max limit, cnt, flags of qmblk */
++#define VZ_DQ_UGID_SETCONFIG 6 /* set ugid_max limit, flags of qmblk */
++#define VZ_DQ_UGID_SETLIMIT 7 /* set ugid B/I limits */
++#define VZ_DQ_UGID_SETINFO 8 /* set ugid info */
++
++/* common structure for vz and ugid quota */
++struct dq_stat {
++ /* blocks limits */
++ __u64 bhardlimit; /* absolute limit in bytes */
++ __u64 bsoftlimit; /* preferred limit in bytes */
++ time_t btime; /* time limit for excessive disk use */
++ __u64 bcurrent; /* current bytes count */
++ /* inodes limits */
++ __u32 ihardlimit; /* absolute limit on allocated inodes */
++ __u32 isoftlimit; /* preferred inode limit */
++ time_t itime; /* time limit for excessive inode use */
++ __u32 icurrent; /* current # allocated inodes */
++};
++
++/* One second resolution for grace times */
++#define CURRENT_TIME_SECONDS (get_seconds())
++
++/* Values for dq_info->flags */
++#define VZ_QUOTA_INODES 0x01 /* inodes limit warning printed */
++#define VZ_QUOTA_SPACE 0x02 /* space limit warning printed */
++
++struct dq_info {
++ time_t bexpire; /* expire timeout for excessive disk use */
++ time_t iexpire; /* expire timeout for excessive inode use */
++ unsigned flags; /* see previos defines */
++};
++
++struct vz_quota_stat {
++ struct dq_stat dq_stat;
++ struct dq_info dq_info;
++};
++
++/* UID/GID interface record - for user-kernel level exchange */
++struct vz_quota_iface {
++ unsigned int qi_id; /* UID/GID this applies to */
++ unsigned int qi_type; /* USRQUOTA|GRPQUOTA */
++ struct dq_stat qi_stat; /* limits, options, usage stats */
++};
++
++#ifdef CONFIG_COMPAT
++#include <linux/compat.h>
++struct compat_dq_stat {
++ /* blocks limits */
++ __u64 bhardlimit; /* absolute limit in bytes */
++ __u64 bsoftlimit; /* preferred limit in bytes */
++ compat_time_t btime; /* time limit for excessive disk use */
++ __u64 bcurrent; /* current bytes count */
++ /* inodes limits */
++ __u32 ihardlimit; /* absolute limit on allocated inodes */
++ __u32 isoftlimit; /* preferred inode limit */
++ compat_time_t itime; /* time limit for excessive inode use */
++ __u32 icurrent; /* current # allocated inodes */
++};
++
++struct compat_dq_info {
++ compat_time_t bexpire; /* expire timeout for excessive disk use */
++ compat_time_t iexpire; /* expire timeout for excessive inode use */
++ unsigned flags; /* see previos defines */
++};
++
++struct compat_vz_quota_stat {
++ struct compat_dq_stat dq_stat;
++ struct compat_dq_info dq_info;
++};
++
++struct compat_vz_quota_iface {
++ unsigned int qi_id; /* UID/GID this applies to */
++ unsigned int qi_type; /* USRQUOTA|GRPQUOTA */
++ struct compat_dq_stat qi_stat; /* limits, options, usage stats */
++};
++
++static inline void compat_dqstat2dqstat(struct compat_dq_stat *odqs,
++ struct dq_stat *dqs)
++{
++ dqs->bhardlimit = odqs->bhardlimit;
++ dqs->bsoftlimit = odqs->bsoftlimit;
++ dqs->bcurrent = odqs->bcurrent;
++ dqs->btime = odqs->btime;
++
++ dqs->ihardlimit = odqs->ihardlimit;
++ dqs->isoftlimit = odqs->isoftlimit;
++ dqs->icurrent = odqs->icurrent;
++ dqs->itime = odqs->itime;
++}
++
++static inline void compat_dqinfo2dqinfo(struct compat_dq_info *odqi,
++ struct dq_info *dqi)
++{
++ dqi->bexpire = odqi->bexpire;
++ dqi->iexpire = odqi->iexpire;
++ dqi->flags = odqi->flags;
++}
++
++static inline void dqstat2compat_dqstat(struct dq_stat *dqs,
++ struct compat_dq_stat *odqs)
++{
++ odqs->bhardlimit = dqs->bhardlimit;
++ odqs->bsoftlimit = dqs->bsoftlimit;
++ odqs->bcurrent = dqs->bcurrent;
++ odqs->btime = (compat_time_t)dqs->btime;
++
++ odqs->ihardlimit = dqs->ihardlimit;
++ odqs->isoftlimit = dqs->isoftlimit;
++ odqs->icurrent = dqs->icurrent;
++ odqs->itime = (compat_time_t)dqs->itime;
++}
++
++static inline void dqinfo2compat_dqinfo(struct dq_info *dqi,
++ struct compat_dq_info *odqi)
++{
++ odqi->bexpire = (compat_time_t)dqi->bexpire;
++ odqi->iexpire = (compat_time_t)dqi->iexpire;
++ odqi->flags = dqi->flags;
++}
++#endif
++
++/* values for flags and dq_flags */
++/* this flag is set if the userspace has been unable to provide usage
++ * information about all ugids
++ * if the flag is set, we don't allocate new UG quota blocks (their
++ * current usage is unknown) or free existing UG quota blocks (not to
++ * lose information that this block is ok) */
++#define VZDQUG_FIXED_SET 0x01
++/* permit to use ugid quota */
++#define VZDQUG_ON 0x02
++#define VZDQ_USRQUOTA 0x10
++#define VZDQ_GRPQUOTA 0x20
++#define VZDQ_NOACT 0x1000 /* not actual */
++#define VZDQ_NOQUOT 0x2000 /* not under quota tree */
++
++struct vz_quota_ugid_stat {
++ unsigned int limit; /* max amount of ugid records */
++ unsigned int count; /* amount of ugid records */
++ unsigned int flags;
++};
++
++struct vz_quota_ugid_setlimit {
++ unsigned int type; /* quota type (USR/GRP) */
++ unsigned int id; /* ugid */
++ struct if_dqblk dqb; /* limits info */
++};
++
++struct vz_quota_ugid_setinfo {
++ unsigned int type; /* quota type (USR/GRP) */
++ struct if_dqinfo dqi; /* grace info */
++};
++
++#ifdef __KERNEL__
++#include <linux/list.h>
++#include <asm/atomic.h>
++#include <linux/time.h>
++#include <linux/vzquota_qlnk.h>
++#include <linux/vzdq_tree.h>
++#include <linux/semaphore.h>
++
++/* Values for dq_info flags */
++#define VZ_QUOTA_INODES 0x01 /* inodes limit warning printed */
++#define VZ_QUOTA_SPACE 0x02 /* space limit warning printed */
++
++/* values for dq_state */
++#define VZDQ_STARTING 0 /* created, not turned on yet */
++#define VZDQ_WORKING 1 /* quota created, turned on */
++#define VZDQ_STOPING 2 /* created, turned on and off */
++
++/* master quota record - one per veid */
++struct vz_quota_master {
++ struct list_head dq_hash; /* next quota in hash list */
++ atomic_t dq_count; /* inode reference count */
++ unsigned int dq_flags; /* see VZDQUG_FIXED_SET */
++ unsigned int dq_state; /* see values above */
++ unsigned int dq_id; /* VEID this applies to */
++ struct dq_stat dq_stat; /* limits, grace, usage stats */
++ struct dq_info dq_info; /* grace times and flags */
++ spinlock_t dq_data_lock; /* for dq_stat */
++
++ struct semaphore dq_sem; /* semaphore to protect
++ ugid tree */
++
++ struct list_head dq_ilink_list; /* list of vz_quota_ilink */
++ struct quotatree_tree *dq_uid_tree; /* vz_quota_ugid tree for UIDs */
++ struct quotatree_tree *dq_gid_tree; /* vz_quota_ugid tree for GIDs */
++ unsigned int dq_ugid_count; /* amount of ugid records */
++ unsigned int dq_ugid_max; /* max amount of ugid records */
++ struct dq_info dq_ugid_info[MAXQUOTAS]; /* ugid grace times */
++
++ struct path dq_root_path; /* path of fs tree */
++ struct super_block *dq_sb; /* superblock of our quota root */
++};
++
++/* UID/GID quota record - one per pair (quota_master, uid or gid) */
++struct vz_quota_ugid {
++ unsigned int qugid_id; /* UID/GID this applies to */
++ struct dq_stat qugid_stat; /* limits, options, usage stats */
++ int qugid_type; /* USRQUOTA|GRPQUOTA */
++ atomic_t qugid_count; /* reference count */
++};
++
++#define VZ_QUOTA_UGBAD ((struct vz_quota_ugid *)0xfeafea11)
++
++struct vz_quota_datast {
++ struct vz_quota_ilink qlnk;
++};
++
++#define VIRTINFO_QUOTA_GETSTAT 0
++#define VIRTINFO_QUOTA_ON 1
++#define VIRTINFO_QUOTA_OFF 2
++#define VIRTINFO_QUOTA_DISABLE 3
++
++struct virt_info_quota {
++ struct super_block *super;
++ struct dq_stat *qstat;
++};
++
++/*
++ * Interface to VZ quota core
++ */
++#define INODE_QLNK(inode) (&(inode)->i_qlnk)
++#define QLNK_INODE(qlnk) container_of((qlnk), struct inode, i_qlnk)
++
++#define VZ_QUOTA_BAD ((struct vz_quota_master *)0xefefefef)
++
++#define VZ_QUOTAO_SETE 1
++#define VZ_QUOTAO_INIT 2
++#define VZ_QUOTAO_DESTR 3
++#define VZ_QUOTAO_SWAP 4
++#define VZ_QUOTAO_INICAL 5
++#define VZ_QUOTAO_DRCAL 6
++#define VZ_QUOTAO_QSET 7
++#define VZ_QUOTAO_TRANS 8
++#define VZ_QUOTAO_ACT 9
++#define VZ_QUOTAO_DTREE 10
++#define VZ_QUOTAO_DET 11
++#define VZ_QUOTAO_ON 12
++#define VZ_QUOTAO_RE_LOCK 13
++
++#define DQUOT_CMD_ALLOC 0
++#define DQUOT_CMD_PREALLOC 1
++#define DQUOT_CMD_CHECK 12
++#define DQUOT_CMD_FORCE 13
++
++extern struct semaphore vz_quota_sem;
++void inode_qmblk_lock(struct super_block *sb);
++void inode_qmblk_unlock(struct super_block *sb);
++void qmblk_data_read_lock(struct vz_quota_master *qmblk);
++void qmblk_data_read_unlock(struct vz_quota_master *qmblk);
++void qmblk_data_write_lock(struct vz_quota_master *qmblk);
++void qmblk_data_write_unlock(struct vz_quota_master *qmblk);
++
++/* for quota operations */
++void vzquota_inode_init_call(struct inode *inode);
++void vzquota_inode_drop_call(struct inode *inode);
++int vzquota_inode_transfer_call(struct inode *, struct iattr *);
++struct vz_quota_master *vzquota_inode_data(struct inode *inode,
++ struct vz_quota_datast *);
++void vzquota_data_unlock(struct inode *inode, struct vz_quota_datast *);
++int vzquota_rename_check(struct inode *inode,
++ struct inode *old_dir, struct inode *new_dir);
++struct vz_quota_master *vzquota_inode_qmblk(struct inode *inode);
++/* for second-level quota */
++struct vz_quota_master *vzquota_find_qmblk(struct super_block *);
++/* for management operations */
++struct vz_quota_master *vzquota_alloc_master(unsigned int quota_id,
++ struct vz_quota_stat *qstat);
++void vzquota_free_master(struct vz_quota_master *);
++struct vz_quota_master *vzquota_find_master(unsigned int quota_id);
++int vzquota_on_qmblk(struct super_block *sb, struct inode *inode,
++ struct vz_quota_master *qmblk, char __user *buf);
++int vzquota_off_qmblk(struct super_block *sb, struct vz_quota_master *qmblk,
++ char __user *buf, int force);
++int vzquota_get_super(struct super_block *sb);
++void vzquota_put_super(struct super_block *sb);
++
++static inline struct vz_quota_master *qmblk_get(struct vz_quota_master *qmblk)
++{
++ if (!atomic_read(&qmblk->dq_count))
++ BUG();
++ atomic_inc(&qmblk->dq_count);
++ return qmblk;
++}
++
++static inline void __qmblk_put(struct vz_quota_master *qmblk)
++{
++ atomic_dec(&qmblk->dq_count);
++}
++
++static inline void qmblk_put(struct vz_quota_master *qmblk)
++{
++ if (!atomic_dec_and_test(&qmblk->dq_count))
++ return;
++ vzquota_free_master(qmblk);
++}
++
++extern struct list_head vzquota_hash_table[];
++extern int vzquota_hash_size;
++
++/*
++ * Interface to VZ UGID quota
++ */
++extern struct quotactl_ops vz_quotactl_operations;
++extern struct dquot_operations vz_quota_operations2;
++extern struct quota_format_type vz_quota_empty_v2_format;
++
++#define QUGID_TREE(qmblk, type) (((type) == USRQUOTA) ? \
++ qmblk->dq_uid_tree : \
++ qmblk->dq_gid_tree)
++
++#define VZDQUG_FIND_DONT_ALLOC 1
++#define VZDQUG_FIND_FAKE 2
++struct vz_quota_ugid *vzquota_find_ugid(struct vz_quota_master *qmblk,
++ unsigned int quota_id, int type, int flags);
++struct vz_quota_ugid *__vzquota_find_ugid(struct vz_quota_master *qmblk,
++ unsigned int quota_id, int type, int flags);
++struct vz_quota_ugid *vzquota_get_ugid(struct vz_quota_ugid *qugid);
++void vzquota_put_ugid(struct vz_quota_master *qmblk,
++ struct vz_quota_ugid *qugid);
++void vzquota_kill_ugid(struct vz_quota_master *qmblk);
++int vzquota_ugid_init(void);
++void vzquota_ugid_release(void);
++int vzquota_transfer_usage(struct inode *inode, int mask,
++ struct vz_quota_ilink *qlnk);
++void vzquota_inode_off(struct inode *inode);
++
++long do_vzquotaugidctl(int cmd, unsigned int quota_id,
++ unsigned int ugid_index, unsigned int ugid_size,
++ void *addr, int compat);
++
++/*
++ * Other VZ quota parts
++ */
++extern struct dquot_operations vz_quota_operations;
++
++long do_vzquotactl(int cmd, unsigned int quota_id,
++ struct vz_quota_stat __user *qstat, const char __user *ve_root,
++ int compat);
++int vzquota_proc_init(void);
++void vzquota_proc_release(void);
++struct vz_quota_master *vzquota_find_qmblk(struct super_block *);
++extern struct semaphore vz_quota_sem;
++
++void vzaquota_init(void);
++void vzaquota_fini(void);
++
++#endif /* __KERNEL__ */
++
++#endif /* _VZDQUOTA_H */
+diff --git a/include/linux/vzquota_qlnk.h b/include/linux/vzquota_qlnk.h
+new file mode 100644
+index 0000000..2788c41
+--- /dev/null
++++ b/include/linux/vzquota_qlnk.h
+@@ -0,0 +1,25 @@
++/*
++ * include/linux/vzquota_qlnk.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef _VZDQUOTA_QLNK_H
++#define _VZDQUOTA_QLNK_H
++
++struct vz_quota_master;
++struct vz_quota_ugid;
++
++/* inode link, used to track inodes using quota via dq_ilink_list */
++struct vz_quota_ilink {
++ struct vz_quota_master *qmblk;
++ struct vz_quota_ugid *qugid[MAXQUOTAS];
++ struct list_head list;
++ unsigned char origin[2];
++};
++
++#endif /* _VZDQUOTA_QLNK_H */
+diff --git a/include/linux/vzratelimit.h b/include/linux/vzratelimit.h
+new file mode 100644
+index 0000000..f26baad
+--- /dev/null
++++ b/include/linux/vzratelimit.h
+@@ -0,0 +1,28 @@
++/*
++ * include/linux/vzratelimit.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __VZ_RATELIMIT_H__
++#define __VZ_RATELIMIT_H__
++
++/*
++ * Generic ratelimiting stuff.
++ */
++
++struct vz_rate_info {
++ int burst;
++ int interval; /* jiffy_t per event */
++ int bucket; /* kind of leaky bucket */
++ unsigned long last; /* last event */
++};
++
++/* Return true if rate limit permits. */
++int vz_ratelimit(struct vz_rate_info *p);
++
++#endif /* __VZ_RATELIMIT_H__ */
+diff --git a/include/linux/vzstat.h b/include/linux/vzstat.h
+new file mode 100644
+index 0000000..5c23ea4
+--- /dev/null
++++ b/include/linux/vzstat.h
+@@ -0,0 +1,182 @@
++/*
++ * include/linux/vzstat.h
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#ifndef __VZSTAT_H__
++#define __VZSTAT_H__
++
++struct swap_cache_info_struct {
++ unsigned long add_total;
++ unsigned long del_total;
++ unsigned long find_success;
++ unsigned long find_total;
++ unsigned long noent_race;
++ unsigned long exist_race;
++ unsigned long remove_race;
++};
++
++struct kstat_lat_snap_struct {
++ cycles_t maxlat, totlat;
++ unsigned long count;
++};
++struct kstat_lat_pcpu_snap_struct {
++ cycles_t maxlat, totlat;
++ unsigned long count;
++ seqcount_t lock;
++} ____cacheline_aligned_in_smp;
++
++struct kstat_lat_struct {
++ struct kstat_lat_snap_struct cur, last;
++ cycles_t avg[3];
++};
++struct kstat_lat_pcpu_struct {
++ struct kstat_lat_pcpu_snap_struct cur[NR_CPUS];
++ cycles_t max_snap;
++ struct kstat_lat_snap_struct last;
++ cycles_t avg[3];
++};
++
++struct kstat_perf_snap_struct {
++ cycles_t wall_tottime, cpu_tottime;
++ cycles_t wall_maxdur, cpu_maxdur;
++ unsigned long count;
++};
++struct kstat_perf_struct {
++ struct kstat_perf_snap_struct cur, last;
++};
++
++struct kstat_zone_avg {
++ unsigned long free_pages_avg[3],
++ nr_active_avg[3],
++ nr_inactive_avg[3];
++};
++
++#define KSTAT_ALLOCSTAT_NR 5
++
++struct kernel_stat_glob {
++ unsigned long nr_unint_avg[3];
++
++ unsigned long alloc_fails[KSTAT_ALLOCSTAT_NR];
++ struct kstat_lat_struct alloc_lat[KSTAT_ALLOCSTAT_NR];
++ struct kstat_lat_pcpu_struct sched_lat;
++ struct kstat_lat_struct swap_in;
++
++ struct kstat_perf_struct ttfp, cache_reap,
++ refill_inact, shrink_icache, shrink_dcache;
++
++ struct kstat_zone_avg zone_avg[3]; /* MAX_NR_ZONES */
++} ____cacheline_aligned;
++
++extern struct kernel_stat_glob kstat_glob ____cacheline_aligned;
++extern spinlock_t kstat_glb_lock;
++
++#ifdef CONFIG_VE
++#define KSTAT_PERF_ENTER(name) \
++ unsigned long flags; \
++ cycles_t start, sleep_time; \
++ \
++ start = get_cycles(); \
++ sleep_time = VE_TASK_INFO(current)->sleep_time; \
++
++#define KSTAT_PERF_LEAVE(name) \
++ spin_lock_irqsave(&kstat_glb_lock, flags); \
++ kstat_glob.name.cur.count++; \
++ start = get_cycles() - start; \
++ if (kstat_glob.name.cur.wall_maxdur < start) \
++ kstat_glob.name.cur.wall_maxdur = start;\
++ kstat_glob.name.cur.wall_tottime += start; \
++ start -= VE_TASK_INFO(current)->sleep_time - \
++ sleep_time; \
++ if (kstat_glob.name.cur.cpu_maxdur < start) \
++ kstat_glob.name.cur.cpu_maxdur = start; \
++ kstat_glob.name.cur.cpu_tottime += start; \
++ spin_unlock_irqrestore(&kstat_glb_lock, flags); \
++
++#else
++#define KSTAT_PERF_ENTER(name)
++#define KSTAT_PERF_LEAVE(name)
++#endif
++
++/*
++ * Add another statistics reading.
++ * Serialization is the caller's due.
++ */
++static inline void KSTAT_LAT_ADD(struct kstat_lat_struct *p,
++ cycles_t dur)
++{
++ p->cur.count++;
++ if (p->cur.maxlat < dur)
++ p->cur.maxlat = dur;
++ p->cur.totlat += dur;
++}
++
++static inline void KSTAT_LAT_PCPU_ADD(struct kstat_lat_pcpu_struct *p, int cpu,
++ cycles_t dur)
++{
++ struct kstat_lat_pcpu_snap_struct *cur;
++
++ cur = &p->cur[cpu];
++ write_seqcount_begin(&cur->lock);
++ cur->count++;
++ if (cur->maxlat < dur)
++ cur->maxlat = dur;
++ cur->totlat += dur;
++ write_seqcount_end(&cur->lock);
++}
++
++/*
++ * Move current statistics to last, clear last.
++ * Serialization is the caller's due.
++ */
++static inline void KSTAT_LAT_UPDATE(struct kstat_lat_struct *p)
++{
++ cycles_t m;
++ memcpy(&p->last, &p->cur, sizeof(p->last));
++ p->cur.maxlat = 0;
++ m = p->last.maxlat;
++ CALC_LOAD(p->avg[0], EXP_1, m)
++ CALC_LOAD(p->avg[1], EXP_5, m)
++ CALC_LOAD(p->avg[2], EXP_15, m)
++}
++
++static inline void KSTAT_LAT_PCPU_UPDATE(struct kstat_lat_pcpu_struct *p)
++{
++ unsigned i, cpu;
++ struct kstat_lat_pcpu_snap_struct snap, *cur;
++ cycles_t m;
++
++ memset(&p->last, 0, sizeof(p->last));
++ for (cpu = 0; cpu < NR_CPUS; cpu++) {
++ cur = &p->cur[cpu];
++ do {
++ i = read_seqcount_begin(&cur->lock);
++ memcpy(&snap, cur, sizeof(snap));
++ } while (read_seqcount_retry(&cur->lock, i));
++ /*
++ * read above and this update of maxlat is not atomic,
++ * but this is OK, since it happens rarely and losing
++ * a couple of peaks is not essential. xemul
++ */
++ cur->maxlat = 0;
++
++ p->last.count += snap.count;
++ p->last.totlat += snap.totlat;
++ if (p->last.maxlat < snap.maxlat)
++ p->last.maxlat = snap.maxlat;
++ }
++
++ m = (p->last.maxlat > p->max_snap ? p->last.maxlat : p->max_snap);
++ CALC_LOAD(p->avg[0], EXP_1, m);
++ CALC_LOAD(p->avg[1], EXP_5, m);
++ CALC_LOAD(p->avg[2], EXP_15, m);
++ /* reset max_snap to calculate it correctly next time */
++ p->max_snap = 0;
++}
++
++#endif /* __VZSTAT_H__ */
+diff --git a/include/net/addrconf.h b/include/net/addrconf.h
+index c216de5..dff9367 100644
+--- a/include/net/addrconf.h
++++ b/include/net/addrconf.h
+@@ -260,5 +260,9 @@ extern int if6_proc_init(void);
+ extern void if6_proc_exit(void);
+ #endif
+
++int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
++ unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
++ __u32 valid_lft);
++
+ #endif
+ #endif
+diff --git a/include/net/af_unix.h b/include/net/af_unix.h
+index 7dd29b7..f830fb4 100644
+--- a/include/net/af_unix.h
++++ b/include/net/af_unix.h
+@@ -9,6 +9,7 @@
+ extern void unix_inflight(struct file *fp);
+ extern void unix_notinflight(struct file *fp);
+ extern void unix_gc(void);
++extern void unix_destruct_fds(struct sk_buff *skb);
+
+ #define UNIX_HASH_SIZE 256
+
+diff --git a/include/net/flow.h b/include/net/flow.h
+index 228b247..d802436 100644
+--- a/include/net/flow.h
++++ b/include/net/flow.h
+@@ -10,6 +10,7 @@
+ #include <linux/in6.h>
+ #include <asm/atomic.h>
+
++struct ve_struct;
+ struct flowi {
+ int oif;
+ int iif;
+@@ -75,6 +76,9 @@ struct flowi {
+ #define fl_icmp_code uli_u.icmpt.code
+ #define fl_ipsec_spi uli_u.spi
+ #define fl_mh_type uli_u.mht.type
++#ifdef CONFIG_VE
++ struct ve_struct *owner_env;
++#endif
+ __u32 secid; /* used by xfrm; see secid.txt */
+ } __attribute__((__aligned__(BITS_PER_LONG/8)));
+
+diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
+index e081eef..7a554cc 100644
+--- a/include/net/inet_frag.h
++++ b/include/net/inet_frag.h
+@@ -15,6 +15,9 @@ struct netns_frags {
+ struct inet_frag_queue {
+ struct hlist_node list;
+ struct netns_frags *net;
++#ifdef CONFIG_VE
++ struct ve_struct *owner_ve;
++#endif
+ struct list_head lru_list; /* lru list member */
+ spinlock_t lock;
+ atomic_t refcnt;
+diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
+index bb619d8..148c512 100644
+--- a/include/net/inet_hashtables.h
++++ b/include/net/inet_hashtables.h
+@@ -75,6 +75,7 @@ struct inet_ehash_bucket {
+ * ports are created in O(1) time? I thought so. ;-) -DaveM
+ */
+ struct inet_bind_bucket {
++ struct ve_struct *owner_env;
+ struct net *ib_net;
+ unsigned short port;
+ signed short fastreuse;
+@@ -198,7 +199,8 @@ extern struct inet_bind_bucket *
+ inet_bind_bucket_create(struct kmem_cache *cachep,
+ struct net *net,
+ struct inet_bind_hashbucket *head,
+- const unsigned short snum);
++ const unsigned short snum,
++ struct ve_struct *env);
+ extern void inet_bind_bucket_destroy(struct kmem_cache *cachep,
+ struct inet_bind_bucket *tb);
+
+diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
+index 9132490..50fe9af 100644
+--- a/include/net/inet_timewait_sock.h
++++ b/include/net/inet_timewait_sock.h
+@@ -81,6 +81,7 @@ struct inet_timewait_death_row {
+ struct inet_hashinfo *hashinfo;
+ int sysctl_tw_recycle;
+ int sysctl_max_tw_buckets;
++ int ub_managed;
+ };
+
+ extern void inet_twdr_hangman(unsigned long data);
+@@ -134,6 +135,7 @@ struct inet_timewait_sock {
+ unsigned long tw_ttd;
+ struct inet_bind_bucket *tw_tb;
+ struct hlist_node tw_death_node;
++ envid_t tw_owner_env;
+ };
+
+ static inline void inet_twsk_add_node(struct inet_timewait_sock *tw,
+diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
+index 7c5c0f7..6d549ac 100644
+--- a/include/net/ip6_fib.h
++++ b/include/net/ip6_fib.h
+@@ -156,6 +156,7 @@ struct fib6_table {
+ u32 tb6_id;
+ rwlock_t tb6_lock;
+ struct fib6_node tb6_root;
++ struct ve_struct *owner_env;
+ };
+
+ #define RT6_TABLE_UNSPEC RT_TABLE_UNSPEC
+diff --git a/include/net/ipv6.h b/include/net/ipv6.h
+index 113028f..52b65c3 100644
+--- a/include/net/ipv6.h
++++ b/include/net/ipv6.h
+@@ -115,7 +115,7 @@ extern struct ctl_path net_ipv6_ctl_path[];
+ struct inet6_dev *_idev = (idev); \
+ if (likely(_idev != NULL)) \
+ SNMP_INC_STATS##modifier((_idev)->stats.statname, (field)); \
+- SNMP_INC_STATS##modifier(statname##_statistics, (field)); \
++ SNMP_INC_STATS##modifier(ve_##statname##_statistics, (field)); \
+ })
+
+ #define _DEVADD(statname, modifier, idev, field, val) \
+@@ -123,9 +123,22 @@ extern struct ctl_path net_ipv6_ctl_path[];
+ struct inet6_dev *_idev = (idev); \
+ if (likely(_idev != NULL)) \
+ SNMP_ADD_STATS##modifier((_idev)->stats.statname, (field), (val)); \
+- SNMP_ADD_STATS##modifier(statname##_statistics, (field), (val));\
++ SNMP_ADD_STATS##modifier(ve_##statname##_statistics, (field), (val));\
+ })
+
++#ifdef CONFIG_VE
++#define ve_ipv6_statistics (get_exec_env()->_ipv6_statistics)
++#define ve_icmpv6_statistics (get_exec_env()->_icmpv6_statistics)
++#define ve_icmpv6msg_statistics (get_exec_env()->_icmpv6msg_statistics)
++
++extern int init_ipv6_mibs(void);
++extern void cleanup_ipv6_mibs(void);
++#else
++#define ve_ipv6_statistics ipv6_statistics
++#define ve_icmpv6_statistics icmpv6_statistics
++#define ve_icmpv6msg_statistics icmpv6msg_statistics
++#endif
++
+ /* MIBs */
+ DECLARE_SNMP_STAT(struct ipstats_mib, ipv6_statistics);
+
+diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
+index a8eb43c..616b640 100644
+--- a/include/net/net_namespace.h
++++ b/include/net/net_namespace.h
+@@ -48,6 +48,13 @@ struct net {
+ struct hlist_head *dev_name_head;
+ struct hlist_head *dev_index_head;
+
++ int ifindex;
++
++#ifdef CONFIG_VE
++ struct completion *sysfs_completion;
++ struct ve_struct *owner_ve;
++#endif
++
+ /* core fib_rules */
+ struct list_head rules_ops;
+ spinlock_t rules_mod_lock;
+diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h
+index 7573d52..9ced12d 100644
+--- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h
++++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h
+@@ -16,8 +16,18 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4;
+ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4;
+ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp;
+
++#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
+ extern int nf_conntrack_ipv4_compat_init(void);
+ extern void nf_conntrack_ipv4_compat_fini(void);
++#else
++static inline int nf_conntrack_ipv4_compat_init(void)
++{
++ return 0;
++}
++static inline void nf_conntrack_ipv4_compat_fini(void)
++{
++}
++#endif
+
+ extern void need_ipv4_conntrack(void);
+
+diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
+index 0741ad5..bf79a6f 100644
+--- a/include/net/netfilter/nf_conntrack.h
++++ b/include/net/netfilter/nf_conntrack.h
+@@ -28,6 +28,10 @@
+
+ #include <net/netfilter/nf_conntrack_tuple.h>
+
++#ifdef CONFIG_VE_IPTABLES
++#include <linux/ve.h>
++#endif
++
+ /* per conntrack: protocol private data */
+ union nf_conntrack_proto {
+ /* insert conntrack proto private data here */
+@@ -125,6 +129,10 @@ struct nf_conn
+ struct nf_ct_ext *ext;
+
+ struct rcu_head rcu;
++
++#ifdef CONFIG_VE_IPTABLES
++ struct ve_struct *ct_owner_env;
++#endif
+ };
+
+ static inline struct nf_conn *
+@@ -188,6 +196,11 @@ extern void nf_conntrack_hash_insert(struct nf_conn *ct);
+
+ extern void nf_conntrack_flush(void);
+
++struct nf_conntrack_helper * nf_ct_helper_find_get( const struct nf_conntrack_tuple *tuple);
++void nf_ct_helper_put(struct nf_conntrack_helper *helper);
++
++struct nf_conntrack_helper * __nf_conntrack_helper_find_byname(const char *name);
++
+ extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
+ unsigned int nhoff, u_int16_t l3num,
+ struct nf_conntrack_tuple *tuple);
+@@ -253,6 +266,7 @@ extern void nf_conntrack_free(struct nf_conn *ct);
+ extern struct nf_conn *
+ nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
+ const struct nf_conntrack_tuple *repl,
++ struct user_beancounter *,
+ gfp_t gfp);
+
+ /* It's confirmed if it is, or has been in the hash table. */
+@@ -276,6 +290,8 @@ extern unsigned int nf_conntrack_htable_size;
+ extern int nf_conntrack_checksum;
+ extern atomic_t nf_conntrack_count;
+ extern int nf_conntrack_max;
++extern int nf_conntrack_disable_ve0;
++extern int ip_conntrack_disable_ve0;
+
+ DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat);
+ #define NF_CT_STAT_INC(count) (__get_cpu_var(nf_conntrack_stat).count++)
+diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
+index a817712..469fdc3 100644
+--- a/include/net/netfilter/nf_conntrack_core.h
++++ b/include/net/netfilter/nf_conntrack_core.h
+@@ -52,6 +52,45 @@ nf_conntrack_find_get(const struct nf_conntrack_tuple *tuple);
+
+ extern int __nf_conntrack_confirm(struct sk_buff *skb);
+
++#if defined(CONFIG_VE_IPTABLES)
++#include <linux/sched.h>
++#define ve_nf_conntrack_hash (get_exec_env()->_nf_conntrack->_nf_conntrack_hash)
++#define ve_nf_conntrack_vmalloc (get_exec_env()->_nf_conntrack->_nf_conntrack_vmalloc)
++#define ve_unconfirmed (get_exec_env()->_nf_conntrack->_unconfirmed)
++#else
++#define ve_nf_conntrack_hash nf_conntrack_hash
++#define ve_nf_conntrack_vmalloc nf_conntrack_vmalloc
++#define ve_unconfirmed unconfirmed
++#endif /* CONFIG_VE_IPTABLES */
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++#define ve_nf_ct_sysctl_header \
++ (get_exec_env()->_nf_conntrack->_nf_ct_sysctl_header)
++#define ve_nf_ct_netfilter_header \
++ (get_exec_env()->_nf_conntrack->_nf_ct_netfilter_header)
++#define ve_nf_ct_sysctl_table \
++ (get_exec_env()->_nf_conntrack->_nf_ct_sysctl_table)
++#define ve_nf_ct_netfilter_table \
++ (get_exec_env()->_nf_conntrack->_nf_ct_netfilter_table)
++#define ve_nf_ct_net_table \
++ (get_exec_env()->_nf_conntrack->_nf_ct_net_table)
++extern void nf_ct_proto_generic_sysctl_cleanup(void);
++extern int nf_ct_proto_generic_sysctl_init(void);
++#else
++#define ve_nf_ct_sysctl_header nf_ct_sysctl_header
++#define ve_nf_ct_netfilter_header nf_ct_netfilter_header
++#define ve_nf_ct_sysctl_table nf_ct_sysctl_table
++#define ve_nf_ct_netfilter_table nf_ct_netfilter_table
++#define ve_nf_ct_net_table nf_ct_net_table
++static inline int nf_ct_proto_generic_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_generic_sysctl_cleanup(void)
++{
++}
++#endif /* CONFIG_VE_IPTABLES */
++
+ /* Confirm a connection: returns NF_DROP if packet must be dropped. */
+ static inline int nf_conntrack_confirm(struct sk_buff *skb)
+ {
+@@ -71,7 +110,9 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
+ const struct nf_conntrack_l3proto *l3proto,
+ const struct nf_conntrack_l4proto *proto);
+
++#ifndef CONFIG_VE_IPTABLES
+ extern struct hlist_head *nf_conntrack_hash;
++#endif
+ extern spinlock_t nf_conntrack_lock ;
+ extern struct hlist_head unconfirmed;
+
+diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
+index f0b9078..4bcf1bd 100644
+--- a/include/net/netfilter/nf_conntrack_ecache.h
++++ b/include/net/netfilter/nf_conntrack_ecache.h
+@@ -34,6 +34,9 @@ nf_conntrack_event_cache(enum ip_conntrack_events event,
+ struct nf_conn *ct = (struct nf_conn *)skb->nfct;
+ struct nf_conntrack_ecache *ecache;
+
++ if (!ve_is_super(get_exec_env()))
++ return;
++
+ local_bh_disable();
+ ecache = &__get_cpu_var(nf_conntrack_ecache);
+ if (ct != ecache->ct)
+@@ -45,7 +48,7 @@ nf_conntrack_event_cache(enum ip_conntrack_events event,
+ static inline void nf_conntrack_event(enum ip_conntrack_events event,
+ struct nf_conn *ct)
+ {
+- if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct))
++ if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct) && ve_is_super(get_exec_env()))
+ atomic_notifier_call_chain(&nf_conntrack_chain, event, ct);
+ }
+
+@@ -57,7 +60,8 @@ static inline void
+ nf_ct_expect_event(enum ip_conntrack_expect_events event,
+ struct nf_conntrack_expect *exp)
+ {
+- atomic_notifier_call_chain(&nf_ct_expect_chain, event, exp);
++ if (ve_is_super(get_exec_env()))
++ atomic_notifier_call_chain(&nf_ct_expect_chain, event, exp);
+ }
+
+ #else /* CONFIG_NF_CONNTRACK_EVENTS */
+diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
+index dfdf4b4..4175cdf 100644
+--- a/include/net/netfilter/nf_conntrack_expect.h
++++ b/include/net/netfilter/nf_conntrack_expect.h
+@@ -6,9 +6,17 @@
+ #define _NF_CONNTRACK_EXPECT_H
+ #include <net/netfilter/nf_conntrack.h>
+
+-extern struct hlist_head *nf_ct_expect_hash;
+ extern unsigned int nf_ct_expect_hsize;
+ extern unsigned int nf_ct_expect_max;
++#ifdef CONFIG_VE_IPTABLES
++#include <linux/sched.h>
++#define ve_nf_ct_expect_hash (get_exec_env()->_nf_conntrack->_nf_ct_expect_hash)
++#define ve_nf_ct_expect_max (get_exec_env()->_nf_conntrack->_nf_ct_expect_max)
++#else
++extern struct hlist_head *nf_ct_expect_hash;
++#define ve_nf_ct_expect_hash nf_ct_expect_hash
++#define ve_nf_ct_expect_max nf_ct_expect_max
++#endif
+
+ struct nf_conntrack_expect
+ {
+@@ -73,6 +81,8 @@ void nf_conntrack_expect_fini(void);
+ struct nf_conntrack_expect *
+ __nf_ct_expect_find(const struct nf_conntrack_tuple *tuple);
+
++void nf_ct_expect_insert(struct nf_conntrack_expect *exp);
++
+ struct nf_conntrack_expect *
+ nf_ct_expect_find_get(const struct nf_conntrack_tuple *tuple);
+
+diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h
+index 0378676..ac81973 100644
+--- a/include/net/netfilter/nf_conntrack_l3proto.h
++++ b/include/net/netfilter/nf_conntrack_l3proto.h
+@@ -42,6 +42,9 @@ struct nf_conntrack_l3proto
+ int (*print_tuple)(struct seq_file *s,
+ const struct nf_conntrack_tuple *);
+
++ /* Called when a conntrack entry is destroyed */
++ void (*destroy)(struct nf_conn *conntrack);
++
+ /*
+ * Called before tracking.
+ * *dataoff: offset of protocol header (TCP, UDP,...) in skb
+@@ -67,6 +70,31 @@ struct nf_conntrack_l3proto
+ struct module *me;
+ };
+
++/* virtualization of l3 protocol's sysctl tables: */
++#if defined(CONFIG_VE_IPTABLES)
++#include <linux/sched.h>
++#define ve_nf_ct3 (get_exec_env()->_nf_conntrack)
++#endif
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++#define ve_nf_ct_l3protos ve_nf_ct3->_nf_ct_l3protos
++#define ve_nf_conntrack_l3proto_ipv4 (ve_nf_ct3->_nf_conntrack_l3proto_ipv4)
++#define ve_nf_conntrack_max (ve_nf_ct3->_nf_conntrack_max)
++#define ve_nf_conntrack_count (ve_nf_ct3->_nf_conntrack_count)
++#define ve_nf_conntrack_checksum (ve_nf_ct3->_nf_conntrack_checksum)
++#else /* !CONFIG_VE_IPTABLES || !CONFIG_SYSCTL: */
++#define ve_nf_ct_l3protos nf_ct_l3protos
++#define ve_nf_conntrack_l3proto_ipv4 &nf_conntrack_l3proto_ipv4
++#define ve_nf_conntrack_max nf_conntrack_max
++#define ve_nf_conntrack_count nf_conntrack_count
++#define ve_nf_conntrack_checksum nf_conntrack_checksum
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
++
++extern int init_nf_ct_l3proto_ipv4(void);
++extern void fini_nf_ct_l3proto_ipv4(void);
++extern int init_nf_ct_l3proto_ipv6(void);
++extern void fini_nf_ct_l3proto_ipv6(void);
++
+ extern struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX];
+
+ /* Protocol registration. */
+@@ -83,7 +111,11 @@ __nf_ct_l3proto_find(u_int16_t l3proto)
+ {
+ if (unlikely(l3proto >= AF_MAX))
+ return &nf_conntrack_l3proto_generic;
+- return rcu_dereference(nf_ct_l3protos[l3proto]);
++#ifdef CONFIG_VE_IPTABLES
++ if (!get_exec_env()->_nf_conntrack)
++ return &nf_conntrack_l3proto_generic;
++#endif
++ return rcu_dereference(ve_nf_ct_l3protos[l3proto]);
+ }
+
+ #endif /*_NF_CONNTRACK_L3PROTO_H*/
+diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h
+index 723df9d..e5be345 100644
+--- a/include/net/netfilter/nf_conntrack_l4proto.h
++++ b/include/net/netfilter/nf_conntrack_l4proto.h
+@@ -97,6 +97,7 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6;
+ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic;
+
+ #define MAX_NF_CT_PROTO 256
++extern struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX];
+
+ extern struct nf_conntrack_l4proto *
+ __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto);
+@@ -117,16 +118,146 @@ extern int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
+ struct nf_conntrack_tuple *t);
+ extern const struct nla_policy nf_ct_port_nla_policy[];
+
++#ifdef CONFIG_SYSCTL
+ /* Log invalid packets */
+ extern unsigned int nf_ct_log_invalid;
++#endif
++
++#ifdef CONFIG_VE_IPTABLES
++#include <linux/sched.h>
++#define ve_nf_ct4 (get_exec_env()->_nf_conntrack)
++#endif
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++
++#define ve_nf_ct_protos (ve_nf_ct4->_nf_ct_protos)
++#define ve_nf_conntrack_l4proto_icmp (ve_nf_ct4->_nf_conntrack_l4proto_icmp)
++#define ve_nf_conntrack_l4proto_icmpv6 \
++ (ve_nf_ct4->_nf_conntrack_l4proto_icmpv6)
++#define ve_nf_conntrack_l4proto_tcp4 (ve_nf_ct4->_nf_conntrack_l4proto_tcp4)
++#define ve_nf_conntrack_l4proto_tcp6 (ve_nf_ct4->_nf_conntrack_l4proto_tcp6)
++#define ve_nf_conntrack_l4proto_udp4 (ve_nf_ct4->_nf_conntrack_l4proto_udp4)
++#define ve_nf_conntrack_l4proto_udp6 (ve_nf_ct4->_nf_conntrack_l4proto_udp6)
++#define ve_nf_conntrack_l4proto_generic \
++ (ve_nf_ct4->_nf_conntrack_l4proto_generic)
++#define ve_nf_ct_log_invalid (ve_nf_ct4->_nf_ct_log_invalid)
++/* TCP: */
++#define ve_nf_ct_tcp_timeouts (ve_nf_ct4->_nf_ct_tcp_timeouts)
++#define ve_nf_ct_tcp_timeout_max_retrans \
++ (ve_nf_ct4->_nf_ct_tcp_timeout_max_retrans)
++#define ve_nf_ct_tcp_timeout_unacknowledged \
++ (ve_nf_ct4->_nf_ct_tcp_timeout_unacknowledged)
++#define ve_nf_ct_tcp_max_retrans (ve_nf_ct4->_nf_ct_tcp_max_retrans)
++#define ve_nf_ct_tcp_loose (ve_nf_ct4->_nf_ct_tcp_loose)
++#define ve_nf_ct_tcp_be_liberal (ve_nf_ct4->_nf_ct_tcp_be_liberal)
++#define ve_tcp_sysctl_table_users (ve_nf_ct4->_tcp_sysctl_table_users)
++#define ve_tcp_sysctl_header (ve_nf_ct4->_tcp_sysctl_header)
++#define ve_tcp_compat_sysctl_header (ve_nf_ct4->_tcp_compat_sysctl_header)
++/* UDP: */
++#define ve_nf_ct_udp_timeout (ve_nf_ct4->_nf_ct_udp_timeout)
++#define ve_nf_ct_udp_timeout_stream (ve_nf_ct4->_nf_ct_udp_timeout_stream)
++#define ve_udp_sysctl_table_users (ve_nf_ct4->_udp_sysctl_table_users)
++#define ve_udp_sysctl_header (ve_nf_ct4->_udp_sysctl_header)
++#define ve_udp_compat_sysctl_header (ve_nf_ct4->_udp_compat_sysctl_header)
++/* ICMP: */
++#define ve_nf_ct_icmp_timeout (ve_nf_ct4->_nf_ct_icmp_timeout)
++#define ve_icmp_sysctl_header (ve_nf_ct4->_icmp_sysctl_header)
++#define ve_icmp_compat_sysctl_header (ve_nf_ct4->_icmp_compat_sysctl_header)
++/* ICMPV6: */
++#define ve_nf_ct_icmpv6_timeout (ve_nf_ct4->_nf_ct_icmpv6_timeout)
++#define ve_icmpv6_sysctl_header (ve_nf_ct4->_icmpv6_sysctl_header)
++/* GENERIC: */
++#define ve_nf_ct_generic_timeout (ve_nf_ct4->_nf_ct_generic_timeout)
++#define ve_generic_sysctl_header (ve_nf_ct4->_generic_sysctl_header)
++#define ve_generic_compat_sysctl_header (ve_nf_ct4->_generic_compat_sysctl_header)
++
++extern void nf_ct_proto_icmp_sysctl_cleanup(void);
++extern int nf_ct_proto_icmp_sysctl_init(void);
++extern void nf_ct_proto_icmpv6_sysctl_cleanup(void);
++extern int nf_ct_proto_icmpv6_sysctl_init(void);
++extern void nf_ct_proto_tcp_sysctl_cleanup(void);
++extern int nf_ct_proto_tcp_sysctl_init(void);
++extern void nf_ct_proto_udp_sysctl_cleanup(void);
++extern int nf_ct_proto_udp_sysctl_init(void);
++
++#else /* !CONFIG_VE_IPTABLES || !CONFIG_SYSCTL: */
++
++#define ve_nf_ct_protos nf_ct_protos
++#define ve_nf_conntrack_l4proto_icmp &nf_conntrack_l4proto_icmp
++#define ve_nf_conntrack_l4proto_icmpv6 &nf_conntrack_l4proto_icmpv6
++#define ve_nf_conntrack_l4proto_tcp4 &nf_conntrack_l4proto_tcp4
++#define ve_nf_conntrack_l4proto_tcp6 &nf_conntrack_l4proto_tcp6
++#define ve_nf_conntrack_l4proto_udp4 &nf_conntrack_l4proto_udp4
++#define ve_nf_conntrack_l4proto_udp6 &nf_conntrack_l4proto_udp6
++#define ve_nf_conntrack_l4proto_generic &nf_conntrack_l4proto_generic
++
++#if defined(CONFIG_SYSCTL)
++
++#define ve_nf_ct_log_invalid nf_ct_log_invalid
++/* TCP: */
++#define ve_nf_ct_tcp_timeouts *tcp_timeouts
++#define ve_nf_ct_tcp_timeout_max_retrans \
++ nf_ct_tcp_timeout_max_retrans
++#define ve_nf_ct_tcp_timeout_unacknowledged \
++ nf_ct_tcp_timeout_unacknowledged
++#define ve_nf_ct_tcp_max_retrans nf_ct_tcp_max_retrans
++#define ve_nf_ct_tcp_loose nf_ct_tcp_loose
++#define ve_nf_ct_tcp_be_liberal nf_ct_tcp_be_liberal
++#define ve_tcp_sysctl_table_users tcp_sysctl_table_users
++#define ve_tcp_sysctl_header tcp_sysctl_header
++/* UDP:*/
++#define ve_nf_ct_udp_timeout nf_ct_udp_timeout
++#define ve_nf_ct_udp_timeout_stream nf_ct_udp_timeout_stream
++#define ve_udp_sysctl_table_users udp_sysctl_table_users
++#define ve_udp_sysctl_header udp_sysctl_header
++/* ICMP: */
++#define ve_nf_ct_icmp_timeout nf_ct_icmp_timeout
++#define ve_icmp_sysctl_header icmp_sysctl_header
++/* ICMPV6: */
++#define ve_nf_ct_icmpv6_timeout nf_ct_icmpv6_timeout
++#define ve_icmpv6_sysctl_header icmpv6_sysctl_header
++/* GENERIC: */
++#define ve_nf_ct_generic_timeout nf_ct_generic_timeout
++#define ve_generic_sysctl_header generic_sysctl_header
++#endif /* CONFIG_SYSCTL */
++
++static inline int nf_ct_proto_icmp_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_icmp_sysctl_cleanup(void)
++{
++}
++static inline int nf_ct_proto_tcp_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_tcp_sysctl_cleanup(void)
++{
++}
++static inline int nf_ct_proto_udp_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_udp_sysctl_cleanup(void)
++{
++}
++static inline int nf_ct_proto_icmpv6_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_icmpv6_sysctl_cleanup(void)
++{
++}
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
+
+ #ifdef CONFIG_SYSCTL
+ #ifdef DEBUG_INVALID_PACKETS
+ #define LOG_INVALID(proto) \
+- (nf_ct_log_invalid == (proto) || nf_ct_log_invalid == IPPROTO_RAW)
++ (ve_nf_ct_log_invalid == (proto) || ve_nf_ct_log_invalid == IPPROTO_RAW)
+ #else
+ #define LOG_INVALID(proto) \
+- ((nf_ct_log_invalid == (proto) || nf_ct_log_invalid == IPPROTO_RAW) \
++ ((ve_nf_ct_log_invalid == (proto) || ve_nf_ct_log_invalid == IPPROTO_RAW) \
+ && net_ratelimit())
+ #endif
+ #else
+diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h
+index 9dc1039..bfa9069 100644
+--- a/include/net/netfilter/nf_nat.h
++++ b/include/net/netfilter/nf_nat.h
+@@ -77,6 +77,8 @@ struct nf_conn_nat
+ #endif
+ };
+
++void nf_nat_hash_conntrack(struct nf_conn *ct);
++
+ /* Set up the info structure to map into this range. */
+ extern unsigned int nf_nat_setup_info(struct nf_conn *ct,
+ const struct nf_nat_range *range,
+@@ -85,6 +87,7 @@ extern unsigned int nf_nat_setup_info(struct nf_conn *ct,
+ /* Is this tuple already taken? (not by us)*/
+ extern int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
+ const struct nf_conn *ignored_conntrack);
++extern void ip_nat_hash_conntrack(struct nf_conn *ct);
+
+ static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
+ {
+diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h
+index e4a18ae..8bb00da 100644
+--- a/include/net/netfilter/nf_nat_rule.h
++++ b/include/net/netfilter/nf_nat_rule.h
+@@ -4,7 +4,7 @@
+ #include <net/netfilter/nf_nat.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
+
+-extern int nf_nat_rule_init(void) __init;
++extern int nf_nat_rule_init(void);
+ extern void nf_nat_rule_cleanup(void);
+ extern int nf_nat_rule_find(struct sk_buff *skb,
+ unsigned int hooknum,
+diff --git a/include/net/netlink_sock.h b/include/net/netlink_sock.h
+new file mode 100644
+index 0000000..ce4701a
+--- /dev/null
++++ b/include/net/netlink_sock.h
+@@ -0,0 +1,23 @@
++#ifndef __NET_NETLINK_SOCK_H
++#define __NET_NETLINK_SOCK_H
++
++struct netlink_sock {
++ /* struct sock has to be the first member of netlink_sock */
++ struct sock sk;
++ u32 pid;
++ u32 dst_pid;
++ u32 dst_group;
++ u32 flags;
++ u32 subscriptions;
++ u32 ngroups;
++ unsigned long *groups;
++ unsigned long state;
++ wait_queue_head_t wait;
++ struct netlink_callback *cb;
++ struct mutex *cb_mutex;
++ struct mutex cb_def_mutex;
++ void (*netlink_rcv)(struct sk_buff *skb);
++ struct module *module;
++};
++
++#endif /* __NET_NETLINK_SOCK_H */
+diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
+index 2932721..a3e3007 100644
+--- a/include/net/netns/ipv6.h
++++ b/include/net/netns/ipv6.h
+@@ -13,6 +13,7 @@ struct netns_sysctl_ipv6 {
+ #ifdef CONFIG_SYSCTL
+ struct ctl_table_header *table;
+ struct ctl_table_header *frags_hdr;
++ struct ctl_table_header *nf_frags_hdr;
+ #endif
+ int bindv6only;
+ int flush_delay;
+@@ -31,6 +32,11 @@ struct netns_ipv6 {
+ struct ipv6_devconf *devconf_all;
+ struct ipv6_devconf *devconf_dflt;
+ struct netns_frags frags;
++ struct netns_frags ct_frags;
++
++#ifdef CONFIG_SYSCTL
++ struct nf_conntrack_l3proto *nf_conntrack_l3proto_ipv6;
++#endif
+ #ifdef CONFIG_NETFILTER
+ struct xt_table *ip6table_filter;
+ struct xt_table *ip6table_mangle;
+@@ -55,5 +61,7 @@ struct netns_ipv6 {
+ struct sock *ndisc_sk;
+ struct sock *tcp_sk;
+ struct sock *igmp_sk;
++
++ struct proc_dir_entry *proc_dev_snmp;
+ };
+ #endif
+diff --git a/include/net/route.h b/include/net/route.h
+index 4f0d8c1..0836235 100644
+--- a/include/net/route.h
++++ b/include/net/route.h
+@@ -138,6 +138,7 @@ static inline void ip_rt_put(struct rtable * rt)
+ #define IPTOS_RT_MASK (IPTOS_TOS_MASK & ~3)
+
+ extern const __u8 ip_tos2prio[16];
++extern int ip_rt_src_check;
+
+ static inline char rt_tos2priority(u8 tos)
+ {
+diff --git a/include/net/sock.h b/include/net/sock.h
+index 06c5259..7fc48ef 100644
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -57,6 +57,8 @@
+ #include <net/dst.h>
+ #include <net/checksum.h>
+
++#include <bc/net.h>
++
+ /*
+ * This structure really needs to be cleaned up.
+ * Most of it is for TCP, and not used by any of
+@@ -279,6 +281,8 @@ struct sock {
+ int (*sk_backlog_rcv)(struct sock *sk,
+ struct sk_buff *skb);
+ void (*sk_destruct)(struct sock *sk);
++ struct sock_beancounter sk_bc;
++ struct ve_struct *owner_env;
+ };
+
+ /*
+@@ -495,6 +499,8 @@ static inline void sk_add_backlog(struct sock *sk, struct sk_buff *skb)
+ })
+
+ extern int sk_stream_wait_connect(struct sock *sk, long *timeo_p);
++extern int __sk_stream_wait_memory(struct sock *sk, long *timeo_p,
++ unsigned long amount);
+ extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p);
+ extern void sk_stream_wait_close(struct sock *sk, long timeo_p);
+ extern int sk_stream_error(struct sock *sk, int flags, int err);
+@@ -729,7 +735,8 @@ static inline int sk_has_account(struct sock *sk)
+ return !!sk->sk_prot->memory_allocated;
+ }
+
+-static inline int sk_wmem_schedule(struct sock *sk, int size)
++static inline int sk_wmem_schedule(struct sock *sk, int size,
++ struct sk_buff *skb)
+ {
+ if (!sk_has_account(sk))
+ return 1;
+@@ -737,12 +744,15 @@ static inline int sk_wmem_schedule(struct sock *sk, int size)
+ __sk_mem_schedule(sk, size, SK_MEM_SEND);
+ }
+
+-static inline int sk_rmem_schedule(struct sock *sk, int size)
++static inline int sk_rmem_schedule(struct sock *sk, struct sk_buff *skb)
+ {
+ if (!sk_has_account(sk))
+ return 1;
+- return size <= sk->sk_forward_alloc ||
+- __sk_mem_schedule(sk, size, SK_MEM_RECV);
++ if (!(skb->truesize <= sk->sk_forward_alloc ||
++ __sk_mem_schedule(sk, skb->truesize, SK_MEM_RECV)))
++ return 0;
++
++ return !ub_sockrcvbuf_charge(sk, skb);
+ }
+
+ static inline void sk_mem_reclaim(struct sock *sk)
+@@ -862,6 +872,11 @@ extern struct sk_buff *sock_alloc_send_skb(struct sock *sk,
+ unsigned long size,
+ int noblock,
+ int *errcode);
++extern struct sk_buff *sock_alloc_send_skb2(struct sock *sk,
++ unsigned long size,
++ unsigned long size2,
++ int noblock,
++ int *errcode);
+ extern void *sock_kmalloc(struct sock *sk, int size,
+ gfp_t priority);
+ extern void sock_kfree_s(struct sock *sk, void *mem, int size);
+@@ -1124,6 +1139,7 @@ static inline int skb_copy_to_page(struct sock *sk, char __user *from,
+
+ static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+ {
++ WARN_ON(skb->destructor);
+ sock_hold(sk);
+ skb->sk = sk;
+ skb->destructor = sock_wfree;
+@@ -1132,6 +1148,7 @@ static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+
+ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
+ {
++ WARN_ON(skb->destructor);
+ skb->sk = sk;
+ skb->destructor = sock_rfree;
+ atomic_add(skb->truesize, &sk->sk_rmem_alloc);
+@@ -1322,6 +1339,13 @@ static inline void sk_change_net(struct sock *sk, struct net *net)
+ sock_net_set(sk, hold_net(net));
+ }
+
++static inline void sk_change_net_get(struct sock *sk, struct net *net)
++{
++ struct net *old_net = sock_net(sk);
++ sock_net_set(sk, get_net(net));
++ put_net(old_net);
++}
++
+ extern void sock_enable_timestamp(struct sock *sk);
+ extern int sock_get_timestamp(struct sock *, struct timeval __user *);
+ extern int sock_get_timestampns(struct sock *, struct timespec __user *);
+diff --git a/include/net/tcp.h b/include/net/tcp.h
+index 8983386..014798b 100644
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -43,6 +43,13 @@
+ #include <net/inet_ecn.h>
+
+ #include <linux/seq_file.h>
++#include <bc/net.h>
++
++#define TCP_PAGE(sk) (sk->sk_sndmsg_page)
++#define TCP_OFF(sk) (sk->sk_sndmsg_off)
++
++#define TW_WSCALE_MASK 0x0f
++#define TW_WSCALE_SPEC 0x10
+
+ extern struct inet_hashinfo tcp_hashinfo;
+
+@@ -221,7 +228,9 @@ extern int sysctl_tcp_mem[3];
+ extern int sysctl_tcp_wmem[3];
+ extern int sysctl_tcp_rmem[3];
+ extern int sysctl_tcp_app_win;
++#ifndef sysctl_tcp_adv_win_scale
+ extern int sysctl_tcp_adv_win_scale;
++#endif
+ extern int sysctl_tcp_tw_reuse;
+ extern int sysctl_tcp_frto;
+ extern int sysctl_tcp_frto_response;
+@@ -236,6 +245,10 @@ extern int sysctl_tcp_base_mss;
+ extern int sysctl_tcp_workaround_signed_windows;
+ extern int sysctl_tcp_slow_start_after_idle;
+ extern int sysctl_tcp_max_ssthresh;
++extern int sysctl_tcp_use_sg;
++extern int sysctl_tcp_max_tw_kmem_fraction;
++extern int sysctl_tcp_max_tw_buckets_ub;
++
+
+ extern atomic_t tcp_memory_allocated;
+ extern atomic_t tcp_sockets_allocated;
+@@ -546,7 +559,11 @@ extern u32 __tcp_select_window(struct sock *sk);
+ * to use only the low 32-bits of jiffies and hide the ugly
+ * casts with the following macro.
+ */
++#ifdef CONFIG_VE
++#define tcp_time_stamp ((__u32)(jiffies + get_exec_env()->jiffies_fixup))
++#else
+ #define tcp_time_stamp ((__u32)(jiffies))
++#endif
+
+ /* This is what the send packet queuing engine uses to pass
+ * TCP per-packet control information to the transmission
+diff --git a/include/net/udp.h b/include/net/udp.h
+index addcdc6..a48c56a 100644
+--- a/include/net/udp.h
++++ b/include/net/udp.h
+@@ -153,6 +153,18 @@ DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6);
+ /* UDP-Lite does not have a standardized MIB yet, so we inherit from UDP */
+ DECLARE_SNMP_STAT(struct udp_mib, udplite_stats_in6);
+
++#ifdef CONFIG_VE
++#define ve_udp_statistics (get_exec_env()->_udp_statistics)
++#define ve_udplite_statistics (get_exec_env()->_udplite_statistics)
++#define ve_udp_stats_in6 (get_exec_env()->_udp_stats_in6)
++#define ve_udplite_stats_in6 (get_exec_env()->_udplite_stats_in6)
++#else
++#define ve_udp_statistics udp_statistics
++#define ve_udplite_statistics udplite_statistics
++#define ve_udp_stats_in6 udp_stats_in6
++#define ve_udplite_stats_in6 udplite_stats_in6
++#endif
++
+ /*
+ * SNMP statistics for UDP and UDP-Lite
+ */
+diff --git a/init/Kconfig b/init/Kconfig
+index c11da38..b054f1e 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -208,7 +208,7 @@ config TASK_XACCT
+
+ config TASK_IO_ACCOUNTING
+ bool "Enable per-task storage I/O accounting (EXPERIMENTAL)"
+- depends on TASK_XACCT
++ depends on TASK_XACCT && BEANCOUNTERS
+ help
+ Collect information on the number of bytes of storage I/O which this
+ task has caused.
+@@ -292,7 +292,7 @@ config CGROUP_DEBUG
+
+ config CGROUP_NS
+ bool "Namespace cgroup subsystem"
+- depends on CGROUPS
++ depends on CGROUPS && !VE
+ help
+ Provides a simple namespace cgroup subsystem to
+ provide hierarchical naming of sets of namespaces,
+@@ -308,7 +308,7 @@ config CGROUP_DEVICE
+
+ config CPUSETS
+ bool "Cpuset support"
+- depends on SMP && CGROUPS
++ depends on SMP && CGROUPS && !VE
+ help
+ This option will let you create and manage CPUSETs which
+ allow dynamically partitioning a system into sets of CPUs and
+@@ -352,17 +352,18 @@ config RT_GROUP_SCHED
+ choice
+ depends on GROUP_SCHED
+ prompt "Basis for grouping tasks"
+- default USER_SCHED
++ default VZ_FAIRSCHED
+
+ config USER_SCHED
+ bool "user id"
++ depends on !VE
+ help
+ This option will choose userid as the basis for grouping
+ tasks, thus providing equal CPU bandwidth to each user.
+
+ config CGROUP_SCHED
+ bool "Control groups"
+- depends on CGROUPS
++ depends on CGROUPS && !VE
+ help
+ This option allows you to create arbitrary task groups
+ using the "cgroup" pseudo filesystem and control
+@@ -370,6 +371,12 @@ config CGROUP_SCHED
+ Refer to Documentation/cgroups.txt for more information
+ on "cgroup" pseudo filesystem.
+
++config VZ_FAIRSCHED
++ bool "OpenVZ groups"
++ help
++ This option add customizable task groups with OpenVZ compatible
++ syscall and procfs interface.
++
+ endchoice
+
+ config CGROUP_CPUACCT
+diff --git a/init/calibrate.c b/init/calibrate.c
+index a379c90..0cacb27 100644
+--- a/init/calibrate.c
++++ b/init/calibrate.c
+@@ -9,6 +9,7 @@
+ #include <linux/init.h>
+ #include <linux/timex.h>
+ #include <linux/smp.h>
++#include <linux/module.h>
+
+ unsigned long lpj_fine;
+ unsigned long preset_lpj;
+@@ -108,6 +109,60 @@ static unsigned long __cpuinit calibrate_delay_direct(void)
+ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
+ #endif
+
++unsigned long cycles_per_jiffy, cycles_per_clock;
++
++static __devinit void calibrate_cycles(void)
++{
++ unsigned long ticks;
++ cycles_t time;
++
++ ticks = jiffies;
++ while (ticks == jiffies)
++ /* nothing */;
++ time = get_cycles();
++ ticks = jiffies;
++ while (ticks == jiffies)
++ /* nothing */;
++
++ time = get_cycles() - time;
++ cycles_per_jiffy = time;
++ if ((time >> 32) != 0) {
++ printk("CPU too fast! timings are incorrect\n");
++ cycles_per_jiffy = -1;
++ }
++}
++
++EXPORT_SYMBOL(cycles_per_jiffy);
++EXPORT_SYMBOL(cycles_per_clock);
++
++static __devinit void calc_cycles_per_jiffy(void)
++{
++#if 0
++ extern unsigned long fast_gettimeoffset_quotient;
++ unsigned long low, high;
++
++ if (fast_gettimeoffset_quotient != 0) {
++ __asm__("divl %2"
++ :"=a" (low), "=d" (high)
++ :"r" (fast_gettimeoffset_quotient),
++ "0" (0), "1" (1000000/HZ));
++
++ cycles_per_jiffy = low;
++ }
++#endif
++ if (cycles_per_jiffy == 0)
++ calibrate_cycles();
++
++ if (cycles_per_jiffy == 0) {
++ printk(KERN_WARNING "Cycles are stuck! "
++ "Some statistics will not be available.");
++ /* to prevent division by zero in cycles_to_(clocks|jiffies) */
++ cycles_per_jiffy = 1;
++ cycles_per_clock = 1;
++ } else
++ cycles_per_clock = cycles_per_jiffy * (HZ / CLOCKS_PER_SEC);
++}
++
+ /*
+ * This is the number of bits of precision for the loops_per_jiffy. Each
+ * bit takes on average 1.5/HZ seconds. This (like the original) is a little
+@@ -173,4 +228,5 @@ void __cpuinit calibrate_delay(void)
+ printk(KERN_CONT "%lu.%02lu BogoMIPS (lpj=%lu)\n",
+ loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ)) % 100, loops_per_jiffy);
++ calc_cycles_per_jiffy();
+ }
+diff --git a/init/main.c b/init/main.c
+index 3820323..ad14217 100644
+--- a/init/main.c
++++ b/init/main.c
+@@ -60,6 +60,9 @@
+ #include <linux/sched.h>
+ #include <linux/signal.h>
+ #include <linux/idr.h>
++#include <linux/fairsched.h>
++
++#include <bc/beancounter.h>
+
+ #include <asm/io.h>
+ #include <asm/bugs.h>
+@@ -105,6 +108,16 @@ extern void tc_init(void);
+ enum system_states system_state;
+ EXPORT_SYMBOL(system_state);
+
++#ifdef CONFIG_VE
++extern void init_ve_system(void);
++extern void init_ve0(void);
++extern void prepare_ve0_process(struct task_struct *tsk);
++#else
++#define init_ve_system() do { } while (0)
++#define init_ve0() do { } while (0)
++#define prepare_ve0_process(tsk) do { } while (0)
++#endif
++
+ /*
+ * Boot command-line arguments
+ */
+@@ -543,6 +556,9 @@ asmlinkage void __init start_kernel(void)
+
+ smp_setup_processor_id();
+
++ prepare_ve0_process(&init_task);
++ init_ve0();
++
+ /*
+ * Need to run as early as possible, to initialize the
+ * lockdep hash:
+@@ -561,6 +577,7 @@ asmlinkage void __init start_kernel(void)
+ * enable them
+ */
+ lock_kernel();
++ ub_init_early();
+ tick_init();
+ boot_cpu_init();
+ page_address_init();
+@@ -666,6 +683,7 @@ asmlinkage void __init start_kernel(void)
+ thread_info_cache_init();
+ fork_init(num_physpages);
+ proc_caches_init();
++ ub_init_late();
+ buffer_init();
+ unnamed_dev_init();
+ key_init();
+@@ -687,6 +705,10 @@ asmlinkage void __init start_kernel(void)
+
+ acpi_early_init(); /* before LAPIC and SMP init */
+
++#ifdef CONFIG_BC_RSS_ACCOUNTING
++ ub_init_pbc();
++#endif
++
+ /* Do the rest non-__init'ed, we're now alive */
+ rest_init();
+ }
+@@ -766,6 +788,8 @@ static void __init do_initcalls(void)
+ */
+ static void __init do_basic_setup(void)
+ {
++ init_ve_system();
++
+ rcu_init_sched(); /* needed by module_init stage. */
+ /* drivers will send hotplug events */
+ init_workqueues();
+@@ -857,6 +881,7 @@ static int __init kernel_init(void * unused)
+ do_pre_smp_initcalls();
+
+ smp_init();
++ fairsched_init_late();
+ sched_init_smp();
+
+ cpuset_init_smp();
+diff --git a/init/version.c b/init/version.c
+index 52a8b98..ccc6262 100644
+--- a/init/version.c
++++ b/init/version.c
+@@ -36,6 +36,12 @@ struct uts_namespace init_uts_ns = {
+ };
+ EXPORT_SYMBOL_GPL(init_uts_ns);
+
++struct new_utsname virt_utsname = {
++ /* we need only this field */
++ .release = UTS_RELEASE,
++};
++EXPORT_SYMBOL(virt_utsname);
++
+ /* FIXED STRINGS! Don't touch! */
+ const char linux_banner[] =
+ "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
+diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
+index 69bc859..521f6f6 100644
+--- a/ipc/ipc_sysctl.c
++++ b/ipc/ipc_sysctl.c
+@@ -271,19 +271,14 @@ static struct ctl_table ipc_kern_table[] = {
+ {}
+ };
+
+-static struct ctl_table ipc_root_table[] = {
+- {
+- .ctl_name = CTL_KERN,
+- .procname = "kernel",
+- .mode = 0555,
+- .child = ipc_kern_table,
+- },
++static struct ctl_path ipc_path[] = {
++ { .ctl_name = CTL_KERN, .procname = "kernel", },
+ {}
+ };
+
+ static int __init ipc_sysctl_init(void)
+ {
+- register_sysctl_table(ipc_root_table);
++ register_sysctl_glob_paths(ipc_path, ipc_kern_table, 1);
+ return 0;
+ }
+
+diff --git a/ipc/msg.c b/ipc/msg.c
+index b4eee1c..4fb6c0f 100644
+--- a/ipc/msg.c
++++ b/ipc/msg.c
+@@ -183,6 +183,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
+ int id, retval;
+ key_t key = params->key;
+ int msgflg = params->flg;
++ int msqid = params->id;
+
+ msq = ipc_rcu_alloc(sizeof(*msq));
+ if (!msq)
+@@ -201,7 +202,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
+ /*
+ * ipc_addid() locks msq
+ */
+- id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni);
++ id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni, msqid);
+ if (id < 0) {
+ security_msg_queue_free(msq);
+ ipc_rcu_putref(msq);
+@@ -323,6 +324,7 @@ asmlinkage long sys_msgget(key_t key, int msgflg)
+
+ msg_params.key = key;
+ msg_params.flg = msgflg;
++ msg_params.id = -1;
+
+ return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
+ }
+@@ -942,3 +944,55 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
+ msq->q_ctime);
+ }
+ #endif
++
++#ifdef CONFIG_VE
++#include <linux/module.h>
++
++int sysvipc_setup_msg(key_t key, int msqid, int msgflg)
++{
++ struct ipc_namespace *ns;
++ struct ipc_ops msg_ops;
++ struct ipc_params msg_params;
++
++ ns = current->nsproxy->ipc_ns;
++
++ msg_ops.getnew = newque;
++ msg_ops.associate = msg_security;
++ msg_ops.more_checks = NULL;
++
++ msg_params.key = key;
++ msg_params.flg = msgflg | IPC_CREAT;
++ msg_params.id = msqid;
++
++ return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
++}
++EXPORT_SYMBOL_GPL(sysvipc_setup_msg);
++
++int sysvipc_walk_msg(int (*func)(int i, struct msg_queue*, void *), void *arg)
++{
++ int err = 0;
++ struct msg_queue * msq;
++ struct ipc_namespace *ns;
++ int next_id;
++ int total, in_use;
++
++ ns = current->nsproxy->ipc_ns;
++
++ down_write(&msg_ids(ns).rw_mutex);
++ in_use = msg_ids(ns).in_use;
++ for (total = 0, next_id = 0; total < in_use; next_id++) {
++ msq = idr_find(&msg_ids(ns).ipcs_idr, next_id);
++ if (msq == NULL)
++ continue;
++ ipc_lock_by_ptr(&msq->q_perm);
++ err = func(ipc_buildid(next_id, msq->q_perm.seq), msq, arg);
++ msg_unlock(msq);
++ if (err)
++ break;
++ total++;
++ }
++ up_write(&msg_ids(ns).rw_mutex);
++ return err;
++}
++EXPORT_SYMBOL_GPL(sysvipc_walk_msg);
++#endif
+diff --git a/ipc/msgutil.c b/ipc/msgutil.c
+index c82c215..d058294 100644
+--- a/ipc/msgutil.c
++++ b/ipc/msgutil.c
+@@ -8,6 +8,7 @@
+ * See the file COPYING for more details.
+ */
+
++#include <linux/module.h>
+ #include <linux/spinlock.h>
+ #include <linux/init.h>
+ #include <linux/security.h>
+@@ -17,6 +18,8 @@
+
+ #include "util.h"
+
++#include <bc/kmem.h>
++
+ struct msg_msgseg {
+ struct msg_msgseg* next;
+ /* the next part of the message follows immediately */
+@@ -25,52 +28,53 @@ struct msg_msgseg {
+ #define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))
+ #define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))
+
+-struct msg_msg *load_msg(const void __user *src, int len)
++struct msg_msg *sysv_msg_load(int (*load)(void * dst, int len, int offset,
++ void * data), int len, void * data)
+ {
+ struct msg_msg *msg;
+ struct msg_msgseg **pseg;
+ int err;
+ int alen;
++ int offset = 0;
+
+ alen = len;
+ if (alen > DATALEN_MSG)
+ alen = DATALEN_MSG;
+
+- msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
++ msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_UBC);
+ if (msg == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ msg->next = NULL;
+ msg->security = NULL;
+
+- if (copy_from_user(msg + 1, src, alen)) {
++ if (load(msg + 1, alen, offset, data)) {
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ len -= alen;
+- src = ((char __user *)src) + alen;
++ offset += alen;
+ pseg = &msg->next;
+ while (len > 0) {
+ struct msg_msgseg *seg;
+ alen = len;
+ if (alen > DATALEN_SEG)
+ alen = DATALEN_SEG;
+- seg = kmalloc(sizeof(*seg) + alen,
+- GFP_KERNEL);
++ seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_UBC);
+ if (seg == NULL) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ *pseg = seg;
+ seg->next = NULL;
+- if (copy_from_user(seg + 1, src, alen)) {
++ if (load(seg + 1, alen, offset, data)) {
+ err = -EFAULT;
+ goto out_err;
+ }
+ pseg = &seg->next;
+ len -= alen;
+- src = ((char __user *)src) + alen;
++ offset += alen;
+ }
+
+ err = security_msg_msg_alloc(msg);
+@@ -83,33 +87,58 @@ out_err:
+ free_msg(msg);
+ return ERR_PTR(err);
+ }
++EXPORT_SYMBOL_GPL(sysv_msg_load);
+
+-int store_msg(void __user *dest, struct msg_msg *msg, int len)
++static int do_load_msg(void * dst, int len, int offset, void * data)
++{
++ return copy_from_user(dst, data + offset, len);
++}
++
++struct msg_msg *load_msg(const void __user *src, int len)
++{
++ return sysv_msg_load(do_load_msg, len, (void*)src);
++}
++
++int sysv_msg_store(struct msg_msg *msg,
++ int (*store)(void * src, int len, int offset, void * data),
++ int len, void * data)
+ {
+ int alen;
++ int offset = 0;
+ struct msg_msgseg *seg;
+-
++
+ alen = len;
+ if (alen > DATALEN_MSG)
+ alen = DATALEN_MSG;
+- if (copy_to_user(dest, msg + 1, alen))
++ if (store(msg + 1, alen, offset, data))
+ return -1;
+
+ len -= alen;
+- dest = ((char __user *)dest) + alen;
++ offset += alen;
+ seg = msg->next;
+ while (len > 0) {
+ alen = len;
+ if (alen > DATALEN_SEG)
+ alen = DATALEN_SEG;
+- if (copy_to_user(dest, seg + 1, alen))
++ if (store(seg + 1, alen, offset, data))
+ return -1;
+ len -= alen;
+- dest = ((char __user *)dest) + alen;
++ offset += alen;
+ seg = seg->next;
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(sysv_msg_store);
++
++static int do_store_msg(void * src, int len, int offset, void * data)
++{
++ return copy_to_user(data + offset, src, len);
++}
++
++int store_msg(void __user *dest, struct msg_msg *msg, int len)
++{
++ return sysv_msg_store(msg, do_store_msg, len, dest);
++}
+
+ void free_msg(struct msg_msg *msg)
+ {
+diff --git a/ipc/sem.c b/ipc/sem.c
+index bf1bc36..d44231c 100644
+--- a/ipc/sem.c
++++ b/ipc/sem.c
+@@ -87,6 +87,8 @@
+ #include <asm/uaccess.h>
+ #include "util.h"
+
++#include <bc/kmem.h>
++
+ #define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS])
+
+ #define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
+@@ -240,6 +242,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
+ key_t key = params->key;
+ int nsems = params->u.nsems;
+ int semflg = params->flg;
++ int semid = params->id;
+
+ if (!nsems)
+ return -EINVAL;
+@@ -263,7 +266,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
+ return retval;
+ }
+
+- id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
++ id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni, semid);
+ if (id < 0) {
+ security_sem_free(sma);
+ ipc_rcu_putref(sma);
+@@ -326,6 +329,7 @@ asmlinkage long sys_semget(key_t key, int nsems, int semflg)
+ sem_params.key = key;
+ sem_params.flg = semflg;
+ sem_params.u.nsems = nsems;
++ sem_params.id = -1;
+
+ return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
+ }
+@@ -941,7 +945,7 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
+
+ undo_list = current->sysvsem.undo_list;
+ if (!undo_list) {
+- undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
++ undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL_UBC);
+ if (undo_list == NULL)
+ return -ENOMEM;
+ spin_lock_init(&undo_list->lock);
+@@ -1006,7 +1010,8 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
+ sem_getref_and_unlock(sma);
+
+ /* step 2: allocate new undo structure */
+- new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
++ new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems,
++ GFP_KERNEL_UBC);
+ if (!new) {
+ sem_putref(sma);
+ return ERR_PTR(-ENOMEM);
+@@ -1068,7 +1073,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
+ if (nsops > ns->sc_semopm)
+ return -E2BIG;
+ if(nsops > SEMOPM_FAST) {
+- sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
++ sops = kmalloc(sizeof(*sops)*nsops, GFP_KERNEL_UBC);
+ if(sops==NULL)
+ return -ENOMEM;
+ }
+@@ -1371,3 +1376,57 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
+ sma->sem_ctime);
+ }
+ #endif
++
++#ifdef CONFIG_VE
++#include <linux/module.h>
++
++int sysvipc_setup_sem(key_t key, int semid, size_t size, int semflg)
++{
++ struct ipc_namespace *ns;
++ struct ipc_ops sem_ops;
++ struct ipc_params sem_params;
++
++ ns = current->nsproxy->ipc_ns;
++
++ sem_ops.getnew = newary;
++ sem_ops.associate = sem_security;
++ sem_ops.more_checks = sem_more_checks;
++
++ sem_params.key = key;
++ sem_params.flg = semflg | IPC_CREAT;
++ sem_params.u.nsems = size;
++ sem_params.id = semid;
++
++ return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
++}
++EXPORT_SYMBOL_GPL(sysvipc_setup_sem);
++
++int sysvipc_walk_sem(int (*func)(int i, struct sem_array*, void *), void *arg)
++{
++ int err = 0;
++ struct sem_array *sma;
++ struct ipc_namespace *ns;
++ int next_id;
++ int total, in_use;
++
++ ns = current->nsproxy->ipc_ns;
++
++ down_write(&sem_ids(ns).rw_mutex);
++ in_use = sem_ids(ns).in_use;
++ for (total = 0, next_id = 0; total < in_use; next_id++) {
++ sma = idr_find(&sem_ids(ns).ipcs_idr, next_id);
++ if (sma == NULL)
++ continue;
++ ipc_lock_by_ptr(&sma->sem_perm);
++ err = func(ipc_buildid(next_id, sma->sem_perm.seq), sma, arg);
++ sem_unlock(sma);
++ if (err)
++ break;
++ total++;
++ }
++ up_write(&sem_ids(ns).rw_mutex);
++ return err;
++}
++EXPORT_SYMBOL_GPL(sysvipc_walk_sem);
++EXPORT_SYMBOL_GPL(exit_sem);
++#endif
+diff --git a/ipc/shm.c b/ipc/shm.c
+index e77ec69..e3395af 100644
+--- a/ipc/shm.c
++++ b/ipc/shm.c
+@@ -39,27 +39,17 @@
+ #include <linux/nsproxy.h>
+ #include <linux/mount.h>
+ #include <linux/ipc_namespace.h>
++#include <linux/shmem_fs.h>
+
+ #include <asm/uaccess.h>
+
+-#include "util.h"
+-
+-struct shm_file_data {
+- int id;
+- struct ipc_namespace *ns;
+- struct file *file;
+- const struct vm_operations_struct *vm_ops;
+-};
++#include <bc/beancounter.h>
++#include <bc/vmpages.h>
+
+-#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data))
++#include "util.h"
+
+-static const struct file_operations shm_file_operations;
+ static struct vm_operations_struct shm_vm_ops;
+
+-#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS])
+-
+-#define shm_unlock(shp) \
+- ipc_unlock(&(shp)->shm_perm)
+
+ static int newseg(struct ipc_namespace *, struct ipc_params *);
+ static void shm_open(struct vm_area_struct *vma);
+@@ -111,20 +101,6 @@ void __init shm_init (void)
+ IPC_SHM_IDS, sysvipc_shm_proc_show);
+ }
+
+-/*
+- * shm_lock_(check_) routines are called in the paths where the rw_mutex
+- * is not necessarily held.
+- */
+-static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
+-{
+- struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id);
+-
+- if (IS_ERR(ipcp))
+- return (struct shmid_kernel *)ipcp;
+-
+- return container_of(ipcp, struct shmid_kernel, shm_perm);
+-}
+-
+ static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
+ int id)
+ {
+@@ -157,6 +133,48 @@ static void shm_open(struct vm_area_struct *vma)
+ shm_unlock(shp);
+ }
+
++static int shmem_lock(struct shmid_kernel *shp, int lock,
++ struct user_struct *user)
++{
++ struct file *file = shp->shm_file;
++ struct inode *inode = file->f_path.dentry->d_inode;
++ struct shmem_inode_info *info = SHMEM_I(inode);
++ unsigned long size;
++
++ size = shp->shm_segsz + PAGE_SIZE - 1;
++
++#ifdef CONFIG_SHMEM
++ spin_lock(&info->lock);
++ if (lock && !(info->flags & VM_LOCKED)) {
++ if (ub_lockedshm_charge(info, size) < 0)
++ goto out_ch;
++
++ if (!user_shm_lock(inode->i_size, user))
++ goto out_user;
++ info->flags |= VM_LOCKED;
++ }
++ if (!lock && (info->flags & VM_LOCKED) && user) {
++ ub_lockedshm_uncharge(info, size);
++ user_shm_unlock(inode->i_size, user);
++ info->flags &= ~VM_LOCKED;
++ }
++ spin_unlock(&info->lock);
++ return 0;
++
++out_user:
++ ub_lockedshm_uncharge(info, size);
++out_ch:
++ spin_unlock(&info->lock);
++ return -ENOMEM;
++#else
++ if (lock && ub_lockedshm_charge(info, size))
++ return -ENOMEM;
++ if (!lock)
++ ub_lockedshm_uncharge(info, size);
++ return 0;
++#endif
++}
++
+ /*
+ * shm_destroy - free the struct shmid_kernel
+ *
+@@ -172,7 +190,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
+ shm_rmid(ns, shp);
+ shm_unlock(shp);
+ if (!is_file_hugepages(shp->shm_file))
+- shmem_lock(shp->shm_file, 0, shp->mlock_user);
++ shmem_lock(shp, 0, shp->mlock_user);
+ else
+ user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size,
+ shp->mlock_user);
+@@ -304,12 +322,13 @@ int is_file_shm_hugepages(struct file *file)
+ return ret;
+ }
+
+-static const struct file_operations shm_file_operations = {
++const struct file_operations shm_file_operations = {
+ .mmap = shm_mmap,
+ .fsync = shm_fsync,
+ .release = shm_release,
+ .get_unmapped_area = shm_get_unmapped_area,
+ };
++EXPORT_SYMBOL_GPL(shm_file_operations);
+
+ static struct vm_operations_struct shm_vm_ops = {
+ .open = shm_open, /* callback for a new vm-area open */
+@@ -334,11 +353,12 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
+ key_t key = params->key;
+ int shmflg = params->flg;
+ size_t size = params->u.size;
++ int shmid = params->id;
+ int error;
+ struct shmid_kernel *shp;
+ int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
+ struct file * file;
+- char name[13];
++ char name[64];
+ int id;
+
+ if (size < SHMMIN || size > ns->shm_ctlmax)
+@@ -362,7 +382,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
+ return error;
+ }
+
+- sprintf (name, "SYSV%08x", key);
++ snprintf (name, sizeof(name), "VE%d-SYSV%08x", VEID(get_exec_env()), key);
+ if (shmflg & SHM_HUGETLB) {
+ /* hugetlb_file_setup takes care of mlock user accounting */
+ file = hugetlb_file_setup(name, size);
+@@ -382,7 +402,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
+ if (IS_ERR(file))
+ goto no_file;
+
+- id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
++ id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni, shmid);
+ if (id < 0) {
+ error = id;
+ goto no_id;
+@@ -455,6 +475,7 @@ asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
+ shm_params.key = key;
+ shm_params.flg = shmflg;
+ shm_params.u.size = size;
++ shm_params.id = -1;
+
+ return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
+ }
+@@ -764,14 +785,14 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
+ if(cmd==SHM_LOCK) {
+ struct user_struct * user = current->user;
+ if (!is_file_hugepages(shp->shm_file)) {
+- err = shmem_lock(shp->shm_file, 1, user);
++ err = shmem_lock(shp, 1, user);
+ if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
+ shp->shm_perm.mode |= SHM_LOCKED;
+ shp->mlock_user = user;
+ }
+ }
+ } else if (!is_file_hugepages(shp->shm_file)) {
+- shmem_lock(shp->shm_file, 0, shp->mlock_user);
++ shmem_lock(shp, 0, shp->mlock_user);
+ shp->shm_perm.mode &= ~SHM_LOCKED;
+ shp->mlock_user = NULL;
+ }
+@@ -1070,3 +1091,67 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
+ shp->shm_ctim);
+ }
+ #endif
++
++#ifdef CONFIG_VE
++#include <linux/module.h>
++
++struct file * sysvipc_setup_shm(key_t key, int shmid, size_t size, int shmflg)
++{
++ struct ipc_namespace *ns;
++ struct ipc_ops shm_ops;
++ struct ipc_params shm_params;
++ struct shmid_kernel *shp;
++ struct file *file;
++ int rv;
++
++ ns = current->nsproxy->ipc_ns;
++
++ shm_ops.getnew = newseg;
++ shm_ops.associate = shm_security;
++ shm_ops.more_checks = shm_more_checks;
++
++ shm_params.key = key;
++ shm_params.flg = shmflg | IPC_CREAT;
++ shm_params.u.size = size;
++ shm_params.id = shmid;
++
++ rv = ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
++ if (rv < 0)
++ return ERR_PTR(rv);
++ shp = shm_lock(ns, rv);
++ BUG_ON(IS_ERR(shp));
++ file = shp->shm_file;
++ get_file(file);
++ shm_unlock(shp);
++ return file;
++}
++EXPORT_SYMBOL_GPL(sysvipc_setup_shm);
++
++int sysvipc_walk_shm(int (*func)(struct shmid_kernel*, void *), void *arg)
++{
++ int err = 0;
++ struct shmid_kernel* shp;
++ struct ipc_namespace *ns;
++ int next_id;
++ int total, in_use;
++
++ ns = current->nsproxy->ipc_ns;
++
++ down_write(&shm_ids(ns).rw_mutex);
++ in_use = shm_ids(ns).in_use;
++ for (total = 0, next_id = 0; total < in_use; next_id++) {
++ shp = idr_find(&shm_ids(ns).ipcs_idr, next_id);
++ if (shp == NULL)
++ continue;
++ ipc_lock_by_ptr(&shp->shm_perm);
++ err = func(shp, arg);
++ shm_unlock(shp);
++ if (err)
++ break;
++ total++;
++ }
++ up_write(&shm_ids(ns).rw_mutex);
++ return err;
++}
++EXPORT_SYMBOL_GPL(sysvipc_walk_shm);
++#endif
+diff --git a/ipc/util.c b/ipc/util.c
+index 49b3ea6..59d302e 100644
+--- a/ipc/util.c
++++ b/ipc/util.c
+@@ -38,6 +38,8 @@
+
+ #include <asm/unistd.h>
+
++#include <bc/kmem.h>
++
+ #include "util.h"
+
+ struct ipc_proc_iface {
+@@ -247,6 +249,7 @@ int ipc_get_maxid(struct ipc_ids *ids)
+ * @ids: IPC identifier set
+ * @new: new IPC permission set
+ * @size: limit for the number of used ids
++ * @reqid: if >= 0, get this id exactly. If -1 -- don't care.
+ *
+ * Add an entry 'new' to the IPC ids idr. The permissions object is
+ * initialised and the first free entry is set up and the id assigned
+@@ -256,10 +259,18 @@ int ipc_get_maxid(struct ipc_ids *ids)
+ * Called with ipc_ids.rw_mutex held as a writer.
+ */
+
+-int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
++int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size, int reqid)
+ {
+ int id, err;
+
++ if (reqid >= 0) {
++ id = reqid % SEQ_MULTIPLIER;
++ err = idr_get_new_above(&ids->ipcs_idr, new, id, &id);
++ if (err || id != (reqid % SEQ_MULTIPLIER))
++ return -EEXIST;
++ goto found;
++ }
++
+ if (size > IPCMNI)
+ size = IPCMNI;
+
+@@ -270,14 +281,19 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
+ if (err)
+ return err;
+
++found:
+ ids->in_use++;
+
+ new->cuid = new->uid = current->euid;
+ new->gid = new->cgid = current->egid;
+
+- new->seq = ids->seq++;
+- if(ids->seq > ids->seq_max)
+- ids->seq = 0;
++ if (reqid >= 0) {
++ new->seq = reqid/SEQ_MULTIPLIER;
++ } else {
++ new->seq = ids->seq++;
++ if(ids->seq > ids->seq_max)
++ ids->seq = 0;
++ }
+
+ new->id = ipc_buildid(id, new->seq);
+ spin_lock_init(&new->lock);
+@@ -445,9 +461,9 @@ void* ipc_alloc(int size)
+ {
+ void* out;
+ if(size > PAGE_SIZE)
+- out = vmalloc(size);
++ out = ub_vmalloc(size);
+ else
+- out = kmalloc(size, GFP_KERNEL);
++ out = kmalloc(size, GFP_KERNEL_UBC);
+ return out;
+ }
+
+@@ -530,14 +546,14 @@ void* ipc_rcu_alloc(int size)
+ * workqueue if necessary (for vmalloc).
+ */
+ if (rcu_use_vmalloc(size)) {
+- out = vmalloc(HDRLEN_VMALLOC + size);
++ out = ub_vmalloc(HDRLEN_VMALLOC + size);
+ if (out) {
+ out += HDRLEN_VMALLOC;
+ container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
+ container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
+ }
+ } else {
+- out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
++ out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL_UBC);
+ if (out) {
+ out += HDRLEN_KMALLOC;
+ container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
+@@ -715,6 +731,7 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id)
+
+ return out;
+ }
++EXPORT_SYMBOL_GPL(ipc_lock);
+
+ struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id)
+ {
+@@ -804,7 +821,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd,
+ goto out_unlock;
+ }
+ if (current->euid == ipcp->cuid ||
+- current->euid == ipcp->uid || capable(CAP_SYS_ADMIN))
++ current->euid == ipcp->uid || capable(CAP_VE_SYS_ADMIN))
+ return ipcp;
+
+ err = -EPERM;
+diff --git a/ipc/util.h b/ipc/util.h
+index 3646b45..5b6df8e 100644
+--- a/ipc/util.h
++++ b/ipc/util.h
+@@ -39,6 +39,7 @@ struct ipc_params {
+ size_t size; /* for shared memories */
+ int nsems; /* for semaphores */
+ } u; /* holds the getnew() specific param */
++ int id;
+ };
+
+ /*
+@@ -68,14 +69,10 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
+ #define ipc_init_proc_interface(path, header, ids, show) do {} while (0)
+ #endif
+
+-#define IPC_SEM_IDS 0
+-#define IPC_MSG_IDS 1
+-#define IPC_SHM_IDS 2
+-
+ #define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER)
+
+ /* must be called with ids->rw_mutex acquired for writing */
+-int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
++int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int, int);
+
+ /* must be called with ids->rw_mutex acquired for reading */
+ int ipc_get_maxid(struct ipc_ids *);
+@@ -102,7 +99,6 @@ void* ipc_rcu_alloc(int size);
+ void ipc_rcu_getref(void *ptr);
+ void ipc_rcu_putref(void *ptr);
+
+-struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
+
+ void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out);
+ void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out);
+@@ -144,12 +140,6 @@ static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
+ spin_lock(&perm->lock);
+ }
+
+-static inline void ipc_unlock(struct kern_ipc_perm *perm)
+-{
+- spin_unlock(&perm->lock);
+- rcu_read_unlock();
+-}
+-
+ struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id);
+ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
+ struct ipc_ops *ops, struct ipc_params *params);
+diff --git a/kernel/Kconfig.openvz b/kernel/Kconfig.openvz
+new file mode 100644
+index 0000000..8e0a503
+--- /dev/null
++++ b/kernel/Kconfig.openvz
+@@ -0,0 +1,91 @@
++# Copyright (C) 2005 SWsoft
++# All rights reserved.
++# Licensing governed by "linux/COPYING.SWsoft" file.
++
++menu "OpenVZ"
++
++config VE
++ bool "Virtual Environment support"
++ default y
++ select NAMESPACES
++ select PID_NS
++ select IPC_NS
++ select UTS_NS
++ select NET_NS
++ select USER_NS
++ select CGROUPS
++ select CGROUP_DEVICE
++ select GROUP_SCHED
++ select FAIR_GROUP_SCHED
++ help
++ This option adds support of virtual Linux running on the original box
++ with fully supported virtual network driver, tty subsystem and
++ configurable access for hardware and other resources.
++
++config VE_CALLS
++ tristate "VE calls interface"
++ depends on VE
++ select VZ_DEV
++ default m
++ help
++ This option controls how to build vzmon code containing VE calls.
++ By default it's build in module vzmon.o
++
++config VZ_GENCALLS
++ bool
++ default y
++
++config VE_NETDEV
++ tristate "VE network device"
++ depends on VE_CALLS && NET
++ select VZ_DEV
++ default m
++ help
++ This option controls whether to build venet device. This is a
++ common interface for networking in VE.
++
++config VE_ETHDEV
++ tristate "Virtual ethernet device"
++ depends on VE_CALLS && NET
++ select VZ_DEV
++ default m
++ help
++ This option controls whether to build virtual ethernet device.
++
++config VZ_DEV
++ tristate "VE device"
++ default m
++ help
++ This option adds support of vzdev device, which is used by
++ user-space applications to control Virtual Environments.
++
++config VE_IPTABLES
++ bool "VE netfiltering"
++ depends on VE && VE_NETDEV && INET && NETFILTER
++ default y
++ help
++ This option controls whether to build VE netfiltering code.
++
++config VZ_WDOG
++ tristate "VE watchdog module"
++ depends on VE_CALLS
++ default m
++ help
++ This option controls building of vzwdog module, which dumps
++ a lot of useful system info on console periodically.
++
++config VZ_CHECKPOINT
++ tristate "Checkpointing & restoring Virtual Environments"
++ depends on VE_CALLS
++ select PM
++ select PM_SLEEP
++ select TUN
++ select VE_ETHDEV
++ select VE_NETDEV
++ default m
++ help
++ This option adds two modules, "cpt" and "rst", which allow
++ to save a running Virtual Environment and restore it
++ on another host (live migration) or on the same host (checkpointing).
++
++endmenu
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 4e1d7df..d8b742c 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -28,6 +28,10 @@ obj-$(CONFIG_PROFILING) += profile.o
+ obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o
+ obj-$(CONFIG_STACKTRACE) += stacktrace.o
+ obj-y += time/
++obj-$(CONFIG_BEANCOUNTERS) += bc/
++obj-y += ve/
++obj-$(CONFIG_VZ_CHECKPOINT) += cpt/
++
+ obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
+ obj-$(CONFIG_LOCKDEP) += lockdep.o
+ ifeq ($(CONFIG_PROC_FS),y)
+@@ -53,7 +57,11 @@ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
+ obj-$(CONFIG_KEXEC) += kexec.o
+ obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
+ obj-$(CONFIG_COMPAT) += compat.o
++ifeq ($(CONFIG_VE),n)
+ obj-$(CONFIG_CGROUPS) += cgroup.o
++else
++obj-$(CONFIG_CGROUPS) += cgroup_lite.o
++endif
+ obj-$(CONFIG_CGROUP_DEBUG) += cgroup_debug.o
+ obj-$(CONFIG_CPUSETS) += cpuset.o
+ obj-$(CONFIG_CGROUP_NS) += ns_cgroup.o
+@@ -84,6 +92,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
+ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
+ obj-$(CONFIG_MARKERS) += marker.o
+ obj-$(CONFIG_LATENCYTOP) += latencytop.o
++obj-$(CONFIG_VZ_FAIRSCHED) += fairsched.o
+ obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
+ obj-$(CONFIG_FTRACE) += trace/
+ obj-$(CONFIG_TRACING) += trace/
+diff --git a/kernel/audit.c b/kernel/audit.c
+index 4414e93..eb95bca 100644
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -666,6 +666,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ char *ctx = NULL;
+ u32 len;
+
++ if (!ve_is_super(skb->owner_env))
++ return -ECONNREFUSED;
++
+ err = audit_netlink_ok(skb, msg_type);
+ if (err)
+ return err;
+diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
+index b7d354e..278ed36 100644
+--- a/kernel/auditfilter.c
++++ b/kernel/auditfilter.c
+@@ -164,8 +164,8 @@ static struct audit_parent *audit_init_parent(struct nameidata *ndp)
+ inotify_init_watch(&parent->wdata);
+ /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */
+ get_inotify_watch(&parent->wdata);
+- wd = inotify_add_watch(audit_ih, &parent->wdata,
+- ndp->path.dentry->d_inode, AUDIT_IN_WATCH);
++ wd = inotify_add_watch_dget(audit_ih, &parent->wdata,
++ &ndp->path, AUDIT_IN_WATCH);
+ if (wd < 0) {
+ audit_free_parent(&parent->wdata);
+ return ERR_PTR(wd);
+diff --git a/kernel/bc/Kconfig b/kernel/bc/Kconfig
+new file mode 100644
+index 0000000..2c3de4a
+--- /dev/null
++++ b/kernel/bc/Kconfig
+@@ -0,0 +1,111 @@
++#
++# User resources part (UBC)
++#
++# Copyright (C) 2005 SWsoft
++# All rights reserved.
++#
++# Licensing governed by "linux/COPYING.SWsoft" file.
++
++menu "User resources"
++
++config BEANCOUNTERS
++ bool "Enable user resource accounting"
++ default y
++ help
++ This patch provides accounting and allows to configure
++ limits for user's consumption of exhaustible system resources.
++ The most important resource controlled by this patch is unswappable
++ memory (either mlock'ed or used by internal kernel structures and
++ buffers). The main goal of this patch is to protect processes
++ from running short of important resources because of an accidental
++ misbehavior of processes or malicious activity aiming to ``kill''
++ the system. It's worth to mention that resource limits configured
++ by setrlimit(2) do not give an acceptable level of protection
++ because they cover only small fraction of resources and work on a
++ per-process basis. Per-process accounting doesn't prevent malicious
++ users from spawning a lot of resource-consuming processes.
++
++config BC_RSS_ACCOUNTING
++ bool "Account physical memory usage"
++ default y
++ depends on BEANCOUNTERS
++ help
++ This allows to estimate per beancounter physical memory usage.
++ Implemented alghorithm accounts shared pages of memory as well,
++ dividing them by number of beancounter which use the page.
++
++config BC_IO_ACCOUNTING
++ bool "Account disk IO"
++ default y
++ depends on BC_RSS_ACCOUNTING
++ help
++ When on this option allows seeing disk IO activity caused by
++ tasks from each UB
++
++config BC_IO_SCHED
++ bool "UBC I/O priority"
++ default y
++ depends on BC_IO_ACCOUNTING && IOSCHED_CFQ
++ help
++ This option controls whether to build CFQ I/O scheduler
++ with support of UBC I/O priority.
++
++config BC_SWAP_ACCOUNTING
++ bool "Account swap usage"
++ default y
++ depends on BEANCOUNTERS
++ help
++ This allows accounting of swap usage.
++
++config BC_PROC
++ bool "Report resource usage in /proc"
++ default y
++ depends on BEANCOUNTERS
++ help
++ Allows a system administrator to inspect resource accounts and limits.
++
++config BC_DEBUG
++ bool "User resources debug features"
++ default n
++ depends on BEANCOUNTERS
++ help
++ Enables to setup debug features for user resource accounting
++
++config BC_DEBUG_IO
++ bool "Debug IO accounting"
++ default y
++ depends on BC_DEBUG && BC_IO_ACCOUNTING
++ help
++ Debugging for IO accointing.
++
++config BC_DEBUG_KMEM
++ bool "Debug kmemsize with cache counters"
++ default n
++ depends on BC_DEBUG
++ help
++ Adds /proc/user_beancounters_debug entry to get statistics
++ about cache usage of each beancounter
++
++config BC_KEEP_UNUSED
++ bool "Keep unused beancounter alive"
++ default y
++ depends on BC_DEBUG
++ help
++ If on, unused beancounters are kept on the hash and maxheld value
++ can be looked through.
++
++config BC_DEBUG_ITEMS
++ bool "Account resources in items rather than in bytes"
++ default y
++ depends on BC_DEBUG
++ help
++ When true some of the resources (e.g. kmemsize) are accounted
++ in items instead of bytes.
++
++config BC_UNLIMITED
++ bool "Use unlimited ubc settings"
++ default y
++ depends on BC_DEBUG
++ help
++ When ON all limits and barriers are set to max values.
++endmenu
+diff --git a/kernel/bc/Makefile b/kernel/bc/Makefile
+new file mode 100644
+index 0000000..e0e6529
+--- /dev/null
++++ b/kernel/bc/Makefile
+@@ -0,0 +1,16 @@
++#
++# User resources part (UBC)
++#
++# Copyright (C) 2005 SWsoft
++# All rights reserved.
++#
++# Licensing governed by "linux/COPYING.SWsoft" file.
++
++obj-y := sys.o beancounter.o dcache.o kmem.o misc.o \
++ vm_pages.o statd.o oom_kill.o
++
++obj-$(CONFIG_NET) += net.o
++obj-$(CONFIG_BC_RSS_ACCOUNTING) += rss_pages.o
++obj-$(CONFIG_BC_PROC) += proc.o
++obj-$(CONFIG_BC_IO_ACCOUNTING) += io_acct.o
++obj-$(CONFIG_BC_IO_SCHED) += io_prio.o
+diff --git a/kernel/bc/beancounter.c b/kernel/bc/beancounter.c
+new file mode 100644
+index 0000000..8cd0ef0
+--- /dev/null
++++ b/kernel/bc/beancounter.c
+@@ -0,0 +1,673 @@
++/*
++ * linux/kernel/bc/beancounter.c
++ *
++ * Copyright (C) 1998 Alan Cox
++ * 1998-2000 Andrey V. Savochkin <saw@saw.sw.com.sg>
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * TODO:
++ * - more intelligent limit check in mremap(): currently the new size is
++ * charged and _then_ old size is uncharged
++ * (almost done: !move_vma case is completely done,
++ * move_vma in its current implementation requires too many conditions to
++ * do things right, because it may be not only expansion, but shrinking
++ * also, plus do_munmap will require an additional parameter...)
++ * - problem: bad pmd page handling
++ * - consider /proc redesign
++ * - TCP/UDP ports
++ * + consider whether __charge_beancounter_locked should be inline
++ *
++ * Changes:
++ * 1999/08/17 Marcelo Tosatti <marcelo@conectiva.com.br>
++ * - Set "barrier" and "limit" parts of limits atomically.
++ * 1999/10/06 Marcelo Tosatti <marcelo@conectiva.com.br>
++ * - setublimit system call.
++ */
++
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/mm.h>
++
++#include <bc/beancounter.h>
++#include <bc/hash.h>
++#include <bc/vmpages.h>
++#include <bc/proc.h>
++#include <bc/io_prio.h>
++
++static struct kmem_cache *ub_cachep;
++static struct user_beancounter default_beancounter;
++struct user_beancounter ub0;
++EXPORT_SYMBOL_GPL(ub0);
++
++const char *ub_rnames[] = {
++ "kmemsize", /* 0 */
++ "lockedpages",
++ "privvmpages",
++ "shmpages",
++ "dummy",
++ "numproc", /* 5 */
++ "physpages",
++ "vmguarpages",
++ "oomguarpages",
++ "numtcpsock",
++ "numflock", /* 10 */
++ "numpty",
++ "numsiginfo",
++ "tcpsndbuf",
++ "tcprcvbuf",
++ "othersockbuf", /* 15 */
++ "dgramrcvbuf",
++ "numothersock",
++ "dcachesize",
++ "numfile",
++ "dummy", /* 20 */
++ "dummy",
++ "dummy",
++ "numiptent",
++ "unused_privvmpages", /* UB_RESOURCES */
++ "tmpfs_respages",
++ "swap_pages",
++ "held_pages",
++};
++
++static void init_beancounter_struct(struct user_beancounter *ub);
++static void init_beancounter_store(struct user_beancounter *ub);
++static void init_beancounter_nolimits(struct user_beancounter *ub);
++
++int print_ub_uid(struct user_beancounter *ub, char *buf, int size)
++{
++ if (ub->parent != NULL)
++ return snprintf(buf, size, "%u.%u",
++ ub->parent->ub_uid, ub->ub_uid);
++ else
++ return snprintf(buf, size, "%u", ub->ub_uid);
++}
++EXPORT_SYMBOL(print_ub_uid);
++
++#define ub_hash_fun(x) ((((x) >> 8) ^ (x)) & (UB_HASH_SIZE - 1))
++#define ub_subhash_fun(p, id) ub_hash_fun((p)->ub_uid + (id) * 17)
++struct hlist_head ub_hash[UB_HASH_SIZE];
++DEFINE_SPINLOCK(ub_hash_lock);
++LIST_HEAD(ub_list_head); /* protected by ub_hash_lock */
++EXPORT_SYMBOL(ub_hash);
++EXPORT_SYMBOL(ub_hash_lock);
++EXPORT_SYMBOL(ub_list_head);
++
++/*
++ * Per user resource beancounting. Resources are tied to their luid.
++ * The resource structure itself is tagged both to the process and
++ * the charging resources (a socket doesn't want to have to search for
++ * things at irq time for example). Reference counters keep things in
++ * hand.
++ *
++ * The case where a user creates resource, kills all his processes and
++ * then starts new ones is correctly handled this way. The refcounters
++ * will mean the old entry is still around with resource tied to it.
++ */
++
++static inline void free_ub(struct user_beancounter *ub)
++{
++ free_percpu(ub->ub_percpu);
++ kmem_cache_free(ub_cachep, ub);
++}
++
++static inline struct user_beancounter *bc_lookup_hash(struct hlist_head *hash,
++ uid_t uid, struct user_beancounter *parent)
++{
++ struct user_beancounter *ub;
++ struct hlist_node *ptr;
++
++ hlist_for_each_entry (ub, ptr, hash, ub_hash)
++ if (ub->ub_uid == uid && ub->parent == parent)
++ return get_beancounter(ub);
++
++ return NULL;
++}
++
++struct user_beancounter *get_beancounter_byuid(uid_t uid, int create)
++{
++ struct user_beancounter *new_ub, *ub;
++ unsigned long flags;
++ struct hlist_head *hash;
++
++ hash = &ub_hash[ub_hash_fun(uid)];
++ new_ub = NULL;
++retry:
++ spin_lock_irqsave(&ub_hash_lock, flags);
++ ub = bc_lookup_hash(hash, uid, NULL);
++ if (ub != NULL) {
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ if (new_ub != NULL)
++ free_ub(new_ub);
++ return ub;
++ }
++
++ if (!create) {
++ /* no ub found */
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++ return NULL;
++ }
++
++ if (new_ub != NULL) {
++ list_add_rcu(&new_ub->ub_list, &ub_list_head);
++ hlist_add_head(&new_ub->ub_hash, hash);
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++ return new_ub;
++ }
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ /* alloc new ub */
++ new_ub = (struct user_beancounter *)kmem_cache_alloc(ub_cachep,
++ GFP_KERNEL);
++ if (new_ub == NULL)
++ return NULL;
++
++ ub_debug(UBD_ALLOC, "Creating ub %p\n", new_ub);
++ memcpy(new_ub, &default_beancounter, sizeof(*new_ub));
++ init_beancounter_struct(new_ub);
++ new_ub->ub_percpu = alloc_percpu(struct ub_percpu_struct);
++ if (new_ub->ub_percpu == NULL)
++ goto fail_free;
++ new_ub->ub_uid = uid;
++ goto retry;
++
++fail_free:
++ kmem_cache_free(ub_cachep, new_ub);
++ return NULL;
++}
++EXPORT_SYMBOL(get_beancounter_byuid);
++
++struct user_beancounter *get_subbeancounter_byid(struct user_beancounter *p,
++ int id, int create)
++{
++ struct user_beancounter *new_ub, *ub;
++ unsigned long flags;
++ struct hlist_head *hash;
++
++ hash = &ub_hash[ub_subhash_fun(p, id)];
++ new_ub = NULL;
++retry:
++ spin_lock_irqsave(&ub_hash_lock, flags);
++ ub = bc_lookup_hash(hash, id, p);
++ if (ub != NULL) {
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ if (new_ub != NULL) {
++ put_beancounter(new_ub->parent);
++ free_ub(new_ub);
++ }
++ return ub;
++ }
++
++ if (!create) {
++ /* no ub found */
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++ return NULL;
++ }
++
++ if (new_ub != NULL) {
++ list_add_rcu(&new_ub->ub_list, &ub_list_head);
++ hlist_add_head(&new_ub->ub_hash, hash);
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++ return new_ub;
++ }
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ /* alloc new ub */
++ new_ub = (struct user_beancounter *)kmem_cache_alloc(ub_cachep,
++ GFP_KERNEL);
++ if (new_ub == NULL)
++ return NULL;
++
++ ub_debug(UBD_ALLOC, "Creating sub %p\n", new_ub);
++ memset(new_ub, 0, sizeof(*new_ub));
++ init_beancounter_nolimits(new_ub);
++ init_beancounter_store(new_ub);
++ init_beancounter_struct(new_ub);
++ new_ub->ub_percpu = alloc_percpu(struct ub_percpu_struct);
++ if (new_ub->ub_percpu == NULL)
++ goto fail_free;
++ new_ub->ub_uid = id;
++ new_ub->parent = get_beancounter(p);
++ goto retry;
++
++fail_free:
++ kmem_cache_free(ub_cachep, new_ub);
++ return NULL;
++}
++EXPORT_SYMBOL(get_subbeancounter_byid);
++
++static void put_warn(struct user_beancounter *ub)
++{
++ char id[64];
++
++ print_ub_uid(ub, id, sizeof(id));
++ printk(KERN_ERR "UB: Bad refcount (%d) on put of %s (%p)\n",
++ atomic_read(&ub->ub_refcount), id, ub);
++}
++
++#ifdef CONFIG_BC_KEEP_UNUSED
++#define release_beancounter(ub) do { } while (0)
++#else
++static int verify_res(struct user_beancounter *ub, int resource,
++ unsigned long held)
++{
++ char id[64];
++
++ if (likely(held == 0))
++ return 1;
++
++ print_ub_uid(ub, id, sizeof(id));
++ printk(KERN_WARNING "Ub %s helds %lu in %s on put\n",
++ id, held, ub_rnames[resource]);
++ return 0;
++}
++
++static inline void bc_verify_held(struct user_beancounter *ub)
++{
++ int i, clean;
++
++ clean = 1;
++ for (i = 0; i < UB_RESOURCES; i++)
++ clean &= verify_res(ub, i, ub->ub_parms[i].held);
++
++ clean &= verify_res(ub, UB_UNUSEDPRIVVM, ub->ub_unused_privvmpages);
++ clean &= verify_res(ub, UB_TMPFSPAGES, ub->ub_tmpfs_respages);
++ clean &= verify_res(ub, UB_SWAPPAGES, ub->ub_swap_pages);
++ clean &= verify_res(ub, UB_HELDPAGES, (unsigned long)ub->ub_held_pages);
++
++ ub_debug_trace(!clean, 5, 60*HZ);
++}
++
++static void bc_free_rcu(struct rcu_head *rcu)
++{
++ struct user_beancounter *ub;
++
++ ub = container_of(rcu, struct user_beancounter, rcu);
++ free_ub(ub);
++}
++
++static void delayed_release_beancounter(struct work_struct *w)
++{
++ struct user_beancounter *ub, *parent;
++ unsigned long flags;
++
++ ub = container_of(w, struct user_beancounter, cleanup.work);
++again:
++ local_irq_save(flags);
++ if (!atomic_dec_and_lock(&ub->ub_refcount, &ub_hash_lock)) {
++ /* raced with get_beancounter_byuid */
++ local_irq_restore(flags);
++ return;
++ }
++
++ hlist_del(&ub->ub_hash);
++ list_del_rcu(&ub->ub_list);
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ bc_verify_held(ub);
++ ub_free_counters(ub);
++ bc_fini_ioprio(&ub->iopriv);
++ parent = ub->parent;
++
++ call_rcu(&ub->rcu, bc_free_rcu);
++ if (parent) {
++ ub = parent;
++ goto again;
++ }
++}
++
++static inline void release_beancounter(struct user_beancounter *ub)
++{
++ struct execute_work *ew;
++
++ ew = &ub->cleanup;
++ INIT_WORK(&ew->work, delayed_release_beancounter);
++ schedule_work(&ew->work);
++}
++#endif
++
++void __put_beancounter(struct user_beancounter *ub)
++{
++ unsigned long flags;
++
++ /* equevalent to atomic_dec_and_lock_irqsave() */
++ local_irq_save(flags);
++ if (likely(!atomic_dec_and_lock(&ub->ub_refcount, &ub_hash_lock))) {
++ if (unlikely(atomic_read(&ub->ub_refcount) < 0))
++ put_warn(ub);
++ local_irq_restore(flags);
++ return;
++ }
++
++ if (unlikely(ub == get_ub0())) {
++ printk(KERN_ERR "Trying to put ub0\n");
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++ return;
++ }
++
++ /* prevent get_beancounter_byuid + put_beancounter() reentrance */
++ atomic_inc(&ub->ub_refcount);
++ spin_unlock_irqrestore(&ub_hash_lock, flags);
++
++ release_beancounter(ub);
++}
++EXPORT_SYMBOL(__put_beancounter);
++
++void put_beancounter_safe(struct user_beancounter *ub)
++{
++ synchronize_rcu();
++ __put_beancounter(ub);
++}
++EXPORT_SYMBOL(put_beancounter_safe);
++
++/*
++ * Generic resource charging stuff
++ */
++
++int __charge_beancounter_locked(struct user_beancounter *ub,
++ int resource, unsigned long val, enum ub_severity strict)
++{
++ ub_debug_resource(resource, "Charging %lu for %d of %p with %lu\n",
++ val, resource, ub, ub->ub_parms[resource].held);
++ /*
++ * ub_value <= UB_MAXVALUE, value <= UB_MAXVALUE, and only one addition
++ * at the moment is possible so an overflow is impossible.
++ */
++ ub->ub_parms[resource].held += val;
++
++ switch (strict) {
++ case UB_HARD:
++ if (ub->ub_parms[resource].held >
++ ub->ub_parms[resource].barrier)
++ break;
++ case UB_SOFT:
++ if (ub->ub_parms[resource].held >
++ ub->ub_parms[resource].limit)
++ break;
++ case UB_FORCE:
++ ub_adjust_maxheld(ub, resource);
++ return 0;
++ default:
++ BUG();
++ }
++
++ if (strict == UB_SOFT && ub_ratelimit(&ub->ub_limit_rl))
++ printk(KERN_INFO "Fatal resource shortage: %s, UB %d.\n",
++ ub_rnames[resource], ub->ub_uid);
++ ub->ub_parms[resource].failcnt++;
++ ub->ub_parms[resource].held -= val;
++ return -ENOMEM;
++}
++
++int charge_beancounter(struct user_beancounter *ub,
++ int resource, unsigned long val, enum ub_severity strict)
++{
++ int retval;
++ struct user_beancounter *p, *q;
++ unsigned long flags;
++
++ retval = -EINVAL;
++ if (val > UB_MAXVALUE)
++ goto out;
++
++ local_irq_save(flags);
++ for (p = ub; p != NULL; p = p->parent) {
++ spin_lock(&p->ub_lock);
++ retval = __charge_beancounter_locked(p, resource, val, strict);
++ spin_unlock(&p->ub_lock);
++ if (retval)
++ goto unroll;
++ }
++out_restore:
++ local_irq_restore(flags);
++out:
++ return retval;
++
++unroll:
++ for (q = ub; q != p; q = q->parent) {
++ spin_lock(&q->ub_lock);
++ __uncharge_beancounter_locked(q, resource, val);
++ spin_unlock(&q->ub_lock);
++ }
++ goto out_restore;
++}
++
++EXPORT_SYMBOL(charge_beancounter);
++
++void __charge_beancounter_notop(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ struct user_beancounter *p;
++ unsigned long flags;
++
++ local_irq_save(flags);
++ for (p = ub; p->parent != NULL; p = p->parent) {
++ spin_lock(&p->ub_lock);
++ __charge_beancounter_locked(p, resource, val, UB_FORCE);
++ spin_unlock(&p->ub_lock);
++ }
++ local_irq_restore(flags);
++}
++
++EXPORT_SYMBOL(__charge_beancounter_notop);
++
++void uncharge_warn(struct user_beancounter *ub, int resource,
++ unsigned long val, unsigned long held)
++{
++ char id[64];
++
++ print_ub_uid(ub, id, sizeof(id));
++ printk(KERN_ERR "Uncharging too much %lu h %lu, res %s ub %s\n",
++ val, held, ub_rnames[resource], id);
++ ub_debug_trace(1, 10, 10*HZ);
++}
++
++void __uncharge_beancounter_locked(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ ub_debug_resource(resource, "Uncharging %lu for %d of %p with %lu\n",
++ val, resource, ub, ub->ub_parms[resource].held);
++ if (ub->ub_parms[resource].held < val) {
++ uncharge_warn(ub, resource,
++ val, ub->ub_parms[resource].held);
++ val = ub->ub_parms[resource].held;
++ }
++ ub->ub_parms[resource].held -= val;
++}
++
++void uncharge_beancounter(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ unsigned long flags;
++ struct user_beancounter *p;
++
++ for (p = ub; p != NULL; p = p->parent) {
++ spin_lock_irqsave(&p->ub_lock, flags);
++ __uncharge_beancounter_locked(p, resource, val);
++ spin_unlock_irqrestore(&p->ub_lock, flags);
++ }
++}
++
++EXPORT_SYMBOL(uncharge_beancounter);
++
++void __uncharge_beancounter_notop(struct user_beancounter *ub,
++ int resource, unsigned long val)
++{
++ struct user_beancounter *p;
++ unsigned long flags;
++
++ local_irq_save(flags);
++ for (p = ub; p->parent != NULL; p = p->parent) {
++ spin_lock(&p->ub_lock);
++ __uncharge_beancounter_locked(p, resource, val);
++ spin_unlock(&p->ub_lock);
++ }
++ local_irq_restore(flags);
++}
++
++EXPORT_SYMBOL(__uncharge_beancounter_notop);
++
++
++/*
++ * Rate limiting stuff.
++ */
++int ub_ratelimit(struct ub_rate_info *p)
++{
++ unsigned long cjif, djif;
++ unsigned long flags;
++ static spinlock_t ratelimit_lock = SPIN_LOCK_UNLOCKED;
++ long new_bucket;
++
++ spin_lock_irqsave(&ratelimit_lock, flags);
++ cjif = jiffies;
++ djif = cjif - p->last;
++ if (djif < p->interval) {
++ if (p->bucket >= p->burst) {
++ spin_unlock_irqrestore(&ratelimit_lock, flags);
++ return 0;
++ }
++ p->bucket++;
++ } else {
++ new_bucket = p->bucket - (djif / (unsigned)p->interval);
++ if (new_bucket < 0)
++ new_bucket = 0;
++ p->bucket = new_bucket + 1;
++ }
++ p->last = cjif;
++ spin_unlock_irqrestore(&ratelimit_lock, flags);
++ return 1;
++}
++EXPORT_SYMBOL(ub_ratelimit);
++
++
++/*
++ * Initialization
++ *
++ * struct user_beancounter contains
++ * - limits and other configuration settings,
++ * with a copy stored for accounting purposes,
++ * - structural fields: lists, spinlocks and so on.
++ *
++ * Before these parts are initialized, the structure should be memset
++ * to 0 or copied from a known clean structure. That takes care of a lot
++ * of fields not initialized explicitly.
++ */
++
++static void init_beancounter_struct(struct user_beancounter *ub)
++{
++ ub->ub_magic = UB_MAGIC;
++ atomic_set(&ub->ub_refcount, 1);
++ spin_lock_init(&ub->ub_lock);
++ INIT_LIST_HEAD(&ub->ub_tcp_sk_list);
++ INIT_LIST_HEAD(&ub->ub_other_sk_list);
++#ifdef CONFIG_BC_DEBUG_KMEM
++ INIT_LIST_HEAD(&ub->ub_cclist);
++#endif
++ bc_init_ioprio(&ub->iopriv);
++}
++
++static void init_beancounter_store(struct user_beancounter *ub)
++{
++ int k;
++
++ for (k = 0; k < UB_RESOURCES; k++) {
++ memcpy(&ub->ub_store[k], &ub->ub_parms[k],
++ sizeof(struct ubparm));
++ }
++}
++
++static void init_beancounter_nolimits(struct user_beancounter *ub)
++{
++ int k;
++
++ for (k = 0; k < UB_RESOURCES; k++) {
++ ub->ub_parms[k].limit = UB_MAXVALUE;
++ /* FIXME: whether this is right for physpages and guarantees? */
++ ub->ub_parms[k].barrier = UB_MAXVALUE;
++ }
++
++ /* FIXME: set unlimited rate? */
++ ub->ub_limit_rl.burst = 4;
++ ub->ub_limit_rl.interval = 300*HZ;
++}
++
++static void init_beancounter_syslimits(struct user_beancounter *ub)
++{
++ unsigned long mp;
++ extern int max_threads;
++ int k;
++
++ mp = num_physpages;
++ ub->ub_parms[UB_KMEMSIZE].limit =
++ mp > (192*1024*1024 >> PAGE_SHIFT) ?
++ 32*1024*1024 : (mp << PAGE_SHIFT) / 6;
++ ub->ub_parms[UB_LOCKEDPAGES].limit = 8;
++ ub->ub_parms[UB_PRIVVMPAGES].limit = UB_MAXVALUE;
++ ub->ub_parms[UB_SHMPAGES].limit = 64;
++ ub->ub_parms[UB_NUMPROC].limit = max_threads / 2;
++ ub->ub_parms[UB_NUMTCPSOCK].limit = 1024;
++ ub->ub_parms[UB_TCPSNDBUF].limit = 1024*4*1024; /* 4k per socket */
++ ub->ub_parms[UB_TCPRCVBUF].limit = 1024*6*1024; /* 6k per socket */
++ ub->ub_parms[UB_NUMOTHERSOCK].limit = 256;
++ ub->ub_parms[UB_DGRAMRCVBUF].limit = 256*4*1024; /* 4k per socket */
++ ub->ub_parms[UB_OTHERSOCKBUF].limit = 256*8*1024; /* 8k per socket */
++ ub->ub_parms[UB_NUMFLOCK].limit = 1024;
++ ub->ub_parms[UB_NUMPTY].limit = 16;
++ ub->ub_parms[UB_NUMSIGINFO].limit = 1024;
++ ub->ub_parms[UB_DCACHESIZE].limit = 1024*1024;
++ ub->ub_parms[UB_NUMFILE].limit = 1024;
++
++ for (k = 0; k < UB_RESOURCES; k++)
++ ub->ub_parms[k].barrier = ub->ub_parms[k].limit;
++
++ ub->ub_limit_rl.burst = 4;
++ ub->ub_limit_rl.interval = 300*HZ;
++}
++
++DEFINE_PER_CPU_STATIC(struct ub_percpu_struct, ub0_percpu);
++
++void __init ub_init_early(void)
++{
++ struct user_beancounter *ub;
++
++ init_cache_counters();
++ ub = get_ub0();
++ memset(ub, 0, sizeof(*ub));
++ ub->ub_uid = 0;
++ init_beancounter_nolimits(ub);
++ init_beancounter_store(ub);
++ init_beancounter_struct(ub);
++ ub->ub_percpu = percpu_static_init(ub0_percpu);
++
++ memset(&current->task_bc, 0, sizeof(struct task_beancounter));
++ (void)set_exec_ub(ub);
++ current->task_bc.task_ub = get_beancounter(ub);
++ __charge_beancounter_locked(ub, UB_NUMPROC, 1, UB_FORCE);
++ current->task_bc.fork_sub = get_beancounter(ub);
++ ub_init_task_bc(&current->task_bc);
++ init_mm.mm_ub = get_beancounter(ub);
++
++ hlist_add_head(&ub->ub_hash, &ub_hash[ub->ub_uid]);
++ list_add(&ub->ub_list, &ub_list_head);
++}
++
++void __init ub_init_late(void)
++{
++ ub_cachep = kmem_cache_create("user_beancounters",
++ sizeof(struct user_beancounter),
++ 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
++
++ memset(&default_beancounter, 0, sizeof(default_beancounter));
++#ifdef CONFIG_BC_UNLIMITED
++ init_beancounter_nolimits(&default_beancounter);
++#else
++ init_beancounter_syslimits(&default_beancounter);
++#endif
++ init_beancounter_store(&default_beancounter);
++ init_beancounter_struct(&default_beancounter);
++}
+diff --git a/kernel/bc/dcache.c b/kernel/bc/dcache.c
+new file mode 100644
+index 0000000..2242d64
+--- /dev/null
++++ b/kernel/bc/dcache.c
+@@ -0,0 +1,399 @@
++/*
++ * kernel/bc/dcache.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/dcache.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/sysctl.h>
++#include <linux/swap.h>
++#include <linux/stop_machine.h>
++#include <linux/cpumask.h>
++#include <linux/nmi.h>
++#include <linux/rwsem.h>
++#include <linux/rcupdate.h>
++#include <linux/highmem.h>
++#include <asm/bitops.h>
++
++#include <bc/beancounter.h>
++#include <bc/kmem.h>
++#include <bc/dcache.h>
++#include <bc/dcache_op.h>
++
++/*
++ * Locking
++ * traverse dcache_lock d_lock
++ * ub_dentry_charge + - +
++ * ub_dentry_uncharge + + -
++ * ub_dentry_charge_nofail + + -
++ *
++ * d_inuse changes are atomic, with special handling of "not in use" <->
++ * "in use" (-1 <-> 0) transitions. We have two sources of non-atomicity
++ * here: (1) in many operations we need to change d_inuse of both dentry and
++ * its parent, and (2) on state transitions we need to adjust the account.
++ *
++ * Regarding (1): we do not have (and do not want) a single lock covering all
++ * operations, so in general it's impossible to get a consistent view of
++ * a tree with respect to d_inuse counters (except by swsuspend). It also
++ * means if a dentry with d_inuse of 0 gets one new in-use child and loses
++ * one, it's d_inuse counter will go either 0 -> 1 -> 0 path or 0 -> -1 -> 0,
++ * and we can't say which way.
++ * Note that path -1 -> 0 -> -1 can't turn into -1 -> -2 -> -1, since
++ * uncharge can be done only after return from charge (with d_genocide being
++ * the only apparent exception).
++ * Regarding (2): there is a similar uncertainty with the dcache account.
++ * If the account is equal to the limit, one more dentry is started to be
++ * used and one is put, the account will either hit the limit (and an error
++ * will be returned), or decrement will happen before increment.
++ *
++ * These races do not really matter.
++ * The only things we want are:
++ * - if a system is suspenede with no in-use dentries, all d_inuse counters
++ * should be correct (-1);
++ * - d_inuse counters should always be >= -1.
++ * This holds if ->parent references are accessed and maintained properly.
++ * In subtle moments (like d_move) dentries exchanging their parents should
++ * both be in-use. At d_genocide time, lookups and charges are assumed to be
++ * impossible.
++ */
++
++/*
++ * Hierarchical accounting
++ * UB argument must NOT be NULL
++ */
++
++static int do_charge_dcache(struct user_beancounter *ub, unsigned long size,
++ enum ub_severity sv)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (__charge_beancounter_locked(ub, UB_KMEMSIZE, CHARGE_SIZE(size), sv))
++ goto out_mem;
++ if (__charge_beancounter_locked(ub, UB_DCACHESIZE, size, sv))
++ goto out_dcache;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return 0;
++
++out_dcache:
++ __uncharge_beancounter_locked(ub, UB_KMEMSIZE, CHARGE_SIZE(size));
++out_mem:
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return -ENOMEM;
++}
++
++static void do_uncharge_dcache(struct user_beancounter *ub,
++ unsigned long size)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __uncharge_beancounter_locked(ub, UB_KMEMSIZE, CHARGE_SIZE(size));
++ __uncharge_beancounter_locked(ub, UB_DCACHESIZE, size);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++static int charge_dcache(struct user_beancounter *ub, unsigned long size,
++ enum ub_severity sv)
++{
++ struct user_beancounter *p, *q;
++
++ for (p = ub; p != NULL; p = p->parent) {
++ if (do_charge_dcache(p, size, sv))
++ goto unroll;
++ }
++ return 0;
++
++unroll:
++ for (q = ub; q != p; q = q->parent)
++ do_uncharge_dcache(q, size);
++ return -ENOMEM;
++}
++
++void uncharge_dcache(struct user_beancounter *ub, unsigned long size)
++{
++ for (; ub != NULL; ub = ub->parent)
++ do_uncharge_dcache(ub, size);
++}
++
++/*
++ * Simple helpers to do maintain account and d_ub field.
++ */
++
++static inline int d_charge(struct dentry_beancounter *d_bc)
++{
++ struct user_beancounter *ub;
++
++ ub = get_beancounter(get_exec_ub());
++ if (charge_dcache(ub, d_bc->d_ubsize, UB_SOFT)) {
++ put_beancounter(ub);
++ return -1;
++ }
++ d_bc->d_ub = ub;
++ return 0;
++}
++
++static inline void d_forced_charge(struct dentry_beancounter *d_bc)
++{
++ struct user_beancounter *ub;
++
++ ub = get_beancounter(get_exec_ub());
++ charge_dcache(ub, d_bc->d_ubsize, UB_FORCE);
++ d_bc->d_ub = ub;
++}
++
++/*
++ * Minor helpers
++ */
++
++extern struct kmem_cache *dentry_cache;
++extern struct kmem_cache *inode_cachep;
++static struct rw_semaphore ub_dentry_alloc_sem;
++
++static inline unsigned long d_charge_size(struct dentry *dentry)
++{
++ /* dentry's d_name is already set to appropriate value (see d_alloc) */
++ return kmem_cache_objuse(inode_cachep) + kmem_cache_objuse(dentry_cache) +
++ (dname_external(dentry) ?
++ kmem_dname_objuse((void *)dentry->d_name.name) : 0);
++}
++
++/*
++ * Entry points from dcache.c
++ */
++
++/*
++ * Set initial d_inuse on d_alloc.
++ * Called with no locks, preemption disabled.
++ */
++int __ub_dentry_alloc(struct dentry *dentry)
++{
++ struct dentry_beancounter *d_bc;
++
++ d_bc = &dentry->dentry_bc;
++ d_bc->d_ub = get_beancounter(get_exec_ub());
++ atomic_set(&d_bc->d_inuse, INUSE_INIT); /* see comment in dcache.h */
++ d_bc->d_ubsize = d_charge_size(dentry);
++
++ if (charge_dcache(d_bc->d_ub, d_bc->d_ubsize, UB_HARD))
++ goto failure;
++ return 0;
++
++failure:
++ put_beancounter(d_bc->d_ub);
++ d_bc->d_ub = NULL;
++ return -ENOMEM;
++}
++void __ub_dentry_alloc_start(void)
++{
++ down_read(&ub_dentry_alloc_sem);
++ current->task_bc.dentry_alloc = 1;
++}
++
++void __ub_dentry_alloc_end(void)
++{
++ current->task_bc.dentry_alloc = 0;
++ up_read(&ub_dentry_alloc_sem);
++}
++
++/*
++ * It is assumed that parent is already in use, so traverse upwards is
++ * limited to one ancestor only.
++ * Called under d_lock and rcu_read_lock.
++ */
++int __ub_dentry_charge(struct dentry *dentry)
++{
++ struct dentry_beancounter *d_bc;
++ struct dentry *parent;
++ int ret;
++
++ if (ub_dget_testone(dentry)) {
++ d_bc = &dentry->dentry_bc;
++ /* state transition -1 => 0 */
++ if (d_charge(d_bc))
++ goto failure;
++
++ if (dentry != dentry->d_parent) {
++ parent = dentry->d_parent;
++ if (ub_dget_testone(parent))
++ BUG();
++ }
++ }
++ return 0;
++
++failure:
++ /*
++ * Here we would like to fail the lookup.
++ * It is not easy: if d_lookup fails, callers expect that a dentry
++ * with the given name doesn't exist, and create a new one.
++ * So, first we forcedly charge for this dentry.
++ * Then try to remove it from cache safely. If it turns out to be
++ * possible, we can return error.
++ */
++ d_forced_charge(d_bc);
++
++ if (dentry != dentry->d_parent) {
++ parent = dentry->d_parent;
++ if (ub_dget_testone(parent))
++ BUG();
++ }
++
++ ret = 0;
++ if (spin_trylock(&dcache_lock)) {
++ if (!list_empty(&dentry->d_subdirs)) {
++ spin_unlock(&dentry->d_lock);
++ spin_unlock(&dcache_lock);
++ rcu_read_unlock();
++ shrink_dcache_parent(dentry);
++ rcu_read_lock();
++ spin_lock(&dcache_lock);
++ spin_lock(&dentry->d_lock);
++ }
++ if (atomic_read(&dentry->d_count) == 1) {
++ __d_drop(dentry);
++ ret = -1;
++ }
++ spin_unlock(&dcache_lock);
++ }
++
++ return ret;
++}
++
++/*
++ * Go up in the tree decreasing d_inuse.
++ * Called under dcache_lock.
++ */
++void __ub_dentry_uncharge(struct dentry *dentry)
++{
++ struct dentry *parent;
++ struct user_beancounter *ub;
++ unsigned long size;
++
++ /* go up until state doesn't change or and root is reached */
++ size = dentry->dentry_bc.d_ubsize;
++ ub = dentry->dentry_bc.d_ub;
++ while (ub_dput_testzero(dentry)) {
++ /* state transition 0 => -1 */
++ uncharge_dcache(ub, size);
++ put_beancounter(ub);
++
++ parent = dentry->d_parent;
++ if (dentry == parent)
++ break;
++
++ dentry = parent;
++ size = dentry->dentry_bc.d_ubsize;
++ ub = dentry->dentry_bc.d_ub;
++ }
++}
++
++/*
++ * Forced charge for __dget_locked, where API doesn't allow to return error.
++ * Called under dcache_lock.
++ */
++void __ub_dentry_charge_nofail(struct dentry *dentry)
++{
++ struct dentry *parent;
++
++ while (ub_dget_testone(dentry)) {
++ /* state transition -1 => 0 */
++ d_forced_charge(&dentry->dentry_bc);
++
++ parent = dentry->d_parent;
++ if (dentry == parent)
++ break;
++ dentry = parent;
++ }
++}
++
++/*
++ * Adaptive accounting
++ */
++
++int ub_dentry_on = 1;
++int ub_dentry_alloc_barrier;
++EXPORT_SYMBOL(ub_dentry_on);
++
++static unsigned long checklowat = 0;
++static unsigned long checkhiwat = ULONG_MAX;
++
++static int sysctl_ub_dentry_chk = 10;
++#define sysctl_ub_lowat sysctl_ub_watermark[0]
++#define sysctl_ub_hiwat sysctl_ub_watermark[1]
++static DECLARE_RWSEM(ub_dentry_alloc_sem);
++/* 1024th of lowmem size */
++static unsigned int sysctl_ub_watermark[2] = {0, 100};
++
++static void ub_dentry_set_limits(unsigned long pages, unsigned long cap)
++{
++ down_write(&ub_dentry_alloc_sem);
++ preempt_disable();
++ checklowat = (pages >> 10) * sysctl_ub_lowat;
++ checkhiwat = (pages >> 10) * sysctl_ub_hiwat;
++ if (checkhiwat > cap) {
++ checkhiwat = cap;
++ checklowat = cap / sysctl_ub_hiwat * sysctl_ub_lowat;
++ }
++ preempt_enable();
++ up_write(&ub_dentry_alloc_sem);
++}
++
++static int ub_dentry_proc_handler(ctl_table *ctl, int write, struct file *filp,
++ void __user *buffer, size_t *lenp, loff_t *ppos)
++{
++ int r;
++
++ r = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
++ if (!r && write)
++ ub_dentry_set_limits(totalram_pages - totalhigh_pages,
++ ULONG_MAX);
++ return r;
++}
++
++static ctl_table ub_dentry_sysctl_table[] = {
++ {
++ .procname = "dentry_check",
++ .data = &sysctl_ub_dentry_chk,
++ .maxlen = sizeof(sysctl_ub_dentry_chk),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {
++ .procname = "dentry_watermark",
++ .data = &sysctl_ub_lowat,
++ .maxlen = sizeof(sysctl_ub_lowat) * 2,
++ .mode = 0644,
++ .proc_handler = ub_dentry_proc_handler,
++ },
++ { .ctl_name = 0 }
++};
++static ctl_table ub_dentry_sysctl_root[] = {
++ {
++ .procname = "ubc",
++ .mode = 0555,
++ .child = ub_dentry_sysctl_table,
++ },
++ { .ctl_name = 0 }
++};
++
++static int __init ub_dentry_init(void)
++{
++ /*
++ * Initial watermarks are limited, to limit walk time.
++ * 384MB translates into 0.8 sec on PIII 866MHz.
++ */
++ ub_dentry_set_limits(totalram_pages - totalhigh_pages,
++ 384 * 1024 * 1024 / PAGE_SIZE);
++ if (register_sysctl_table(ub_dentry_sysctl_root) == NULL)
++ return -ENOMEM;
++ return 0;
++}
++__initcall(ub_dentry_init);
+diff --git a/kernel/bc/io_acct.c b/kernel/bc/io_acct.c
+new file mode 100644
+index 0000000..e8d6c38
+--- /dev/null
++++ b/kernel/bc/io_acct.c
+@@ -0,0 +1,500 @@
++/*
++ * kernel/bc/io_acct.c
++ *
++ * Copyright (C) 2006 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * Pavel Emelianov <xemul@openvz.org>
++ *
++ */
++
++#include <linux/mm.h>
++#include <linux/mempool.h>
++#include <linux/proc_fs.h>
++#include <linux/virtinfo.h>
++#include <linux/pagemap.h>
++
++#include <bc/beancounter.h>
++#include <bc/io_acct.h>
++#include <bc/rss_pages.h>
++#include <bc/vmpages.h>
++#include <bc/proc.h>
++
++static struct mempool_s *pb_pool;
++
++#define PB_MIN_IO (1024)
++
++static inline struct page_beancounter *io_pb_alloc(void)
++{
++ return mempool_alloc(pb_pool, GFP_ATOMIC);
++}
++
++static inline void io_pb_free(struct page_beancounter *pb)
++{
++ mempool_free(pb, pb_pool);
++}
++
++struct page_beancounter **page_pblist(struct page *page)
++{
++ struct page_beancounter **pb, *iopb;
++
++ pb = &page_pbc(page);
++ iopb = iopb_to_pb(*pb);
++
++ return iopb == NULL ? pb : &iopb->page_pb_list;
++}
++
++/*
++ * We save the context page was set dirty to use it later
++ * when the real write starts. If the page is mapped then
++ * IO pb is stores like this:
++ *
++ * Before saving:
++ *
++ * +- page -------+
++ * | ... |
++ * | page_pb +---+
++ * +--------------+ | +-----+ +-----+ +-----+
++ * +-> | pb1 | -> | pb2 | - ... -> | pbN | -+
++ * +-----+ +-----+ +-----+ |
++ * ^ |
++ * +---------------------------------+
++ *
++ * After saving:
++ *
++ * +- page -------+ +- io pb ------+
++ * | ... | | ... |
++ * | page_pb +----> | page_pb_list +-+
++ * +--------------+ +--------------+ |
++ * |
++ * +-------------------+
++ * |
++ * | +-----+ +-----+ +-----+
++ * +-> | pb1 | -> | pb2 | - ... -> | pbN | -+
++ * +-----+ +-----+ +-----+ |
++ * ^ |
++ * +---------------------------------+
++ *
++ * And the page_pblist(...) function returns pointer to the place that
++ * points to this pbX ring.
++ */
++
++#ifdef CONFIG_BC_DEBUG_IO
++static LIST_HEAD(pb_io_list);
++static unsigned long anon_pages, not_released;
++
++static inline void io_debug_save(struct page_beancounter *pb,
++ struct page_beancounter *mpb)
++{
++ pb->io_debug = (mpb == NULL);
++ list_add(&pb->io_list, &pb_io_list);
++}
++
++static inline void io_debug_release(struct page_beancounter *pb)
++{
++ list_del(&pb->io_list);
++}
++
++void ub_io_release_debug(struct page *page)
++{
++ struct page_beancounter *pb;
++ static int once = 0;
++
++ pb = page_pbc(page);
++ if (likely(iopb_to_pb(pb) == NULL))
++ return;
++
++ if (!once) {
++ printk("BUG: Page has an IO bc but is not expectd to\n");
++ dump_stack();
++ once = 1;
++ }
++
++ spin_lock(&pb_lock);
++ not_released++;
++ pb = iopb_to_pb(pb);
++ page_pbc(page) = NULL;
++ io_debug_release(pb);
++ pb->ub->io_pb_held--;
++ spin_unlock(&pb_lock);
++
++ put_beancounter(pb->ub);
++ io_pb_free(pb);
++}
++
++static inline int io_debug_precheck_save(struct page *page)
++{
++ if (unlikely(PageAnon(page))) {
++ anon_pages++;
++ return 1;
++ }
++
++ return 0;
++}
++
++static inline int io_debug_precheck_release(struct page *page)
++{
++ return 0;
++}
++#else
++#define io_debug_save(pb, mpb) do { } while (0)
++#define io_debug_release(pb) do { } while (0)
++#define io_debug_precheck_save(page) (0)
++#define io_debug_precheck_release(p) (0)
++#endif
++
++static inline void set_page_io(struct page *page, struct page_beancounter *pb,
++ struct page_beancounter *mapped_pb)
++{
++ unsigned long val;
++
++ val = (unsigned long)pb | PAGE_IO_MARK;
++ pb->page = page;
++
++ page_pbc(page) = (struct page_beancounter *)val;
++ io_debug_save(pb, mapped_pb);
++ pb->ub->io_pb_held++;
++}
++
++static inline void put_page_io(struct page *page, struct page_beancounter *pb)
++{
++ pb->ub->io_pb_held--;
++ io_debug_release(pb);
++ page_pbc(page) = pb->page_pb_list;
++}
++
++void ub_io_save_context(struct page *page, size_t bytes_dirtied)
++{
++ struct user_beancounter *ub;
++ struct page_beancounter *pb, *mapped_pb, *io_pb;
++
++ if (unlikely(in_interrupt())) {
++ WARN_ON_ONCE(1);
++ return;
++ }
++
++ /*
++ * FIXME - this can happen from atomic context and
++ * it's probably not that good to loose some requests
++ */
++
++ pb = io_pb_alloc();
++ io_pb = NULL;
++
++ spin_lock(&pb_lock);
++ if (io_debug_precheck_save(page))
++ goto out_unlock;
++
++ mapped_pb = page_pbc(page);
++ io_pb = iopb_to_pb(mapped_pb);
++ if (io_pb != NULL) {
++ /*
++ * this page has an IO - release it and force a new one
++ * We could also race with page cleaning - see below
++ */
++ mapped_pb = io_pb->page_pb_list;
++ put_page_io(page, io_pb);
++ }
++
++ /*
++ * If the page is mapped we must save the context
++ * it maps to. If the page isn't mapped we use current
++ * context as this is a regular write.
++ */
++
++ if (mapped_pb != NULL)
++ ub = top_beancounter(mapped_pb->ub);
++ else
++ ub = get_io_ub();
++
++ if (!PageDirty(page)) {
++ /*
++ * race with clear_page_dirty(_for_io) - account
++ * writes for ub_io_release_context()
++ */
++ if (io_pb != NULL)
++ io_pb->ub->bytes_wrote += PAGE_CACHE_SIZE;
++ if (pb != NULL)
++ io_pb_free(pb);
++ goto out_unlock;
++ }
++
++ if (pb == NULL) {
++ ub->bytes_dirty_missed += bytes_dirtied;
++ goto out_unlock;
++ }
++
++ /*
++ * the page may become clean here, but the context will be seen
++ * in ub_io_release_context()
++ */
++
++ pb->ub = get_beancounter(ub);
++ pb->page_pb_list = mapped_pb;
++ ub->bytes_dirtied += bytes_dirtied;
++
++ set_page_io(page, pb, mapped_pb);
++
++out_unlock:
++ spin_unlock(&pb_lock);
++
++ if (io_pb != NULL) {
++ put_beancounter(io_pb->ub);
++ io_pb_free(io_pb);
++ }
++}
++
++void ub_io_release_context(struct page *page, size_t wrote)
++{
++ struct page_beancounter *pb;
++
++ if (io_debug_precheck_release(page))
++ return;
++
++ if (unlikely(in_interrupt())) {
++ WARN_ON_ONCE(1);
++ return;
++ }
++
++ spin_lock(&pb_lock);
++ pb = iopb_to_pb(page_pbc(page));
++ if (unlikely(pb == NULL))
++ /*
++ * this may happen if we failed to allocate
++ * context in ub_io_save_context or raced with it
++ */
++ goto out_unlock;
++
++ if (wrote)
++ pb->ub->bytes_wrote += wrote;
++
++ put_page_io(page, pb);
++out_unlock:
++ spin_unlock(&pb_lock);
++
++ if (pb != NULL) {
++ put_beancounter(pb->ub);
++ io_pb_free(pb);
++ }
++}
++
++void __init ub_init_io(struct kmem_cache *pb_cachep)
++{
++ pb_pool = mempool_create_slab_pool(PB_MIN_IO, pb_cachep);
++ if (pb_pool == NULL)
++ panic("Can't create pb_pool");
++}
++
++#ifdef CONFIG_PROC_FS
++#define in_flight(var) (var > var##_done ? var - var##_done : 0)
++
++static int bc_ioacct_show(struct seq_file *f, void *v)
++{
++ int i;
++ unsigned long long read, write, cancel;
++ unsigned long sync, sync_done;
++ unsigned long fsync, fsync_done;
++ unsigned long fdsync, fdsync_done;
++ unsigned long frsync, frsync_done;
++ unsigned long reads, writes;
++ unsigned long long rchar, wchar;
++ struct user_beancounter *ub;
++
++ ub = seq_beancounter(f);
++
++ read = write = cancel = 0;
++ sync = sync_done = fsync = fsync_done =
++ fdsync = fdsync_done = frsync = frsync_done = 0;
++ reads = writes = 0;
++ rchar = wchar = 0;
++ for_each_online_cpu(i) {
++ struct ub_percpu_struct *ub_percpu;
++ ub_percpu = per_cpu_ptr(ub->ub_percpu, i);
++
++ read += ub_percpu->bytes_read;
++ write += ub_percpu->bytes_wrote;
++ cancel += ub_percpu->bytes_cancelled;
++
++ sync += ub_percpu->sync;
++ fsync += ub_percpu->fsync;
++ fdsync += ub_percpu->fdsync;
++ frsync += ub_percpu->frsync;
++ sync_done += ub_percpu->sync_done;
++ fsync_done += ub_percpu->fsync_done;
++ fdsync_done += ub_percpu->fdsync_done;
++ frsync_done += ub_percpu->frsync_done;
++
++ reads += ub_percpu->read;
++ writes += ub_percpu->write;
++ rchar += ub_percpu->rchar;
++ wchar += ub_percpu->wchar;
++ }
++
++ seq_printf(f, bc_proc_llu_fmt, "read", read);
++ seq_printf(f, bc_proc_llu_fmt, "write", ub->bytes_wrote + write);
++ seq_printf(f, bc_proc_llu_fmt, "dirty", ub->bytes_dirtied);
++ seq_printf(f, bc_proc_llu_fmt, "cancel", cancel);
++ seq_printf(f, bc_proc_llu_fmt, "missed", ub->bytes_dirty_missed);
++
++ seq_printf(f, bc_proc_lu_lfmt, "syncs_total", sync);
++ seq_printf(f, bc_proc_lu_lfmt, "fsyncs_total", fsync);
++ seq_printf(f, bc_proc_lu_lfmt, "fdatasyncs_total", fdsync);
++ seq_printf(f, bc_proc_lu_lfmt, "range_syncs_total", frsync);
++
++ seq_printf(f, bc_proc_lu_lfmt, "syncs_active", in_flight(sync));
++ seq_printf(f, bc_proc_lu_lfmt, "fsyncs_active", in_flight(fsync));
++ seq_printf(f, bc_proc_lu_lfmt, "fdatasyncs_active", in_flight(fsync));
++ seq_printf(f, bc_proc_lu_lfmt, "range_syncs_active", in_flight(frsync));
++
++ seq_printf(f, bc_proc_lu_lfmt, "vfs_reads", reads);
++ seq_printf(f, bc_proc_llu_fmt, "vfs_read_chars", rchar);
++ seq_printf(f, bc_proc_lu_lfmt, "vfs_writes", writes);
++ seq_printf(f, bc_proc_llu_fmt, "vfs_write_chars", wchar);
++
++ seq_printf(f, bc_proc_lu_lfmt, "io_pbs", ub->io_pb_held);
++ return 0;
++}
++
++static struct bc_proc_entry bc_ioacct_entry = {
++ .name = "ioacct",
++ .u.show = bc_ioacct_show,
++};
++
++#ifdef CONFIG_BC_DEBUG_IO
++#define PTR_SIZE (int)(sizeof(void *) * 2)
++#define INT_SIZE (int)(sizeof(int) * 2)
++
++static int bc_io_show(struct seq_file *f, void *v)
++{
++ struct list_head *lh;
++ struct page_beancounter *pb;
++ struct page *pg;
++
++ lh = (struct list_head *)v;
++ if (lh == &pb_io_list) {
++ seq_printf(f, "Races: anon %lu missed %lu\n",
++ anon_pages, not_released);
++
++ seq_printf(f, "%-*s %-1s %-*s %-4s %*s %*s "
++ "%-*s %-*s %-1s %-*s %-*s\n",
++ PTR_SIZE, "pb", "",
++ PTR_SIZE, "page", "flg",
++ INT_SIZE, "cnt", INT_SIZE, "mcnt",
++ PTR_SIZE, "pb_list",
++ PTR_SIZE, "page_pb", "",
++ PTR_SIZE, "mapping",
++ INT_SIZE, "ub");
++ return 0;
++ }
++
++ pb = list_entry(lh, struct page_beancounter, io_list);
++ pg = pb->page;
++ seq_printf(f, "%p %c %p %c%c%c%c %*d %*d %p %p %c %p %d\n",
++ pb, pb->io_debug ? 'e' : 'm', pg,
++ PageDirty(pg) ? 'D' : 'd',
++ PageAnon(pg) ? 'A' : 'a',
++ PageWriteback(pg) ? 'W' : 'w',
++ PageLocked(pg) ? 'L' : 'l',
++ INT_SIZE, page_count(pg),
++ INT_SIZE, page_mapcount(pg),
++ pb->page_pb_list, page_pbc(pg),
++ iopb_to_pb(page_pbc(pg)) == pb ? ' ' : '!',
++ pg->mapping, pb->ub->ub_uid);
++ return 0;
++}
++
++static void *bc_io_start(struct seq_file *f, loff_t *ppos)
++{
++ spin_lock(&pb_lock);
++ return seq_list_start_head(&pb_io_list, *ppos);
++}
++
++static void *bc_io_next(struct seq_file *f, void *v, loff_t *ppos)
++{
++ return seq_list_next(v, &pb_io_list, ppos);
++}
++
++static void bc_io_stop(struct seq_file *f, void *v)
++{
++ spin_unlock(&pb_lock);
++}
++
++static struct seq_operations bc_io_seq_ops = {
++ .start = bc_io_start,
++ .next = bc_io_next,
++ .stop = bc_io_stop,
++ .show = bc_io_show,
++};
++
++static int bc_io_open(struct inode *inode, struct file *filp)
++{
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return -EACCES;
++
++ return seq_open(filp, &bc_io_seq_ops);
++}
++static struct file_operations bc_io_debug_ops = {
++ .open = bc_io_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static struct bc_proc_entry bc_ioacct_debug_entry = {
++ .name = "ioacct_debug",
++ .u.fops = &bc_io_debug_ops,
++};
++#endif
++
++static int bc_ioacct_notify(struct vnotifier_block *self,
++ unsigned long event, void *arg, int old_ret)
++{
++ struct user_beancounter *ub;
++ unsigned long *vm_events;
++ unsigned long long bin, bout;
++ int i;
++
++ if (event != VIRTINFO_VMSTAT)
++ return old_ret;
++
++ ub = top_beancounter(get_exec_ub());
++ if (ub == get_ub0())
++ return old_ret;
++
++ /* Think over: do we need to account here bytes_dirty_missed? */
++ bout = ub->bytes_wrote;
++ bin = 0;
++ for_each_online_cpu(i) {
++ bout += per_cpu_ptr(ub->ub_percpu, i)->bytes_wrote;
++ bin += per_cpu_ptr(ub->ub_percpu, i)->bytes_read;
++ }
++
++ /* convert to Kbytes */
++ bout >>= 10;
++ bin >>= 10;
++
++ vm_events = ((unsigned long *)arg) + NR_VM_ZONE_STAT_ITEMS;
++ vm_events[PGPGOUT] = (unsigned long)bout;
++ vm_events[PGPGIN] = (unsigned long)bin;
++ return NOTIFY_OK;
++}
++
++static struct vnotifier_block bc_ioacct_nb = {
++ .notifier_call = bc_ioacct_notify,
++};
++
++static int __init bc_ioacct_init(void)
++{
++#ifdef CONFIG_BC_DEBUG_IO
++ bc_register_proc_root_entry(&bc_ioacct_debug_entry);
++#endif
++ bc_register_proc_entry(&bc_ioacct_entry);
++
++ virtinfo_notifier_register(VITYPE_GENERAL, &bc_ioacct_nb);
++ return 0;
++}
++
++late_initcall(bc_ioacct_init);
++#endif
+diff --git a/kernel/bc/io_prio.c b/kernel/bc/io_prio.c
+new file mode 100644
+index 0000000..20aa133
+--- /dev/null
++++ b/kernel/bc/io_prio.c
+@@ -0,0 +1,288 @@
++/*
++ * kernel/bc/io_prio.c
++ *
++ * Copyright (C) 2007 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * Vasily Tarasov <vtaras@openvz.org>
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/cfq-iosched.h>
++#include <bc/io_prio.h>
++#include <bc/beancounter.h>
++#include <bc/hash.h>
++#include <bc/io_acct.h>
++#include <linux/blkdev.h>
++
++struct cfq_bc_data *__find_cfq_bc(struct ub_iopriv *iopriv,
++ struct cfq_data *cfqd)
++{
++ struct cfq_bc_data *cfq_bc;
++
++ list_for_each_entry(cfq_bc, &iopriv->cfq_bc_head, cfq_bc_list)
++ if (cfq_bc->cfqd == cfqd)
++ return cfq_bc;
++
++ return NULL;
++}
++
++struct cfq_bc_data *bc_find_cfq_bc(struct ub_iopriv *iopriv,
++ struct cfq_data *cfqd)
++{
++ struct cfq_bc_data *cfq_bc;
++ unsigned long flags;
++
++ read_lock_irqsave(&iopriv->cfq_bc_list_lock, flags);
++ cfq_bc = __find_cfq_bc(iopriv, cfqd);
++ read_unlock_irqrestore(&iopriv->cfq_bc_list_lock, flags);
++ return cfq_bc;
++}
++struct cfq_bc_data *bc_findcreate_cfq_bc(struct ub_iopriv *iopriv,
++ struct cfq_data *cfqd, gfp_t gfp_mask)
++{
++ struct cfq_bc_data *cfq_bc_new;
++ struct cfq_bc_data *cfq_bc;
++ unsigned long flags;
++
++ cfq_bc = bc_find_cfq_bc(iopriv, cfqd);
++ if (cfq_bc)
++ return cfq_bc;
++
++ cfq_bc_new = kzalloc(sizeof(*cfq_bc_new), gfp_mask);
++ if (!cfq_bc_new)
++ return NULL;
++
++ cfq_init_cfq_bc(cfq_bc_new);
++ cfq_bc_new->cfqd = cfqd;
++ cfq_bc_new->ub_iopriv = iopriv;
++
++ write_lock_irqsave(&iopriv->cfq_bc_list_lock, flags);
++ cfq_bc = __find_cfq_bc(iopriv, cfqd);
++ if (cfq_bc)
++ kfree(cfq_bc_new);
++ else {
++ list_add_tail(&cfq_bc_new->cfq_bc_list,
++ &iopriv->cfq_bc_head);
++ cfq_bc = cfq_bc_new;
++ }
++ write_unlock_irqrestore(&iopriv->cfq_bc_list_lock, flags);
++
++ return cfq_bc;
++}
++
++void bc_init_ioprio(struct ub_iopriv *iopriv)
++{
++ INIT_LIST_HEAD(&iopriv->cfq_bc_head);
++ rwlock_init(&iopriv->cfq_bc_list_lock);
++ iopriv->ioprio = UB_IOPRIO_BASE;
++}
++
++static void inline bc_cfq_bc_check_empty(struct cfq_bc_data *cfq_bc)
++{
++ BUG_ON(!RB_EMPTY_ROOT(&cfq_bc->service_tree.rb));
++}
++
++static void bc_release_cfq_bc(struct cfq_bc_data *cfq_bc)
++{
++ struct cfq_data *cfqd;
++ elevator_t *eq;
++ int i;
++
++ cfqd = cfq_bc->cfqd;
++ eq = cfqd->queue->elevator;
++
++ for (i = 0; i < CFQ_PRIO_LISTS; i++) {
++ if (cfq_bc->async_cfqq[0][i]) {
++ eq->ops->put_queue(cfq_bc->async_cfqq[0][i]);
++ cfq_bc->async_cfqq[0][i] = NULL;
++ }
++ if (cfq_bc->async_cfqq[1][i]) {
++ eq->ops->put_queue(cfq_bc->async_cfqq[1][i]);
++ cfq_bc->async_cfqq[1][i] = NULL;
++ }
++ }
++ if (cfq_bc->async_idle_cfqq) {
++ eq->ops->put_queue(cfq_bc->async_idle_cfqq);
++ cfq_bc->async_idle_cfqq = NULL;
++ }
++ /*
++ * Note: this cfq_bc is already not in active list,
++ * but can be still pointed from cfqd as active.
++ */
++ cfqd->active_cfq_bc = NULL;
++
++ bc_cfq_bc_check_empty(cfq_bc);
++ list_del(&cfq_bc->cfq_bc_list);
++ kfree(cfq_bc);
++}
++
++void bc_fini_ioprio(struct ub_iopriv *iopriv)
++{
++ struct cfq_bc_data *cfq_bc;
++ struct cfq_bc_data *cfq_bc_tmp;
++ unsigned long flags;
++ spinlock_t *queue_lock;
++
++ /*
++ * Don't get cfq_bc_list_lock since ub is already dead,
++ * but async cfqqs are still in hash list, consequently
++ * queue_lock should be hold.
++ */
++ list_for_each_entry_safe(cfq_bc, cfq_bc_tmp,
++ &iopriv->cfq_bc_head, cfq_bc_list) {
++ queue_lock = cfq_bc->cfqd->queue->queue_lock;
++ spin_lock_irqsave(queue_lock, flags);
++ bc_release_cfq_bc(cfq_bc);
++ spin_unlock_irqrestore(queue_lock, flags);
++ }
++}
++
++void bc_cfq_exit_queue(struct cfq_data *cfqd)
++{
++ struct cfq_bc_data *cfq_bc;
++ struct user_beancounter *ub;
++
++ local_irq_disable();
++ for_each_beancounter(ub) {
++ write_lock(&ub->iopriv.cfq_bc_list_lock);
++ cfq_bc = __find_cfq_bc(&ub->iopriv, cfqd);
++ if (!cfq_bc) {
++ write_unlock(&ub->iopriv.cfq_bc_list_lock);
++ continue;
++ }
++ bc_release_cfq_bc(cfq_bc);
++ write_unlock(&ub->iopriv.cfq_bc_list_lock);
++ }
++ local_irq_enable();
++}
++
++int bc_expired(struct cfq_data *cfqd)
++{
++ return time_after(jiffies, cfqd->slice_end) ? 1 : 0;
++}
++
++static inline int bc_empty(struct cfq_bc_data *cfq_bc)
++{
++ /*
++ * consider BC as empty only if there is no requests
++ * in elevator _and_ in driver
++ */
++ if (!cfq_bc->rqnum && !cfq_bc->on_dispatch)
++ return 1;
++
++ return 0;
++}
++
++static inline unsigned long bc_time_slice_by_ioprio(unsigned int ioprio,
++ unsigned int base_slice)
++{
++ return base_slice +
++ (base_slice * (ioprio - UB_IOPRIO_MIN))
++ / (UB_IOPRIO_MAX - UB_IOPRIO_MIN - 1);
++}
++
++static inline void bc_set_active(struct cfq_data *cfqd)
++{
++ if (list_empty(&cfqd->act_cfq_bc_head)) {
++ cfqd->active_cfq_bc = NULL;
++ return;
++ }
++
++ cfqd->active_cfq_bc = list_first_entry(&cfqd->act_cfq_bc_head,
++ struct cfq_bc_data, act_cfq_bc_list);
++ list_move_tail(&cfqd->active_cfq_bc->act_cfq_bc_list,
++ &cfqd->act_cfq_bc_head);
++ cfqd->slice_end = jiffies +
++ bc_time_slice_by_ioprio(cfqd->active_cfq_bc->ub_iopriv->ioprio,
++ cfqd->cfq_ub_slice);
++}
++
++void bc_schedule_active(struct cfq_data *cfqd)
++{
++ if (bc_expired(cfqd) || !cfqd->active_cfq_bc ||
++ bc_empty(cfqd->active_cfq_bc))
++ bc_set_active(cfqd);
++}
++
++void bc_inc_rqnum(struct cfq_queue *cfqq)
++{
++ struct cfq_bc_data *cfq_bc;
++
++ cfq_bc = cfqq->cfq_bc;
++
++ if (!cfq_bc->rqnum)
++ list_add_tail(&cfq_bc->act_cfq_bc_list,
++ &cfqq->cfqd->act_cfq_bc_head);
++
++ cfq_bc->rqnum++;
++}
++
++void bc_dec_rqnum(struct cfq_queue *cfqq)
++{
++ struct cfq_bc_data *cfq_bc;
++
++ cfq_bc = cfqq->cfq_bc;
++
++ cfq_bc->rqnum--;
++
++ if (!cfq_bc->rqnum)
++ list_del(&cfq_bc->act_cfq_bc_list);
++}
++
++unsigned long bc_set_ioprio(int ubid, int ioprio)
++{
++ struct user_beancounter *ub;
++
++ if (ioprio < UB_IOPRIO_MIN || ioprio >= UB_IOPRIO_MAX)
++ return -ERANGE;
++
++ ub = get_beancounter_byuid(ubid, 0);
++ if (!ub)
++ return -ESRCH;
++
++ ub->iopriv.ioprio = ioprio;
++ put_beancounter(ub);
++
++ return 0;
++}
++
++struct user_beancounter *bc_io_switch_context(struct page *page)
++{
++ struct page_beancounter *pb;
++ struct user_beancounter *old_ub = NULL;
++
++ pb = page_iopb(page);
++ pb = iopb_to_pb(pb);
++ if (pb) {
++ get_beancounter(pb->ub);
++ old_ub = set_exec_ub(pb->ub);
++ }
++
++ return old_ub;
++}
++
++void bc_io_restore_context(struct user_beancounter *ub)
++{
++ struct user_beancounter *old_ub;
++
++ if (ub) {
++ old_ub = set_exec_ub(ub);
++ put_beancounter(old_ub);
++ }
++}
++
++EXPORT_SYMBOL(bc_io_switch_context);
++EXPORT_SYMBOL(bc_io_restore_context);
++EXPORT_SYMBOL(__find_cfq_bc);
++EXPORT_SYMBOL(bc_fini_ioprio);
++EXPORT_SYMBOL(bc_init_ioprio);
++EXPORT_SYMBOL(bc_findcreate_cfq_bc);
++EXPORT_SYMBOL(bc_cfq_exit_queue);
++EXPORT_SYMBOL(bc_expired);
++EXPORT_SYMBOL(bc_schedule_active);
++EXPORT_SYMBOL(bc_inc_rqnum);
++EXPORT_SYMBOL(bc_dec_rqnum);
+diff --git a/kernel/bc/kmem.c b/kernel/bc/kmem.c
+new file mode 100644
+index 0000000..74c4179
+--- /dev/null
++++ b/kernel/bc/kmem.c
+@@ -0,0 +1,406 @@
++/*
++ * kernel/bc/kmem.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/highmem.h>
++#include <linux/vmalloc.h>
++#include <linux/mm.h>
++#include <linux/gfp.h>
++#include <linux/swap.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <bc/beancounter.h>
++#include <bc/kmem.h>
++#include <bc/rss_pages.h>
++#include <bc/hash.h>
++#include <bc/proc.h>
++
++/*
++ * Initialization
++ */
++
++/*
++ * Slab accounting
++ */
++
++#ifdef CONFIG_BC_DEBUG_KMEM
++
++#define CC_HASH_SIZE 1024
++static struct ub_cache_counter *cc_hash[CC_HASH_SIZE];
++spinlock_t cc_lock;
++
++static void __free_cache_counters(struct user_beancounter *ub,
++ struct kmem_cache *cachep)
++{
++ struct ub_cache_counter *cc, **pprev, *del;
++ int i;
++ unsigned long flags;
++
++ del = NULL;
++ spin_lock_irqsave(&cc_lock, flags);
++ for (i = 0; i < CC_HASH_SIZE; i++) {
++ pprev = &cc_hash[i];
++ cc = cc_hash[i];
++ while (cc != NULL) {
++ if (cc->ub != ub && cc->cachep != cachep) {
++ pprev = &cc->next;
++ cc = cc->next;
++ continue;
++ }
++
++ list_del(&cc->ulist);
++ *pprev = cc->next;
++ cc->next = del;
++ del = cc;
++ cc = *pprev;
++ }
++ }
++ spin_unlock_irqrestore(&cc_lock, flags);
++
++ while (del != NULL) {
++ cc = del->next;
++ kfree(del);
++ del = cc;
++ }
++}
++
++void ub_free_counters(struct user_beancounter *ub)
++{
++ __free_cache_counters(ub, NULL);
++}
++
++void ub_kmemcache_free(struct kmem_cache *cachep)
++{
++ __free_cache_counters(NULL, cachep);
++}
++
++void __init init_cache_counters(void)
++{
++ memset(cc_hash, 0, CC_HASH_SIZE * sizeof(cc_hash[0]));
++ spin_lock_init(&cc_lock);
++}
++
++#define cc_hash_fun(ub, cachep) ( \
++ (((unsigned long)(ub) >> L1_CACHE_SHIFT) ^ \
++ ((unsigned long)(ub) >> (BITS_PER_LONG / 2)) ^ \
++ ((unsigned long)(cachep) >> L1_CACHE_SHIFT) ^ \
++ ((unsigned long)(cachep) >> (BITS_PER_LONG / 2)) \
++ ) & (CC_HASH_SIZE - 1))
++
++static int change_slab_charged(struct user_beancounter *ub,
++ struct kmem_cache *cachep, long val)
++{
++ struct ub_cache_counter *cc, *new_cnt, **pprev;
++ unsigned long flags;
++
++ new_cnt = NULL;
++again:
++ spin_lock_irqsave(&cc_lock, flags);
++ cc = cc_hash[cc_hash_fun(ub, cachep)];
++ while (cc) {
++ if (cc->ub == ub && cc->cachep == cachep)
++ goto found;
++ cc = cc->next;
++ }
++
++ if (new_cnt != NULL)
++ goto insert;
++
++ spin_unlock_irqrestore(&cc_lock, flags);
++
++ new_cnt = kmalloc(sizeof(*new_cnt), GFP_ATOMIC);
++ if (new_cnt == NULL)
++ return -ENOMEM;
++
++ new_cnt->counter = 0;
++ new_cnt->ub = ub;
++ new_cnt->cachep = cachep;
++ goto again;
++
++insert:
++ pprev = &cc_hash[cc_hash_fun(ub, cachep)];
++ new_cnt->next = *pprev;
++ *pprev = new_cnt;
++ list_add(&new_cnt->ulist, &ub->ub_cclist);
++ cc = new_cnt;
++ new_cnt = NULL;
++
++found:
++ cc->counter += val;
++ spin_unlock_irqrestore(&cc_lock, flags);
++ if (new_cnt)
++ kfree(new_cnt);
++ return 0;
++}
++
++static inline int inc_slab_charged(struct user_beancounter *ub,
++ struct kmem_cache *cachep)
++{
++ return change_slab_charged(ub, cachep, 1);
++}
++
++static inline void dec_slab_charged(struct user_beancounter *ub,
++ struct kmem_cache *cachep)
++{
++ if (change_slab_charged(ub, cachep, -1) < 0)
++ BUG();
++}
++
++#include <linux/vmalloc.h>
++
++#define inc_pages_charged(ub, order) ub_percpu_add(ub, \
++ pages_charged, 1 << order)
++#define dec_pages_charged(ub, order) ub_percpu_sub(ub, \
++ pages_charged, 1 << order)
++
++#ifdef CONFIG_PROC_FS
++static int bc_kmem_debug_show(struct seq_file *f, void *v)
++{
++ struct user_beancounter *ub;
++ struct ub_cache_counter *cc;
++ long pages, vmpages, pbc;
++ int i;
++
++ ub = seq_beancounter(f);
++
++ pages = vmpages = pbc = 0;
++ for_each_online_cpu(i) {
++ pages += per_cpu_ptr(ub->ub_percpu, i)->pages_charged;
++ vmpages += per_cpu_ptr(ub->ub_percpu, i)->vmalloc_charged;
++ pbc += per_cpu_ptr(ub->ub_percpu, i)->pbcs;
++ }
++ if (pages < 0)
++ pages = 0;
++ if (vmpages < 0)
++ vmpages = 0;
++
++ seq_printf(f, bc_proc_lu_lu_fmt, "pages", pages, PAGE_SIZE);
++ seq_printf(f, bc_proc_lu_lu_fmt, "vmalloced", vmpages, PAGE_SIZE);
++ seq_printf(f, bc_proc_lu_lu_fmt, "pbcs", pbc,
++ sizeof(struct page_beancounter));
++
++ spin_lock_irq(&cc_lock);
++ list_for_each_entry (cc, &ub->ub_cclist, ulist) {
++ struct kmem_cache *cachep;
++
++ cachep = cc->cachep;
++ seq_printf(f, bc_proc_lu_lu_fmt,
++ kmem_cache_name(cachep),
++ cc->counter,
++ kmem_cache_objuse(cachep));
++ }
++ spin_unlock_irq(&cc_lock);
++ return 0;
++}
++
++static struct bc_proc_entry bc_kmem_debug_entry = {
++ .name = "kmem_debug",
++ .u.show = bc_kmem_debug_show,
++};
++
++static int __init bc_kmem_debug_init(void)
++{
++ bc_register_proc_entry(&bc_kmem_debug_entry);
++ return 0;
++}
++
++late_initcall(bc_kmem_debug_init);
++#endif
++
++#else
++#define inc_slab_charged(ub, cache) (0)
++#define dec_slab_charged(ub, cache) do { } while (0)
++#define inc_pages_charged(ub, cache) do { } while (0)
++#define dec_pages_charged(ub, cache) do { } while (0)
++#endif
++
++#define UB_KMEM_QUANT (PAGE_SIZE * 4)
++
++/* called with IRQ disabled */
++int ub_kmemsize_charge(struct user_beancounter *ub,
++ unsigned long size,
++ enum ub_severity strict)
++{
++ struct task_beancounter *tbc;
++
++ tbc = &current->task_bc;
++ if (ub != tbc->task_ub || size > UB_KMEM_QUANT)
++ goto just_charge;
++ if (tbc->kmem_precharged >= size) {
++ tbc->kmem_precharged -= size;
++ return 0;
++ }
++
++ if (charge_beancounter(ub, UB_KMEMSIZE, UB_KMEM_QUANT, UB_HARD) == 0) {
++ tbc->kmem_precharged += UB_KMEM_QUANT - size;
++ return 0;
++ }
++
++just_charge:
++ return charge_beancounter(ub, UB_KMEMSIZE, size, strict);
++}
++
++/* called with IRQ disabled */
++void ub_kmemsize_uncharge(struct user_beancounter *ub,
++ unsigned long size)
++{
++ struct task_beancounter *tbc;
++
++ if (size > UB_MAXVALUE) {
++ printk("ub_kmemsize_uncharge: size %lu\n", size);
++ dump_stack();
++ }
++
++ tbc = &current->task_bc;
++ if (ub != tbc->task_ub)
++ goto just_uncharge;
++
++ tbc->kmem_precharged += size;
++ if (tbc->kmem_precharged < UB_KMEM_QUANT * 2)
++ return;
++ size = tbc->kmem_precharged - UB_KMEM_QUANT;
++ tbc->kmem_precharged -= size;
++
++just_uncharge:
++ uncharge_beancounter(ub, UB_KMEMSIZE, size);
++}
++
++/* called with IRQ disabled */
++int ub_slab_charge(struct kmem_cache *cachep, void *objp, gfp_t flags)
++{
++ unsigned int size;
++ struct user_beancounter *ub;
++
++ ub = get_beancounter(get_exec_ub());
++ if (ub == NULL)
++ return 0;
++
++ size = CHARGE_SIZE(kmem_cache_objuse(cachep));
++ if (ub_kmemsize_charge(ub, size,
++ (flags & __GFP_SOFT_UBC ? UB_SOFT : UB_HARD)))
++ goto out_err;
++
++ if (inc_slab_charged(ub, cachep) < 0) {
++ ub_kmemsize_uncharge(ub, size);
++ goto out_err;
++ }
++ *ub_slab_ptr(cachep, objp) = ub;
++ return 0;
++
++out_err:
++ put_beancounter(ub);
++ return -ENOMEM;
++}
++
++/* called with IRQ disabled */
++void ub_slab_uncharge(struct kmem_cache *cachep, void *objp)
++{
++ unsigned int size;
++ struct user_beancounter **ub_ref;
++
++ ub_ref = ub_slab_ptr(cachep, objp);
++ if (*ub_ref == NULL)
++ return;
++
++ dec_slab_charged(*ub_ref, cachep);
++ size = CHARGE_SIZE(kmem_cache_objuse(cachep));
++ ub_kmemsize_uncharge(*ub_ref, size);
++ put_beancounter(*ub_ref);
++ *ub_ref = NULL;
++}
++
++/*
++ * Pages accounting
++ */
++
++int ub_page_charge(struct page *page, int order, gfp_t mask)
++{
++ struct user_beancounter *ub;
++ unsigned long flags;
++
++ ub = NULL;
++ if (!(mask & __GFP_UBC))
++ goto out;
++
++ ub = get_beancounter(get_exec_ub());
++ if (ub == NULL)
++ goto out;
++
++ local_irq_save(flags);
++ if (ub_kmemsize_charge(ub, CHARGE_ORDER(order),
++ (mask & __GFP_SOFT_UBC ? UB_SOFT : UB_HARD)))
++ goto err;
++
++ inc_pages_charged(ub, order);
++ local_irq_restore(flags);
++out:
++ BUG_ON(page_ub(page) != NULL);
++ page_ub(page) = ub;
++ return 0;
++
++err:
++ local_irq_restore(flags);
++ BUG_ON(page_ub(page) != NULL);
++ put_beancounter(ub);
++ return -ENOMEM;
++}
++
++void ub_page_uncharge(struct page *page, int order)
++{
++ struct user_beancounter *ub;
++ unsigned long flags;
++
++ ub = page_ub(page);
++ if (ub == NULL)
++ return;
++
++ BUG_ON(ub->ub_magic != UB_MAGIC);
++ dec_pages_charged(ub, order);
++ local_irq_save(flags);
++ ub_kmemsize_uncharge(ub, CHARGE_ORDER(order));
++ local_irq_restore(flags);
++ put_beancounter(ub);
++ page_ub(page) = NULL;
++}
++
++/*
++ * takes init_mm.page_table_lock
++ * some outer lock to protect pages from vmalloced area must be held
++ */
++struct user_beancounter *vmalloc_ub(void *obj)
++{
++ struct page *pg;
++
++ pg = vmalloc_to_page(obj);
++ if (pg == NULL)
++ return NULL;
++
++ return page_ub(pg);
++}
++
++EXPORT_SYMBOL(vmalloc_ub);
++
++struct user_beancounter *mem_ub(void *obj)
++{
++ struct user_beancounter *ub;
++
++ if ((unsigned long)obj >= VMALLOC_START &&
++ (unsigned long)obj < VMALLOC_END)
++ ub = vmalloc_ub(obj);
++ else
++ ub = slab_ub(obj);
++
++ return ub;
++}
++
++EXPORT_SYMBOL(mem_ub);
+diff --git a/kernel/bc/misc.c b/kernel/bc/misc.c
+new file mode 100644
+index 0000000..20c28a7
+--- /dev/null
++++ b/kernel/bc/misc.c
+@@ -0,0 +1,455 @@
++/*
++ * kernel/bc/misc.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/signal.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/sched.h>
++#include <linux/module.h>
++
++#include <bc/beancounter.h>
++#include <bc/kmem.h>
++#include <bc/proc.h>
++
++#define UB_FILE_MINQUANT 3
++#define UB_FILE_MAXQUANT 10
++#define UB_FILE_INIQUANT 4
++
++static unsigned long ub_file_precharge(struct task_beancounter *task_bc,
++ struct user_beancounter *ub, unsigned long *kmemsize);
++
++extern struct kmem_cache *filp_cachep;
++
++static inline unsigned long ub_file_kmemsize(unsigned long nr)
++{
++ return CHARGE_SIZE(kmem_cache_objuse(filp_cachep)) * nr;
++}
++
++/*
++ * Task staff
++ */
++
++static void init_task_sub(struct task_struct *parent,
++ struct task_struct *tsk,
++ struct task_beancounter *old_bc)
++{
++ struct task_beancounter *new_bc;
++ struct user_beancounter *sub;
++
++ new_bc = &tsk->task_bc;
++ sub = old_bc->fork_sub;
++ new_bc->fork_sub = get_beancounter(sub);
++ new_bc->task_fnode = NULL;
++ new_bc->task_freserv = old_bc->task_freserv;
++ old_bc->task_freserv = NULL;
++ memset(&new_bc->task_data, 0, sizeof(new_bc->task_data));
++ new_bc->pgfault_handle = 0;
++ new_bc->pgfault_allot = 0;
++}
++
++void ub_init_task_bc(struct task_beancounter *tbc)
++{
++ tbc->file_precharged = 0;
++ tbc->file_quant = UB_FILE_INIQUANT;
++ tbc->file_count = 0;
++
++ tbc->kmem_precharged = 0;
++ tbc->dentry_alloc = 0;
++}
++
++int ub_task_charge(struct task_struct *parent, struct task_struct *task)
++{
++ struct task_beancounter *old_bc;
++ struct task_beancounter *new_bc;
++ struct user_beancounter *ub, *pub;
++ unsigned long file_nr, kmemsize;
++ unsigned long flags;
++
++ old_bc = &parent->task_bc;
++ ub = old_bc->fork_sub;
++ new_bc = &task->task_bc;
++ new_bc->task_ub = get_beancounter(ub);
++ new_bc->exec_ub = get_beancounter(ub);
++
++ pub = top_beancounter(ub);
++ spin_lock_irqsave(&pub->ub_lock, flags);
++ if (unlikely(__charge_beancounter_locked(pub, UB_NUMPROC,
++ 1, UB_HARD) < 0))
++ goto out_numproc;
++
++ ub_init_task_bc(new_bc);
++ file_nr = ub_file_precharge(new_bc, pub, &kmemsize);
++ spin_unlock_irqrestore(&pub->ub_lock, flags);
++
++ charge_beancounter_notop(ub, UB_NUMPROC, 1);
++ if (likely(file_nr)) {
++ charge_beancounter_notop(ub, UB_NUMFILE, file_nr);
++ charge_beancounter_notop(ub, UB_KMEMSIZE, kmemsize);
++ }
++
++ init_task_sub(parent, task, old_bc);
++ return 0;
++
++out_numproc:
++ spin_unlock_irqrestore(&pub->ub_lock, flags);
++ __put_beancounter_batch(ub, 2);
++ return -ENOMEM;
++}
++
++extern atomic_t dbgpre;
++
++void ub_task_uncharge(struct task_struct *task)
++{
++ struct task_beancounter *task_bc;
++ struct user_beancounter *pub;
++ unsigned long file_nr, file_kmemsize;
++ unsigned long flags;
++
++ task_bc = &task->task_bc;
++ pub = top_beancounter(task_bc->task_ub);
++ spin_lock_irqsave(&pub->ub_lock, flags);
++ __uncharge_beancounter_locked(pub, UB_NUMPROC, 1);
++ file_nr = task_bc->file_precharged;
++ if (likely(file_nr))
++ __uncharge_beancounter_locked(pub,
++ UB_NUMFILE, file_nr);
++
++ /* see comment in ub_file_charge */
++ task_bc->file_precharged = 0;
++ file_kmemsize = ub_file_kmemsize(file_nr);
++ if (likely(file_kmemsize))
++ __uncharge_beancounter_locked(pub,
++ UB_KMEMSIZE, file_kmemsize);
++ spin_unlock_irqrestore(&pub->ub_lock, flags);
++
++ uncharge_beancounter_notop(task_bc->task_ub, UB_NUMPROC, 1);
++ if (likely(file_nr)) {
++ uncharge_beancounter_notop(task_bc->task_ub,
++ UB_NUMFILE, file_nr);
++ __put_beancounter_batch(task_bc->task_ub, file_nr);
++ }
++ if (likely(file_kmemsize))
++ uncharge_beancounter_notop(task_bc->task_ub,
++ UB_KMEMSIZE, file_kmemsize);
++}
++
++void ub_task_put(struct task_struct *task)
++{
++ struct task_beancounter *task_bc;
++ struct user_beancounter *pub;
++ unsigned long kmemsize, flags;
++
++ task_bc = &task->task_bc;
++
++ pub = top_beancounter(task_bc->task_ub);
++ spin_lock_irqsave(&pub->ub_lock, flags);
++ kmemsize = task_bc->kmem_precharged;
++ task_bc->kmem_precharged = 0;
++ if (likely(kmemsize))
++ __uncharge_beancounter_locked(pub, UB_KMEMSIZE, kmemsize);
++ spin_unlock_irqrestore(&pub->ub_lock, flags);
++ if (likely(kmemsize))
++ uncharge_beancounter_notop(task_bc->task_ub, UB_KMEMSIZE, kmemsize);
++
++ put_beancounter(task_bc->exec_ub);
++ put_beancounter(task_bc->task_ub);
++ put_beancounter(task_bc->fork_sub);
++ /* can't be freed elsewhere, failures possible in the middle of fork */
++ if (task_bc->task_freserv != NULL)
++ kfree(task_bc->task_freserv);
++
++ task_bc->exec_ub = (struct user_beancounter *)0xdeadbcbc;
++ task_bc->task_ub = (struct user_beancounter *)0xdead100c;
++ BUG_ON(task_bc->kmem_precharged != 0);
++}
++
++/*
++ * Files and file locks.
++ */
++/*
++ * For NUMFILE, we do not take a lock and call charge function
++ * for every file. We try to charge in batches, keeping local reserve on
++ * task. For experimental purposes, batch size is adaptive and depends
++ * on numfile barrier, number of processes, and the history of successes and
++ * failures of batch charges.
++ *
++ * Per-task fields have the following meaning
++ * file_precharged number of files charged to beancounter in advance,
++ * file_quant logarithm of batch size
++ * file_count counter of charge successes, to reduce batch size
++ * fluctuations.
++ */
++static unsigned long ub_file_precharge(struct task_beancounter *task_bc,
++ struct user_beancounter *ub, unsigned long *kmemsize)
++{
++ unsigned long n, kmem;
++
++ n = 1UL << task_bc->file_quant;
++ if (ub->ub_parms[UB_NUMPROC].held >
++ (ub->ub_parms[UB_NUMFILE].barrier >>
++ task_bc->file_quant))
++ goto nopre;
++ if (unlikely(__charge_beancounter_locked(ub, UB_NUMFILE, n, UB_HARD)))
++ goto nopre;
++ kmem = ub_file_kmemsize(n);
++ if (unlikely(__charge_beancounter_locked(ub, UB_KMEMSIZE,
++ kmem, UB_HARD)))
++ goto nopre_kmem;
++
++ task_bc->file_precharged += n;
++ get_beancounter_batch(task_bc->task_ub, n);
++ task_bc->file_count++;
++ if (task_bc->file_quant < UB_FILE_MAXQUANT &&
++ task_bc->file_count >= task_bc->file_quant) {
++ task_bc->file_quant++;
++ task_bc->file_count = 0;
++ }
++ *kmemsize = kmem;
++ return n;
++
++nopre_kmem:
++ __uncharge_beancounter_locked(ub, UB_NUMFILE, n);
++nopre:
++ if (task_bc->file_quant > UB_FILE_MINQUANT)
++ task_bc->file_quant--;
++ task_bc->file_count = 0;
++ return 0;
++}
++
++int ub_file_charge(struct file *f)
++{
++ struct user_beancounter *ub, *pub;
++ struct task_beancounter *task_bc;
++ unsigned long file_nr, kmem;
++ unsigned long flags;
++ int err;
++
++ task_bc = &current->task_bc;
++ ub = get_exec_ub();
++ if (unlikely(ub != task_bc->task_ub))
++ goto just_charge;
++
++ if (likely(task_bc->file_precharged > 0)) {
++ /*
++ * files are put via RCU in 2.6.16 so during
++ * this decrement an IRQ can happen and called
++ * ub_files_uncharge() will mess file_precharged
++ *
++ * ub_task_uncharge() is called via RCU also so no
++ * protection is needed there
++ *
++ * Xemul
++ */
++
++ local_irq_save(flags);
++ task_bc->file_precharged--;
++ local_irq_restore(flags);
++
++ f->f_ub = ub;
++ return 0;
++ }
++
++ pub = top_beancounter(ub);
++ spin_lock_irqsave(&pub->ub_lock, flags);
++ file_nr = ub_file_precharge(task_bc, pub, &kmem);
++ if (unlikely(!file_nr))
++ goto last_try;
++ spin_unlock(&pub->ub_lock);
++ task_bc->file_precharged--;
++ local_irq_restore(flags);
++
++ charge_beancounter_notop(ub, UB_NUMFILE, file_nr);
++ charge_beancounter_notop(ub, UB_KMEMSIZE, kmem);
++ f->f_ub = ub;
++ return 0;
++
++just_charge:
++ pub = top_beancounter(ub);
++ spin_lock_irqsave(&pub->ub_lock, flags);
++last_try:
++ kmem = ub_file_kmemsize(1);
++ err = __charge_beancounter_locked(pub, UB_NUMFILE, 1, UB_HARD);
++ if (likely(!err)) {
++ err = __charge_beancounter_locked(pub, UB_KMEMSIZE,
++ kmem, UB_HARD);
++ if (unlikely(err))
++ __uncharge_beancounter_locked(pub, UB_NUMFILE, 1);
++ }
++ spin_unlock_irqrestore(&pub->ub_lock, flags);
++ if (likely(!err)) {
++ charge_beancounter_notop(ub, UB_NUMFILE, 1);
++ charge_beancounter_notop(ub, UB_KMEMSIZE, kmem);
++ f->f_ub = get_beancounter(ub);
++ }
++ return err;
++}
++
++void ub_file_uncharge(struct file *f)
++{
++ struct user_beancounter *ub, *pub;
++ struct task_beancounter *task_bc;
++ unsigned long nr;
++
++ ub = f->f_ub;
++ task_bc = &current->task_bc;
++ if (likely(ub == task_bc->task_ub)) {
++ task_bc->file_precharged++;
++ pub = top_beancounter(ub);
++ if (ub_barrier_farnr(pub, UB_NUMFILE) &&
++ ub_barrier_farsz(pub, UB_KMEMSIZE))
++ return;
++ if (task_bc->file_precharged < (1UL << task_bc->file_quant))
++ return;
++ nr = task_bc->file_precharged
++ - (1UL << (task_bc->file_quant - 1));
++ task_bc->file_precharged -= nr;
++ __put_beancounter_batch(ub, nr);
++ uncharge_beancounter(ub, UB_NUMFILE, nr);
++ uncharge_beancounter(ub, UB_KMEMSIZE, ub_file_kmemsize(nr));
++ } else {
++ uncharge_beancounter(ub, UB_NUMFILE, 1);
++ uncharge_beancounter(ub, UB_KMEMSIZE, ub_file_kmemsize(1));
++ put_beancounter(ub);
++ }
++}
++
++int ub_flock_charge(struct file_lock *fl, int hard)
++{
++ struct user_beancounter *ub;
++ int err;
++
++ /* No need to get_beancounter here since it's already got in slab */
++ ub = slab_ub(fl);
++ if (ub == NULL)
++ return 0;
++
++ err = charge_beancounter(ub, UB_NUMFLOCK, 1, hard ? UB_HARD : UB_SOFT);
++ if (!err)
++ fl->fl_charged = 1;
++ return err;
++}
++
++void ub_flock_uncharge(struct file_lock *fl)
++{
++ struct user_beancounter *ub;
++
++ /* Ub will be put in slab */
++ ub = slab_ub(fl);
++ if (ub == NULL || !fl->fl_charged)
++ return;
++
++ uncharge_beancounter(ub, UB_NUMFLOCK, 1);
++ fl->fl_charged = 0;
++}
++
++/*
++ * Signal handling
++ */
++
++static int do_ub_siginfo_charge(struct user_beancounter *ub,
++ unsigned long size)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (__charge_beancounter_locked(ub, UB_KMEMSIZE, size, UB_HARD))
++ goto out_kmem;
++
++ if (__charge_beancounter_locked(ub, UB_NUMSIGINFO, 1, UB_HARD))
++ goto out_num;
++
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return 0;
++
++out_num:
++ __uncharge_beancounter_locked(ub, UB_KMEMSIZE, size);
++out_kmem:
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return -ENOMEM;
++}
++
++static void do_ub_siginfo_uncharge(struct user_beancounter *ub,
++ unsigned long size)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __uncharge_beancounter_locked(ub, UB_KMEMSIZE, size);
++ __uncharge_beancounter_locked(ub, UB_NUMSIGINFO, 1);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++int ub_siginfo_charge(struct sigqueue *sq, struct user_beancounter *ub)
++{
++ unsigned long size;
++ struct user_beancounter *p, *q;
++
++ size = CHARGE_SIZE(kmem_obj_objuse(sq));
++ for (p = ub; p != NULL; p = p->parent) {
++ if (do_ub_siginfo_charge(p, size))
++ goto unroll;
++ }
++
++ sq->sig_ub = get_beancounter(ub);
++ return 0;
++
++unroll:
++ for (q = ub; q != p; q = q->parent)
++ do_ub_siginfo_uncharge(q, size);
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(ub_siginfo_charge);
++
++void ub_siginfo_uncharge(struct sigqueue *sq)
++{
++ unsigned long size;
++ struct user_beancounter *ub, *p;
++
++ p = ub = sq->sig_ub;
++ sq->sig_ub = NULL;
++ size = CHARGE_SIZE(kmem_obj_objuse(sq));
++ for (; ub != NULL; ub = ub->parent)
++ do_ub_siginfo_uncharge(ub, size);
++ put_beancounter(p);
++}
++
++/*
++ * PTYs
++ */
++
++int ub_pty_charge(struct tty_struct *tty)
++{
++ struct user_beancounter *ub;
++ int retval;
++
++ ub = slab_ub(tty);
++ retval = 0;
++ if (ub && tty->driver->subtype == PTY_TYPE_MASTER &&
++ !test_bit(TTY_CHARGED, &tty->flags)) {
++ retval = charge_beancounter(ub, UB_NUMPTY, 1, UB_HARD);
++ if (!retval)
++ set_bit(TTY_CHARGED, &tty->flags);
++ }
++ return retval;
++}
++
++void ub_pty_uncharge(struct tty_struct *tty)
++{
++ struct user_beancounter *ub;
++
++ ub = slab_ub(tty);
++ if (ub && tty->driver->subtype == PTY_TYPE_MASTER &&
++ test_bit(TTY_CHARGED, &tty->flags)) {
++ uncharge_beancounter(ub, UB_NUMPTY, 1);
++ clear_bit(TTY_CHARGED, &tty->flags);
++ }
++}
+diff --git a/kernel/bc/net.c b/kernel/bc/net.c
+new file mode 100644
+index 0000000..ace2cb6
+--- /dev/null
++++ b/kernel/bc/net.c
+@@ -0,0 +1,1152 @@
++/*
++ * linux/kernel/bc/net.c
++ *
++ * Copyright (C) 1998-2004 Andrey V. Savochkin <saw@saw.sw.com.sg>
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ * TODO:
++ * - sizeof(struct inode) charge
++ * = tcp_mem_schedule() feedback based on ub limits
++ * + measures so that one socket won't exhaust all send buffers,
++ * see bug in bugzilla
++ * = sk->socket check for NULL in snd_wakeups
++ * (tcp_write_space checks for NULL itself)
++ * + in tcp_close(), orphaned socket abortion should be based on ubc
++ * resources (same in tcp_out_of_resources)
++ * Beancounter should also have separate orphaned socket counter...
++ * + for rcv, in-order segment should be accepted
++ * if only barrier is exceeded
++ * = tcp_rmem_schedule() feedback based on ub limits
++ * - repair forward_alloc mechanism for receive buffers
++ * It's idea is that some buffer space is pre-charged so that receive fast
++ * path doesn't need to take spinlocks and do other heavy stuff
++ * + tcp_prune_queue actions based on ub limits
++ * + window adjustments depending on available buffers for receive
++ * - window adjustments depending on available buffers for send
++ * + race around usewreserv
++ * + avoid allocating new page for each tiny-gram, see letter from ANK
++ * + rename ub_sock_lock
++ * + sk->sleep wait queue probably can be used for all wakeups, and
++ * sk->ub_wait is unnecessary
++ * + for UNIX sockets, the current algorithm will lead to
++ * UB_UNIX_MINBUF-sized messages only for non-blocking case
++ * - charge for af_packet sockets
++ * + all datagram sockets should be charged to NUMUNIXSOCK
++ * - we do not charge for skb copies and clones staying in device queues
++ * + live-lock if number of sockets is big and buffer limits are small
++ * [diff-ubc-dbllim3]
++ * - check that multiple readers/writers on the same socket won't cause fatal
++ * consequences
++ * - check allocation/charge orders
++ * + There is potential problem with callback_lock. In *snd_wakeup we take
++ * beancounter first, in sock_def_error_report - callback_lock first.
++ * then beancounter. This is not a problem if callback_lock taken
++ * readonly, but anyway...
++ * - SKB_CHARGE_SIZE doesn't include the space wasted by slab allocator
++ * General kernel problems:
++ * - in tcp_sendmsg(), if allocation fails, non-blocking sockets with ASYNC
++ * notification won't get signals
++ * - datagram_poll looks racy
++ *
++ */
++
++#include <linux/net.h>
++#include <linux/slab.h>
++#include <linux/gfp.h>
++#include <linux/err.h>
++#include <linux/socket.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++
++#include <net/sock.h>
++#include <net/tcp.h>
++
++#include <bc/beancounter.h>
++#include <bc/net.h>
++#include <bc/debug.h>
++
++/* by some reason it is not used currently */
++#define UB_SOCK_MAINTAIN_WMEMPRESSURE 0
++
++
++/* Skb truesize definition. Bad place. Den */
++
++static inline int skb_chargesize_head(struct sk_buff *skb)
++{
++ return skb_charge_size(skb_end_pointer(skb) - skb->head +
++ sizeof(struct skb_shared_info));
++}
++
++int skb_charge_fullsize(struct sk_buff *skb)
++{
++ int chargesize;
++ struct sk_buff *skbfrag;
++
++ chargesize = skb_chargesize_head(skb) +
++ PAGE_SIZE * skb_shinfo(skb)->nr_frags;
++ if (likely(skb_shinfo(skb)->frag_list == NULL))
++ return chargesize;
++ for (skbfrag = skb_shinfo(skb)->frag_list;
++ skbfrag != NULL;
++ skbfrag = skbfrag->next) {
++ chargesize += skb_charge_fullsize(skbfrag);
++ }
++ return chargesize;
++}
++EXPORT_SYMBOL(skb_charge_fullsize);
++
++static int ub_sock_makewreserv_locked(struct sock *sk,
++ int bufid, unsigned long size);
++
++int __ub_too_many_orphans(struct sock *sk, int count)
++{
++ struct user_beancounter *ub;
++
++ if (sock_has_ubc(sk)) {
++ ub = top_beancounter(sock_bc(sk)->ub);
++ if (count >= ub->ub_parms[UB_NUMTCPSOCK].barrier >> 2)
++ return 1;
++ }
++ return 0;
++}
++
++/*
++ * Queueing
++ */
++
++static void ub_sock_snd_wakeup(struct user_beancounter *ub)
++{
++ struct list_head *p;
++ struct sock *sk;
++ struct sock_beancounter *skbc;
++ struct socket *sock;
++ unsigned long added;
++
++ while (!list_empty(&ub->ub_other_sk_list)) {
++ p = ub->ub_other_sk_list.next;
++ skbc = list_entry(p, struct sock_beancounter, ub_sock_list);
++ sk = skbc_sock(skbc);
++
++ added = 0;
++ sock = sk->sk_socket;
++ if (sock == NULL) {
++ /* sk being destroyed */
++ list_del_init(&skbc->ub_sock_list);
++ continue;
++ }
++
++ ub_debug(UBD_NET_SLEEP,
++ "Checking queue, waiting %lu, reserv %lu\n",
++ skbc->ub_waitspc, skbc->poll_reserv);
++ added = -skbc->poll_reserv;
++ if (ub_sock_makewreserv_locked(sk, UB_OTHERSOCKBUF,
++ skbc->ub_waitspc))
++ break;
++ added += skbc->poll_reserv;
++
++ list_del_init(&skbc->ub_sock_list);
++
++ /*
++ * See comments in ub_tcp_snd_wakeup.
++ * Locking note: both unix_write_space and
++ * sock_def_write_space take callback_lock themselves.
++ * We take it here just to be on the safe side and to
++ * act the same way as ub_tcp_snd_wakeup does.
++ */
++ sock_hold(sk);
++ read_lock(&sk->sk_callback_lock);
++ spin_unlock(&ub->ub_lock);
++
++ sk->sk_write_space(sk);
++ read_unlock(&sk->sk_callback_lock);
++
++ if (skbc->ub != ub && added)
++ charge_beancounter_notop(skbc->ub,
++ UB_OTHERSOCKBUF, added);
++ sock_put(sk);
++
++ spin_lock(&ub->ub_lock);
++ }
++}
++
++static void ub_tcp_snd_wakeup(struct user_beancounter *ub)
++{
++ struct list_head *p;
++ struct sock *sk;
++ struct sock_beancounter *skbc;
++ struct socket *sock;
++ unsigned long added;
++
++ while (!list_empty(&ub->ub_tcp_sk_list)) {
++ p = ub->ub_tcp_sk_list.next;
++ skbc = list_entry(p, struct sock_beancounter, ub_sock_list);
++ sk = skbc_sock(skbc);
++
++ added = 0;
++ sock = sk->sk_socket;
++ if (sock == NULL) {
++ /* sk being destroyed */
++ list_del_init(&skbc->ub_sock_list);
++ continue;
++ }
++
++ ub_debug(UBD_NET_SLEEP,
++ "Checking queue, waiting %lu, reserv %lu\n",
++ skbc->ub_waitspc, skbc->poll_reserv);
++ added = -skbc->poll_reserv;
++ if (ub_sock_makewreserv_locked(sk, UB_TCPSNDBUF,
++ skbc->ub_waitspc))
++ break;
++ added += skbc->poll_reserv;
++
++ list_del_init(&skbc->ub_sock_list);
++
++ /*
++ * Send async notifications and wake up.
++ * Locking note: we get callback_lock here because
++ * tcp_write_space is over-optimistic about calling context
++ * (socket lock is presumed). So we get the lock here although
++ * it belongs to the callback.
++ */
++ sock_hold(sk);
++ read_lock(&sk->sk_callback_lock);
++ spin_unlock(&ub->ub_lock);
++
++ sk->sk_write_space(sk);
++ read_unlock(&sk->sk_callback_lock);
++
++ if (skbc->ub != ub && added)
++ charge_beancounter_notop(skbc->ub, UB_TCPSNDBUF, added);
++ sock_put(sk);
++
++ spin_lock(&ub->ub_lock);
++ }
++}
++
++void ub_sock_snd_queue_add(struct sock *sk, int res, unsigned long size)
++{
++ unsigned long flags;
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long added_reserv;
++
++ if (!sock_has_ubc(sk))
++ return;
++
++ skbc = sock_bc(sk);
++ ub = top_beancounter(skbc->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub_debug(UBD_NET_SLEEP, "attempt to charge for %lu\n", size);
++ added_reserv = -skbc->poll_reserv;
++ if (!ub_sock_makewreserv_locked(sk, res, size)) {
++ /*
++ * It looks a bit hackish, but it is compatible with both
++ * wait_for_xx_ubspace and poll.
++ * This __set_current_state is equivalent to a wakeup event
++ * right after spin_unlock_irqrestore.
++ */
++ __set_current_state(TASK_RUNNING);
++ added_reserv += skbc->poll_reserv;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ if (added_reserv)
++ charge_beancounter_notop(skbc->ub, res, added_reserv);
++ return;
++ }
++
++ ub_debug(UBD_NET_SLEEP, "Adding sk to queue\n");
++ skbc->ub_waitspc = size;
++ if (!list_empty(&skbc->ub_sock_list)) {
++ ub_debug(UBD_NET_SOCKET,
++ "re-adding socket to beancounter %p.\n", ub);
++ goto out;
++ }
++
++ switch (res) {
++ case UB_TCPSNDBUF:
++ list_add_tail(&skbc->ub_sock_list,
++ &ub->ub_tcp_sk_list);
++ break;
++ case UB_OTHERSOCKBUF:
++ list_add_tail(&skbc->ub_sock_list,
++ &ub->ub_other_sk_list);
++ break;
++ default:
++ BUG();
++ }
++out:
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++EXPORT_SYMBOL(ub_sock_snd_queue_add);
++
++long ub_sock_wait_for_space(struct sock *sk, long timeo, unsigned long size)
++{
++ DECLARE_WAITQUEUE(wait, current);
++
++ add_wait_queue(sk->sk_sleep, &wait);
++ for (;;) {
++ if (signal_pending(current))
++ break;
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!ub_sock_make_wreserv(sk, UB_OTHERSOCKBUF, size))
++ break;
++
++ if (sk->sk_shutdown & SEND_SHUTDOWN)
++ break;
++ if (sk->sk_err)
++ break;
++ ub_sock_snd_queue_add(sk, UB_OTHERSOCKBUF, size);
++ timeo = schedule_timeout(timeo);
++ }
++ __set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sk_sleep, &wait);
++ return timeo;
++}
++
++void ub_sock_sndqueuedel(struct sock *sk)
++{
++ struct user_beancounter *ub;
++ struct sock_beancounter *skbc;
++ unsigned long flags;
++
++ if (!sock_has_ubc(sk))
++ return;
++ skbc = sock_bc(sk);
++
++ /* race with write_space callback of other socket */
++ ub = top_beancounter(skbc->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ list_del_init(&skbc->ub_sock_list);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++/*
++ * Helpers
++ */
++
++static inline void __ub_skb_set_charge(struct sk_buff *skb, struct sock *sk,
++ unsigned long size, int resource)
++{
++ WARN_ON_ONCE(skb_bc(skb)->ub != NULL);
++
++ skb_bc(skb)->ub = sock_bc(sk)->ub;
++ skb_bc(skb)->charged = size;
++ skb_bc(skb)->resource = resource;
++}
++
++void ub_skb_set_charge(struct sk_buff *skb, struct sock *sk,
++ unsigned long size, int resource)
++{
++ if (!sock_has_ubc(sk))
++ return;
++
++ if (sock_bc(sk)->ub == NULL)
++ BUG();
++
++ __ub_skb_set_charge(skb, sk, size, resource);
++
++ /* Ugly. Ugly. Skb in sk writequeue can live without ref to sk */
++ if (skb->sk == NULL)
++ skb->sk = sk;
++}
++
++EXPORT_SYMBOL(ub_skb_set_charge);
++
++static inline void ub_skb_set_uncharge(struct sk_buff *skb)
++{
++ skb_bc(skb)->ub = NULL;
++ skb_bc(skb)->charged = 0;
++ skb_bc(skb)->resource = 0;
++}
++
++static void ub_update_rmem_thres(struct sock_beancounter *skub)
++{
++ struct user_beancounter *ub;
++
++ if (skub && skub->ub) {
++ ub = top_beancounter(skub->ub);
++ ub->ub_rmem_thres = ub->ub_parms[UB_TCPRCVBUF].barrier /
++ (ub->ub_parms[UB_NUMTCPSOCK].held + 1);
++ }
++}
++
++static inline void ub_sock_wcharge_dec(struct sock *sk,
++ unsigned long chargesize)
++{
++ /* The check sk->sk_family != PF_NETLINK is made as the skb is
++ * queued to the kernel end of socket while changed to the user one.
++ * Den */
++ if (unlikely(sock_bc(sk)->ub_wcharged) && sk->sk_family != PF_NETLINK) {
++ if (sock_bc(sk)->ub_wcharged > chargesize)
++ sock_bc(sk)->ub_wcharged -= chargesize;
++ else
++ sock_bc(sk)->ub_wcharged = 0;
++ }
++}
++
++/*
++ * Charge socket number
++ */
++
++static inline void sk_alloc_beancounter(struct sock *sk)
++{
++ struct sock_beancounter *skbc;
++
++ skbc = sock_bc(sk);
++ memset(skbc, 0, sizeof(struct sock_beancounter));
++}
++
++static inline void sk_free_beancounter(struct sock *sk)
++{
++}
++
++static int __sock_charge(struct sock *sk, int res)
++{
++ struct sock_beancounter *skbc;
++ struct user_beancounter *cub, *ub;
++ unsigned long added_reserv, added_forw;
++ unsigned long flags;
++
++ cub = get_exec_ub();
++ if (unlikely(cub == NULL))
++ return 0;
++
++ sk_alloc_beancounter(sk);
++ skbc = sock_bc(sk);
++ INIT_LIST_HEAD(&skbc->ub_sock_list);
++
++ ub = top_beancounter(cub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (unlikely(__charge_beancounter_locked(ub, res, 1, UB_HARD) < 0))
++ goto out_limit;
++
++ added_reserv = 0;
++ added_forw = 0;
++ if (res == UB_NUMTCPSOCK) {
++ added_reserv = skb_charge_size(MAX_TCP_HEADER +
++ 1500 - sizeof(struct iphdr) -
++ sizeof(struct tcphdr));
++ added_reserv *= 4;
++ ub->ub_parms[UB_TCPSNDBUF].held += added_reserv;
++ if (!ub_barrier_farsz(ub, UB_TCPSNDBUF)) {
++ ub->ub_parms[UB_TCPSNDBUF].held -= added_reserv;
++ added_reserv = 0;
++ }
++ skbc->poll_reserv = added_reserv;
++
++ added_forw = SK_MEM_QUANTUM * 4;
++ ub->ub_parms[UB_TCPRCVBUF].held += added_forw;
++ if (!ub_barrier_farsz(ub, UB_TCPRCVBUF)) {
++ ub->ub_parms[UB_TCPRCVBUF].held -= added_forw;
++ added_forw = 0;
++ }
++ skbc->forw_space = added_forw;
++ }
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ charge_beancounter_notop(cub, res, 1);
++ if (added_reserv)
++ charge_beancounter_notop(cub, UB_TCPSNDBUF, added_reserv);
++ if (added_forw)
++ charge_beancounter_notop(cub, UB_TCPRCVBUF, added_forw);
++
++ skbc->ub = get_beancounter(cub);
++ return 0;
++
++out_limit:
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ sk_free_beancounter(sk);
++ return -ENOMEM;
++}
++
++int ub_tcp_sock_charge(struct sock *sk)
++{
++ int ret;
++
++ ret = __sock_charge(sk, UB_NUMTCPSOCK);
++ ub_update_rmem_thres(sock_bc(sk));
++
++ return ret;
++}
++
++int ub_other_sock_charge(struct sock *sk)
++{
++ return __sock_charge(sk, UB_NUMOTHERSOCK);
++}
++
++EXPORT_SYMBOL(ub_other_sock_charge);
++
++int ub_sock_charge(struct sock *sk, int family, int type)
++{
++ return (IS_TCP_SOCK(family, type) ?
++ ub_tcp_sock_charge(sk) : ub_other_sock_charge(sk));
++}
++
++EXPORT_SYMBOL(ub_sock_charge);
++
++/*
++ * Uncharge socket number
++ */
++
++void ub_sock_uncharge(struct sock *sk)
++{
++ int is_tcp_sock;
++ unsigned long flags;
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long reserv, forw;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return;
++
++ is_tcp_sock = IS_TCP_SOCK(sk->sk_family, sk->sk_type);
++ skbc = sock_bc(sk);
++ ub_debug(UBD_NET_SOCKET, "Calling ub_sock_uncharge on %p\n", sk);
++
++ ub = top_beancounter(skbc->ub);
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (!list_empty(&skbc->ub_sock_list)) {
++ ub_debug(UBD_NET_SOCKET,
++ "ub_sock_uncharge: removing from ub(%p) queue.\n",
++ skbc);
++ list_del_init(&skbc->ub_sock_list);
++ }
++
++ reserv = skbc->poll_reserv;
++ forw = skbc->forw_space;
++ __uncharge_beancounter_locked(ub,
++ (is_tcp_sock ? UB_TCPSNDBUF : UB_OTHERSOCKBUF),
++ reserv);
++ if (forw)
++ __uncharge_beancounter_locked(ub,
++ (is_tcp_sock ? UB_TCPRCVBUF : UB_DGRAMRCVBUF),
++ forw);
++ __uncharge_beancounter_locked(ub,
++ (is_tcp_sock ? UB_NUMTCPSOCK : UB_NUMOTHERSOCK), 1);
++
++ ub_sock_wcharge_dec(sk, reserv);
++ if (unlikely(skbc->ub_wcharged))
++ printk(KERN_WARNING
++ "ub_sock_uncharge: wch=%lu for ub %p (%d).\n",
++ skbc->ub_wcharged, skbc->ub, skbc->ub->ub_uid);
++ skbc->poll_reserv = 0;
++ skbc->forw_space = 0;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ uncharge_beancounter_notop(skbc->ub,
++ (is_tcp_sock ? UB_TCPSNDBUF : UB_OTHERSOCKBUF),
++ reserv);
++ if (forw)
++ uncharge_beancounter_notop(skbc->ub,
++ (is_tcp_sock ? UB_TCPRCVBUF : UB_DGRAMRCVBUF),
++ forw);
++ uncharge_beancounter_notop(skbc->ub,
++ (is_tcp_sock ? UB_NUMTCPSOCK : UB_NUMOTHERSOCK), 1);
++
++ put_beancounter(skbc->ub);
++ sk_free_beancounter(sk);
++}
++
++/*
++ * Special case for netlink_dump - (un)charges precalculated size
++ */
++
++int ub_nlrcvbuf_charge(struct sk_buff *skb, struct sock *sk)
++{
++ int ret;
++ unsigned long chargesize;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ chargesize = skb_charge_fullsize(skb);
++ ret = charge_beancounter(sock_bc(sk)->ub,
++ UB_OTHERSOCKBUF, chargesize, UB_HARD);
++ if (ret < 0)
++ return ret;
++ ub_skb_set_charge(skb, sk, chargesize, UB_OTHERSOCKBUF);
++ return ret;
++}
++
++/*
++ * Poll reserve accounting
++ *
++ * This is the core of socket buffer management (along with queueing/wakeup
++ * functions. The rest of buffer accounting either call these functions, or
++ * repeat parts of their logic for some simpler cases.
++ */
++
++static int ub_sock_makewreserv_locked(struct sock *sk,
++ int bufid, unsigned long size)
++{
++ unsigned long wcharge_added;
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++
++ skbc = sock_bc(sk);
++ if (skbc->poll_reserv >= size) /* no work to be done */
++ goto out;
++
++ ub = top_beancounter(skbc->ub);
++ ub->ub_parms[bufid].held += size - skbc->poll_reserv;
++
++ wcharge_added = 0;
++ /*
++ * Logic:
++ * 1) when used memory hits barrier, we set wmem_pressure;
++ * wmem_pressure is reset under barrier/2;
++ * between barrier/2 and barrier we limit per-socket buffer growth;
++ * 2) each socket is guaranteed to get (limit-barrier)/maxsockets
++ * calculated on the base of memory eaten after the barrier is hit
++ */
++ skbc = sock_bc(sk);
++#if UB_SOCK_MAINTAIN_WMEMPRESSURE
++ if (!ub_hfbarrier_hit(ub, bufid)) {
++ if (ub->ub_wmem_pressure)
++ ub_debug(UBD_NET_SEND, "makewres: pressure -> 0 "
++ "sk %p sz %lu pr %lu hd %lu wc %lu sb %d.\n",
++ sk, size, skbc->poll_reserv,
++ ub->ub_parms[bufid].held,
++ skbc->ub_wcharged, sk->sk_sndbuf);
++ ub->ub_wmem_pressure = 0;
++ }
++#endif
++ if (ub_barrier_hit(ub, bufid)) {
++#if UB_SOCK_MAINTAIN_WMEMPRESSURE
++ if (!ub->ub_wmem_pressure)
++ ub_debug(UBD_NET_SEND, "makewres: pressure -> 1 "
++ "sk %p sz %lu pr %lu hd %lu wc %lu sb %d.\n",
++ sk, size, skbc->poll_reserv,
++ ub->ub_parms[bufid].held,
++ skbc->ub_wcharged, sk->sk_sndbuf);
++ ub->ub_wmem_pressure = 1;
++#endif
++ if (sk->sk_family == PF_NETLINK)
++ goto unroll;
++ wcharge_added = size - skbc->poll_reserv;
++ skbc->ub_wcharged += wcharge_added;
++ if (skbc->ub_wcharged * ub->ub_parms[bid2sid(bufid)].limit +
++ ub->ub_parms[bufid].barrier >
++ ub->ub_parms[bufid].limit)
++ goto unroll_wch;
++ }
++ if (ub->ub_parms[bufid].held > ub->ub_parms[bufid].limit)
++ goto unroll;
++
++ ub_adjust_maxheld(ub, bufid);
++ skbc->poll_reserv = size;
++out:
++ return 0;
++
++unroll_wch:
++ skbc->ub_wcharged -= wcharge_added;
++unroll:
++ ub_debug(UBD_NET_SEND,
++ "makewres: deny "
++ "sk %p sz %lu pr %lu hd %lu wc %lu sb %d.\n",
++ sk, size, skbc->poll_reserv, ub->ub_parms[bufid].held,
++ skbc->ub_wcharged, sk->sk_sndbuf);
++ ub->ub_parms[bufid].failcnt++;
++ ub->ub_parms[bufid].held -= size - skbc->poll_reserv;
++
++ if (sk->sk_socket != NULL) {
++ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
++ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
++ }
++ return -ENOMEM;
++}
++
++int ub_sock_make_wreserv(struct sock *sk, int bufid, unsigned long size)
++{
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long flags;
++ unsigned long added_reserv;
++ int err;
++
++ skbc = sock_bc(sk);
++
++ /*
++ * This function provides that there is sufficient reserve upon return
++ * only if sk has only one user. We can check poll_reserv without
++ * serialization and avoid locking if the reserve already exists.
++ */
++ if (unlikely(!sock_has_ubc(sk)) || likely(skbc->poll_reserv >= size))
++ return 0;
++
++ ub = top_beancounter(skbc->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ added_reserv = -skbc->poll_reserv;
++ err = ub_sock_makewreserv_locked(sk, bufid, size);
++ added_reserv += skbc->poll_reserv;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ if (added_reserv)
++ charge_beancounter_notop(skbc->ub, bufid, added_reserv);
++
++ return err;
++}
++
++EXPORT_SYMBOL(ub_sock_make_wreserv);
++
++int ub_sock_get_wreserv(struct sock *sk, int bufid, unsigned long size)
++{
++ struct sock_beancounter *skbc;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ /* optimize for the case if socket has sufficient reserve */
++ ub_sock_make_wreserv(sk, bufid, size);
++ skbc = sock_bc(sk);
++ if (likely(skbc->poll_reserv >= size)) {
++ skbc->poll_reserv -= size;
++ return 0;
++ }
++ return -ENOMEM;
++}
++
++EXPORT_SYMBOL(ub_sock_get_wreserv);
++
++static void ub_sock_do_ret_wreserv(struct sock *sk, int bufid,
++ unsigned long size, unsigned long ressize)
++{
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long extra;
++ unsigned long flags;
++
++ skbc = sock_bc(sk);
++ ub = top_beancounter(skbc->ub);
++
++ extra = 0;
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ skbc->poll_reserv += size;
++ if (skbc->poll_reserv > ressize) {
++ extra = skbc->poll_reserv - ressize;
++ ub_sock_wcharge_dec(sk, extra);
++ skbc->poll_reserv = ressize;
++
++ __uncharge_beancounter_locked(ub, bufid, extra);
++ if (bufid == UB_TCPSNDBUF)
++ ub_tcp_snd_wakeup(ub);
++ else
++ ub_sock_snd_wakeup(ub);
++ }
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ if (extra)
++ uncharge_beancounter_notop(skbc->ub, bufid, extra);
++}
++
++void ub_sock_ret_wreserv(struct sock *sk, int bufid,
++ unsigned long size, unsigned long ressize)
++{
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return;
++
++ skbc = sock_bc(sk);
++ ub = top_beancounter(skbc->ub);
++ /* check if the reserve can be kept */
++ if (ub_barrier_farsz(ub, bufid)) {
++ skbc->poll_reserv += size;
++ return;
++ }
++ ub_sock_do_ret_wreserv(sk, bufid, size, ressize);
++}
++
++/*
++ * UB_DGRAMRCVBUF
++ */
++
++static int ub_dgramrcvbuf_charge(struct sock *sk, struct sk_buff *skb)
++{
++ unsigned long chargesize;
++
++ chargesize = skb_charge_fullsize(skb);
++ if (charge_beancounter(sock_bc(sk)->ub, UB_DGRAMRCVBUF,
++ chargesize, UB_HARD))
++ return -ENOMEM;
++
++ ub_skb_set_charge(skb, sk, chargesize, UB_DGRAMRCVBUF);
++ return 0;
++}
++
++int ub_sockrcvbuf_charge(struct sock *sk, struct sk_buff *skb)
++{
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ if (IS_TCP_SOCK(sk->sk_family, sk->sk_type))
++ return ub_tcprcvbuf_charge(sk, skb);
++ else
++ return ub_dgramrcvbuf_charge(sk, skb);
++}
++
++EXPORT_SYMBOL(ub_sockrcvbuf_charge);
++
++static void ub_sockrcvbuf_uncharge(struct sk_buff *skb)
++{
++ uncharge_beancounter(skb_bc(skb)->ub, UB_DGRAMRCVBUF,
++ skb_bc(skb)->charged);
++ ub_skb_set_uncharge(skb);
++}
++
++/*
++ * UB_TCPRCVBUF
++ */
++
++int ub_sock_tcp_chargerecv(struct sock *sk, struct sk_buff *skb,
++ enum ub_severity strict)
++{
++ int retval;
++ unsigned long flags;
++ struct user_beancounter *ub;
++ struct sock_beancounter *skbc;
++ unsigned long chargesize;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++ skbc = sock_bc(sk);
++
++ chargesize = skb_charge_fullsize(skb);
++ if (likely(skbc->forw_space >= chargesize)) {
++ skbc->forw_space -= chargesize;
++ __ub_skb_set_charge(skb, sk, chargesize, UB_TCPRCVBUF);
++ return 0;
++ }
++
++ /*
++ * Memory pressure reactions:
++ * 1) set UB_RMEM_KEEP (clearing UB_RMEM_EXPAND)
++ * 2) set UB_RMEM_SHRINK and tcp_clamp_window()
++ * tcp_collapse_queues() if rmem_alloc > rcvbuf
++ * 3) drop OFO, tcp_purge_ofo()
++ * 4) drop all.
++ * Currently, we do #2 and #3 at once (which means that current
++ * collapsing of OFO queue in tcp_collapse_queues() is a waste of time,
++ * for example...)
++ * On memory pressure we jump from #0 to #3, and when the pressure
++ * subsides, to #1.
++ */
++ retval = 0;
++ ub = top_beancounter(sock_bc(sk)->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_parms[UB_TCPRCVBUF].held += chargesize;
++ if (ub->ub_parms[UB_TCPRCVBUF].held >
++ ub->ub_parms[UB_TCPRCVBUF].barrier &&
++ strict != UB_FORCE)
++ goto excess;
++ ub_adjust_maxheld(ub, UB_TCPRCVBUF);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++out:
++ if (retval == 0) {
++ charge_beancounter_notop(sock_bc(sk)->ub, UB_TCPRCVBUF,
++ chargesize);
++ ub_skb_set_charge(skb, sk, chargesize, UB_TCPRCVBUF);
++ }
++ return retval;
++
++excess:
++ ub->ub_rmem_pressure = UB_RMEM_SHRINK;
++ if (strict == UB_HARD)
++ retval = -ENOMEM;
++ if (ub->ub_parms[UB_TCPRCVBUF].held > ub->ub_parms[UB_TCPRCVBUF].limit)
++ retval = -ENOMEM;
++ /*
++ * We try to leave numsock*maxadvmss as a reserve for sockets not
++ * queueing any data yet (if the difference between the barrier and the
++ * limit is enough for this reserve).
++ */
++ if (ub->ub_parms[UB_TCPRCVBUF].held +
++ ub->ub_parms[UB_NUMTCPSOCK].limit * ub->ub_maxadvmss
++ > ub->ub_parms[UB_TCPRCVBUF].limit &&
++ atomic_read(&sk->sk_rmem_alloc))
++ retval = -ENOMEM;
++ if (retval) {
++ ub->ub_parms[UB_TCPRCVBUF].held -= chargesize;
++ ub->ub_parms[UB_TCPRCVBUF].failcnt++;
++ }
++ ub_adjust_maxheld(ub, UB_TCPRCVBUF);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ goto out;
++}
++EXPORT_SYMBOL(ub_sock_tcp_chargerecv);
++
++static void ub_tcprcvbuf_uncharge(struct sk_buff *skb)
++{
++ unsigned long flags;
++ unsigned long held, bar;
++ int prev_pres;
++ struct user_beancounter *ub;
++
++ ub = top_beancounter(skb_bc(skb)->ub);
++ if (ub_barrier_farsz(ub, UB_TCPRCVBUF)) {
++ sock_bc(skb->sk)->forw_space += skb_bc(skb)->charged;
++ ub_skb_set_uncharge(skb);
++ return;
++ }
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (ub->ub_parms[UB_TCPRCVBUF].held < skb_bc(skb)->charged) {
++ printk(KERN_ERR "Uncharging %d for tcprcvbuf of %p with %lu\n",
++ skb_bc(skb)->charged,
++ ub, ub->ub_parms[UB_TCPRCVBUF].held);
++ /* ass-saving bung */
++ skb_bc(skb)->charged = ub->ub_parms[UB_TCPRCVBUF].held;
++ }
++ ub->ub_parms[UB_TCPRCVBUF].held -= skb_bc(skb)->charged;
++ held = ub->ub_parms[UB_TCPRCVBUF].held;
++ bar = ub->ub_parms[UB_TCPRCVBUF].barrier;
++ prev_pres = ub->ub_rmem_pressure;
++ if (held <= bar - (bar >> 2))
++ ub->ub_rmem_pressure = UB_RMEM_EXPAND;
++ else if (held <= bar)
++ ub->ub_rmem_pressure = UB_RMEM_KEEP;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ uncharge_beancounter_notop(skb_bc(skb)->ub, UB_TCPRCVBUF,
++ skb_bc(skb)->charged);
++ ub_skb_set_uncharge(skb);
++}
++
++
++/*
++ * UB_OTHERSOCKBUF and UB_TCPSNDBUF
++ */
++
++static void ub_socksndbuf_uncharge(struct sk_buff *skb)
++{
++ unsigned long flags;
++ struct user_beancounter *ub, *cub;
++ unsigned long chargesize;
++
++ cub = skb_bc(skb)->ub;
++ ub = top_beancounter(cub);
++ chargesize = skb_bc(skb)->charged;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __uncharge_beancounter_locked(ub, UB_OTHERSOCKBUF, chargesize);
++ if (skb->sk != NULL && sock_has_ubc(skb->sk))
++ ub_sock_wcharge_dec(skb->sk, chargesize);
++ ub_sock_snd_wakeup(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ uncharge_beancounter_notop(cub, UB_OTHERSOCKBUF, chargesize);
++ ub_skb_set_uncharge(skb);
++}
++
++/* expected to be called under socket lock */
++static void ub_tcpsndbuf_uncharge(struct sk_buff *skb)
++{
++ /*
++ * ub_sock_ret_wreserv call is abused here, we just want to uncharge
++ * skb size. However, to reduce duplication of the code doing
++ * ub_hfbarrier_hit check, ub_wcharged reduction, and wakeup we call
++ * a function that already does all of this. 2006/04/27 SAW
++ */
++ ub_sock_ret_wreserv(skb->sk, UB_TCPSNDBUF, skb_bc(skb)->charged,
++ sock_bc(skb->sk)->poll_reserv);
++ ub_skb_set_uncharge(skb);
++}
++
++void ub_skb_uncharge(struct sk_buff *skb)
++{
++ switch (skb_bc(skb)->resource) {
++ case UB_TCPSNDBUF:
++ ub_tcpsndbuf_uncharge(skb);
++ break;
++ case UB_TCPRCVBUF:
++ ub_tcprcvbuf_uncharge(skb);
++ break;
++ case UB_DGRAMRCVBUF:
++ ub_sockrcvbuf_uncharge(skb);
++ break;
++ case UB_OTHERSOCKBUF:
++ ub_socksndbuf_uncharge(skb);
++ break;
++ }
++}
++
++EXPORT_SYMBOL(ub_skb_uncharge); /* due to skb_orphan()/conntracks */
++
++/*
++ * Other sock reserve managment
++ */
++
++int ub_sock_getwres_other(struct sock *sk, unsigned long size)
++{
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long flags;
++ unsigned long added_reserv;
++ int err;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ /*
++ * Nothing except beancounter lock protects skbc->poll_reserv.
++ * So, take the lock and do the job.
++ * Dances with added_reserv repeat ub_sock_make_wreserv.
++ */
++ skbc = sock_bc(sk);
++ ub = top_beancounter(skbc->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ added_reserv = -skbc->poll_reserv;
++ err = ub_sock_makewreserv_locked(sk, UB_OTHERSOCKBUF, size);
++ added_reserv += skbc->poll_reserv;
++ if (!err)
++ skbc->poll_reserv -= size;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ if (added_reserv)
++ charge_beancounter_notop(skbc->ub, UB_OTHERSOCKBUF, added_reserv);
++
++ return err;
++}
++EXPORT_SYMBOL(ub_sock_getwres_other);
++
++void ub_sock_retwres_other(struct sock *sk,
++ unsigned long size, unsigned long ressize)
++{
++ if (unlikely(!sock_has_ubc(sk)))
++ return;
++
++ ub_sock_do_ret_wreserv(sk, UB_OTHERSOCKBUF, size, ressize);
++}
++
++/*
++ * TCP send buffers accouting. Paged part
++ */
++
++int ub_sock_tcp_chargepage(struct sock *sk)
++{
++ struct sock_beancounter *skbc;
++ unsigned long extra;
++ int err;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ skbc = sock_bc(sk);
++ ub_sock_make_wreserv(sk, UB_TCPSNDBUF, PAGE_SIZE);
++ if (likely(skbc->poll_reserv >= PAGE_SIZE)) {
++ skbc->poll_reserv -= PAGE_SIZE;
++ return 0;
++ }
++
++ /*
++ * Ok, full page is not available.
++ * However, this function must succeed if poll previously indicated
++ * that write is possible. We better make a forced charge here
++ * than reserve a whole page in poll.
++ */
++ err = ub_sock_make_wreserv(sk, UB_TCPSNDBUF, SOCK_MIN_UBCSPACE);
++ if (unlikely(err < 0))
++ goto out;
++ if (skbc->poll_reserv < PAGE_SIZE) {
++ extra = PAGE_SIZE - skbc->poll_reserv;
++ err = charge_beancounter(skbc->ub, UB_TCPSNDBUF, extra,
++ UB_FORCE);
++ if (err < 0)
++ goto out;
++ skbc->poll_reserv += extra;
++ }
++ skbc->poll_reserv -= PAGE_SIZE;
++ return 0;
++
++out:
++ return err;
++}
++
++void ub_sock_tcp_detachpage(struct sock *sk)
++{
++ struct sk_buff *skb;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return;
++
++ /* The page is just detached from socket. The last skb in queue
++ with paged part holds referrence to it */
++ skb = skb_peek_tail(&sk->sk_write_queue);
++ if (skb == NULL) {
++ /* If the queue is empty - all data is sent and page is about
++ to be freed */
++ ub_sock_ret_wreserv(sk, UB_TCPSNDBUF, PAGE_SIZE,
++ sock_bc(sk)->poll_reserv);
++ } else {
++ /* Last skb is a good aproximation for a last skb with
++ paged part */
++ skb_bc(skb)->charged += PAGE_SIZE;
++ }
++}
++
++/*
++ * TCPSNDBUF charge functions below are called in the following cases:
++ * - sending of SYN, SYN-ACK, FIN, the latter charge is forced by
++ * some technical reasons in TCP code;
++ * - fragmentation of TCP packets.
++ * These functions are allowed but not required to use poll_reserv.
++ * Originally, these functions didn't do that, since it didn't make
++ * any sense. Now, since poll_reserv now has a function of general reserve,
++ * they use it.
++ */
++int ub_sock_tcp_chargesend(struct sock *sk, struct sk_buff *skb,
++ enum ub_severity strict)
++{
++ int ret;
++ unsigned long chargesize;
++ struct sock_beancounter *skbc;
++ struct user_beancounter *ub;
++ unsigned long flags;
++
++ if (unlikely(!sock_has_ubc(sk)))
++ return 0;
++
++ skbc = sock_bc(sk);
++ chargesize = skb_charge_fullsize(skb);
++ if (likely(skbc->poll_reserv >= chargesize)) {
++ skbc->poll_reserv -= chargesize;
++ __ub_skb_set_charge(skb, sk, chargesize, UB_TCPSNDBUF);
++ /* XXX hack, see ub_skb_set_charge */
++ skb->sk = sk;
++ return 0;
++ }
++
++ ub = top_beancounter(skbc->ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ret = __charge_beancounter_locked(ub, UB_TCPSNDBUF,
++ chargesize, strict);
++ /*
++ * Note: this check is not equivalent of the corresponding check
++ * in makewreserv. It's similar in spirit, but an equivalent check
++ * would be too long and complicated here.
++ */
++ if (!ret && ub_barrier_hit(ub, UB_TCPSNDBUF))
++ skbc->ub_wcharged += chargesize;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ if (likely(!ret)) {
++ charge_beancounter_notop(skbc->ub, UB_TCPSNDBUF, chargesize);
++ ub_skb_set_charge(skb, sk, chargesize, UB_TCPSNDBUF);
++ }
++ return ret;
++}
++EXPORT_SYMBOL(ub_sock_tcp_chargesend);
++
++/*
++ * Initialization
++ */
++
++int __init skbc_cache_init(void)
++{
++ return 0;
++}
+diff --git a/kernel/bc/oom_kill.c b/kernel/bc/oom_kill.c
+new file mode 100644
+index 0000000..c79e826
+--- /dev/null
++++ b/kernel/bc/oom_kill.c
+@@ -0,0 +1,200 @@
++#include <linux/wait.h>
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/swap.h>
++#include <linux/cpuset.h>
++#include <linux/module.h>
++
++#include <bc/beancounter.h>
++#include <bc/oom_kill.h>
++#include <bc/hash.h>
++
++#define UB_OOM_TIMEOUT (5 * HZ)
++
++int oom_generation;
++int oom_kill_counter;
++static DEFINE_SPINLOCK(oom_lock);
++static DECLARE_WAIT_QUEUE_HEAD(oom_wq);
++
++static inline int ub_oom_completed(struct task_struct *tsk)
++{
++ if (test_tsk_thread_flag(tsk, TIF_MEMDIE))
++ /* we were oom killed - just die */
++ return 1;
++ if (tsk->task_bc.oom_generation != oom_generation)
++ /* some task was succesfully killed */
++ return 1;
++ return 0;
++}
++
++static void ub_clear_oom(void)
++{
++ struct user_beancounter *ub;
++
++ rcu_read_lock();
++ for_each_beancounter(ub)
++ ub->ub_oom_noproc = 0;
++ rcu_read_unlock();
++}
++
++/* Called with cpuset_lock held */
++int ub_oom_lock(void)
++{
++ int timeout;
++ DEFINE_WAIT(oom_w);
++ struct task_struct *tsk;
++
++ tsk = current;
++
++ spin_lock(&oom_lock);
++ if (!oom_kill_counter)
++ goto out_do_oom;
++
++ timeout = UB_OOM_TIMEOUT;
++ while (1) {
++ if (ub_oom_completed(tsk)) {
++ spin_unlock(&oom_lock);
++ return -EINVAL;
++ }
++
++ if (timeout == 0)
++ break;
++
++ __set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&oom_wq, &oom_w);
++ spin_unlock(&oom_lock);
++ cpuset_unlock();
++
++ timeout = schedule_timeout(timeout);
++
++ cpuset_lock();
++ spin_lock(&oom_lock);
++ remove_wait_queue(&oom_wq, &oom_w);
++ }
++
++out_do_oom:
++ ub_clear_oom();
++ return 0;
++}
++
++static inline long ub_current_overdraft(struct user_beancounter *ub)
++{
++ return ub->ub_parms[UB_OOMGUARPAGES].held +
++ ((ub->ub_parms[UB_KMEMSIZE].held
++ + ub->ub_parms[UB_TCPSNDBUF].held
++ + ub->ub_parms[UB_TCPRCVBUF].held
++ + ub->ub_parms[UB_OTHERSOCKBUF].held
++ + ub->ub_parms[UB_DGRAMRCVBUF].held)
++ >> PAGE_SHIFT) - ub->ub_parms[UB_OOMGUARPAGES].barrier;
++}
++
++int ub_oom_task_skip(struct user_beancounter *ub, struct task_struct *tsk)
++{
++ struct user_beancounter *mm_ub;
++
++ if (ub == NULL)
++ return 0;
++
++ task_lock(tsk);
++ if (tsk->mm == NULL)
++ mm_ub = NULL;
++ else
++ mm_ub = tsk->mm->mm_ub;
++
++ while (mm_ub != NULL && mm_ub != ub)
++ mm_ub = mm_ub->parent;
++ task_unlock(tsk);
++
++ return mm_ub != ub;
++}
++
++struct user_beancounter *ub_oom_select_worst(void)
++{
++ struct user_beancounter *ub, *walkp;
++ long ub_maxover;
++
++ ub_maxover = 0;
++ ub = NULL;
++
++ rcu_read_lock();
++ for_each_beancounter (walkp) {
++ long ub_overdraft;
++
++ if (walkp->parent != NULL)
++ continue;
++ if (walkp->ub_oom_noproc)
++ continue;
++
++ ub_overdraft = ub_current_overdraft(walkp);
++ if (ub_overdraft > ub_maxover && get_beancounter_rcu(walkp)) {
++ put_beancounter(ub);
++ ub = walkp;
++ ub_maxover = ub_overdraft;
++ }
++ }
++
++ if (ub)
++ ub->ub_oom_noproc = 1;
++ rcu_read_unlock();
++
++ return ub;
++}
++
++void ub_oom_mm_killed(struct user_beancounter *ub)
++{
++ static struct ub_rate_info ri = { 5, 60*HZ };
++
++ /* increment is serialized with oom_lock */
++ ub->ub_parms[UB_OOMGUARPAGES].failcnt++;
++
++ if (ub_ratelimit(&ri))
++ show_mem();
++}
++
++void ub_oom_unlock(void)
++{
++ spin_unlock(&oom_lock);
++}
++
++void ub_oom_task_dead(struct task_struct *tsk)
++{
++ spin_lock(&oom_lock);
++ oom_kill_counter = 0;
++ oom_generation++;
++
++ printk("OOM killed process %s (pid=%d, ve=%d) exited, "
++ "free=%lu gen=%d.\n",
++ tsk->comm, tsk->pid, VEID(tsk->ve_task_info.owner_env),
++ nr_free_pages(), oom_generation);
++ /* if there is time to sleep in ub_oom_lock -> sleep will continue */
++ wake_up_all(&oom_wq);
++ spin_unlock(&oom_lock);
++}
++
++void ub_out_of_memory(struct user_beancounter *scope)
++{
++ struct user_beancounter *ub;
++ struct task_struct *p;
++
++ cpuset_lock();
++ spin_lock(&oom_lock);
++ ub_clear_oom();
++ ub = get_beancounter(scope);
++
++ read_lock(&tasklist_lock);
++retry:
++ p = select_bad_process(ub, NULL);
++ if (p == NULL || PTR_ERR(p) == -1UL)
++ goto unlock;
++
++ if (oom_kill_process(p, (gfp_t)-1, -1, NULL, "UB Out of memory"))
++ goto retry;
++
++ put_beancounter(ub);
++
++unlock:
++ read_unlock(&tasklist_lock);
++ spin_unlock(&oom_lock);
++ cpuset_unlock();
++}
++EXPORT_SYMBOL(ub_out_of_memory);
+diff --git a/kernel/bc/proc.c b/kernel/bc/proc.c
+new file mode 100644
+index 0000000..5b1ae4b
+--- /dev/null
++++ b/kernel/bc/proc.c
+@@ -0,0 +1,682 @@
++/*
++ * kernel/bc/proc.c
++ *
++ * Copyright (C) 2006 OpenVZ. SWsoft Inc.
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/proc_fs.h>
++#include <linux/seq_file.h>
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <bc/beancounter.h>
++#include <bc/hash.h>
++#include <bc/rss_pages.h>
++#include <bc/proc.h>
++
++/* Generic output formats */
++#if BITS_PER_LONG == 32
++const char *bc_proc_lu_fmt = "\t%-20s %10lu\n";
++const char *bc_proc_lu_lfmt = "\t%-20s %21lu\n";
++const char *bc_proc_llu_fmt = "\t%-20s %21llu\n";
++const char *bc_proc_lu_lu_fmt = "\t%-20s %10lu %10lu\n";
++#else
++const char *bc_proc_lu_fmt = "\t%-20s %21lu\n";
++const char *bc_proc_lu_lfmt = "\t%-20s %21lu\n";
++const char *bc_proc_llu_fmt = "\t%-20s %21llu\n";
++const char *bc_proc_lu_lu_fmt = "\t%-20s %21lu %21lu\n";
++#endif
++
++#if BITS_PER_LONG == 32
++static const char *head_fmt = "%10s %-12s %10s %10s %10s %10s %10s\n";
++static const char *res_fmt = "%10s %-12s %10lu %10lu %10lu %10lu %10lu\n";
++#else
++static const char *head_fmt = "%10s %-12s %20s %20s %20s %20s %20s\n";
++static const char *res_fmt = "%10s %-12s %20lu %20lu %20lu %20lu %20lu\n";
++#endif
++
++static void ub_show_res(struct seq_file *f, struct user_beancounter *ub,
++ int r, int show_uid)
++{
++ int len;
++ char ub_uid[64];
++
++ if (show_uid && r == 0) {
++ len = print_ub_uid(ub, ub_uid, sizeof(ub_uid) - 2);
++ ub_uid[len] = ':';
++ ub_uid[len + 1] = '\0';
++ } else
++ strcpy(ub_uid, "");
++
++ seq_printf(f, res_fmt, ub_uid, ub_rnames[r],
++ ub->ub_parms[r].held,
++ ub->ub_parms[r].maxheld,
++ ub->ub_parms[r].barrier,
++ ub->ub_parms[r].limit,
++ ub->ub_parms[r].failcnt);
++}
++
++static void __show_resources(struct seq_file *f, struct user_beancounter *ub,
++ int show_uid)
++{
++ int i;
++
++ for (i = 0; i < UB_RESOURCES_COMPAT; i++)
++ if (strcmp(ub_rnames[i], "dummy") != 0)
++ ub_show_res(f, ub, i, show_uid);
++
++ for (i = UB_RESOURCES_COMPAT; i < UB_RESOURCES; i++)
++ ub_show_res(f, ub, i, show_uid);
++}
++
++static int bc_resources_show(struct seq_file *f, void *v)
++{
++ __show_resources(f, seq_beancounter(f), 0);
++ return 0;
++}
++
++static struct bc_proc_entry bc_resources_entry = {
++ .name = "resources",
++ .u.show = bc_resources_show,
++};
++
++#ifdef CONFIG_UBC_DEBUG
++static int bc_debug_show(struct seq_file *f, void *v)
++{
++ struct user_beancounter *ub;
++ char buf[64];
++
++ ub = seq_beancounter(f);
++ print_ub_uid(ub, buf, sizeof(buf));
++ seq_printf(f, "uid: %s\n", buf);
++ seq_printf(f, "ref: %d\n", atomic_read(&ub->ub_refcount));
++
++ seq_printf(f, "bc: %p\n", ub);
++ seq_printf(f, "par: %p\n", ub->parent);
++ seq_printf(f, "priv: %p\n", ub->private_data);
++ return 0;
++}
++
++static struct bc_proc_entry bc_debug_entry = {
++ .name = "debug",
++ .u.show = bc_debug_show,
++};
++#endif
++
++static int ub_show(struct seq_file *f, void *v)
++{
++ int i;
++
++ for (i = 0; i < UB_RESOURCES_COMPAT; i++)
++ ub_show_res(f, (struct user_beancounter *)v, i, 1);
++ return 0;
++}
++
++static int res_show(struct seq_file *f, void *v)
++{
++ __show_resources(f, (struct user_beancounter *)v, 1);
++ return 0;
++}
++
++static int ub_accessible(struct user_beancounter *exec,
++ struct user_beancounter *target)
++{
++ struct user_beancounter *p, *q;
++
++ p = top_beancounter(exec);
++ q = top_beancounter(target);
++
++ return (p == get_ub0() || p == q);
++}
++
++static void ub_show_header(struct seq_file *f)
++{
++ seq_printf(f, "Version: 2.5\n");
++ seq_printf(f, head_fmt, "uid", "resource",
++ "held", "maxheld", "barrier", "limit", "failcnt");
++}
++
++static void *ub_start(struct seq_file *f, loff_t *ppos)
++{
++ struct user_beancounter *ub;
++ struct user_beancounter *exec_ub;
++ unsigned long pos;
++
++ pos = *ppos;
++ if (pos == 0)
++ ub_show_header(f);
++
++ exec_ub = get_exec_ub();
++
++ rcu_read_lock();
++ for_each_beancounter(ub) {
++ if (ub->parent != NULL)
++ continue;
++ if (!ub_accessible(exec_ub, ub))
++ continue;
++ if (pos-- == 0)
++ return ub;
++ }
++ return NULL;
++}
++
++static void *ub_next(struct seq_file *f, void *v, loff_t *ppos)
++{
++ struct user_beancounter *ub;
++ struct list_head *entry;
++ struct user_beancounter *exec_ub;
++
++ exec_ub = get_exec_ub();
++ ub = (struct user_beancounter *)v;
++
++ entry = &ub->ub_list;
++
++ list_for_each_continue_rcu(entry, &ub_list_head) {
++ ub = list_entry(entry, struct user_beancounter, ub_list);
++ if (ub->parent != NULL)
++ continue;
++ if (!ub_accessible(exec_ub, ub))
++ continue;
++
++ (*ppos)++;
++ return ub;
++ }
++ return NULL;
++}
++
++static void ub_stop(struct seq_file *f, void *v)
++{
++ rcu_read_unlock();
++}
++
++static struct seq_operations ub_seq_ops = {
++ .start = ub_start,
++ .next = ub_next,
++ .stop = ub_stop,
++ .show = ub_show,
++};
++
++static int ub_open(struct inode *inode, struct file *filp)
++{
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return -EACCES;
++
++ return seq_open(filp, &ub_seq_ops);
++}
++
++static struct file_operations ub_file_operations = {
++ .open = ub_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static struct seq_operations res_seq_ops = {
++ .start = ub_start,
++ .next = ub_next,
++ .stop = ub_stop,
++ .show = res_show,
++};
++
++static int res_open(struct inode *inode, struct file *filp)
++{
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return -EACCES;
++
++ return seq_open(filp, &res_seq_ops);
++}
++
++static struct file_operations resources_operations = {
++ .open = res_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static struct bc_proc_entry bc_all_resources_entry = {
++ .name = "resources",
++ .u.fops = &resources_operations,
++};
++
++/*
++ * Generic showing stuff
++ */
++
++static int cookies, num_entries;
++static struct bc_proc_entry *bc_entries __read_mostly;
++static struct bc_proc_entry *bc_root_entries __read_mostly;
++static DEFINE_SPINLOCK(bc_entries_lock);
++static struct proc_dir_entry *bc_proc_root;
++
++void bc_register_proc_entry(struct bc_proc_entry *e)
++{
++ spin_lock(&bc_entries_lock);
++ e->cookie = ++cookies;
++ e->next = bc_entries;
++ bc_entries = e;
++ num_entries++;
++ spin_unlock(&bc_entries_lock);
++}
++
++EXPORT_SYMBOL(bc_register_proc_entry);
++
++void bc_register_proc_root_entry(struct bc_proc_entry *e)
++{
++ spin_lock(&bc_entries_lock);
++ e->cookie = ++cookies;
++ e->next = bc_root_entries;
++ bc_root_entries = e;
++ bc_proc_root->nlink++;
++ spin_unlock(&bc_entries_lock);
++}
++
++EXPORT_SYMBOL(bc_register_proc_root_entry);
++
++/*
++ * small helpers
++ */
++
++static inline unsigned long bc_make_ino(struct user_beancounter *ub)
++{
++ unsigned long ret;
++
++ ret = 0xbc000000;
++ if (ub->parent)
++ ret |= ((ub->parent->ub_uid) << 4);
++ ret |= (ub->ub_uid + 1);
++ return ret;
++}
++
++static inline unsigned long bc_make_file_ino(struct bc_proc_entry *de)
++{
++ return 0xbe000000 + de->cookie;
++}
++
++static int bc_d_delete(struct dentry *d)
++{
++ return 1;
++}
++
++static void bc_d_release(struct dentry *d)
++{
++ put_beancounter((struct user_beancounter *)d->d_fsdata);
++}
++
++static struct inode_operations bc_entry_iops;
++static struct file_operations bc_entry_fops;
++static struct dentry_operations bc_dentry_ops = {
++ .d_delete = bc_d_delete,
++ .d_release = bc_d_release,
++};
++
++/*
++ * common directory operations' helpers
++ */
++
++static int bc_readdir(struct file *file, filldir_t filler, void *data,
++ struct user_beancounter *parent)
++{
++ int err = 0;
++ loff_t pos, filled;
++ struct user_beancounter *ub, *prev;
++ struct bc_proc_entry *pde;
++
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return -EPERM;
++
++ pos = file->f_pos;
++ if (pos == 0) {
++ err = (*filler)(data, ".", 1, pos,
++ file->f_dentry->d_inode->i_ino, DT_DIR);
++ if (err < 0) {
++ err = 0;
++ goto out;
++ }
++ pos++;
++ }
++
++ if (pos == 1) {
++ err = (*filler)(data, "..", 2, pos,
++ parent_ino(file->f_dentry), DT_DIR);
++ if (err < 0) {
++ err = 0;
++ goto out;
++ }
++ pos++;
++ }
++
++ filled = 2;
++ for (pde = (parent == NULL ? bc_root_entries : bc_entries);
++ pde != NULL; pde = pde->next) {
++ if (filled++ < pos)
++ continue;
++
++ err = (*filler)(data, pde->name, strlen(pde->name), pos,
++ bc_make_file_ino(pde), DT_REG);
++ if (err < 0) {
++ err = 0;
++ goto out;
++ }
++ pos++;
++ }
++
++ rcu_read_lock();
++ prev = NULL;
++ ub = list_entry(&ub_list_head, struct user_beancounter, ub_list);
++ while (1) {
++ int len;
++ unsigned long ino;
++ char buf[64];
++
++ ub = list_entry(rcu_dereference(ub->ub_list.next),
++ struct user_beancounter, ub_list);
++ if (&ub->ub_list == &ub_list_head)
++ break;
++
++ if (ub->parent != parent)
++ continue;
++
++ if (filled++ < pos)
++ continue;
++
++ if (!get_beancounter_rcu(ub))
++ continue;
++
++ rcu_read_unlock();
++ put_beancounter(prev);
++
++ len = print_ub_uid(ub, buf, sizeof(buf));
++ ino = bc_make_ino(ub);
++
++ err = (*filler)(data, buf, len, pos, ino, DT_DIR);
++ if (err < 0) {
++ err = 0;
++ put_beancounter(ub);
++ goto out;
++ }
++
++ rcu_read_lock();
++ prev = ub;
++ pos++;
++ }
++ rcu_read_unlock();
++ put_beancounter(prev);
++out:
++ file->f_pos = pos;
++ return err;
++}
++
++static int bc_looktest(struct inode *ino, void *data)
++{
++ return ino->i_op == &bc_entry_iops && ino->i_private == data;
++}
++
++static int bc_lookset(struct inode *ino, void *data)
++{
++ struct user_beancounter *ub;
++
++ ub = (struct user_beancounter *)data;
++ ino->i_private = data;
++ ino->i_ino = bc_make_ino(ub);
++ ino->i_fop = &bc_entry_fops;
++ ino->i_op = &bc_entry_iops;
++ ino->i_mode = S_IFDIR | S_IRUSR | S_IXUGO;
++ /* subbeancounters are not included, but who cares? */
++ ino->i_nlink = num_entries + 2;
++ ino->i_gid = 0;
++ ino->i_uid = 0;
++ return 0;
++}
++
++static struct dentry *bc_lookup(struct user_beancounter *ub, struct inode *dir,
++ struct dentry *dentry)
++{
++ struct inode *ino;
++
++ ino = iget5_locked(dir->i_sb, ub->ub_uid, bc_looktest, bc_lookset, ub);
++ if (ino == NULL)
++ goto out_put;
++
++ unlock_new_inode(ino);
++ dentry->d_op = &bc_dentry_ops;
++ dentry->d_fsdata = ub;
++ d_add(dentry, ino);
++ return NULL;
++
++out_put:
++ put_beancounter(ub);
++ return ERR_PTR(-ENOENT);
++}
++
++/*
++ * files (bc_proc_entry) manipulations
++ */
++
++static struct dentry *bc_lookup_file(struct inode *dir,
++ struct dentry *dentry, struct bc_proc_entry *root,
++ int (*test)(struct inode *, void *),
++ int (*set)(struct inode *, void *))
++{
++ struct bc_proc_entry *pde;
++ struct inode *ino;
++
++ for (pde = root; pde != NULL; pde = pde->next)
++ if (strcmp(pde->name, dentry->d_name.name) == 0)
++ break;
++
++ if (pde == NULL)
++ return ERR_PTR(-ESRCH);
++
++ ino = iget5_locked(dir->i_sb, pde->cookie, test, set, pde);
++ if (ino == NULL)
++ return ERR_PTR(-ENOENT);
++
++ unlock_new_inode(ino);
++ dentry->d_op = &bc_dentry_ops;
++ d_add(dentry, ino);
++ return NULL;
++}
++
++static int bc_file_open(struct inode *ino, struct file *filp)
++{
++ struct bc_proc_entry *de;
++ struct user_beancounter *ub;
++
++ de = (struct bc_proc_entry *)ino->i_private;
++ ub = (struct user_beancounter *)filp->f_dentry->d_parent->d_fsdata;
++ BUG_ON(ub->ub_magic != UB_MAGIC);
++
++ /*
++ * ub can't disappear: we hold d_parent, he holds the beancounter
++ */
++ return single_open(filp, de->u.show, ub);
++}
++
++static struct file_operations bc_file_ops = {
++ .open = bc_file_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static int bc_looktest_entry(struct inode *ino, void *data)
++{
++ return ino->i_fop == &bc_file_ops && ino->i_private == data;
++}
++
++static int bc_lookset_entry(struct inode *ino, void *data)
++{
++ struct bc_proc_entry *de;
++
++ de = (struct bc_proc_entry *)data;
++ ino->i_private = data;
++ ino->i_ino = bc_make_file_ino(de);
++ ino->i_fop = &bc_file_ops,
++ ino->i_mode = S_IFREG | S_IRUSR;
++ ino->i_nlink = 1;
++ ino->i_gid = 0;
++ ino->i_uid = 0;
++ return 0;
++}
++
++static inline struct dentry *bc_lookup_files(struct inode *dir,
++ struct dentry *de)
++{
++ return bc_lookup_file(dir, de, bc_entries,
++ bc_looktest_entry, bc_lookset_entry);
++}
++
++static int bc_looktest_root_entry(struct inode *ino, void *data)
++{
++ struct bc_proc_entry *de;
++
++ de = (struct bc_proc_entry *)data;
++ return ino->i_fop == de->u.fops && ino->i_private == data;
++}
++
++static int bc_lookset_root_entry(struct inode *ino, void *data)
++{
++ struct bc_proc_entry *de;
++
++ de = (struct bc_proc_entry *)data;
++ ino->i_private = data;
++ ino->i_ino = bc_make_file_ino(de);
++ ino->i_fop = de->u.fops;
++ ino->i_mode = S_IFREG | S_IRUSR;
++ ino->i_nlink = 1;
++ ino->i_gid = 0;
++ ino->i_uid = 0;
++ return 0;
++}
++
++static inline struct dentry *bc_lookup_root_files(struct inode *dir,
++ struct dentry *de)
++{
++ return bc_lookup_file(dir, de, bc_root_entries,
++ bc_looktest_root_entry, bc_lookset_root_entry);
++}
++
++/*
++ * /proc/bc/.../<id> directory operations
++ */
++
++static int bc_entry_readdir(struct file *file, void *data, filldir_t filler)
++{
++ return bc_readdir(file, filler, data,
++ (struct user_beancounter *)file->f_dentry->d_fsdata);
++}
++
++static struct dentry *bc_entry_lookup(struct inode *dir, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ int id;
++ char *end;
++ struct user_beancounter *par, *ub;
++ struct dentry *de;
++
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return ERR_PTR(-EPERM);
++
++ de = bc_lookup_files(dir, dentry);
++ if (de != ERR_PTR(-ESRCH))
++ return de;
++
++ id = simple_strtol(dentry->d_name.name, &end, 10);
++ if (*end != '.')
++ return ERR_PTR(-ENOENT);
++
++ par = (struct user_beancounter *)dir->i_private;
++ if (par->ub_uid != id)
++ return ERR_PTR(-ENOENT);
++
++ id = simple_strtol(end + 1, &end, 10);
++ if (*end != '\0')
++ return ERR_PTR(-ENOENT);
++
++ ub = get_subbeancounter_byid(par, id, 0);
++ if (ub == NULL)
++ return ERR_PTR(-ENOENT);
++
++ return bc_lookup(ub, dir, dentry);
++}
++
++static struct file_operations bc_entry_fops = {
++ .read = generic_read_dir,
++ .readdir = bc_entry_readdir,
++};
++
++static struct inode_operations bc_entry_iops = {
++ .lookup = bc_entry_lookup,
++};
++
++/*
++ * /proc/bc directory operations
++ */
++
++static int bc_root_readdir(struct file *file, void *data, filldir_t filler)
++{
++ return bc_readdir(file, filler, data, NULL);
++}
++
++static struct dentry *bc_root_lookup(struct inode *dir, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ int id;
++ char *end;
++ struct user_beancounter *ub;
++ struct dentry *de;
++
++ if (!(capable(CAP_DAC_OVERRIDE) && capable(CAP_DAC_READ_SEARCH)))
++ return ERR_PTR(-EPERM);
++
++ de = bc_lookup_root_files(dir, dentry);
++ if (de != ERR_PTR(-ESRCH))
++ return de;
++
++ id = simple_strtol(dentry->d_name.name, &end, 10);
++ if (*end != '\0')
++ return ERR_PTR(-ENOENT);
++
++ ub = get_beancounter_byuid(id, 0);
++ if (ub == NULL)
++ return ERR_PTR(-ENOENT);
++
++ return bc_lookup(ub, dir, dentry);
++}
++
++static struct file_operations bc_root_fops = {
++ .read = generic_read_dir,
++ .readdir = bc_root_readdir,
++};
++
++static struct inode_operations bc_root_iops = {
++ .lookup = bc_root_lookup,
++};
++
++static int __init ub_init_proc(void)
++{
++ struct proc_dir_entry *entry;
++
++ bc_proc_root = create_proc_entry("bc",
++ S_IFDIR | S_IRUGO | S_IXUGO, NULL);
++ if (bc_proc_root == NULL)
++ panic("Can't create /proc/bc entry");
++
++ bc_proc_root->proc_fops = &bc_root_fops;
++ bc_proc_root->proc_iops = &bc_root_iops;
++
++ bc_register_proc_entry(&bc_resources_entry);
++#ifdef CONFIG_UBC_DEBUG
++ bc_register_proc_entry(&bc_debug_entry);
++#endif
++ bc_register_proc_root_entry(&bc_all_resources_entry);
++
++ entry = proc_create("user_beancounters",
++ S_IRUGO, &glob_proc_root, &ub_file_operations);
++ return 0;
++}
++
++core_initcall(ub_init_proc);
+diff --git a/kernel/bc/rss_pages.c b/kernel/bc/rss_pages.c
+new file mode 100644
+index 0000000..391585e
+--- /dev/null
++++ b/kernel/bc/rss_pages.c
+@@ -0,0 +1,437 @@
++/*
++ * kernel/bc/rss_pages.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/gfp.h>
++#include <linux/vmalloc.h>
++
++#include <bc/beancounter.h>
++#include <bc/hash.h>
++#include <bc/vmpages.h>
++#include <bc/rss_pages.h>
++#include <bc/io_acct.h>
++
++static struct kmem_cache *pb_cachep;
++spinlock_t pb_lock = SPIN_LOCK_UNLOCKED;
++static struct page_beancounter **pb_hash_table;
++static unsigned int pb_hash_mask;
++
++/*
++ * Auxiliary staff
++ */
++
++static inline struct page_beancounter *next_page_pb(struct page_beancounter *p)
++{
++ return list_entry(p->page_list.next, struct page_beancounter,
++ page_list);
++}
++
++static inline struct page_beancounter *prev_page_pb(struct page_beancounter *p)
++{
++ return list_entry(p->page_list.prev, struct page_beancounter,
++ page_list);
++}
++
++/*
++ * Held pages manipulation
++ */
++static inline void set_held_pages(struct user_beancounter *bc)
++{
++ /* all three depend on ub_held_pages */
++ __ub_update_physpages(bc);
++ __ub_update_oomguarpages(bc);
++ __ub_update_privvm(bc);
++}
++
++static inline void do_dec_held_pages(struct user_beancounter *ub, int value)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_held_pages -= value;
++ set_held_pages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++static void dec_held_pages(struct user_beancounter *ub, int value)
++{
++ for (; ub != NULL; ub = ub->parent)
++ do_dec_held_pages(ub, value);
++}
++
++static inline void do_inc_held_pages(struct user_beancounter *ub, int value)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_held_pages += value;
++ set_held_pages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++static void inc_held_pages(struct user_beancounter *ub, int value)
++{
++ for (; ub != NULL; ub = ub->parent)
++ do_inc_held_pages(ub, value);
++}
++
++/*
++ * Alloc - free
++ */
++
++inline int pb_alloc(struct page_beancounter **pbc)
++{
++ *pbc = kmem_cache_alloc(pb_cachep, GFP_KERNEL);
++ if (*pbc != NULL) {
++ (*pbc)->next_hash = NULL;
++ (*pbc)->pb_magic = PB_MAGIC;
++ }
++ return (*pbc == NULL);
++}
++
++inline void pb_free(struct page_beancounter **pb)
++{
++ if (*pb != NULL) {
++ kmem_cache_free(pb_cachep, *pb);
++ *pb = NULL;
++ }
++}
++
++void pb_free_list(struct page_beancounter **p_pb)
++{
++ struct page_beancounter *list, *pb;
++
++ list = *p_pb;
++ if (list == PBC_COPY_SAME)
++ return;
++
++ while (list) {
++ pb = list;
++ list = list->next_hash;
++ pb_free(&pb);
++ }
++ *p_pb = NULL;
++}
++
++/*
++ * head -> <new objs> -> <old objs> -> ...
++ */
++static int __alloc_list(struct page_beancounter **head, int num)
++{
++ struct page_beancounter *pb;
++
++ while (num > 0) {
++ if (pb_alloc(&pb))
++ return -1;
++ pb->next_hash = *head;
++ *head = pb;
++ num--;
++ }
++
++ return num;
++}
++
++/*
++ * Ensure that the list contains at least num elements.
++ * p_pb points to an initialized list, may be of the zero length.
++ *
++ * mm->page_table_lock should be held
++ */
++int pb_alloc_list(struct page_beancounter **p_pb, int num)
++{
++ struct page_beancounter *list;
++
++ for (list = *p_pb; list != NULL && num; list = list->next_hash, num--);
++ if (!num)
++ return 0;
++
++ /*
++ * *p_pb(after) *p_pb (before)
++ * \ \
++ * <new objs> -...-> <old objs> -> ...
++ */
++ if (__alloc_list(p_pb, num) < 0)
++ goto nomem;
++ return 0;
++
++nomem:
++ pb_free_list(p_pb);
++ return -ENOMEM;
++}
++
++/*
++ * Allocates a page_beancounter for each
++ * user_beancounter in a hash
++ */
++int pb_alloc_all(struct page_beancounter **pbs)
++{
++ int need_alloc;
++ struct user_beancounter *ub;
++
++ need_alloc = 0;
++ rcu_read_lock();
++ for_each_beancounter(ub)
++ need_alloc++;
++ rcu_read_unlock();
++
++ if (!__alloc_list(pbs, need_alloc))
++ return 0;
++
++ pb_free_list(pbs);
++ return -ENOMEM;
++}
++
++/*
++ * Hash routines
++ */
++
++static inline int pb_hash(struct user_beancounter *ub, struct page *page)
++{
++ return (page_to_pfn(page) + (ub->ub_uid << 10)) & pb_hash_mask;
++}
++
++/* pb_lock should be held */
++static inline void insert_pb(struct page_beancounter *p, struct page *page,
++ struct user_beancounter *ub, int hash)
++{
++ p->page = page;
++ p->ub = get_beancounter(ub);
++ p->next_hash = pb_hash_table[hash];
++ pb_hash_table[hash] = p;
++ inc_pbc_count(ub);
++}
++
++/*
++ * Heart
++ */
++
++static int __pb_dup_ref(struct page *page, struct user_beancounter *bc,
++ int hash)
++{
++ struct page_beancounter *p;
++
++ for (p = pb_hash_table[hash];
++ p != NULL && (p->page != page || p->ub != bc);
++ p = p->next_hash);
++ if (p == NULL)
++ return -1;
++
++ PB_COUNT_INC(p->refcount);
++ return 0;
++}
++
++static void __pb_add_ref(struct page *page, struct user_beancounter *bc,
++ struct page_beancounter **ppb, int hash)
++{
++ struct page_beancounter *head, *p, **hp;
++ int shift;
++
++ p = *ppb;
++ *ppb = p->next_hash;
++
++ insert_pb(p, page, bc, hash);
++ hp = page_pblist(page);
++ head = *hp;
++
++ if (head != NULL) {
++ /*
++ * Move the first element to the end of the list.
++ * List head (pb_head) is set to the next entry.
++ * Note that this code works even if head is the only element
++ * on the list (because it's cyclic).
++ */
++ BUG_ON(head->pb_magic != PB_MAGIC);
++ *hp = next_page_pb(head);
++ PB_SHIFT_INC(head->refcount);
++ shift = PB_SHIFT_GET(head->refcount);
++ /*
++ * Update user beancounter, the share of head has been changed.
++ * Note that the shift counter is taken after increment.
++ */
++ dec_held_pages(head->ub, UB_PAGE_WEIGHT >> shift);
++ /* add the new page beancounter to the end of the list */
++ head = *hp;
++ list_add_tail(&p->page_list, &head->page_list);
++ } else {
++ *hp = p;
++ shift = 0;
++ INIT_LIST_HEAD(&p->page_list);
++ }
++
++ p->refcount = PB_REFCOUNT_MAKE(shift, 1);
++ /* update user beancounter for the new page beancounter */
++ inc_held_pages(bc, UB_PAGE_WEIGHT >> shift);
++}
++
++void pb_add_ref(struct page *page, struct mm_struct *mm,
++ struct page_beancounter **p_pb)
++{
++ int hash;
++ struct user_beancounter *bc;
++
++ bc = mm->mm_ub;
++ if (bc == NULL)
++ return;
++
++ if (!PageAnon(page) && is_shmem_mapping(page->mapping))
++ return;
++
++ hash = pb_hash(bc, page);
++
++ spin_lock(&pb_lock);
++ if (__pb_dup_ref(page, bc, hash))
++ __pb_add_ref(page, bc, p_pb, hash);
++ spin_unlock(&pb_lock);
++}
++
++void pb_dup_ref(struct page *page, struct mm_struct *mm,
++ struct page_beancounter **p_pb)
++{
++ int hash;
++ struct user_beancounter *bc;
++
++ bc = mm->mm_ub;
++ if (bc == NULL)
++ return;
++
++ if (!PageAnon(page) && is_shmem_mapping(page->mapping))
++ return;
++
++ hash = pb_hash(bc, page);
++
++ spin_lock(&pb_lock);
++ if (*page_pblist(page) == NULL)
++ /*
++ * pages like ZERO_PAGE must not be accounted in pbc
++ * so on fork we just skip them
++ */
++ goto out_unlock;
++
++ if (unlikely(*p_pb != PBC_COPY_SAME))
++ __pb_add_ref(page, bc, p_pb, hash);
++ else if (unlikely(__pb_dup_ref(page, bc, hash)))
++ WARN_ON(1);
++out_unlock:
++ spin_unlock(&pb_lock);
++}
++
++void pb_remove_ref(struct page *page, struct mm_struct *mm)
++{
++ int hash;
++ struct user_beancounter *bc;
++ struct page_beancounter *p, **q, *f;
++ int shift, shiftt;
++
++ bc = mm->mm_ub;
++ if (bc == NULL)
++ return;
++
++ if (!PageAnon(page) && is_shmem_mapping(page->mapping))
++ return;
++
++ hash = pb_hash(bc, page);
++
++ spin_lock(&pb_lock);
++ for (q = pb_hash_table + hash, p = *q;
++ p != NULL && (p->page != page || p->ub != bc);
++ q = &p->next_hash, p = *q);
++ if (p == NULL)
++ goto out_unlock;
++
++ PB_COUNT_DEC(p->refcount);
++ if (PB_COUNT_GET(p->refcount))
++ /*
++ * More references from the same user beancounter exist.
++ * Nothing needs to be done.
++ */
++ goto out_unlock;
++
++ /* remove from the hash list */
++ f = p;
++ *q = p->next_hash;
++
++ shift = PB_SHIFT_GET(p->refcount);
++
++ dec_held_pages(p->ub, UB_PAGE_WEIGHT >> shift);
++
++ q = page_pblist(page);
++ if (*q == p) {
++ if (list_empty(&p->page_list)) {
++ *q = NULL;
++ goto out_free;
++ }
++
++ *q = next_page_pb(p);
++ }
++ list_del(&p->page_list);
++
++ /* Now balance the list. Move the tail and adjust its shift counter. */
++ p = prev_page_pb(*q);
++ shiftt = PB_SHIFT_GET(p->refcount);
++ *q = p;
++ PB_SHIFT_DEC(p->refcount);
++
++ inc_held_pages(p->ub, UB_PAGE_WEIGHT >> shiftt);
++
++ /*
++ * If the shift counter of the moved beancounter is different from the
++ * removed one's, repeat the procedure for one more tail beancounter
++ */
++ if (shiftt > shift) {
++ p = prev_page_pb(*q);
++ *q = p;
++ PB_SHIFT_DEC(p->refcount);
++ inc_held_pages(p->ub, UB_PAGE_WEIGHT >> shiftt);
++ }
++out_free:
++ dec_pbc_count(f->ub);
++ spin_unlock(&pb_lock);
++
++ put_beancounter(f->ub);
++ pb_free(&f);
++ return;
++
++out_unlock:
++ spin_unlock(&pb_lock);
++}
++
++struct user_beancounter *pb_grab_page_ub(struct page *page)
++{
++ struct page_beancounter *pb;
++ struct user_beancounter *ub;
++
++ spin_lock(&pb_lock);
++ pb = *page_pblist(page);
++ ub = (pb == NULL ? ERR_PTR(-EINVAL) :
++ get_beancounter(pb->ub));
++ spin_unlock(&pb_lock);
++ return ub;
++}
++
++void __init ub_init_pbc(void)
++{
++ unsigned long hash_size;
++
++ pb_cachep = kmem_cache_create("page_beancounter",
++ sizeof(struct page_beancounter), 0,
++ SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
++ hash_size = num_physpages >> 2;
++ for (pb_hash_mask = 1;
++ (hash_size & pb_hash_mask) != hash_size;
++ pb_hash_mask = (pb_hash_mask << 1) + 1);
++ hash_size = pb_hash_mask + 1;
++ printk(KERN_INFO "Page beancounter hash is %lu entries.\n", hash_size);
++ pb_hash_table = vmalloc(hash_size * sizeof(struct page_beancounter *));
++ memset(pb_hash_table, 0, hash_size * sizeof(struct page_beancounter *));
++
++ ub_init_io(pb_cachep);
++}
+diff --git a/kernel/bc/statd.c b/kernel/bc/statd.c
+new file mode 100644
+index 0000000..bf6354b
+--- /dev/null
++++ b/kernel/bc/statd.c
+@@ -0,0 +1,453 @@
++/*
++ * kernel/bc/statd.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/timer.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/jiffies.h>
++#include <linux/list.h>
++#include <linux/errno.h>
++#include <linux/suspend.h>
++#include <linux/freezer.h>
++
++#include <asm/uaccess.h>
++#include <asm/param.h>
++
++#include <bc/beancounter.h>
++#include <bc/hash.h>
++#include <bc/statd.h>
++
++static spinlock_t ubs_notify_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(ubs_notify_list);
++static long ubs_min_interval;
++static ubstattime_t ubs_start_time, ubs_end_time;
++static struct timer_list ubs_timer;
++
++static int ubstat_get_list(void __user *buf, long size)
++{
++ int retval;
++ struct user_beancounter *ub, *ubp;
++ long *page, *ptr, *end;
++ int len;
++
++ page = (long *)__get_free_page(GFP_KERNEL);
++ if (page == NULL)
++ return -ENOMEM;
++
++ retval = 0;
++ ubp = NULL;
++ ptr = page;
++ end = page + PAGE_SIZE / sizeof(*ptr);
++
++ spin_lock_irq(&ub_hash_lock);
++ for_each_beancounter(ub) {
++ if (ub->parent != NULL)
++ continue;
++ *ptr++ = ub->ub_uid;
++ if (ptr != end)
++ continue;
++
++ get_beancounter(ub);
++ spin_unlock_irq(&ub_hash_lock);
++
++ put_beancounter(ubp);
++ ubp = ub;
++
++ len = min_t(long, (ptr - page) * sizeof(*ptr), size);
++ if (copy_to_user(buf, page, len)) {
++ retval = -EFAULT;
++ goto out_put;
++ }
++ retval += len;
++ if (len < PAGE_SIZE)
++ goto out_put;
++ buf += len;
++ size -= len;
++
++ ptr = page;
++ end = page + PAGE_SIZE / sizeof(*ptr);
++
++ spin_lock_irq(&ub_hash_lock);
++ }
++ spin_unlock_irq(&ub_hash_lock);
++
++ put_beancounter(ubp);
++ size = min_t(long, (ptr - page) * sizeof(*ptr), size);
++ if (size > 0 && copy_to_user(buf, page, size)) {
++ retval = -EFAULT;
++ goto out_put;
++ }
++ retval += size;
++
++out_put:
++ put_beancounter(ubp);
++ free_page((unsigned long)page);
++ return retval;
++}
++
++static int ubstat_gettime(void __user *buf, long size)
++{
++ ubgettime_t data;
++ int retval;
++
++ spin_lock(&ubs_notify_lock);
++ data.start_time = ubs_start_time;
++ data.end_time = ubs_end_time;
++ data.cur_time = ubs_start_time + (jiffies - ubs_start_time * HZ) / HZ;
++ spin_unlock(&ubs_notify_lock);
++
++ retval = min_t(long, sizeof(data), size);
++ if (copy_to_user(buf, &data, retval))
++ retval = -EFAULT;
++ return retval;
++}
++
++static int ubstat_do_read_one(struct user_beancounter *ub, int res, void *kbuf)
++{
++ struct {
++ ubstattime_t start_time;
++ ubstattime_t end_time;
++ ubstatparm_t param[1];
++ } *data;
++
++ data = kbuf;
++ data->start_time = ubs_start_time;
++ data->end_time = ubs_end_time;
++
++ data->param[0].maxheld = ub->ub_store[res].maxheld;
++ data->param[0].failcnt = ub->ub_store[res].failcnt;
++
++ return sizeof(*data);
++}
++
++static int ubstat_do_read_all(struct user_beancounter *ub, void *kbuf, int size)
++{
++ int wrote;
++ struct {
++ ubstattime_t start_time;
++ ubstattime_t end_time;
++ ubstatparm_t param[UB_RESOURCES];
++ } *data;
++ int resource;
++
++ data = kbuf;
++ data->start_time = ubs_start_time;
++ data->end_time = ubs_end_time;
++ wrote = sizeof(data->start_time) + sizeof(data->end_time);
++
++ for (resource = 0; resource < UB_RESOURCES; resource++) {
++ if (size < wrote + sizeof(data->param[resource]))
++ break;
++ data->param[resource].maxheld = ub->ub_store[resource].maxheld;
++ data->param[resource].failcnt = ub->ub_store[resource].failcnt;
++ wrote += sizeof(data->param[resource]);
++ }
++
++ return wrote;
++}
++
++static int ubstat_do_read_full(struct user_beancounter *ub, void *kbuf,
++ int size)
++{
++ int wrote;
++ struct {
++ ubstattime_t start_time;
++ ubstattime_t end_time;
++ ubstatparmf_t param[UB_RESOURCES];
++ } *data;
++ int resource;
++
++ data = kbuf;
++ data->start_time = ubs_start_time;
++ data->end_time = ubs_end_time;
++ wrote = sizeof(data->start_time) + sizeof(data->end_time);
++
++ for (resource = 0; resource < UB_RESOURCES; resource++) {
++ if (size < wrote + sizeof(data->param[resource]))
++ break;
++ /* The beginning of ubstatparmf_t matches struct ubparm. */
++ memcpy(&data->param[resource], &ub->ub_store[resource],
++ sizeof(ub->ub_store[resource]));
++ data->param[resource].__unused1 = 0;
++ data->param[resource].__unused2 = 0;
++ wrote += sizeof(data->param[resource]);
++ }
++ return wrote;
++}
++
++static int ubstat_get_stat(struct user_beancounter *ub, long cmd,
++ void __user *buf, long size)
++{
++ void *kbuf;
++ int retval;
++
++ kbuf = (void *)__get_free_page(GFP_KERNEL);
++ if (kbuf == NULL)
++ return -ENOMEM;
++
++ spin_lock(&ubs_notify_lock);
++ switch (UBSTAT_CMD(cmd)) {
++ case UBSTAT_READ_ONE:
++ retval = -EINVAL;
++ if (UBSTAT_PARMID(cmd) >= UB_RESOURCES)
++ break;
++ retval = ubstat_do_read_one(ub,
++ UBSTAT_PARMID(cmd), kbuf);
++ break;
++ case UBSTAT_READ_ALL:
++ retval = ubstat_do_read_all(ub, kbuf, PAGE_SIZE);
++ break;
++ case UBSTAT_READ_FULL:
++ retval = ubstat_do_read_full(ub, kbuf, PAGE_SIZE);
++ break;
++ default:
++ retval = -EINVAL;
++ }
++ spin_unlock(&ubs_notify_lock);
++
++ if (retval > 0) {
++ retval = min_t(long, retval, size);
++ if (copy_to_user(buf, kbuf, retval))
++ retval = -EFAULT;
++ }
++
++ free_page((unsigned long)kbuf);
++ return retval;
++}
++
++static int ubstat_handle_notifrq(ubnotifrq_t *req)
++{
++ int retval;
++ struct ub_stat_notify *new_notify;
++ struct list_head *entry;
++ struct task_struct *tsk_to_free;
++
++ new_notify = kmalloc(sizeof(new_notify), GFP_KERNEL);
++ if (new_notify == NULL)
++ return -ENOMEM;
++
++ tsk_to_free = NULL;
++ INIT_LIST_HEAD(&new_notify->list);
++
++ spin_lock(&ubs_notify_lock);
++ list_for_each(entry, &ubs_notify_list) {
++ struct ub_stat_notify *notify;
++
++ notify = list_entry(entry, struct ub_stat_notify, list);
++ if (notify->task == current) {
++ kfree(new_notify);
++ new_notify = notify;
++ break;
++ }
++ }
++
++ retval = -EINVAL;
++ if (req->maxinterval < 1)
++ goto out_unlock;
++ if (req->maxinterval > TIME_MAX_SEC)
++ req->maxinterval = TIME_MAX_SEC;
++ if (req->maxinterval < ubs_min_interval) {
++ unsigned long dif;
++
++ ubs_min_interval = req->maxinterval;
++ dif = (ubs_timer.expires - jiffies + HZ - 1) / HZ;
++ if (dif > req->maxinterval)
++ mod_timer(&ubs_timer,
++ ubs_timer.expires -
++ (dif - req->maxinterval) * HZ);
++ }
++
++ if (entry != &ubs_notify_list) {
++ list_del(&new_notify->list);
++ tsk_to_free = new_notify->task;
++ }
++ if (req->signum) {
++ new_notify->task = current;
++ get_task_struct(new_notify->task);
++ new_notify->signum = req->signum;
++ list_add(&new_notify->list, &ubs_notify_list);
++ } else
++ kfree(new_notify);
++ retval = 0;
++out_unlock:
++ spin_unlock(&ubs_notify_lock);
++ if (tsk_to_free != NULL)
++ put_task_struct(tsk_to_free);
++ return retval;
++}
++
++/*
++ * former sys_ubstat
++ */
++long do_ubstat(int func, unsigned long arg1, unsigned long arg2,
++ void __user *buf, long size)
++{
++ int retval;
++ struct user_beancounter *ub;
++
++ if (func == UBSTAT_UBPARMNUM)
++ return UB_RESOURCES;
++ if (func == UBSTAT_UBLIST)
++ return ubstat_get_list(buf, size);
++ if (!(capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)))
++ return -EPERM;
++
++ if (func == UBSTAT_GETTIME) {
++ retval = ubstat_gettime(buf, size);
++ goto notify;
++ }
++
++ ub = get_exec_ub();
++ if (ub != NULL && ub->ub_uid == arg1)
++ get_beancounter(ub);
++ else /* FIXME must be if (ve_is_super) */
++ ub = get_beancounter_byuid(arg1, 0);
++
++ if (ub == NULL)
++ return -ESRCH;
++
++ retval = ubstat_get_stat(ub, func, buf, size);
++ put_beancounter(ub);
++notify:
++ /* Handle request for notification */
++ if (retval >= 0) {
++ ubnotifrq_t notifrq;
++ int err;
++
++ err = -EFAULT;
++ if (!copy_from_user(&notifrq, (void __user *)arg2,
++ sizeof(notifrq)))
++ err = ubstat_handle_notifrq(&notifrq);
++ if (err)
++ retval = err;
++ }
++
++ return retval;
++}
++
++static void ubstat_save_onestat(struct user_beancounter *ub)
++{
++ int resource;
++
++ /* called with local irq disabled */
++ spin_lock(&ub->ub_lock);
++ for (resource = 0; resource < UB_RESOURCES; resource++) {
++ memcpy(&ub->ub_store[resource], &ub->ub_parms[resource],
++ sizeof(struct ubparm));
++ ub->ub_parms[resource].minheld =
++ ub->ub_parms[resource].maxheld =
++ ub->ub_parms[resource].held;
++ }
++ spin_unlock(&ub->ub_lock);
++}
++
++static void ubstat_save_statistics(void)
++{
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ local_irq_save(flags);
++ for_each_beancounter (ub)
++ ubstat_save_onestat(ub);
++ local_irq_restore(flags);
++}
++
++static void ubstatd_timeout(unsigned long __data)
++{
++ struct task_struct *p;
++
++ p = (struct task_struct *) __data;
++ wake_up_process(p);
++}
++
++/*
++ * Safe wrapper for send_sig. It prevents a race with release_task
++ * for sighand.
++ * Should be called under tasklist_lock.
++ */
++static void task_send_sig(struct ub_stat_notify *notify)
++{
++ if (likely(notify->task->sighand != NULL))
++ send_sig(notify->signum, notify->task, 1);
++}
++
++static inline void do_notifies(void)
++{
++ LIST_HEAD(notif_free_list);
++ struct ub_stat_notify *notify;
++ struct ub_stat_notify *tmp;
++
++ spin_lock(&ubs_notify_lock);
++ ubs_start_time = ubs_end_time;
++ /*
++ * the expression below relies on time being unsigned long and
++ * arithmetic promotion rules
++ */
++ ubs_end_time += (ubs_timer.expires - ubs_start_time * HZ) / HZ;
++ mod_timer(&ubs_timer, ubs_timer.expires + ubs_min_interval * HZ);
++ ubs_min_interval = TIME_MAX_SEC;
++ /* save statistics accumulated for the interval */
++ ubstat_save_statistics();
++ /* send signals */
++ read_lock(&tasklist_lock);
++ while (!list_empty(&ubs_notify_list)) {
++ notify = list_entry(ubs_notify_list.next,
++ struct ub_stat_notify, list);
++ task_send_sig(notify);
++ list_del(&notify->list);
++ list_add(&notify->list, &notif_free_list);
++ }
++ read_unlock(&tasklist_lock);
++ spin_unlock(&ubs_notify_lock);
++
++ list_for_each_entry_safe(notify, tmp, &notif_free_list, list) {
++ put_task_struct(notify->task);
++ kfree(notify);
++ }
++}
++
++/*
++ * Kernel thread
++ */
++static int ubstatd(void *unused)
++{
++ /* daemonize call will take care of signals */
++ daemonize("ubstatd");
++
++ ubs_timer.data = (unsigned long)current;
++ ubs_timer.function = ubstatd_timeout;
++ add_timer(&ubs_timer);
++
++ while (1) {
++ set_task_state(current, TASK_INTERRUPTIBLE);
++ if (time_after(ubs_timer.expires, jiffies)) {
++ schedule();
++ try_to_freeze();
++ continue;
++ }
++
++ __set_task_state(current, TASK_RUNNING);
++ do_notifies();
++ }
++ return 0;
++}
++
++static int __init ubstatd_init(void)
++{
++ init_timer(&ubs_timer);
++ ubs_timer.expires = TIME_MAX_JIF;
++ ubs_min_interval = TIME_MAX_SEC;
++ ubs_start_time = ubs_end_time = 0;
++
++ kernel_thread(ubstatd, NULL, 0);
++ return 0;
++}
++
++module_init(ubstatd_init);
+diff --git a/kernel/bc/sys.c b/kernel/bc/sys.c
+new file mode 100644
+index 0000000..798166b
+--- /dev/null
++++ b/kernel/bc/sys.c
+@@ -0,0 +1,173 @@
++/*
++ * kernel/bc/sys.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/virtinfo.h>
++#include <linux/compat.h>
++#include <asm/uaccess.h>
++
++#include <bc/beancounter.h>
++
++/*
++ * The (rather boring) getluid syscall
++ */
++asmlinkage long sys_getluid(void)
++{
++ struct user_beancounter *ub;
++
++ ub = get_exec_ub();
++ if (ub == NULL)
++ return -EINVAL;
++
++ return ub->ub_uid;
++}
++
++/*
++ * The setluid syscall
++ */
++asmlinkage long sys_setluid(uid_t uid)
++{
++ struct user_beancounter *ub;
++ struct task_beancounter *task_bc;
++ int error;
++
++ task_bc = &current->task_bc;
++
++ /* You may not disown a setluid */
++ error = -EINVAL;
++ if (uid == (uid_t)-1)
++ goto out;
++
++ /* You may only set an ub as root */
++ error = -EPERM;
++ if (!capable(CAP_SETUID))
++ goto out;
++ /*
++ * The ub once set is irrevocable to all
++ * unless it's set from ve0.
++ */
++ if (!ve_is_super(get_exec_env()))
++ goto out;
++
++ /* Ok - set up a beancounter entry for this user */
++ error = -ENOBUFS;
++ ub = get_beancounter_byuid(uid, 1);
++ if (ub == NULL)
++ goto out;
++
++ ub_debug(UBD_ALLOC | UBD_LIMIT, "setluid, bean %p (count %d) "
++ "for %.20s pid %d\n",
++ ub, atomic_read(&ub->ub_refcount),
++ current->comm, current->pid);
++ /* install bc */
++ error = virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_NEWUBC, ub);
++ if (!(error & NOTIFY_FAIL)) {
++ put_beancounter(task_bc->exec_ub);
++ task_bc->exec_ub = ub;
++ if (!(error & NOTIFY_OK)) {
++ put_beancounter(task_bc->fork_sub);
++ task_bc->fork_sub = get_beancounter(ub);
++ }
++ error = 0;
++ } else {
++ put_beancounter(ub);
++ error = -ENOBUFS;
++ }
++out:
++ return error;
++}
++
++long do_setublimit(uid_t uid, unsigned long resource,
++ unsigned long *new_limits)
++{
++ int error;
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ error = -EPERM;
++ if(!capable(CAP_SYS_RESOURCE))
++ goto out;
++
++ if (!ve_is_super(get_exec_env()))
++ goto out;
++
++ error = -EINVAL;
++ if (resource >= UB_RESOURCES)
++ goto out;
++
++ error = -EINVAL;
++ if (new_limits[0] > UB_MAXVALUE || new_limits[1] > UB_MAXVALUE)
++ goto out;
++
++ error = -ENOENT;
++ ub = get_beancounter_byuid(uid, 0);
++ if (ub == NULL) {
++ ub_debug(UBD_LIMIT, "No login bc for uid %d\n", uid);
++ goto out;
++ }
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_parms[resource].barrier = new_limits[0];
++ ub->ub_parms[resource].limit = new_limits[1];
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++
++ put_beancounter(ub);
++
++ error = 0;
++out:
++ return error;
++}
++
++/*
++ * The setbeanlimit syscall
++ */
++asmlinkage long sys_setublimit(uid_t uid, unsigned long resource,
++ unsigned long __user *limits)
++{
++ unsigned long new_limits[2];
++
++ if (copy_from_user(&new_limits, limits, sizeof(new_limits)))
++ return -EFAULT;
++
++ return do_setublimit(uid, resource, new_limits);
++}
++
++extern long do_ubstat(int func, unsigned long arg1, unsigned long arg2,
++ void __user *buf, long size);
++asmlinkage long sys_ubstat(int func, unsigned long arg1, unsigned long arg2,
++ void __user *buf, long size)
++{
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
++
++ return do_ubstat(func, arg1, arg2, buf, size);
++}
++
++#ifdef CONFIG_COMPAT
++asmlinkage long compat_sys_setublimit(uid_t uid, int resource,
++ unsigned int __user *limits)
++{
++ unsigned int u_new_limits[2];
++ unsigned long new_limits[2];
++
++ if (copy_from_user(&u_new_limits, limits, sizeof(u_new_limits)))
++ return -EFAULT;
++
++ new_limits[0] = u_new_limits[0];
++ new_limits[1] = u_new_limits[1];
++
++ return do_setublimit(uid, resource, new_limits);
++}
++
++asmlinkage long compat_sys_ubstat(int func, unsigned int arg1,
++ unsigned int arg2, compat_uptr_t *buf, long size)
++{
++ return sys_ubstat(func, arg1, arg2, buf, size);
++}
++#endif
+diff --git a/kernel/bc/vm_pages.c b/kernel/bc/vm_pages.c
+new file mode 100644
+index 0000000..e98134b
+--- /dev/null
++++ b/kernel/bc/vm_pages.c
+@@ -0,0 +1,549 @@
++/*
++ * kernel/bc/vm_pages.c
++ *
++ * Copyright (C) 2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <linux/virtinfo.h>
++#include <linux/module.h>
++#include <linux/shmem_fs.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
++
++#include <asm/pgtable.h>
++#include <asm/page.h>
++
++#include <bc/beancounter.h>
++#include <bc/vmpages.h>
++#include <bc/proc.h>
++
++static inline unsigned long pages_in_pte_range(struct vm_area_struct *vma,
++ pmd_t *pmd, unsigned long addr, unsigned long end,
++ unsigned long *ret)
++{
++ pte_t *pte;
++ spinlock_t *ptl;
++
++ pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
++ do {
++ if (!pte_none(*pte) && pte_present(*pte))
++ (*ret)++;
++ } while (pte++, addr += PAGE_SIZE, (addr != end));
++ pte_unmap_unlock(pte - 1, ptl);
++
++ return addr;
++}
++
++static inline unsigned long pages_in_pmd_range(struct vm_area_struct *vma,
++ pud_t *pud, unsigned long addr, unsigned long end,
++ unsigned long *ret)
++{
++ pmd_t *pmd;
++ unsigned long next;
++
++ pmd = pmd_offset(pud, addr);
++ do {
++ next = pmd_addr_end(addr, end);
++ if (pmd_none_or_clear_bad(pmd))
++ continue;
++ next = pages_in_pte_range(vma, pmd, addr, next, ret);
++ } while (pmd++, addr = next, (addr != end));
++
++ return addr;
++}
++
++static inline unsigned long pages_in_pud_range(struct vm_area_struct *vma,
++ pgd_t *pgd, unsigned long addr, unsigned long end,
++ unsigned long *ret)
++{
++ pud_t *pud;
++ unsigned long next;
++
++ pud = pud_offset(pgd, addr);
++ do {
++ next = pud_addr_end(addr, end);
++ if (pud_none_or_clear_bad(pud))
++ continue;
++ next = pages_in_pmd_range(vma, pud, addr, next, ret);
++ } while (pud++, addr = next, (addr != end));
++
++ return addr;
++}
++
++unsigned long pages_in_vma_range(struct vm_area_struct *vma,
++ unsigned long addr, unsigned long end)
++{
++ pgd_t *pgd;
++ unsigned long next;
++ unsigned long ret;
++
++ ret = 0;
++ BUG_ON(addr >= end);
++ pgd = pgd_offset(vma->vm_mm, addr);
++ do {
++ next = pgd_addr_end(addr, end);
++ if (pgd_none_or_clear_bad(pgd))
++ continue;
++ next = pages_in_pud_range(vma, pgd, addr, next, &ret);
++ } while (pgd++, addr = next, (addr != end));
++ return ret;
++}
++
++void __ub_update_physpages(struct user_beancounter *ub)
++{
++ ub->ub_parms[UB_PHYSPAGES].held = ub->ub_tmpfs_respages
++ + (ub->ub_held_pages >> UB_PAGE_WEIGHT_SHIFT);
++ ub_adjust_maxheld(ub, UB_PHYSPAGES);
++}
++
++void __ub_update_oomguarpages(struct user_beancounter *ub)
++{
++ ub->ub_parms[UB_OOMGUARPAGES].held =
++ ub->ub_parms[UB_PHYSPAGES].held + ub->ub_swap_pages;
++ ub_adjust_maxheld(ub, UB_OOMGUARPAGES);
++}
++
++void __ub_update_privvm(struct user_beancounter *ub)
++{
++ ub->ub_parms[UB_PRIVVMPAGES].held =
++ (ub->ub_held_pages >> UB_PAGE_WEIGHT_SHIFT)
++ + ub->ub_unused_privvmpages
++ + ub->ub_parms[UB_SHMPAGES].held;
++ ub_adjust_maxheld(ub, UB_PRIVVMPAGES);
++}
++
++static inline int __charge_privvm_locked(struct user_beancounter *ub,
++ unsigned long s, enum ub_severity strict)
++{
++ if (__charge_beancounter_locked(ub, UB_PRIVVMPAGES, s, strict) < 0)
++ return -ENOMEM;
++
++ ub->ub_unused_privvmpages += s;
++ return 0;
++}
++
++static void __unused_privvm_dec_locked(struct user_beancounter *ub,
++ long size)
++{
++ /* catch possible overflow */
++ if (ub->ub_unused_privvmpages < size) {
++ uncharge_warn(ub, UB_UNUSEDPRIVVM,
++ size, ub->ub_unused_privvmpages);
++ size = ub->ub_unused_privvmpages;
++ }
++ ub->ub_unused_privvmpages -= size;
++ __ub_update_privvm(ub);
++}
++
++void __ub_unused_privvm_dec(struct mm_struct *mm, long size)
++{
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return;
++
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __unused_privvm_dec_locked(ub, size);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++void ub_unused_privvm_sub(struct mm_struct *mm,
++ struct vm_area_struct *vma, unsigned long count)
++{
++ if (VM_UB_PRIVATE(vma->vm_flags, vma->vm_file))
++ __ub_unused_privvm_dec(mm, count);
++}
++
++void ub_unused_privvm_add(struct mm_struct *mm,
++ struct vm_area_struct *vma, unsigned long size)
++{
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ ub = mm->mm_ub;
++ if (ub == NULL || !VM_UB_PRIVATE(vma->vm_flags, vma->vm_file))
++ return;
++
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_unused_privvmpages += size;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++int ub_protected_charge(struct mm_struct *mm, unsigned long size,
++ unsigned long newflags, struct vm_area_struct *vma)
++{
++ unsigned long flags;
++ struct file *file;
++ struct user_beancounter *ub;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return PRIVVM_NO_CHARGE;
++
++ flags = vma->vm_flags;
++ if (!((newflags ^ flags) & VM_WRITE))
++ return PRIVVM_NO_CHARGE;
++
++ file = vma->vm_file;
++ if (!VM_UB_PRIVATE(newflags | VM_WRITE, file))
++ return PRIVVM_NO_CHARGE;
++
++ if (flags & VM_WRITE)
++ return PRIVVM_TO_SHARED;
++
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (__charge_privvm_locked(ub, size, UB_SOFT) < 0)
++ goto err;
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return PRIVVM_TO_PRIVATE;
++
++err:
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return PRIVVM_ERROR;
++}
++
++int ub_memory_charge(struct mm_struct *mm, unsigned long size,
++ unsigned vm_flags, struct file *vm_file, int sv)
++{
++ struct user_beancounter *ub, *ubl;
++ unsigned long flags;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return 0;
++
++ size >>= PAGE_SHIFT;
++ if (size > UB_MAXVALUE)
++ return -EINVAL;
++
++ BUG_ON(sv != UB_SOFT && sv != UB_HARD);
++
++ if (vm_flags & VM_LOCKED) {
++ if (charge_beancounter(ub, UB_LOCKEDPAGES, size, sv))
++ goto out_err;
++ }
++ if (VM_UB_PRIVATE(vm_flags, vm_file)) {
++ ubl = top_beancounter(ub);
++ spin_lock_irqsave(&ubl->ub_lock, flags);
++ if (__charge_privvm_locked(ubl, size, sv))
++ goto out_private;
++ spin_unlock_irqrestore(&ubl->ub_lock, flags);
++ }
++ return 0;
++
++out_private:
++ spin_unlock_irqrestore(&ubl->ub_lock, flags);
++ if (vm_flags & VM_LOCKED)
++ uncharge_beancounter(ub, UB_LOCKEDPAGES, size);
++out_err:
++ return -ENOMEM;
++}
++
++void ub_memory_uncharge(struct mm_struct *mm, unsigned long size,
++ unsigned vm_flags, struct file *vm_file)
++{
++ struct user_beancounter *ub;
++ unsigned long flags;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return;
++
++ size >>= PAGE_SHIFT;
++
++ if (vm_flags & VM_LOCKED)
++ uncharge_beancounter(ub, UB_LOCKEDPAGES, size);
++ if (VM_UB_PRIVATE(vm_flags, vm_file)) {
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __unused_privvm_dec_locked(ub, size);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ }
++}
++
++int ub_locked_charge(struct mm_struct *mm, unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return 0;
++
++ return charge_beancounter(ub, UB_LOCKEDPAGES,
++ size >> PAGE_SHIFT, UB_HARD);
++}
++
++void ub_locked_uncharge(struct mm_struct *mm, unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ ub = mm->mm_ub;
++ if (ub == NULL)
++ return;
++
++ uncharge_beancounter(ub, UB_LOCKEDPAGES, size >> PAGE_SHIFT);
++}
++
++int ub_lockedshm_charge(struct shmem_inode_info *shi, unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ ub = shi->shmi_ub;
++ if (ub == NULL)
++ return 0;
++
++ return charge_beancounter(ub, UB_LOCKEDPAGES,
++ size >> PAGE_SHIFT, UB_HARD);
++}
++
++void ub_lockedshm_uncharge(struct shmem_inode_info *shi, unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ ub = shi->shmi_ub;
++ if (ub == NULL)
++ return;
++
++ uncharge_beancounter(ub, UB_LOCKEDPAGES, size >> PAGE_SHIFT);
++}
++
++
++static inline void do_ub_tmpfs_respages_inc(struct user_beancounter *ub)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_tmpfs_respages++;
++ __ub_update_physpages(ub);
++ __ub_update_oomguarpages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++void ub_tmpfs_respages_inc(struct shmem_inode_info *shi)
++{
++ struct user_beancounter *ub;
++
++ for (ub = shi->shmi_ub; ub != NULL; ub = ub->parent)
++ do_ub_tmpfs_respages_inc(ub);
++}
++
++static inline void do_ub_tmpfs_respages_sub(struct user_beancounter *ub,
++ unsigned long size)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ /* catch possible overflow */
++ if (ub->ub_tmpfs_respages < size) {
++ uncharge_warn(ub, UB_TMPFSPAGES,
++ size, ub->ub_tmpfs_respages);
++ size = ub->ub_tmpfs_respages;
++ }
++ ub->ub_tmpfs_respages -= size;
++ /* update values what is the most interesting */
++ __ub_update_physpages(ub);
++ __ub_update_oomguarpages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++void ub_tmpfs_respages_sub(struct shmem_inode_info *shi,
++ unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ for (ub = shi->shmi_ub; ub != NULL; ub = ub->parent)
++ do_ub_tmpfs_respages_sub(ub, size);
++}
++
++int ub_shmpages_charge(struct shmem_inode_info *shi, unsigned long size)
++{
++ int ret;
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ ub = shi->shmi_ub;
++ if (ub == NULL)
++ return 0;
++
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ret = __charge_beancounter_locked(ub, UB_SHMPAGES, size, UB_HARD);
++ if (ret == 0)
++ __ub_update_privvm(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++ return ret;
++}
++
++void ub_shmpages_uncharge(struct shmem_inode_info *shi, unsigned long size)
++{
++ unsigned long flags;
++ struct user_beancounter *ub;
++
++ ub = shi->shmi_ub;
++ if (ub == NULL)
++ return;
++
++ ub = top_beancounter(ub);
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ __uncharge_beancounter_locked(ub, UB_SHMPAGES, size);
++ __ub_update_privvm(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++#ifdef CONFIG_BC_SWAP_ACCOUNTING
++static inline void do_ub_swapentry_inc(struct user_beancounter *ub)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ ub->ub_swap_pages++;
++ __ub_update_oomguarpages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++void ub_swapentry_inc(struct swap_info_struct *si, pgoff_t num,
++ struct user_beancounter *ub)
++{
++ si->swap_ubs[num] = get_beancounter(ub);
++ for (; ub != NULL; ub = ub->parent)
++ do_ub_swapentry_inc(ub);
++}
++EXPORT_SYMBOL(ub_swapentry_inc);
++
++static inline void do_ub_swapentry_dec(struct user_beancounter *ub)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&ub->ub_lock, flags);
++ if (ub->ub_swap_pages <= 0)
++ uncharge_warn(ub, UB_SWAPPAGES, 1, ub->ub_swap_pages);
++ else
++ ub->ub_swap_pages--;
++ __ub_update_oomguarpages(ub);
++ spin_unlock_irqrestore(&ub->ub_lock, flags);
++}
++
++void ub_swapentry_dec(struct swap_info_struct *si, pgoff_t num)
++{
++ struct user_beancounter *ub, *ubp;
++
++ ub = si->swap_ubs[num];
++ si->swap_ubs[num] = NULL;
++ for (ubp = ub; ubp != NULL; ubp = ubp->parent)
++ do_ub_swapentry_dec(ubp);
++ put_beancounter(ub);
++}
++EXPORT_SYMBOL(ub_swapentry_dec);
++
++int ub_swap_init(struct swap_info_struct *si, pgoff_t num)
++{
++ struct user_beancounter **ubs;
++
++ ubs = vmalloc(num * sizeof(struct user_beancounter *));
++ if (ubs == NULL)
++ return -ENOMEM;
++
++ memset(ubs, 0, num * sizeof(struct user_beancounter *));
++ si->swap_ubs = ubs;
++ return 0;
++}
++
++void ub_swap_fini(struct swap_info_struct *si)
++{
++ if (si->swap_ubs) {
++ vfree(si->swap_ubs);
++ si->swap_ubs = NULL;
++ }
++}
++#endif
++
++static int vmguar_enough_memory(struct vnotifier_block *self,
++ unsigned long event, void *arg, int old_ret)
++{
++ struct user_beancounter *ub;
++
++ if (event != VIRTINFO_ENOUGHMEM)
++ return old_ret;
++ /*
++ * If it's a kernel thread, don't care about it.
++ * Added in order aufsd to run smoothly over ramfs.
++ */
++ if (!current->mm)
++ return NOTIFY_DONE;
++
++ ub = top_beancounter(current->mm->mm_ub);
++ if (ub->ub_parms[UB_PRIVVMPAGES].held >
++ ub->ub_parms[UB_VMGUARPAGES].barrier)
++ return old_ret;
++
++ return NOTIFY_OK;
++}
++
++static struct vnotifier_block vmguar_notifier_block = {
++ .notifier_call = vmguar_enough_memory
++};
++
++static int __init init_vmguar_notifier(void)
++{
++ virtinfo_notifier_register(VITYPE_GENERAL, &vmguar_notifier_block);
++ return 0;
++}
++
++static void __exit fini_vmguar_notifier(void)
++{
++ virtinfo_notifier_unregister(VITYPE_GENERAL, &vmguar_notifier_block);
++}
++
++module_init(init_vmguar_notifier);
++module_exit(fini_vmguar_notifier);
++
++#ifdef CONFIG_PROC_FS
++static int bc_vmaux_show(struct seq_file *f, void *v)
++{
++ struct user_beancounter *ub;
++ unsigned long swap, unmap;
++ int i;
++
++ ub = seq_beancounter(f);
++
++ swap = unmap = 0;
++ for_each_online_cpu(i) {
++ swap += per_cpu_ptr(ub->ub_percpu, i)->swapin;
++ unmap += per_cpu_ptr(ub->ub_percpu, i)->unmap;
++ }
++
++ seq_printf(f, bc_proc_lu_fmt, ub_rnames[UB_UNUSEDPRIVVM],
++ ub->ub_unused_privvmpages);
++ seq_printf(f, bc_proc_lu_fmt, ub_rnames[UB_TMPFSPAGES],
++ ub->ub_tmpfs_respages);
++ seq_printf(f, bc_proc_lu_fmt, ub_rnames[UB_SWAPPAGES],
++ ub->ub_swap_pages);
++
++ seq_printf(f, bc_proc_lu_fmt, "swapin", swap);
++ seq_printf(f, bc_proc_lu_fmt, "unmap", unmap);
++ return 0;
++}
++static struct bc_proc_entry bc_vmaux_entry = {
++ .name = "vmaux",
++ .u.show = bc_vmaux_show,
++};
++
++static int __init bc_vmaux_init(void)
++{
++ bc_register_proc_entry(&bc_vmaux_entry);
++ return 0;
++}
++
++late_initcall(bc_vmaux_init);
++#endif
+diff --git a/kernel/capability.c b/kernel/capability.c
+index 33e51e7..9c6a925 100644
+--- a/kernel/capability.c
++++ b/kernel/capability.c
+@@ -19,7 +19,8 @@
+ * This lock protects task->cap_* for all tasks including current.
+ * Locking rule: acquire this prior to tasklist_lock.
+ */
+-static DEFINE_SPINLOCK(task_capability_lock);
++DEFINE_SPINLOCK(task_capability_lock);
++EXPORT_SYMBOL(task_capability_lock);
+
+ /*
+ * Leveraged for setting/resetting capabilities
+@@ -167,7 +168,7 @@ static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
+ pgrp = find_vpid(pgrp_nr);
+ do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
+ target = g;
+- while_each_thread(g, target) {
++ while_each_thread_ve(g, target) {
+ if (!security_capset_check(target, effective,
+ inheritable, permitted)) {
+ security_capset_set(target, effective,
+@@ -201,7 +202,7 @@ static inline int cap_set_all(kernel_cap_t *effective,
+ spin_lock(&task_capability_lock);
+ read_lock(&tasklist_lock);
+
+- do_each_thread(g, target) {
++ do_each_thread_ve(g, target) {
+ if (target == current
+ || is_container_init(target->group_leader))
+ continue;
+@@ -211,7 +212,7 @@ static inline int cap_set_all(kernel_cap_t *effective,
+ continue;
+ ret = 0;
+ security_capset_set(target, effective, inheritable, permitted);
+- } while_each_thread(g, target);
++ } while_each_thread_ve(g, target);
+
+ read_unlock(&tasklist_lock);
+ spin_unlock(&task_capability_lock);
+diff --git a/kernel/cgroup.c b/kernel/cgroup.c
+index a0123d7..8412865 100644
+--- a/kernel/cgroup.c
++++ b/kernel/cgroup.c
+@@ -1773,7 +1773,7 @@ static void cgroup_enable_task_cg_lists(void)
+ struct task_struct *p, *g;
+ write_lock(&css_set_lock);
+ use_task_css_set_links = 1;
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ task_lock(p);
+ /*
+ * We should check if the process is exiting, otherwise
+@@ -1783,7 +1783,7 @@ static void cgroup_enable_task_cg_lists(void)
+ if (!(p->flags & PF_EXITING) && list_empty(&p->cg_list))
+ list_add(&p->cg_list, &p->cgroups->tasks);
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ write_unlock(&css_set_lock);
+ }
+
+@@ -2873,9 +2873,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
+ again:
+ root = subsys->root;
+ if (root == &rootnode) {
+- printk(KERN_INFO
+- "Not cloning cgroup for unused subsystem %s\n",
+- subsys->name);
+ mutex_unlock(&cgroup_mutex);
+ return 0;
+ }
+diff --git a/kernel/cgroup_lite.c b/kernel/cgroup_lite.c
+new file mode 100644
+index 0000000..dd89b3b
+--- /dev/null
++++ b/kernel/cgroup_lite.c
+@@ -0,0 +1,226 @@
++/*
++ * lite cgroups engine
++ */
++
++#include <linux/cgroup.h>
++#include <linux/seq_file.h>
++#include <linux/fs.h>
++#include <linux/ve.h>
++#include <linux/proc_fs.h>
++#include <linux/module.h>
++
++#define SUBSYS(_x) &_x ## _subsys,
++
++static struct cgroup_subsys *subsys[] = {
++#include <linux/cgroup_subsys.h>
++};
++
++static struct css_set init_css_set;
++static struct cgroup init_cgroup;
++static struct cftype *subsys_cftypes[CGROUP_SUBSYS_COUNT];
++
++static int init_css_set_subsystems(struct cgroup *g, struct css_set *set)
++{
++ int i;
++ struct cgroup_subsys_state *ss;
++
++ for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
++ struct cgroup_subsys *cs = subsys[i];
++
++ ss = cs->create(cs, g);
++ if (IS_ERR(ss))
++ goto destroy;
++
++ g->subsys[i] = ss;
++ set->subsys[i] = ss;
++ atomic_set(&ss->refcnt, 0);
++ ss->cgroup = g;
++ }
++ return 0;
++
++destroy:
++ for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
++ struct cgroup_subsys *cs = subsys[i];
++
++ if (g->subsys[i])
++ cs->destroy(cs, g);
++ }
++ return PTR_ERR(ss);
++}
++
++int init_ve_cgroups(struct ve_struct *ve)
++{
++ int err = -ENOMEM;
++ struct cgroup *g;
++ struct css_set *cs;
++
++ g = kzalloc(sizeof(struct cgroup), GFP_KERNEL);
++ if (g == NULL)
++ goto err_galloc;
++
++ cs = kzalloc(sizeof(struct css_set), GFP_KERNEL);
++ if (cs == NULL)
++ goto err_calloc;
++
++ g->parent = &init_cgroup;
++ err = init_css_set_subsystems(g, cs);
++ if (err)
++ goto err_subsys;
++
++ g->parent = &init_cgroup;
++ ve->ve_cgroup = g;
++ ve->ve_css_set = cs;
++ return 0;
++
++err_subsys:
++ kfree(cs);
++err_calloc:
++ kfree(g);
++err_galloc:
++ return err;
++}
++EXPORT_SYMBOL(init_ve_cgroups);
++
++void fini_ve_cgroups(struct ve_struct *ve)
++{
++ int i;
++ struct cgroup *g = ve->ve_cgroup;
++ struct css_set *css = ve->ve_css_set;
++
++ for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
++ struct cgroup_subsys *cs = subsys[i];
++ struct cgroup_subsys_state *ss = css->subsys[i];
++
++ BUG_ON(ss != g->subsys[i]);
++
++ if (cs->pre_destroy)
++ cs->pre_destroy(cs, g);
++
++ if (atomic_read(&ss->refcnt))
++ printk(KERN_ERR "CG: leaking %d/%s subsys\n",
++ ve->veid, subsys[i]->name);
++ else
++ cs->destroy(cs, g);
++ }
++
++ kfree(g);
++ kfree(css);
++ ve->ve_cgroup = NULL;
++ ve->ve_css_set = NULL;
++}
++EXPORT_SYMBOL(fini_ve_cgroups);
++
++/*
++ * task lifecycle
++ */
++
++void cgroup_fork(struct task_struct *child)
++{
++ child->cgroups = current->cgroups;
++}
++
++void cgroup_fork_callbacks(struct task_struct *child)
++{
++}
++
++void cgroup_post_fork(struct task_struct *child)
++{
++}
++
++void cgroup_exit(struct task_struct *tsk, int dummy)
++{
++ tsk->cgroups = &init_css_set;
++}
++
++int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry)
++{
++ return -ENODATA;
++}
++
++/*
++ * proc struts
++ */
++
++static int proc_cgroup_show(struct seq_file *m, void *v)
++{
++ struct task_struct *tsk;
++
++ tsk = pid_task((struct pid *)m->private, PIDTYPE_PID);
++ seq_printf(m, "%p\n", tsk->cgroups);
++ return 0;
++}
++
++static int cgroup_open(struct inode *inode, struct file *file)
++{
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ return single_open(file, proc_cgroup_show, PROC_I(inode)->pid);
++}
++
++struct file_operations proc_cgroup_operations = {
++ .open = cgroup_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++/*
++ * cgroups misc struts
++ */
++
++int cgroup_add_files(struct cgroup *cgrp, struct cgroup_subsys *subsys,
++ const struct cftype cft[], int count)
++{
++ int idx = subsys->subsys_id;
++ static DEFINE_SPINLOCK(add_files_lock);
++
++ if (unlikely(subsys_cftypes[idx] == NULL)) {
++ spin_lock(&add_files_lock);
++ if (subsys_cftypes[idx] == NULL)
++ subsys_cftypes[idx] = (struct cftype *)cft;
++ spin_unlock(&add_files_lock);
++ }
++
++ BUG_ON(subsys_cftypes[idx] != cft);
++ return 0;
++}
++
++void cgroup_lock(void)
++{
++}
++
++void cgroup_unlock(void)
++{
++}
++
++bool cgroup_lock_live_group(struct cgroup *cg)
++{
++ return 1;
++}
++
++
++int cgroup_is_removed(const struct cgroup *cgrp)
++{
++ return 0;
++}
++
++int __init cgroup_init_early(void)
++{
++ int i;
++
++ init_task.cgroups = &init_css_set;
++ for (i = 0; i < CGROUP_SUBSYS_COUNT; i++)
++ BUG_ON(subsys[i]->early_init);
++
++ return 0;
++}
++
++int __init cgroup_init(void)
++{
++ get_ve0()->ve_cgroup = &init_cgroup;
++ get_ve0()->ve_css_set = &init_css_set;
++ if (init_css_set_subsystems(&init_cgroup, &init_css_set) != 0)
++ panic("CG: Can't init initial set\n");
++ return 0;
++}
+diff --git a/kernel/compat.c b/kernel/compat.c
+index 32c254a..58506ef 100644
+--- a/kernel/compat.c
++++ b/kernel/compat.c
+@@ -22,6 +22,7 @@
+ #include <linux/security.h>
+ #include <linux/timex.h>
+ #include <linux/migrate.h>
++#include <linux/module.h>
+ #include <linux/posix-timers.h>
+
+ #include <asm/uaccess.h>
+@@ -40,7 +41,7 @@ int put_compat_timespec(const struct timespec *ts, struct compat_timespec __user
+ __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
+ }
+
+-static long compat_nanosleep_restart(struct restart_block *restart)
++long compat_nanosleep_restart(struct restart_block *restart)
+ {
+ struct compat_timespec __user *rmtp;
+ struct timespec rmt;
+@@ -62,6 +63,7 @@ static long compat_nanosleep_restart(struct restart_block *restart)
+
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(compat_nanosleep_restart);
+
+ asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp,
+ struct compat_timespec __user *rmtp)
+diff --git a/kernel/cpt/Makefile b/kernel/cpt/Makefile
+new file mode 100644
+index 0000000..d97cc31
+--- /dev/null
++++ b/kernel/cpt/Makefile
+@@ -0,0 +1,53 @@
++#
++#
++# kernel/cpt/Makefile
++#
++# Copyright (C) 2000-2005 SWsoft
++# All rights reserved.
++#
++# Licensing governed by "linux/COPYING.SWsoft" file.
++
++obj-$(CONFIG_VZ_CHECKPOINT) += vzcpt.o vzrst.o
++
++vzcpt-objs := cpt_proc.o cpt_dump.o cpt_obj.o cpt_context.o cpt_process.o \
++ cpt_mm.o cpt_files.o cpt_kernel.o \
++ cpt_socket.o cpt_socket_in.o cpt_tty.o cpt_sysvipc.o cpt_net.o \
++ cpt_conntrack.o cpt_epoll.o
++
++vzrst-objs := rst_proc.o rst_undump.o rst_context.o rst_process.o \
++ rst_mm.o rst_files.o \
++ rst_socket.o rst_socket_in.o rst_tty.o rst_sysvipc.o rst_net.o \
++ rst_conntrack.o rst_epoll.o
++
++ifeq ($(CONFIG_BEANCOUNTERS), y)
++vzcpt-objs += cpt_ubc.o
++vzrst-objs += rst_ubc.o
++endif
++
++ifeq ($(CONFIG_INOTIFY_USER), y)
++vzcpt-objs += cpt_inotify.o
++vzrst-objs += rst_inotify.o
++endif
++
++vzrst-objs += cpt_exports.o
++
++ifeq ($(CONFIG_VZ_CHECKPOINT), m)
++vzrst-objs += cpt_obj.o cpt_kernel.o
++endif
++
++ifeq ($(CONFIG_VZ_CHECKPOINT_ITER), y)
++vzcpt-objs += cpt_iterative.o
++vzrst-objs += rst_iterative.o
++endif
++
++ifeq ($(CONFIG_VZ_CHECKPOINT_LAZY), y)
++vzcpt-objs += cpt_pagein.o
++vzrst-objs += rst_pagein.o
++endif
++
++ifeq ($(CONFIG_X86_64), y)
++vzcpt-objs += cpt_x8664.o
++ifeq ($(CONFIG_VZ_CHECKPOINT), m)
++vzrst-objs += cpt_x8664.o
++endif
++endif
+diff --git a/kernel/cpt/cpt_conntrack.c b/kernel/cpt/cpt_conntrack.c
+new file mode 100644
+index 0000000..19dcf32
+--- /dev/null
++++ b/kernel/cpt/cpt_conntrack.c
+@@ -0,0 +1,365 @@
++/*
++ *
++ * kernel/cpt/cpt_conntrack.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/unistd.h>
++#include <linux/ve.h>
++#include <linux/vzcalluser.h>
++#include <linux/cpt_image.h>
++#include <linux/icmp.h>
++#include <linux/ip.h>
++
++#if defined(CONFIG_VE_IPTABLES) && \
++ (defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE))
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++
++/* How does it work?
++ *
++ * Network is disabled, so new conntrack entries will not appear.
++ * However, some of them can disappear because of timeouts.
++ *
++ * So, we take read_lock, collect all required information atomically,
++ * essentially, creating parallel "refcount" structures holding pointers.
++ * We delete conntrack timers as well, so the structures cannot disappear
++ * after releasing the lock. Now, after releasing lock we can dump everything
++ * safely. And on exit we restore timers to their original values.
++ *
++ * Note, this approach is not going to work in VE0.
++ */
++
++struct ct_holder
++{
++ struct ct_holder *next;
++ struct ip_conntrack_tuple_hash *cth;
++ int index;
++};
++
++static void encode_tuple(struct cpt_ipct_tuple *v, struct ip_conntrack_tuple *tuple)
++{
++ v->cpt_dst = tuple->dst.ip;
++ v->cpt_dstport = tuple->dst.u.all;
++ v->cpt_protonum = tuple->dst.protonum;
++ v->cpt_dir = tuple->dst.dir;
++
++ v->cpt_src = tuple->src.ip;
++ v->cpt_srcport = tuple->src.u.all;
++}
++
++static int dump_one_expect(struct cpt_ip_connexpect_image *v,
++ struct ip_conntrack_expect *exp,
++ int sibling, cpt_context_t *ctx)
++{
++ int err = 0;
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_NET_CONNTRACK_EXPECT;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ encode_tuple(&v->cpt_tuple, &exp->tuple);
++ encode_tuple(&v->cpt_mask, &exp->mask);
++ v->cpt_sibling_conntrack = sibling;
++ v->cpt_flags = exp->flags;
++ v->cpt_seq = exp->id;
++ v->cpt_dir = 0;
++ v->cpt_manip_proto = 0;
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++ v->cpt_manip_proto = exp->saved_proto.all;
++ v->cpt_dir = exp->dir;
++#endif
++ v->cpt_timeout = 0;
++ if (exp->master->helper->timeout)
++ v->cpt_timeout = exp->timeout.expires - jiffies;
++ return err;
++}
++
++/* NOTE. We use one page to dump list of expectations. This may be not enough
++ * in theory. In practice there is only one expectation per conntrack record.
++ * Moreover, taking into account that _ALL_ of expecations are saved in one
++ * global list, which is looked up each incoming/outpging packet, the system
++ * would be severely dead when even one conntrack would have so much of
++ * expectations. Shortly, I am not going to repair this.
++ */
++
++static int dump_expect_list(struct ip_conntrack *ct, struct ct_holder *list,
++ cpt_context_t *ctx)
++{
++ int err = 0;
++ unsigned long pg;
++ struct cpt_ip_connexpect_image *v;
++ struct ip_conntrack_expect *exp;
++
++ if (ct->expecting == 0)
++ return err;
++ if (ct->expecting*sizeof(struct cpt_ip_connexpect_image) > PAGE_SIZE)
++ return -ENOBUFS;
++
++ pg = __get_free_page(GFP_KERNEL);
++ if (!pg)
++ return -ENOMEM;
++ v = (struct cpt_ip_connexpect_image *)pg;
++
++ read_lock_bh(&ip_conntrack_lock);
++ list_for_each_entry(exp, &ve_ip_conntrack_expect_list, list) {
++ int sibling;
++
++ if (exp->master != ct)
++ continue;
++
++ if (ct->helper == NULL) {
++ eprintk_ctx("conntrack: no helper and non-trivial expectation\n");
++ err = -EINVAL;
++ break;
++ }
++
++ sibling = 0;
++#if 0
++ /* That's all? No need to calculate sibling? */
++ if (exp->sibling) {
++ struct ct_holder *c;
++ for (c = list; c; c = c->next) {
++ if (tuplehash_to_ctrack(c->cth) == exp->sibling) {
++ sibling = c->index;
++ break;
++ }
++ }
++ /* NOTE: exp->sibling could be not "confirmed" and, hence,
++ * out of hash table. We should just ignore such a sibling,
++ * the connection is going to be retried, the packet
++ * apparently was lost somewhere.
++ */
++ if (sibling == 0)
++ dprintk_ctx("sibling conntrack is not found\n");
++ }
++#endif
++
++ /* If the expectation still does not have exp->sibling
++ * and timer is not running, it is about to die on another
++ * cpu. Skip it. */
++ if (!sibling &&
++ ct->helper->timeout &&
++ !timer_pending(&exp->timeout)) {
++ dprintk_ctx("conntrack: expectation: no timer\n");
++ continue;
++ }
++
++ err = dump_one_expect(v, exp, sibling, ctx);
++ if (err)
++ break;
++
++ v++;
++ }
++ read_unlock_bh(&ip_conntrack_lock);
++
++ if (err == 0 && (unsigned long)v != pg)
++ ctx->write((void*)pg, (unsigned long)v - pg, ctx);
++
++ free_page(pg);
++ return err;
++}
++
++static int dump_one_ct(struct ct_holder *c, struct ct_holder *list,
++ cpt_context_t *ctx)
++{
++ struct ip_conntrack_tuple_hash *h = c->cth;
++ struct ip_conntrack *ct = tuplehash_to_ctrack(h);
++ struct cpt_ip_conntrack_image v;
++ int err = 0;
++
++ if (sizeof(v.cpt_proto_data) != sizeof(ct->proto)) {
++ eprintk_ctx("conntrack module ct->proto version mismatch\n");
++ return -EINVAL;
++ }
++
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_CONNTRACK;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_ARRAY;
++
++ read_lock_bh(&ip_conntrack_lock);
++ v.cpt_status = ct->status;
++ v.cpt_timeout = ct->timeout.expires - jiffies;
++ v.cpt_ct_helper = (ct->helper != NULL);
++ v.cpt_index = c->index;
++ v.cpt_id = ct->id;
++ v.cpt_mark = 0;
++#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
++ v.cpt_mark = ct->mark;
++#endif
++ encode_tuple(&v.cpt_tuple[0], &ct->tuplehash[0].tuple);
++ encode_tuple(&v.cpt_tuple[1], &ct->tuplehash[1].tuple);
++ memcpy(&v.cpt_proto_data, &ct->proto, sizeof(v.cpt_proto_data));
++ memcpy(&v.cpt_help_data, &ct->help, sizeof(v.cpt_help_data));
++
++ v.cpt_masq_index = 0;
++ v.cpt_initialized = 0;
++ v.cpt_num_manips = 0;
++ v.cpt_nat_helper = 0;
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
++ defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
++ v.cpt_masq_index = ct->nat.masq_index;
++#endif
++ /* "help" data is used by pptp, difficult to support */
++ v.cpt_nat_seq[0].cpt_correction_pos = ct->nat.info.seq[0].correction_pos;
++ v.cpt_nat_seq[0].cpt_offset_before = ct->nat.info.seq[0].offset_before;
++ v.cpt_nat_seq[0].cpt_offset_after = ct->nat.info.seq[0].offset_after;
++ v.cpt_nat_seq[1].cpt_correction_pos = ct->nat.info.seq[1].correction_pos;
++ v.cpt_nat_seq[1].cpt_offset_before = ct->nat.info.seq[1].offset_before;
++ v.cpt_nat_seq[1].cpt_offset_after = ct->nat.info.seq[1].offset_after;
++#endif
++ read_unlock_bh(&ip_conntrack_lock);
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ err = dump_expect_list(ct, list, ctx);
++
++ cpt_close_object(ctx);
++ return err;
++}
++
++int cpt_dump_ip_conntrack(cpt_context_t * ctx)
++{
++ struct ct_holder *ct_list = NULL;
++ struct ct_holder *c, **cp;
++ int err = 0;
++ int index = 0;
++ int idx;
++
++ if (get_exec_env()->_ip_conntrack == NULL)
++ return 0;
++
++ for (idx = atomic_read(&(get_exec_env()->_ip_conntrack->_ip_conntrack_count)); idx >= 0; idx--) {
++ c = kmalloc(sizeof(struct ct_holder), GFP_KERNEL);
++ if (c == NULL) {
++ err = -ENOMEM;
++ goto done;
++ }
++ memset(c, 0, sizeof(struct ct_holder));
++ c->next = ct_list;
++ ct_list = c;
++ }
++
++ c = ct_list;
++
++ read_lock_bh(&ip_conntrack_lock);
++ for (idx = 0; idx < ip_conntrack_htable_size; idx++) {
++ struct ip_conntrack_tuple_hash *h;
++ list_for_each_entry(h, &ve_ip_conntrack_hash[idx], list) {
++ /* Skip reply tuples, they are covered by original
++ * direction. */
++ if (DIRECTION(h))
++ continue;
++
++ /* Oops, we have not enough of holders...
++ * It is impossible. */
++ if (unlikely(c == NULL)) {
++ read_unlock_bh(&ip_conntrack_lock);
++ eprintk_ctx("unexpected conntrack appeared\n");
++ err = -ENOMEM;
++ goto done;
++ }
++
++ /* If timer is not running, it means that it
++ * has just been scheduled on another cpu.
++ * We should skip this conntrack, it is about to be
++ * destroyed. */
++ if (!del_timer(&tuplehash_to_ctrack(h)->timeout)) {
++ dprintk_ctx("conntrack: no timer\n");
++ continue;
++ }
++
++ /* Timer is deleted. refcnt is _not_ decreased.
++ * We are going to restore the timer on exit
++ * from this function. */
++ c->cth = h;
++ c->index = ++index;
++ c = c->next;
++ }
++ }
++ read_unlock_bh(&ip_conntrack_lock);
++
++ /* No conntracks? Good. */
++ if (index == 0)
++ goto done;
++
++ /* Comb the list a little. */
++ cp = &ct_list;
++ while ((c = *cp) != NULL) {
++ /* Discard unused entries; they can appear, if some
++ * entries were timed out since we preallocated the list.
++ */
++ if (c->cth == NULL) {
++ *cp = c->next;
++ kfree(c);
++ continue;
++ }
++
++ /* Move conntracks attached to expectations to the beginning
++ * of the list. */
++ if (tuplehash_to_ctrack(c->cth)->master && c != ct_list) {
++ *cp = c->next;
++ c->next = ct_list;
++ ct_list = c;
++ dprintk_ctx("conntrack: %d moved in list\n", c->index);
++ continue;
++ }
++ cp = &c->next;
++ }
++
++ cpt_open_section(ctx, CPT_SECT_NET_CONNTRACK);
++
++ for (c = ct_list; c; c = c->next) {
++ err = dump_one_ct(c, ct_list, ctx);
++ if (err)
++ goto done;
++ }
++
++ cpt_close_section(ctx);
++
++done:
++ while ((c = ct_list) != NULL) {
++ ct_list = c->next;
++ if (c->cth) {
++ /* Restore timer. refcnt is preserved. */
++ add_timer(&tuplehash_to_ctrack(c->cth)->timeout);
++ }
++ kfree(c);
++ }
++ return err;
++}
++
++#endif
+diff --git a/kernel/cpt/cpt_context.c b/kernel/cpt/cpt_context.c
+new file mode 100644
+index 0000000..88c403d
+--- /dev/null
++++ b/kernel/cpt/cpt_context.c
+@@ -0,0 +1,257 @@
++/*
++ *
++ * kernel/cpt/cpt_context.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++
++static void file_write(const void *addr, size_t count, struct cpt_context *ctx)
++{
++ mm_segment_t oldfs;
++ ssize_t err = -EBADF;
++ struct file *file = ctx->file;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (file)
++ err = file->f_op->write(file, addr, count, &file->f_pos);
++ set_fs(oldfs);
++ if (err != count && !ctx->write_error)
++ ctx->write_error = err < 0 ? err : -EIO;
++}
++
++static void file_pwrite(void *addr, size_t count, struct cpt_context *ctx, loff_t pos)
++{
++ mm_segment_t oldfs;
++ ssize_t err = -EBADF;
++ struct file *file = ctx->file;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (file)
++ err = file->f_op->write(file, addr, count, &pos);
++ set_fs(oldfs);
++ if (err != count && !ctx->write_error)
++ ctx->write_error = err < 0 ? err : -EIO;
++}
++
++static void file_align(struct cpt_context *ctx)
++{
++ struct file *file = ctx->file;
++
++ if (file)
++ file->f_pos = CPT_ALIGN(file->f_pos);
++}
++
++void cpt_context_init(struct cpt_context *ctx)
++{
++ int i;
++
++ memset(ctx, 0, sizeof(*ctx));
++
++ init_MUTEX(&ctx->main_sem);
++ ctx->refcount = 1;
++
++ ctx->current_section = -1;
++ ctx->current_object = -1;
++ ctx->pagesize = PAGE_SIZE;
++ ctx->write = file_write;
++ ctx->pwrite = file_pwrite;
++ ctx->align = file_align;
++ for (i=0; i < CPT_SECT_MAX; i++)
++ ctx->sections[i] = CPT_NULL;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ init_completion(&ctx->pgin_notify);
++#endif
++ cpt_object_init(ctx);
++}
++
++int cpt_open_dumpfile(struct cpt_context *ctx)
++{
++ ctx->tmpbuf = (char*)__get_free_page(GFP_KERNEL);
++ if (ctx->tmpbuf == NULL)
++ return -ENOMEM;
++ __cpt_release_buf(ctx);
++ return 0;
++}
++
++int cpt_close_dumpfile(struct cpt_context *ctx)
++{
++ if (ctx->file) {
++ fput(ctx->file);
++ ctx->file = NULL;
++ }
++ if (ctx->tmpbuf) {
++ free_page((unsigned long)ctx->tmpbuf);
++ ctx->tmpbuf = NULL;
++ }
++ if (ctx->write_error)
++ eprintk_ctx("error while writing dump file: %d\n", ctx->write_error);
++ return ctx->write_error;
++}
++
++int cpt_major_hdr_out(struct cpt_context *ctx)
++{
++ struct cpt_major_hdr hdr;
++
++ if (ctx->file == NULL)
++ return 0;
++
++ memset(&hdr, 0, sizeof(hdr));
++ hdr.cpt_signature[0] = CPT_SIGNATURE0;
++ hdr.cpt_signature[1] = CPT_SIGNATURE1;
++ hdr.cpt_signature[2] = CPT_SIGNATURE2;
++ hdr.cpt_signature[3] = CPT_SIGNATURE3;
++ hdr.cpt_hdrlen = sizeof(hdr);
++ hdr.cpt_image_version = CPT_VERSION_27;
++#ifdef CONFIG_X86_64
++ hdr.cpt_os_arch = CPT_OS_ARCH_EMT64;
++#elif defined(CONFIG_X86_32)
++ hdr.cpt_os_arch = CPT_OS_ARCH_I386;
++#elif defined(CONFIG_IA64)
++ hdr.cpt_os_arch = CPT_OS_ARCH_IA64;
++#else
++#error Arch is not supported
++#endif
++ hdr.cpt_ve_features = (__u32)ctx->features;
++ hdr.cpt_ve_features2 = (__u32)(ctx->features>>32);
++ hdr.cpt_pagesize = (__u16)PAGE_SIZE;
++ hdr.cpt_hz = HZ;
++ hdr.cpt_start_jiffies64 = ctx->virt_jiffies64;
++ hdr.cpt_start_sec = ctx->start_time.tv_sec;
++ hdr.cpt_start_nsec = ctx->start_time.tv_nsec;
++ hdr.cpt_cpu_caps[0] = ctx->src_cpu_flags;
++ hdr.cpt_kernel_config[0] = ctx->kernel_config_flags;
++ hdr.cpt_iptables_mask = ctx->iptables_mask;
++
++ ctx->write(&hdr, sizeof(hdr), ctx);
++ return 0;
++}
++
++int cpt_close_section(struct cpt_context *ctx)
++{
++ if (ctx->file && ctx->current_section >= 0) {
++ __u64 next = ctx->file->f_pos - ctx->current_section;
++ ctx->pwrite(&next, 8, ctx, ctx->current_section);
++ ctx->current_section = -1;
++ }
++ return 0;
++}
++EXPORT_SYMBOL(cpt_close_section);
++
++int cpt_open_section(struct cpt_context *ctx, __u32 type)
++{
++ struct cpt_section_hdr hdr;
++
++ if (ctx->file == NULL)
++ return 0;
++
++ cpt_close_section(ctx);
++
++ ctx->current_section = ctx->file->f_pos;
++ ctx->sections[type] = ctx->current_section;
++
++ hdr.cpt_next = 0;
++ hdr.cpt_section = type;
++ hdr.cpt_hdrlen = sizeof(hdr);
++ hdr.cpt_align = 0;
++ ctx->write(&hdr, sizeof(hdr), ctx);
++
++ return 0;
++}
++EXPORT_SYMBOL(cpt_open_section);
++
++
++int cpt_close_object(struct cpt_context *ctx)
++{
++ if (ctx->file && ctx->current_object >= 0) {
++ __u64 next = ctx->file->f_pos - ctx->current_object;
++ ctx->pwrite(&next, 8, ctx, ctx->current_object);
++ ctx->current_object = -1;
++ }
++ return 0;
++}
++EXPORT_SYMBOL(cpt_close_object);
++
++int cpt_open_object(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ if (ctx->file == NULL)
++ return 0;
++
++ cpt_close_object(ctx);
++
++ ctx->current_object = ctx->file->f_pos;
++ if (obj)
++ cpt_obj_setpos(obj, ctx->current_object, ctx);
++
++ return 0;
++}
++EXPORT_SYMBOL(cpt_open_object);
++
++int cpt_push_object(loff_t *saved, struct cpt_context *ctx)
++{
++ if (ctx->file) {
++ *saved = ctx->current_object;
++ ctx->current_object = ctx->file->f_pos;
++ }
++ return 0;
++}
++EXPORT_SYMBOL(cpt_push_object);
++
++int cpt_pop_object(loff_t *saved, struct cpt_context *ctx)
++{
++ ctx->current_object = *saved;
++ return 0;
++}
++EXPORT_SYMBOL(cpt_pop_object);
++
++int cpt_dump_tail(struct cpt_context *ctx)
++{
++ struct cpt_major_tail hdr;
++ int i;
++
++ if (ctx->file == NULL)
++ return 0;
++
++ cpt_open_section(ctx, CPT_SECT_TRAILER);
++ memset(&hdr, 0, sizeof(hdr));
++ hdr.cpt_next = sizeof(hdr);
++ hdr.cpt_object = CPT_OBJ_TRAILER;
++ hdr.cpt_hdrlen = sizeof(hdr);
++ hdr.cpt_content = CPT_CONTENT_VOID;
++ hdr.cpt_lazypages = 0;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ hdr.cpt_lazypages = ctx->lazypages;
++#endif
++ hdr.cpt_64bit = ctx->tasks64;
++ hdr.cpt_signature[0] = CPT_SIGNATURE0;
++ hdr.cpt_signature[1] = CPT_SIGNATURE1;
++ hdr.cpt_signature[2] = CPT_SIGNATURE2;
++ hdr.cpt_signature[3] = CPT_SIGNATURE3;
++ hdr.cpt_nsect = CPT_SECT_MAX_INDEX;
++ for (i = 0; i < CPT_SECT_MAX_INDEX; i++)
++ hdr.cpt_sections[i] = ctx->sections[i];
++
++ ctx->write(&hdr, sizeof(hdr), ctx);
++ cpt_close_section(ctx);
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_context.h b/kernel/cpt/cpt_context.h
+new file mode 100644
+index 0000000..e4f82f9
+--- /dev/null
++++ b/kernel/cpt/cpt_context.h
+@@ -0,0 +1,215 @@
++#include <linux/fs.h>
++#include <asm/uaccess.h>
++#include <bc/beancounter.h>
++
++#define CPT_CTX_ERROR -1
++#define CPT_CTX_IDLE 0
++#define CPT_CTX_SUSPENDING 1
++#define CPT_CTX_SUSPENDED 2
++#define CPT_CTX_DUMPING 3
++#define CPT_CTX_UNDUMPING 4
++#define CPT_CTX_UNDUMPED 5
++
++#define CPT_TID(tsk) task_pid_nr(tsk), task_pid_vnr(tsk), (tsk)->comm
++#define CPT_FID "%d,%d(%s)"
++
++
++typedef struct cpt_context
++{
++ struct list_head ctx_list;
++ int refcount;
++ int ctx_state;
++ int objcount;
++ int sticky;
++ struct semaphore main_sem;
++
++ struct file *errorfile;
++ struct file *statusfile;
++ struct file *lockfile;
++
++ int errno;
++ char *error_msg;
++ loff_t err_offset;
++
++ struct file *file;
++ char *tmpbuf;
++ int pagesize;
++#ifdef CONFIG_VZ_CHECKPOINT_ITER
++ int iter_done;
++ void *iter_dir;
++ struct user_beancounter *iter_ub;
++#endif
++ loff_t current_section;
++ loff_t current_object;
++
++ loff_t sections[CPT_SECT_MAX];
++
++ __u32 errormask;
++ __u32 write_error;
++
++ struct list_head object_array[CPT_OBJ_MAX];
++
++ void (*write)(const void *addr, size_t count, struct cpt_context *ctx);
++ void (*pwrite)(void *addr, size_t count, struct cpt_context *ctx, loff_t pos);
++ ssize_t (*read)(void *addr, size_t count, struct cpt_context *ctx);
++ ssize_t (*pread)(void *addr, size_t count, struct cpt_context *ctx, loff_t pos);
++ void (*align)(struct cpt_context *ctx);
++ int ve_id;
++ int contextid;
++ struct timespec cpt_monotonic_time; /* Host monotonic time at the moment of cpt/rst
++ * corresponging to start_time */
++ __u64 virt_jiffies64; /* Virtual jiffies64. It is == cpt_jiffies64 when
++ * VE did not migrate. */
++ struct timespec start_time;
++ struct timespec delta_time;
++ __s64 delta_nsec;
++ int image_version;
++ __u16 image_arch;
++ __u64 iptables_mask;
++ __u64 features;
++
++#define CPT_ANONVMA_HBITS (sizeof(void*) == 4 ? 10 : 9)
++#define CPT_ANONVMA_HSIZE (1<<CPT_ANONVMA_HBITS)
++ struct hlist_head *anonvmas;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ struct file *pagein_file_in;
++ struct file *pagein_file_out;
++ int lazy_vm;
++ int lazypages;
++ int lazytype;
++ struct task_struct *pgin_task;
++ unsigned long last_pagein;
++ struct pagein_desc **pgin_dir;
++ struct pgin_device *pagein_dev;
++ struct completion pgin_notify;
++ struct completion *pgind_completion;
++ struct swap_info_struct *pgin_swp;
++#endif
++ int tasks64;
++ __u32 src_cpu_flags;
++ __u32 dst_cpu_flags;
++ __u32 kernel_config_flags;
++
++ __u32 last_vpid;
++
++ struct filejob *filejob_queue;
++
++ int slm_count;
++
++ char *vdso;
++
++#ifdef CONFIG_BEANCOUNTERS
++ /* Store here ubc limits and barriers during undumping,
++ and restore them before resuming */
++ struct ubparm saved_ubc[UB_RESOURCES];
++#endif
++} cpt_context_t;
++
++typedef struct {
++ int pid;
++ cpt_context_t *ctx;
++ struct completion done;
++} pagein_info_t;
++
++int pagein_info_printf(char *buf, cpt_context_t *ctx);
++
++int cpt_open_dumpfile(struct cpt_context *);
++int cpt_close_dumpfile(struct cpt_context *);
++int rst_open_dumpfile(struct cpt_context *);
++void rst_close_dumpfile(struct cpt_context *);
++void cpt_context_init(struct cpt_context *);
++void rst_context_init(struct cpt_context *);
++void cpt_context_destroy(struct cpt_context *);
++
++void rst_report_error(int err, cpt_context_t *ctx);
++
++
++int cpt_major_hdr_out(struct cpt_context *ctx);
++int cpt_dump_tail(struct cpt_context *ctx);
++int cpt_close_section(struct cpt_context *ctx);
++int cpt_open_section(struct cpt_context *ctx, __u32 type);
++int cpt_close_object(struct cpt_context *ctx);
++int cpt_open_object(cpt_object_t *obj, struct cpt_context *ctx);
++int cpt_push_object(loff_t *saved, struct cpt_context *ctx);
++int cpt_pop_object(loff_t *saved, struct cpt_context *ctx);
++
++int rst_get_section(int type, struct cpt_context * ctx, loff_t *, loff_t *);
++__u8 *__rst_get_name(loff_t *pos_p, struct cpt_context *ctx);
++__u8 *rst_get_name(loff_t pos, struct cpt_context *ctx);
++void rst_put_name(__u8 *name, struct cpt_context *ctx);
++int _rst_get_object(int type, loff_t pos, void *tmp, int size, struct cpt_context *ctx);
++void * __rst_get_object(int type, loff_t pos, struct cpt_context *ctx);
++
++pid_t vpid_to_pid(pid_t);
++
++#define rst_get_object(type, pos, tmp, ctx) \
++ _rst_get_object((type), (pos), (tmp), sizeof(*(tmp)), (ctx))
++
++extern int debug_level;
++
++#define cpt_printk(lvl, fmt, args...) do { \
++ if (lvl <= debug_level) \
++ printk(fmt, ##args); \
++ } while (0)
++
++#define dprintk(a...) cpt_printk(3, "CPT DBG: " a)
++#define dprintk_ctx(f, arg...) dprintk("%p,%u: " f, ctx, ctx->ve_id, ##arg)
++
++#define wprintk(a...) cpt_printk(2, "CPT WRN: " a)
++#define wprintk_ctx(f, arg...) wprintk("%p,%u: " f, ctx, ctx->ve_id, ##arg)
++
++#define eprintk(a...) cpt_printk(1, "CPT ERR: " a)
++#define eprintk_ctx(f, arg...) \
++do { \
++ eprintk("%p,%u :" f, ctx, ctx->ve_id, ##arg); \
++ if (ctx->error_msg && ctx->err_offset < PAGE_SIZE) \
++ ctx->err_offset += snprintf((char*)(ctx->error_msg + \
++ ctx->err_offset), \
++ PAGE_SIZE - ctx->err_offset, \
++ "Error: " f, ##arg); \
++} while(0)
++
++#define CPT_TMPBUF_FREE 0x789adf12
++#define CPT_TMPBUF_BUSY 0xabcd9876
++
++static inline void *cpt_get_buf(cpt_context_t *ctx)
++{
++ void *buf = ctx->tmpbuf;
++
++ BUG_ON(*(u32*)(buf + PAGE_SIZE - 4) != CPT_TMPBUF_FREE);
++ *(u32*)(buf + PAGE_SIZE - 4) = CPT_TMPBUF_BUSY;
++ return buf;
++}
++
++static inline void __cpt_release_buf(cpt_context_t *ctx)
++{
++ void *buf = ctx->tmpbuf;
++
++ *(u32*)(buf + PAGE_SIZE - 4) = CPT_TMPBUF_FREE;
++}
++
++static inline void cpt_release_buf(cpt_context_t *ctx)
++{
++ void *buf = ctx->tmpbuf;
++
++ BUG_ON(*(u32*)(buf + PAGE_SIZE - 4) != CPT_TMPBUF_BUSY);
++ *(u32*)(buf + PAGE_SIZE - 4) = CPT_TMPBUF_FREE;
++}
++
++static inline void cpt_flush_error(cpt_context_t *ctx)
++{
++ mm_segment_t oldfs;
++
++ if (ctx->errorfile && ctx->error_msg && ctx->err_offset) {
++ if (ctx->errorfile->f_op && ctx->errorfile->f_op->write) {
++ oldfs = get_fs();
++ set_fs(KERNEL_DS);
++ ctx->errorfile->f_op->write(ctx->errorfile,
++ ctx->error_msg, ctx->err_offset,
++ &ctx->errorfile->f_pos);
++ set_fs(oldfs);
++ }
++ ctx->error_msg[0] = 0;
++ ctx->err_offset = 0;
++ }
++}
+diff --git a/kernel/cpt/cpt_dump.c b/kernel/cpt/cpt_dump.c
+new file mode 100644
+index 0000000..f329506
+--- /dev/null
++++ b/kernel/cpt/cpt_dump.c
+@@ -0,0 +1,1250 @@
++/*
++ *
++ * kernel/cpt/cpt_dump.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++#include <linux/ptrace.h>
++#include <linux/smp_lock.h>
++#include <linux/ve.h>
++#include <linux/ve_proto.h>
++#include <linux/virtinfo.h>
++#include <linux/virtinfoscp.h>
++#include <bc/task.h>
++#include <linux/cpt_image.h>
++#include <linux/nsproxy.h>
++#include <linux/mnt_namespace.h>
++#include <linux/netdevice.h>
++#include <linux/nfcalls.h>
++#include <linux/dcache.h>
++#include <linux/if_tun.h>
++#include <linux/utsname.h>
++#include <linux/pid_namespace.h>
++#include <linux/ipc_namespace.h>
++#include <linux/netdevice.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_dump.h"
++#include "cpt_files.h"
++#include "cpt_mm.h"
++#include "cpt_process.h"
++#include "cpt_net.h"
++#include "cpt_socket.h"
++#include "cpt_ubc.h"
++#include "cpt_kernel.h"
++
++
++static int vps_child_level(struct task_struct *root, struct task_struct *c)
++{
++ int level = 0;
++ int veid = VE_TASK_INFO(c)->owner_env->veid;
++
++ while (VE_TASK_INFO(c)->owner_env->veid == veid) {
++ if (c->pid != c->tgid)
++ c = c->group_leader;
++ if (c == root)
++ return level;
++
++ c = c->parent;
++ level++;
++ }
++ return -1;
++}
++
++static inline int freezable(struct task_struct * p)
++{
++ if (p->exit_state)
++ return 0;
++
++ switch (p->state) {
++ case EXIT_ZOMBIE:
++ case EXIT_DEAD:
++ case TASK_STOPPED:
++#if TASK_TRACED != TASK_STOPPED
++ case TASK_TRACED:
++#endif
++ return 0;
++ default:
++ return 1;
++ }
++}
++
++static void wake_ve(cpt_context_t *ctx)
++{
++ struct task_struct *p, *g;
++
++ do_each_thread_ve(g, p) {
++ spin_lock_irq(&p->sighand->siglock);
++ if (p->flags & PF_FROZEN) {
++ p->flags &= ~PF_FROZEN;
++ wake_up_process(p);
++ }
++ spin_unlock_irq(&p->sighand->siglock);
++ } while_each_thread_ve(g, p);
++}
++
++/*
++ * Some comment is necessary about PF_FREEZE,PF_FROZEN,TIF_FREEZE...
++ *
++ * SWSUSP uses PF_FREEZE flag in tsk->flags raising it in context
++ * of another process. Apparently, it is unacceptable on SMP.
++ * Let's take freeze_processes() in kernel/power/process.c as an example.
++ * Unserialized modifications tsk->flags easily
++ * (believe or not, but it happens with probability of almost 100% :-))
++ * creates the situation when setting PF_FREEZE in freeze_processes(),
++ * which quickly spins raising PF_FREEZE of all the processes,
++ * _clears_ PF_FROZEN just set in refrigerator(), so that suspend deadlocks.
++ *
++ * So, to make things clean, we require that those flags may be modified
++ * only under tsk->sighand->siglock, which is quite natural because PF_FREEZE
++ * is just a kind of signal.
++ *
++ * It is not enough, because we are still not allowed to change tsk->flags
++ * in context of another process, we can corrupt another flags, when the process
++ * running on another cpu modifies them. So, we use TIF_FREEZE in thread flags,
++ * which can be changed atomically.
++ *
++ * PF_FROZEN also changes in context of another process, but this happens
++ * only when the process is already in refrigerator() which does not modify
++ * tsk->flags.
++ */
++
++static int check_process_external(struct task_struct *p)
++{
++ if (pid_alive(p)) {
++ if (p->pids[PIDTYPE_PID].pid->level == 0)
++ return PIDTYPE_PID;
++ if (p->pids[PIDTYPE_PGID].pid->level == 0)
++ return PIDTYPE_PGID;
++ if (p->pids[PIDTYPE_SID].pid->level == 0)
++ return PIDTYPE_SID;
++ }
++
++ return PIDTYPE_MAX;
++}
++
++enum
++{
++ OBSTACLE_NOGO = -1,
++ OBSTACLE_TIMEOUT = -2,
++ OBSTACLE_TRYAGAIN = -3,
++};
++
++#define SUSPEND_TIMEOUT (10UL*HZ)
++
++static int vps_stop_tasks(struct cpt_context *ctx)
++{
++ unsigned long start_time = jiffies;
++ unsigned long target, timeout;
++ struct task_struct *p, *g;
++ int todo;
++ int round = 0;
++
++ do_gettimespec(&ctx->start_time);
++ do_posix_clock_monotonic_gettime(&ctx->cpt_monotonic_time);
++ ctx->virt_jiffies64 = get_jiffies_64() + get_exec_env()->jiffies_fixup;
++
++ read_lock(&tasklist_lock);
++
++ atomic_inc(&get_exec_env()->suspend);
++ timeout = HZ/5;
++ target = jiffies + timeout;
++
++ for(;;) {
++ struct task_struct *root;
++ todo = 0;
++
++ root = find_task_by_vpid(1);
++ if (!root) {
++ read_unlock(&tasklist_lock);
++ eprintk_ctx("cannot find ve init\n");
++ atomic_dec(&get_exec_env()->suspend);
++ return -ESRCH;
++ }
++
++ do_each_thread_ve(g, p) {
++ if (vps_child_level(root, p) >= 0) {
++ switch (check_process_external(p)) {
++ case PIDTYPE_PID:
++ eprintk_ctx("external process %d/%d(%s) inside CT (e.g. vzctl enter or vzctl exec).\n",
++ task_pid_vnr(p), p->pid, p->comm);
++ todo = OBSTACLE_NOGO;
++ goto out;
++ case PIDTYPE_PGID:
++ eprintk_ctx("external process group %d/%d(%s) inside CT "
++ "(e.g. vzctl enter or vzctl exec).\n",
++ task_pgrp_vnr(p), p->pid, p->comm);
++ todo = OBSTACLE_NOGO;
++ goto out;
++ case PIDTYPE_SID:
++ eprintk_ctx("external process session %d/%d(%s) inside CT "
++ "(e.g. vzctl enter or vzctl exec).\n",
++ task_session_vnr(p), p->pid, p->comm);
++ todo = OBSTACLE_NOGO;
++ goto out;
++ }
++ if (p->vfork_done) {
++ /* Task between vfork()...exec()
++ * cannot be frozen, because parent
++ * wait in uninterruptible state.
++ * So, we do nothing, waiting for
++ * exec(), unless:
++ */
++ if (p->state == TASK_STOPPED ||
++ p->state == TASK_TRACED) {
++ eprintk_ctx("task " CPT_FID " is stopped while vfork(). "
++ "Checkpointing is impossible.\n",
++ CPT_TID(p));
++ todo = OBSTACLE_NOGO;
++ /* It is fatal, _user_ stopped
++ * vfork()ing task, so that we
++ * cannot suspend now.
++ */
++ } else {
++ todo = OBSTACLE_TRYAGAIN;
++ }
++ goto out;
++ }
++ if (p->signal->group_exit_task &&
++ p->signal->notify_count) {
++ /* exec() waits for threads' death */
++ wprintk_ctx("task " CPT_FID " waits for threads' death\n", CPT_TID(p));
++ todo = OBSTACLE_TRYAGAIN;
++ goto out;
++ }
++ if (p->state == TASK_TRACED
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
++ && !p->stopped_state
++#endif
++ ) {
++ int ptrace_id = p->pn_state;
++ /* Debugger waits for signal. */
++ switch (ptrace_id) {
++ case PN_STOP_TF:
++ case PN_STOP_TF_RT:
++ case PN_STOP_ENTRY:
++ case PN_STOP_FORK:
++ case PN_STOP_VFORK:
++ case PN_STOP_SIGNAL:
++ case PN_STOP_EXIT:
++ case PN_STOP_LEAVE:
++ break;
++ default:
++ eprintk_ctx("task " CPT_FID " is stopped by debugger while %d.\n", CPT_TID(p), ptrace_id);
++ todo = OBSTACLE_NOGO;
++ goto out;
++ }
++ }
++#ifdef CONFIG_UTRACE
++ if (check_utrace(p, root, ctx)) {
++ eprintk_ctx("task " CPT_FID " is utraced. Checkpointing is impossible.\n", CPT_TID(p));
++ todo = OBSTACLE_NOGO;
++ goto out;
++ }
++#endif
++ if (p->flags & PF_NOFREEZE) {
++ eprintk_ctx("task " CPT_FID " is unfreezable. Checkpointing is impossible.\n", CPT_TID(p));
++ todo = OBSTACLE_NOGO;
++ goto out;
++ }
++
++ if (!freezable(p))
++ continue;
++
++ spin_lock_irq(&p->sighand->siglock);
++ if (!(p->flags & PF_FROZEN)) {
++ set_tsk_thread_flag(p, TIF_FREEZE);
++ signal_wake_up(p, 0);
++ }
++ spin_unlock_irq(&p->sighand->siglock);
++
++ if (p->flags & PF_FROZEN) {
++ if (p->state != TASK_UNINTERRUPTIBLE)
++ printk("Holy Crap 1 %ld " CPT_FID "\n", p->state, CPT_TID(p));
++ continue;
++ }
++
++ if (round == 10)
++ wprintk_ctx(CPT_FID " is running\n", CPT_TID(p));
++
++ todo++;
++ } else {
++ if (p != current) {
++ eprintk_ctx("foreign process %d/%d(%s) inside CT (e.g. vzctl enter or vzctl exec).\n",
++ task_pid_vnr(p), task_pid_nr(p), p->comm);
++ todo = OBSTACLE_NOGO;
++ goto out;
++ }
++ }
++ } while_each_thread_ve(g, p);
++
++ if (todo > 0) {
++ /* No visible obstacles, but VE did not freeze
++ * for timeout. Interrupt suspend, if it is major
++ * timeout or signal; if it is minor timeout
++ * we will wake VE and restart suspend.
++ */
++ if (time_after(jiffies, start_time + SUSPEND_TIMEOUT)
++ || signal_pending(current))
++ todo = OBSTACLE_TIMEOUT;
++ else if (time_after(jiffies, target))
++ todo = OBSTACLE_TRYAGAIN;
++ }
++
++out:
++ if (todo < 0) {
++ atomic_dec(&get_exec_env()->suspend);
++
++ wake_ve(ctx);
++
++#if 0
++ /* This is sign of failure of printk(), which is not
++ * ours. So, no prefixes. */
++ printk(">\n");
++#endif
++ }
++
++ read_unlock(&tasklist_lock);
++
++ if (!todo) {
++ atomic_dec(&get_exec_env()->suspend);
++ return 0;
++ }
++
++ switch (todo) {
++ case OBSTACLE_NOGO:
++ eprintk_ctx("suspend is impossible now.\n");
++ return -EAGAIN;
++
++ case OBSTACLE_TIMEOUT:
++ eprintk_ctx("interrupted or timed out.\n");
++ return -EINTR;
++
++ case OBSTACLE_TRYAGAIN:
++ if (time_after(jiffies, start_time + SUSPEND_TIMEOUT) ||
++ signal_pending(current)) {
++ wprintk_ctx("suspend timed out\n");
++ return -EAGAIN;
++ }
++
++ wprintk_ctx("minor suspend timeout (%lu) expired, "
++ "trying again\n", timeout);
++
++ /* Try again. VE is awake, give it some time to run. */
++ current->state = TASK_INTERRUPTIBLE;
++ schedule_timeout(HZ);
++
++ /* After a short wait restart suspend
++ * with longer timeout */
++ atomic_inc(&get_exec_env()->suspend);
++ timeout = min(timeout<<1, SUSPEND_TIMEOUT);
++ target = jiffies + timeout;
++ break;
++
++ default:
++ if (round > 0) {
++ /* VE is partially frozen, give processes
++ * a chance to enter to refrigerator(). */
++ current->state = TASK_INTERRUPTIBLE;
++ schedule_timeout(HZ/20);
++ } else {
++ yield();
++ }
++ }
++
++ read_lock(&tasklist_lock);
++ round++;
++ }
++}
++
++static int cpt_unlock_ve(struct cpt_context *ctx)
++{
++ struct ve_struct *env;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++ down_write(&env->op_sem);
++ env->is_locked = 0;
++ up_write(&env->op_sem);
++ put_ve(env);
++ return 0;
++}
++
++int cpt_resume(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ virtinfo_notifier_call(VITYPE_SCP, VIRTINFO_SCP_DMPFIN, ctx);
++
++ cpt_unlock_sockets(ctx);
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ if (ctx->pgin_task) {
++ wait_for_completion(&ctx->pgin_notify);
++ put_task_struct(ctx->pgin_task);
++ ctx->pgin_task = NULL;
++ }
++#endif
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++
++ spin_lock_irq(&tsk->sighand->siglock);
++ if (tsk->flags & PF_FROZEN) {
++ tsk->flags &= ~PF_FROZEN;
++ wake_up_process(tsk);
++ } else if (freezable(tsk)) {
++ eprintk_ctx("strange, %s not frozen\n", tsk->comm );
++ }
++ spin_unlock_irq(&tsk->sighand->siglock);
++ put_task_struct(tsk);
++ }
++
++ cpt_resume_network(ctx);
++
++ cpt_unlock_ve(ctx);
++
++ cpt_finish_ubc(ctx);
++ cpt_object_destroy(ctx);
++ return 0;
++}
++
++int cpt_kill(struct cpt_context *ctx)
++{
++ int err = 0;
++ struct ve_struct *env;
++ cpt_object_t *obj;
++ struct task_struct *root_task = NULL;
++ long delay;
++
++ if (!ctx->ve_id)
++ return -EINVAL;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++
++ /* from here cpt_kill succeeds */
++ virtinfo_notifier_call(VITYPE_SCP, VIRTINFO_SCP_DMPFIN, ctx);
++
++ if (current->ve_task_info.owner_env == env) {
++ wprintk_ctx("attempt to kill ve from inside, escaping...\n");
++ ve_move_task(current, get_ve0());
++ }
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ if (ctx->pgin_task) {
++ wait_for_completion(&ctx->pgin_notify);
++ put_task_struct(ctx->pgin_task);
++ ctx->pgin_task = NULL;
++ }
++#endif
++
++ cpt_kill_sockets(ctx);
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++
++ if (tsk->exit_state) {
++ put_task_struct(tsk);
++ continue;
++ }
++
++ if (task_pid_vnr(tsk) == 1) {
++ root_task = tsk;
++ continue;
++ }
++
++ tsk->robust_list = NULL;
++#ifdef CONFIG_COMPAT
++ tsk->compat_robust_list = NULL;
++#endif
++ tsk->clear_child_tid = NULL;
++
++ if (tsk->ptrace) {
++ write_lock_irq(&tasklist_lock);
++ tsk->ptrace = 0;
++ if (!list_empty(&tsk->ptrace_entry)) {
++ list_del_init(&tsk->ptrace_entry);
++ /*
++ * This code used to be here:
++ * remove_parent(tsk);
++ * tsk->parent = tsk->parent;
++ * add_parent(tsk);
++ */
++ }
++ write_unlock_irq(&tasklist_lock);
++ }
++
++ send_sig(SIGKILL, tsk, 1);
++
++ spin_lock_irq(&tsk->sighand->siglock);
++ sigfillset(&tsk->blocked);
++ sigdelsetmask(&tsk->blocked, sigmask(SIGKILL));
++ set_tsk_thread_flag(tsk, TIF_SIGPENDING);
++ if (tsk->flags & PF_FROZEN)
++ tsk->flags &= ~PF_FROZEN;
++ spin_unlock_irq(&tsk->sighand->siglock);
++
++ wake_up_process(tsk);
++ put_task_struct(tsk);
++ }
++
++ yield();
++
++ if (root_task != NULL) {
++ send_sig(SIGKILL, root_task, 1);
++
++ spin_lock_irq(&root_task->sighand->siglock);
++ sigfillset(&root_task->blocked);
++ sigdelsetmask(&root_task->blocked, sigmask(SIGKILL));
++ set_tsk_thread_flag(root_task, TIF_SIGPENDING);
++ clear_tsk_thread_flag(root_task, TIF_FREEZE);
++ if (root_task->flags & PF_FROZEN)
++ root_task->flags &= ~PF_FROZEN;
++ spin_unlock_irq(&root_task->sighand->siglock);
++
++ wake_up_process(root_task);
++ put_task_struct(root_task);
++ }
++
++ cpt_finish_ubc(ctx);
++ cpt_object_destroy(ctx);
++
++ delay = 1;
++ while (atomic_read(&env->counter) != 1) {
++ if (signal_pending(current))
++ break;
++ current->state = TASK_INTERRUPTIBLE;
++ delay = (delay < HZ) ? (delay << 1) : HZ;
++ schedule_timeout(delay);
++ }
++ put_ve(env);
++
++ return err;
++}
++
++#ifdef CONFIG_BEANCOUNTERS
++static void collect_task_ubc(struct task_struct *t, struct cpt_context *ctx)
++{
++ struct task_beancounter *tbc;
++
++ tbc = &(t->task_bc);
++ cpt_add_ubc(tbc->exec_ub, ctx);
++ cpt_add_ubc(tbc->task_ub, ctx);
++ cpt_add_ubc(tbc->fork_sub, ctx);
++}
++#else
++static void inline collect_task_ubc(struct task_struct *t,
++ struct cpt_context *ctx)
++{ return; }
++#endif
++
++static cpt_object_t * remember_task(struct task_struct * child,
++ cpt_object_t * head, cpt_context_t * ctx)
++{
++ cpt_object_t *cobj;
++
++ if (freezable(child) && !(child->flags&PF_FROZEN)) {
++ eprintk_ctx("process " CPT_FID " is not frozen\n", CPT_TID(child));
++ put_task_struct(child);
++ return NULL;
++ }
++
++ if (lookup_cpt_object(CPT_OBJ_TASK, child, ctx)) BUG();
++ if ((cobj = alloc_cpt_object(GFP_KERNEL, ctx)) == NULL) {
++ put_task_struct(child);
++ return NULL;
++ }
++ cobj->o_count = 1;
++ cpt_obj_setobj(cobj, child, ctx);
++ insert_cpt_object(CPT_OBJ_TASK, cobj, head, ctx);
++ collect_task_ubc(child, ctx);
++ return cobj;
++}
++
++static int vps_collect_tasks(struct cpt_context *ctx)
++{
++ int err = -ESRCH;
++ cpt_object_t *obj;
++ struct task_struct *root;
++ read_lock(&tasklist_lock);
++ root = find_task_by_vpid(1);
++ if (root)
++ get_task_struct(root);
++ read_unlock(&tasklist_lock);
++
++ if (!root) {
++ err = -ESRCH;
++ eprintk_ctx("vps_collect_tasks: cannot find root\n");
++ goto out;
++ }
++
++ if ((obj = alloc_cpt_object(GFP_KERNEL, ctx)) == NULL) {
++ put_task_struct(root);
++ return -ENOMEM;
++ }
++ obj->o_count = 1;
++ cpt_obj_setobj(obj, root, ctx);
++ intern_cpt_object(CPT_OBJ_TASK, obj, ctx);
++ collect_task_ubc(root, ctx);
++
++ /* Collect process subtree recursively */
++ for_each_object(obj, CPT_OBJ_TASK) {
++ cpt_object_t *head = obj;
++ struct task_struct *tsk = obj->o_obj;
++ struct task_struct *child;
++
++ if (freezable(tsk) && !(tsk->flags&PF_FROZEN)) {
++ eprintk_ctx("process " CPT_FID " is not frozen\n", CPT_TID(tsk));
++ err = -EINVAL;
++ goto out;
++ }
++
++ if (tsk->state == TASK_RUNNING)
++ printk("Holy Crap 2 %ld " CPT_FID "\n", tsk->state, CPT_TID(tsk));
++
++ wait_task_inactive(tsk, 0);
++
++ err = check_task_state(tsk, ctx);
++ if (err)
++ goto out;
++
++ if (tsk->pid == tsk->tgid) {
++ child = tsk;
++ for (;;) {
++ read_lock(&tasklist_lock);
++ child = next_thread(child);
++ if (child != tsk)
++ get_task_struct(child);
++ read_unlock(&tasklist_lock);
++
++ if (child == tsk)
++ break;
++
++ if (child->parent != tsk->parent) {
++ put_task_struct(child);
++ eprintk_ctx("illegal thread structure, kernel bug\n");
++ err = -EINVAL;
++ goto out;
++ }
++
++ if ((head = remember_task(child, head, ctx)) == NULL) {
++ eprintk_ctx("task obj allocation failure\n");
++ err = -ENOMEM;
++ goto out;
++ }
++ }
++ }
++
++ /* About locking. VE is frozen. But lists of children
++ * may change at least for init, when entered task reparents
++ * to init and when reparented task exits. If we take care
++ * of this case, we still can unlock while scanning
++ * tasklists.
++ */
++ read_lock(&tasklist_lock);
++ list_for_each_entry(child, &tsk->children, sibling) {
++ if (child->parent != tsk)
++ continue;
++ if (child->pid != child->tgid)
++ continue;
++ get_task_struct(child);
++ read_unlock(&tasklist_lock);
++
++ if ((head = remember_task(child, head, ctx)) == NULL) {
++ eprintk_ctx("task obj allocation failure\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ read_lock(&tasklist_lock);
++ }
++
++ list_for_each_entry(child, &tsk->ptraced, ptrace_entry) {
++ if (child->parent != tsk)
++ continue;
++ if (child->pid != child->tgid)
++ continue;
++ get_task_struct(child);
++ read_unlock(&tasklist_lock);
++
++ if ((head = remember_task(child, head, ctx)) == NULL) {
++ eprintk_ctx("task obj allocation failure\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ read_lock(&tasklist_lock);
++ }
++ read_unlock(&tasklist_lock);
++ }
++
++ return 0;
++
++out:
++ while (!list_empty(&ctx->object_array[CPT_OBJ_TASK])) {
++ struct list_head *head = ctx->object_array[CPT_OBJ_TASK].next;
++ cpt_object_t *obj = list_entry(head, cpt_object_t, o_list);
++ struct task_struct *tsk;
++
++ list_del(head);
++ tsk = obj->o_obj;
++ put_task_struct(tsk);
++ free_cpt_object(obj, ctx);
++ }
++ return err;
++}
++
++static int cpt_collect(struct cpt_context *ctx)
++{
++ int err;
++
++ if ((err = cpt_collect_mm(ctx)) != 0)
++ return err;
++
++ if ((err = cpt_collect_sysv(ctx)) != 0)
++ return err;
++
++ if ((err = cpt_collect_files(ctx)) != 0)
++ return err;
++
++ if ((err = cpt_collect_fs(ctx)) != 0)
++ return err;
++
++ if ((err = cpt_collect_namespace(ctx)) != 0)
++ return err;
++
++ if ((err = cpt_collect_signals(ctx)) != 0)
++ return err;
++
++ if (virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_COLLECT, ctx) & NOTIFY_FAIL)
++ return -ECHRNG;
++
++ return 0;
++}
++
++static int cpt_dump_veinfo(cpt_context_t *ctx)
++{
++ struct cpt_veinfo_image *i = cpt_get_buf(ctx);
++ struct ve_struct *ve;
++ struct timespec delta;
++ struct ipc_namespace *ns;
++
++ cpt_open_section(ctx, CPT_SECT_VEINFO);
++ cpt_open_object(NULL, ctx);
++
++ memset(i, 0, sizeof(*i));
++
++ i->cpt_next = CPT_NULL;
++ i->cpt_object = CPT_OBJ_VEINFO;
++ i->cpt_hdrlen = sizeof(*i);
++ i->cpt_content = CPT_CONTENT_VOID;
++
++ ve = get_exec_env();
++ ns = ve->ve_ns->ipc_ns;
++
++ if (ns->shm_ctlall > 0xFFFFFFFFU)
++ i->shm_ctl_all = 0xFFFFFFFFU;
++ if (ns->shm_ctlmax > 0xFFFFFFFFU)
++ i->shm_ctl_max = 0xFFFFFFFFU;
++ i->shm_ctl_mni = ns->shm_ctlmni;
++
++ i->msg_ctl_max = ns->msg_ctlmax;
++ i->msg_ctl_mni = ns->msg_ctlmni;
++ i->msg_ctl_mnb = ns->msg_ctlmnb;
++
++ BUILD_BUG_ON(sizeof(ns->sem_ctls) != sizeof(i->sem_ctl_arr));
++ i->sem_ctl_arr[0] = ns->sem_ctls[0];
++ i->sem_ctl_arr[1] = ns->sem_ctls[1];
++ i->sem_ctl_arr[2] = ns->sem_ctls[2];
++ i->sem_ctl_arr[3] = ns->sem_ctls[3];
++
++ do_posix_clock_monotonic_gettime(&delta);
++ _set_normalized_timespec(&delta,
++ delta.tv_sec - ve->start_timespec.tv_sec,
++ delta.tv_nsec - ve->start_timespec.tv_nsec);
++ i->start_timespec_delta = cpt_timespec_export(&delta);
++ i->start_jiffies_delta = get_jiffies_64() - ve->start_jiffies;
++
++ i->last_pid = ve->ve_ns->pid_ns->last_pid;
++
++ ctx->write(i, sizeof(*i), ctx);
++ cpt_release_buf(ctx);
++ cpt_close_object(ctx);
++ cpt_close_section(ctx);
++ return 0;
++}
++
++static int cpt_dump_utsname(cpt_context_t *ctx)
++{
++ int len;
++ struct cpt_object_hdr o;
++ struct ve_struct *ve;
++ struct uts_namespace *ns;
++
++ cpt_open_section(ctx, CPT_SECT_UTSNAME);
++
++ ve = get_exec_env();
++ ns = ve->ve_ns->uts_ns;
++
++ cpt_open_object(NULL, ctx);
++ len = strlen(ns->name.nodename);
++ o.cpt_next = CPT_NULL;
++ o.cpt_object = CPT_OBJ_NAME;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_NAME;
++
++ ctx->write(&o, sizeof(o), ctx);
++ ctx->write(ns->name.nodename, len+1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++
++ cpt_open_object(NULL, ctx);
++ len = strlen(ns->name.domainname);
++ o.cpt_next = CPT_NULL;
++ o.cpt_object = CPT_OBJ_NAME;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_NAME;
++
++ ctx->write(&o, sizeof(o), ctx);
++ ctx->write(ns->name.domainname, len+1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++
++ cpt_close_section(ctx);
++ return 0;
++}
++
++#ifndef CONFIG_IA64
++static int cpt_dump_vsyscall(cpt_context_t *ctx)
++{
++ struct cpt_page_block *pgb = cpt_get_buf(ctx);
++
++ cpt_open_section(ctx, CPT_SECT_VSYSCALL);
++ cpt_open_object(NULL, ctx);
++
++ pgb->cpt_next = CPT_NULL;
++ pgb->cpt_object = CPT_OBJ_VSYSCALL;
++ pgb->cpt_hdrlen = sizeof(*pgb);
++ pgb->cpt_content = CPT_CONTENT_DATA;
++ pgb->cpt_start = cpt_ptr_export(vsyscall_addr);
++ pgb->cpt_end = pgb->cpt_start + PAGE_SIZE;
++
++ ctx->write(pgb, sizeof(*pgb), ctx);
++ cpt_release_buf(ctx);
++
++ ctx->write(vsyscall_addr, PAGE_SIZE, ctx);
++
++ cpt_close_object(ctx);
++ cpt_close_section(ctx);
++ return 0;
++}
++#endif
++
++int cpt_dump(struct cpt_context *ctx)
++{
++ struct ve_struct *oldenv, *env;
++ struct nsproxy *old_ns;
++ int err, err2 = 0;
++
++ if (!ctx->ve_id)
++ return -EINVAL;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++
++ down_read(&env->op_sem);
++ err = -ESRCH;
++ if (!env->is_running)
++ goto out_noenv;
++ if (!env->is_locked)
++ goto out_noenv;
++ err = -EINVAL;
++ if (env->ve_ns->pid_ns->flags & PID_NS_HIDDEN) {
++ printk(KERN_WARNING "CT: checkpointing not supported yet"
++ " for hidden pid namespaces.\n");
++ goto out_noenv;
++ }
++
++ oldenv = set_exec_env(env);
++ old_ns = current->nsproxy;
++ current->nsproxy = env->ve_ns;
++
++ /* Phase 2: real checkpointing */
++ err = cpt_open_dumpfile(ctx);
++ if (err)
++ goto out;
++
++ cpt_major_hdr_out(ctx);
++
++ if (!err)
++ err = cpt_dump_veinfo(ctx);
++ if (!err)
++ err = cpt_dump_ubc(ctx);
++ if (!err)
++ err = cpt_dump_files(ctx);
++ if (!err)
++ err = cpt_dump_files_struct(ctx);
++ if (!err)
++ err = cpt_dump_fs_struct(ctx);
++ /* netdevices should be dumped after dumping open files
++ as we need to restore netdevice binding to /dev/net/tun file */
++ if (!err)
++ err = cpt_dump_ifinfo(ctx);
++ if (!err)
++ err = cpt_dump_namespace(ctx);
++ if (!err)
++ err = cpt_dump_sighand(ctx);
++ if (!err)
++ err = cpt_dump_vm(ctx);
++ if (!err)
++ err = cpt_dump_sysvsem(ctx);
++ if (!err)
++ err = cpt_dump_sysvmsg(ctx);
++ if (!err)
++ err = cpt_dump_tasks(ctx);
++ if (!err)
++ err = cpt_dump_orphaned_sockets(ctx);
++#if defined(CONFIG_VE_IPTABLES) && \
++ (defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE))
++ if (!err)
++ err = cpt_dump_ip_conntrack(ctx);
++#endif
++ if (!err) {
++ if (virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_DUMP, ctx) & NOTIFY_FAIL)
++ err = -ECHRNG;
++ }
++ if (!err)
++ err = cpt_dump_utsname(ctx);
++
++#ifndef CONFIG_IA64
++ if (!err)
++ err = cpt_dump_vsyscall(ctx);
++#endif
++
++ if (!err)
++ err = cpt_dump_tail(ctx);
++
++ err2 = cpt_close_dumpfile(ctx);
++
++out:
++ current->nsproxy = old_ns;
++ set_exec_env(oldenv);
++out_noenv:
++ up_read(&env->op_sem);
++ put_ve(env);
++ return err ? : err2;
++}
++
++int cpt_vps_suspend(struct cpt_context *ctx)
++{
++ struct ve_struct *oldenv, *env;
++ struct nsproxy *old_ns;
++ int err = 0;
++
++ ctx->kernel_config_flags = test_kernel_config();
++ cpt_object_init(ctx);
++
++ if (!ctx->ve_id) {
++ env = get_exec_env();
++ if (env == get_ve0())
++ return -EINVAL;
++ wprintk("undefined ve_id\n");
++ ctx->ve_id = env->veid;
++ get_ve(env);
++ } else {
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++ }
++
++#ifdef CONFIG_VE_IPTABLES
++ ctx->iptables_mask = env->_iptables_modules;
++#endif
++ ctx->features = env->features;
++
++ down_write(&env->op_sem);
++ err = -ESRCH;
++ if (!env->is_running)
++ goto out_noenv;
++
++ err = -EBUSY;
++ if (env->is_locked)
++ goto out_noenv;
++ env->is_locked = 1;
++ downgrade_write(&env->op_sem);
++
++ oldenv = set_exec_env(env);
++ old_ns = current->nsproxy;
++ current->nsproxy = env->ve_ns;
++
++ /* Phase 0: find and stop all the tasks */
++ if ((err = vps_stop_tasks(ctx)) != 0)
++ goto out;
++
++ if ((err = cpt_suspend_network(ctx)) != 0)
++ goto out_wake;
++
++ /* At the moment all the state is frozen. We do not need to lock
++ * the state, which can be changed only if the tasks are running.
++ */
++
++ /* Phase 1: collect task tree */
++ if ((err = vps_collect_tasks(ctx)) != 0)
++ goto out_wake;
++
++ /* Phase 1': collect all the resources */
++ if ((err = cpt_collect(ctx)) != 0)
++ goto out;
++
++out:
++ current->nsproxy = old_ns;
++ set_exec_env(oldenv);
++ up_read(&env->op_sem);
++ put_ve(env);
++ return err;
++
++out_noenv:
++ up_write(&env->op_sem);
++ put_ve(env);
++ return err;
++
++out_wake:
++ read_lock(&tasklist_lock);
++ wake_ve(ctx);
++ read_unlock(&tasklist_lock);
++ goto out;
++}
++
++static void check_unsupported_netdevices(struct cpt_context *ctx, __u32 *caps)
++{
++ struct net *net = get_exec_env()->ve_netns;
++ struct net_device *dev;
++
++ read_lock(&dev_base_lock);
++ for_each_netdev(net, dev) {
++ if (dev != net->loopback_dev
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++ && !(KSYMREF(veth_open) && dev->open == KSYMREF(veth_open))
++#endif
++#if defined(CONFIG_VE_NETDEV) || defined(CONFIG_VE_NETDEV_MODULE)
++ && dev != get_exec_env()->_venet_dev
++#endif
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ && dev->open != tun_net_open
++#endif
++ ) {
++ eprintk_ctx("unsupported netdevice %s\n", dev->name);
++ *caps |= (1<<CPT_UNSUPPORTED_NETDEV);
++ }
++ }
++ read_unlock(&dev_base_lock);
++}
++
++static void check_one_process(struct cpt_context *ctx, __u32 *caps,
++ unsigned int flags, struct ve_struct *env,
++ struct task_struct *root, struct task_struct *p)
++{
++ struct mnt_namespace *ns;
++
++ if (tsk_used_math(p)) {
++ *caps |= flags & ((1<<CPT_CPU_X86_FXSR) |
++ (1<<CPT_CPU_X86_SSE) |
++ (1<<CPT_CPU_X86_SSE2) |
++ (1<<CPT_CPU_X86_MMX) |
++ (1<<CPT_CPU_X86_3DNOW) |
++ (1<<CPT_CPU_X86_3DNOW2));
++ }
++ /* This is not 100% true. VE could migrate with vdso using int80.
++ * In this case we do not need SEP/SYSCALL32 caps. It is not so easy
++ * to test, so that we do not. */
++#ifdef CONFIG_X86_64
++ if (!(task_thread_info(p)->flags & _TIF_IA32))
++ *caps |= flags & ((1<<CPT_CPU_X86_EMT64)|(1<<CPT_CPU_X86_SYSCALL));
++ else if (p->mm && p->mm->context.vdso) {
++ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
++ *caps |= flags & (1<<CPT_CPU_X86_SEP);
++ else
++ *caps |= flags & (1<<CPT_CPU_X86_SYSCALL32);
++ }
++#elif defined(CONFIG_X86_32)
++ if (p->mm && p->mm->context.vdso)
++ *caps |= flags & (1<<CPT_CPU_X86_SEP);
++#endif
++#ifdef CONFIG_IA64
++ if (!IS_IA32_PROCESS(task_pt_regs(p)))
++ *caps |= (1<<CPT_CPU_X86_IA64);
++#endif
++ if (vps_child_level(root, p) >= 0) {
++ switch (check_process_external(p)) {
++ case PIDTYPE_PID:
++ eprintk_ctx("external process %d/%d(%s) inside CT (e.g. vzctl enter or vzctl exec).\n", task_pid_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_EXTERNAL_PROCESS);
++ break;
++ case PIDTYPE_PGID:
++ eprintk_ctx("external process group %d/%d(%s) inside CT "
++ "(e.g. vzctl enter or vzctl exec).\n",
++ task_pgrp_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_EXTERNAL_PROCESS);
++ break;
++ case PIDTYPE_SID:
++ eprintk_ctx("external process session %d/%d(%s) inside CT "
++ "(e.g. vzctl enter or vzctl exec).\n",
++ task_session_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_EXTERNAL_PROCESS);
++ }
++ } else {
++ eprintk_ctx("foreign process %d/%d(%s) inside CT (e.g. vzctl enter or vzctl exec).\n", task_pid_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_EXTERNAL_PROCESS);
++ }
++ task_lock(p);
++ ns = NULL;
++ if (p->nsproxy) {
++ ns = p->nsproxy->mnt_ns;
++ if (ns)
++ get_mnt_ns(ns);
++ }
++ task_unlock(p);
++ if (ns) {
++ if (ns != current->nsproxy->mnt_ns) {
++ eprintk_ctx("namespaces are not supported: process %d/%d(%s)\n", task_pid_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_NAMESPACES);
++ }
++ put_mnt_ns(ns);
++ }
++ if (p->policy != SCHED_NORMAL) {
++ eprintk_ctx("scheduler policy is not supported %d/%d(%s)\n", task_pid_vnr(p), p->pid, p->comm);
++ *caps |= (1<<CPT_SCHEDULER_POLICY);
++ }
++#ifdef CONFIG_UTRACE
++ if (check_utrace(p, root, ctx)) {
++ eprintk_ctx("task %d/%d(%s) is ptraced from host system\n", p->pid, virt_pid(p), p->comm);
++ *caps |= (1<<CPT_PTRACED_FROM_VE0);
++ }
++#endif
++ if (cpt_check_unsupported(p, ctx)) {
++ *caps |= (1<<CPT_UNSUPPORTED_MISC);
++ }
++}
++
++static void check_unsupported_mounts(struct cpt_context *ctx, __u32 *caps,
++ struct ve_struct *env, struct mnt_namespace *n, char *path_buf)
++{
++ struct list_head *p;
++ char *path;
++
++ down_read(&namespace_sem);
++ list_for_each(p, &n->list) {
++ struct vfsmount *mnt = list_entry(p, struct vfsmount, mnt_list);
++ struct path p;
++
++ p.dentry = mnt->mnt_root;
++ p.mnt = mnt;
++ path = __d_path(&p, &env->root_path,
++ path_buf, PAGE_SIZE);
++ if (IS_ERR(path))
++ continue;
++
++ if (check_one_vfsmount(mnt)) {
++ eprintk_ctx("Unsupported filesystem %s\n", mnt->mnt_sb->s_type->name);
++ *caps |= (1<<CPT_UNSUPPORTED_FSTYPE);
++ }
++ }
++ up_read(&namespace_sem);
++}
++
++int cpt_vps_caps(struct cpt_context *ctx, __u32 *caps)
++{
++ struct task_struct *p;
++ struct task_struct *root;
++ struct ve_struct *env;
++ struct ve_struct *old_env;
++ struct nsproxy *old_ns;
++ struct mnt_namespace *n;
++ int err;
++ unsigned int flags = test_cpu_caps();
++
++ if (!ctx->ve_id)
++ return -EINVAL;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (env == NULL)
++ return -ESRCH;
++
++ *caps = flags & (1<<CPT_CPU_X86_CMOV);
++
++ old_env = set_exec_env(env);
++ old_ns = current->nsproxy;
++ current->nsproxy = env->ve_ns;
++
++ check_unsupported_netdevices(ctx, caps);
++
++ read_lock(&tasklist_lock);
++ root = find_task_by_vpid(1);
++ if (!root) {
++ read_unlock(&tasklist_lock);
++ eprintk_ctx("cannot find ve init\n");
++ err = -ESRCH;
++ goto out;
++ }
++ get_task_struct(root);
++ for (p = __first_task_ve(env); p != NULL ; p = __next_task_ve(env, p))
++ check_one_process(ctx, caps, flags, env, root, p);
++ read_unlock(&tasklist_lock);
++
++ task_lock(root);
++ n = NULL;
++ if (root->nsproxy) {
++ n = root->nsproxy->mnt_ns;
++ if (n)
++ get_mnt_ns(n);
++ }
++ task_unlock(root);
++ if (n) {
++ char *path_buf;
++
++ path_buf = (char *) __get_free_page(GFP_KERNEL);
++ if (!path_buf) {
++ put_mnt_ns(n);
++ err = -ENOMEM;
++ goto out_root;
++ }
++
++ check_unsupported_mounts(ctx, caps, env, n, path_buf);
++
++ free_page((unsigned long) path_buf);
++ put_mnt_ns(n);
++ }
++
++ err = 0;
++
++out_root:
++ put_task_struct(root);
++out:
++ current->nsproxy = old_ns;
++ set_exec_env(old_env);
++ put_ve(env);
++
++ return err;
++}
+diff --git a/kernel/cpt/cpt_dump.h b/kernel/cpt/cpt_dump.h
+new file mode 100644
+index 0000000..71f6d94
+--- /dev/null
++++ b/kernel/cpt/cpt_dump.h
+@@ -0,0 +1,16 @@
++int cpt_dump(struct cpt_context *cpt);
++int rst_undump(struct cpt_context *cpt);
++int cpt_suspend(struct cpt_context *cpt);
++int cpt_resume(struct cpt_context *cpt);
++int cpt_kill(struct cpt_context *cpt);
++int rst_clean(struct cpt_context *cpt);
++int rst_resume(struct cpt_context *cpt);
++int rst_kill(struct cpt_context *cpt);
++
++int cpt_freeze_one(pid_t pid, int freeze);
++int cpt_vps_suspend(struct cpt_context *ctx);
++int vps_rst_undump(struct cpt_context *ctx);
++
++int cpt_vps_caps(struct cpt_context *ctx, __u32 *caps);
++
++int cpt_check_unsupported(struct task_struct *tsk, struct cpt_context *ctx);
+diff --git a/kernel/cpt/cpt_epoll.c b/kernel/cpt/cpt_epoll.c
+new file mode 100644
+index 0000000..81d2b98
+--- /dev/null
++++ b/kernel/cpt/cpt_epoll.c
+@@ -0,0 +1,113 @@
++/*
++ *
++ * kernel/cpt/cpt_epoll.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mnt_namespace.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/vzcalluser.h>
++#include <linux/eventpoll.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#include "cpt_syscalls.h"
++
++int cpt_dump_epolldev(cpt_object_t *obj, cpt_context_t *ctx)
++{
++ int err = 0;
++ struct file *file = obj->o_obj;
++ struct eventpoll *ep;
++ struct rb_node *rbp;
++ struct cpt_epoll_image ei;
++
++ if (file->f_op != &eventpoll_fops) {
++ eprintk_ctx("bad epoll file\n");
++ return -EINVAL;
++ }
++
++ ep = file->private_data;
++
++ /* eventpoll.c does not protect open /proc/N/fd, silly.
++ * Opener will get an invalid file with uninitialized private_data
++ */
++ if (unlikely(ep == NULL)) {
++ eprintk_ctx("bad epoll device\n");
++ return -EINVAL;
++ }
++
++ cpt_open_object(NULL, ctx);
++
++ ei.cpt_next = CPT_NULL;
++ ei.cpt_object = CPT_OBJ_EPOLL;
++ ei.cpt_hdrlen = sizeof(ei);
++ ei.cpt_content = CPT_CONTENT_ARRAY;
++ ei.cpt_file = obj->o_pos;
++
++ ctx->write(&ei, sizeof(ei), ctx);
++
++ mutex_lock(&epmutex);
++ for (rbp = rb_first(&ep->rbr); rbp; rbp = rb_next(rbp)) {
++ loff_t saved_obj;
++ cpt_object_t *tobj;
++ struct cpt_epoll_file_image efi;
++ struct epitem *epi;
++ epi = rb_entry(rbp, struct epitem, rbn);
++ tobj = lookup_cpt_object(CPT_OBJ_FILE, epi->ffd.file, ctx);
++ if (tobj == NULL) {
++ eprintk_ctx("epoll device refers to an external file\n");
++ err = -EBUSY;
++ break;
++ }
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ efi.cpt_next = CPT_NULL;
++ efi.cpt_object = CPT_OBJ_EPOLL_FILE;
++ efi.cpt_hdrlen = sizeof(efi);
++ efi.cpt_content = CPT_CONTENT_VOID;
++ efi.cpt_file = tobj->o_pos;
++ efi.cpt_fd = epi->ffd.fd;
++ efi.cpt_events = epi->event.events;
++ efi.cpt_data = epi->event.data;
++ efi.cpt_revents = 0;
++ efi.cpt_ready = 0;
++ if (!list_empty(&epi->rdllink))
++ efi.cpt_ready = 1;
++
++ ctx->write(&efi, sizeof(efi), ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ mutex_unlock(&epmutex);
++
++ cpt_close_object(ctx);
++
++ return err;
++}
++
+diff --git a/kernel/cpt/cpt_exports.c b/kernel/cpt/cpt_exports.c
+new file mode 100644
+index 0000000..f492331
+--- /dev/null
++++ b/kernel/cpt/cpt_exports.c
+@@ -0,0 +1,13 @@
++#include <linux/module.h>
++#include <asm/signal.h>
++
++#include "cpt_obj.h"
++
++EXPORT_SYMBOL(alloc_cpt_object);
++EXPORT_SYMBOL(intern_cpt_object);
++EXPORT_SYMBOL(insert_cpt_object);
++EXPORT_SYMBOL(__cpt_object_add);
++EXPORT_SYMBOL(cpt_object_add);
++EXPORT_SYMBOL(cpt_object_get);
++EXPORT_SYMBOL(lookup_cpt_object);
++EXPORT_SYMBOL(lookup_cpt_obj_bypos);
+diff --git a/kernel/cpt/cpt_files.c b/kernel/cpt/cpt_files.c
+new file mode 100644
+index 0000000..376222e
+--- /dev/null
++++ b/kernel/cpt/cpt_files.c
+@@ -0,0 +1,1626 @@
++/*
++ *
++ * kernel/cpt/cpt_files.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mnt_namespace.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/smp_lock.h>
++#include <linux/pagemap.h>
++#include <asm/uaccess.h>
++#include <linux/vzcalluser.h>
++#include <linux/ve_proto.h>
++#include <bc/kmem.h>
++#include <linux/cpt_image.h>
++#include <linux/if_tun.h>
++#include <linux/fdtable.h>
++#include <linux/shm.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_socket.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#include "cpt_syscalls.h"
++
++void cpt_printk_dentry(struct dentry *d, struct vfsmount *mnt)
++{
++ char *path;
++ struct path p;
++ unsigned long pg = __get_free_page(GFP_KERNEL);
++
++ if (!pg)
++ return;
++
++ p.dentry = d;
++ p.mnt = mnt;
++ path = d_path(&p, (char *)pg, PAGE_SIZE);
++
++ if (!IS_ERR(path))
++ eprintk("<%s>", path);
++ free_page(pg);
++}
++
++int cpt_verify_overmount(char *path, struct dentry *d, struct vfsmount *mnt,
++ cpt_context_t *ctx)
++{
++ if (path[0] == '/' && !(!IS_ROOT(d) && d_unhashed(d))) {
++ struct nameidata nd;
++ if (path_lookup(path, 0, &nd)) {
++ eprintk_ctx("d_path cannot be looked up %s\n", path);
++ return -EINVAL;
++ }
++ if (nd.path.dentry != d || nd.path.mnt != mnt) {
++ eprintk_ctx("d_path is invisible %s\n", path);
++ path_put(&nd.path);
++ return -EINVAL;
++ }
++ path_put(&nd.path);
++ }
++ return 0;
++}
++
++static int
++cpt_replaced(struct dentry * de, struct vfsmount *mnt, cpt_context_t * ctx)
++{
++ int result = 0;
++
++#if defined(CONFIG_VZFS_FS) || defined(CONFIG_VZFS_FS_MODULE)
++ char *path;
++ unsigned long pg;
++ struct dentry * renamed_dentry;
++ struct path p;
++
++ if (de->d_sb->s_magic != FSMAGIC_VEFS)
++ return 0;
++ if (de->d_inode->i_nlink != 0 ||
++ atomic_read(&de->d_inode->i_writecount) > 0)
++ return 0;
++
++ renamed_dentry = vefs_replaced_dentry(de);
++ if (renamed_dentry == NULL)
++ return 0;
++
++ pg = __get_free_page(GFP_KERNEL);
++ if (!pg)
++ return 0;
++
++ p.dentry = de;
++ p.mnt = mnt;
++ path = d_path(&p, (char *)pg, PAGE_SIZE);
++ if (!IS_ERR(path)) {
++ int len;
++ struct nameidata nd;
++
++ len = pg + PAGE_SIZE - 1 - (unsigned long)path;
++ if (len >= sizeof("(deleted) ") - 1 &&
++ !memcmp(path, "(deleted) ", sizeof("(deleted) ") - 1)) {
++ len -= sizeof("(deleted) ") - 1;
++ path += sizeof("(deleted) ") - 1;
++ }
++
++ if (path_lookup(path, 0, &nd) == 0) {
++ if (mnt == nd.path.mnt &&
++ vefs_is_renamed_dentry(nd.path.dentry, renamed_dentry))
++ result = 1;
++ path_put(&nd.path);
++ }
++ }
++ free_page(pg);
++#endif
++ return result;
++}
++
++static int cpt_dump_dentry(struct dentry *d, struct vfsmount *mnt,
++ int replaced, cpt_context_t *ctx)
++{
++ int len;
++ char *path;
++ struct path p;
++ char *pg = cpt_get_buf(ctx);
++ loff_t saved;
++
++ p.dentry = d;
++ p.mnt = mnt;
++ path = d_path(&p, pg, PAGE_SIZE);
++ len = PTR_ERR(path);
++
++ if (IS_ERR(path)) {
++ struct cpt_object_hdr o;
++ char tmp[1];
++
++ /* VZ changes d_path() to return EINVAL, when path
++ * is not supposed to be visible inside VE.
++ * This changes behaviour of d_path() comparing
++ * to mainstream kernel, f.e. d_path() fails
++ * on any kind of shared memory. Maybe, there are
++ * another cases, but I am aware only about this one.
++ * So, we just ignore error on shmem mounts and proceed.
++ * Otherwise, checkpointing is prohibited because
++ * of reference to an invisible file.
++ */
++ if (len != -EINVAL ||
++ mnt != get_exec_env()->shmem_mnt)
++ eprintk_ctx("d_path err=%d\n", len);
++ else
++ len = 0;
++
++ cpt_push_object(&saved, ctx);
++ cpt_open_object(NULL, ctx);
++ o.cpt_next = CPT_NULL;
++ o.cpt_object = CPT_OBJ_NAME;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_NAME;
++ tmp[0] = 0;
++
++ ctx->write(&o, sizeof(o), ctx);
++ ctx->write(tmp, 1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved, ctx);
++
++ __cpt_release_buf(ctx);
++ return len;
++ } else {
++ struct cpt_object_hdr o;
++
++ len = pg + PAGE_SIZE - 1 - path;
++ if (replaced &&
++ len >= sizeof("(deleted) ") - 1 &&
++ !memcmp(path, "(deleted) ", sizeof("(deleted) ") - 1)) {
++ len -= sizeof("(deleted) ") - 1;
++ path += sizeof("(deleted) ") - 1;
++ }
++ o.cpt_next = CPT_NULL;
++ o.cpt_object = CPT_OBJ_NAME;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_NAME;
++ path[len] = 0;
++
++ if (cpt_verify_overmount(path, d, mnt, ctx)) {
++ __cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++
++ cpt_push_object(&saved, ctx);
++ cpt_open_object(NULL, ctx);
++ ctx->write(&o, sizeof(o), ctx);
++ ctx->write(path, len+1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved, ctx);
++ __cpt_release_buf(ctx);
++ }
++ return 0;
++}
++
++int cpt_dump_string(const char *s, struct cpt_context *ctx)
++{
++ int len;
++ struct cpt_object_hdr o;
++
++ cpt_open_object(NULL, ctx);
++ len = strlen(s);
++ o.cpt_next = CPT_NULL;
++ o.cpt_object = CPT_OBJ_NAME;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_NAME;
++
++ ctx->write(&o, sizeof(o), ctx);
++ ctx->write(s, len+1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ return 0;
++}
++
++static int
++cpt_dump_filename(struct file *file, int replaced, cpt_context_t *ctx)
++{
++ return cpt_dump_dentry(file->f_dentry, file->f_vfsmnt, replaced, ctx);
++}
++
++int cpt_dump_inode(struct dentry *d, struct vfsmount *mnt, struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_inode_image *v = cpt_get_buf(ctx);
++ struct kstat sbuf;
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_INODE;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ if ((err = vfs_getattr(mnt, d, &sbuf)) != 0) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++
++ v->cpt_dev = d->d_inode->i_sb->s_dev;
++ v->cpt_ino = d->d_inode->i_ino;
++ v->cpt_mode = sbuf.mode;
++ v->cpt_nlink = sbuf.nlink;
++ v->cpt_uid = sbuf.uid;
++ v->cpt_gid = sbuf.gid;
++ v->cpt_rdev = d->d_inode->i_rdev;
++ v->cpt_size = sbuf.size;
++ v->cpt_atime = cpt_timespec_export(&sbuf.atime);
++ v->cpt_mtime = cpt_timespec_export(&sbuf.mtime);
++ v->cpt_ctime = cpt_timespec_export(&sbuf.ctime);
++ v->cpt_blksize = sbuf.blksize;
++ v->cpt_blocks = sbuf.blocks;
++ v->cpt_sb = d->d_inode->i_sb->s_magic;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ return 0;
++}
++
++int cpt_collect_files(cpt_context_t * ctx)
++{
++ int err;
++ cpt_object_t *obj;
++ int index = 0;
++
++ /* Collect process fd sets */
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->files && cpt_object_add(CPT_OBJ_FILES, tsk->files, ctx) == NULL)
++ return -ENOMEM;
++ }
++
++ /* Collect files from fd sets */
++ for_each_object(obj, CPT_OBJ_FILES) {
++ int fd;
++ struct files_struct *f = obj->o_obj;
++
++ cpt_obj_setindex(obj, index++, ctx);
++
++ if (obj->o_count != atomic_read(&f->count)) {
++ eprintk_ctx("files_struct is referenced outside %d %d\n", obj->o_count, atomic_read(&f->count));
++ return -EBUSY;
++ }
++
++ for (fd = 0; fd < f->fdt->max_fds; fd++) {
++ struct file *file = fcheck_files(f, fd);
++ if (file && cpt_object_add(CPT_OBJ_FILE, file, ctx) == NULL)
++ return -ENOMEM;
++ }
++ }
++
++ /* Collect files queued by AF_UNIX sockets. */
++ if ((err = cpt_collect_passedfds(ctx)) < 0)
++ return err;
++
++ /* OK. At this point we should count all the references. */
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ struct file *parent;
++ cpt_object_t *ino_obj;
++
++ if (obj->o_count != atomic_long_read(&file->f_count)) {
++ eprintk_ctx("file struct is referenced outside %d %ld\n", obj->o_count, atomic_long_read(&file->f_count));
++ cpt_printk_dentry(file->f_dentry, file->f_vfsmnt);
++ return -EBUSY;
++ }
++
++ switch (file->f_dentry->d_inode->i_sb->s_magic) {
++ case FSMAGIC_FUTEX:
++ case FSMAGIC_MQUEUE:
++ case FSMAGIC_BDEV:
++#ifndef CONFIG_INOTIFY_USER
++ case FSMAGIC_INOTIFY:
++#endif
++ eprintk_ctx("file on unsupported FS: magic %08lx\n", file->f_dentry->d_inode->i_sb->s_magic);
++ return -EBUSY;
++ }
++
++ /* Collect inode. It is necessary mostly to resolve deleted
++ * hard links. */
++ ino_obj = cpt_object_add(CPT_OBJ_INODE, file->f_dentry->d_inode, ctx);
++ if (ino_obj == NULL)
++ return -ENOMEM;
++
++ parent = ino_obj->o_parent;
++ if (!parent || (!IS_ROOT(parent->f_dentry) && d_unhashed(parent->f_dentry)))
++ ino_obj->o_parent = file;
++
++ if (S_ISCHR(file->f_dentry->d_inode->i_mode)) {
++ int maj = imajor(file->f_dentry->d_inode);
++ if (maj == PTY_MASTER_MAJOR ||
++ (maj >= UNIX98_PTY_MASTER_MAJOR &&
++ maj < UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT) ||
++ maj == PTY_SLAVE_MAJOR ||
++ maj == UNIX98_PTY_SLAVE_MAJOR ||
++ maj == TTYAUX_MAJOR) {
++ err = cpt_collect_tty(file, ctx);
++ if (err)
++ return err;
++ }
++ }
++
++ if (S_ISSOCK(file->f_dentry->d_inode->i_mode)) {
++ err = cpt_collect_socket(file, ctx);
++ if (err)
++ return err;
++ }
++ }
++
++ err = cpt_index_sockets(ctx);
++
++ return err;
++}
++
++/* /dev/ptmx is special, all the files share one inode, but real tty backend
++ * is attached via file->private_data.
++ */
++
++static inline int is_cloning_inode(struct inode *ino)
++{
++ return S_ISCHR(ino->i_mode) &&
++ ino->i_rdev == MKDEV(TTYAUX_MAJOR,2);
++}
++
++static int dump_one_flock(struct file_lock *fl, int owner, struct cpt_context *ctx)
++{
++ pid_t pid;
++ struct cpt_flock_image *v = cpt_get_buf(ctx);
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_FLOCK;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ v->cpt_owner = owner;
++
++ pid = fl->fl_pid;
++ if (pid) {
++ pid = pid_to_vpid(fl->fl_pid);
++ if (pid == -1) {
++ if (!(fl->fl_flags&FL_FLOCK)) {
++ eprintk_ctx("posix lock from another container?\n");
++ cpt_release_buf(ctx);
++ return -EBUSY;
++ }
++ pid = 0;
++ }
++ }
++
++ v->cpt_pid = pid;
++ v->cpt_start = fl->fl_start;
++ v->cpt_end = fl->fl_end;
++ v->cpt_flags = fl->fl_flags;
++ v->cpt_type = fl->fl_type;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ return 0;
++}
++
++
++int cpt_dump_flock(struct file *file, struct cpt_context *ctx)
++{
++ int err = 0;
++ struct file_lock *fl;
++
++ lock_kernel();
++ for (fl = file->f_dentry->d_inode->i_flock;
++ fl; fl = fl->fl_next) {
++ if (file != fl->fl_file)
++ continue;
++ if (fl->fl_flags & FL_LEASE) {
++ eprintk_ctx("lease lock is not supported\n");
++ err = -EINVAL;
++ break;
++ }
++ if (fl->fl_flags & FL_POSIX) {
++ cpt_object_t *obj;
++ obj = lookup_cpt_object(CPT_OBJ_FILES, fl->fl_owner, ctx);
++ if (obj) {
++ dump_one_flock(fl, obj->o_index, ctx);
++ continue;
++ } else {
++ eprintk_ctx("unknown lock owner %p\n", fl->fl_owner);
++ err = -EINVAL;
++ }
++ }
++ if (fl->fl_flags & FL_FLOCK) {
++ dump_one_flock(fl, -1, ctx);
++ continue;
++ }
++ }
++ unlock_kernel();
++ return err;
++}
++
++static int dump_one_file(cpt_object_t *obj, struct file *file, cpt_context_t *ctx)
++{
++ int err = 0;
++ cpt_object_t *iobj;
++ struct cpt_file_image *v = cpt_get_buf(ctx);
++ struct kstat sbuf;
++ int replaced = 0;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_FILE;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_flags = file->f_flags;
++ v->cpt_mode = file->f_mode;
++ v->cpt_pos = file->f_pos;
++ v->cpt_uid = file->f_uid;
++ v->cpt_gid = file->f_gid;
++
++ vfs_getattr(file->f_vfsmnt, file->f_dentry, &sbuf);
++
++ v->cpt_i_mode = sbuf.mode;
++ v->cpt_lflags = 0;
++ if (IS_ROOT(file->f_dentry))
++ v->cpt_lflags |= CPT_DENTRY_ROOT;
++ else if (d_unhashed(file->f_dentry)) {
++ if (cpt_replaced(file->f_dentry, file->f_vfsmnt, ctx)) {
++ v->cpt_lflags |= CPT_DENTRY_REPLACED;
++ replaced = 1;
++ } else {
++ v->cpt_lflags |= CPT_DENTRY_DELETED;
++ }
++ }
++ if (is_cloning_inode(file->f_dentry->d_inode))
++ v->cpt_lflags |= CPT_DENTRY_CLONING;
++ if (file->f_dentry->d_inode->i_sb->s_magic == FSMAGIC_PROC)
++ v->cpt_lflags |= CPT_DENTRY_PROC;
++ v->cpt_inode = CPT_NULL;
++ if (!(v->cpt_lflags & CPT_DENTRY_REPLACED)) {
++ iobj = lookup_cpt_object(CPT_OBJ_INODE, file->f_dentry->d_inode, ctx);
++ if (iobj)
++ v->cpt_inode = iobj->o_pos;
++ }
++ v->cpt_priv = CPT_NULL;
++ v->cpt_fown_fd = -1;
++ if (S_ISCHR(v->cpt_i_mode)) {
++ iobj = lookup_cpt_object(CPT_OBJ_TTY, file->private_data, ctx);
++ if (iobj) {
++ v->cpt_priv = iobj->o_pos;
++ if (file->f_flags&FASYNC)
++ v->cpt_fown_fd = cpt_tty_fasync(file, ctx);
++ }
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ if (file->f_op && file->f_op->open == tun_chr_open)
++ v->cpt_lflags |= CPT_DENTRY_TUNTAP;
++#endif
++ }
++ if (S_ISSOCK(v->cpt_i_mode)) {
++ if (obj->o_index < 0) {
++ eprintk_ctx("BUG: no socket index\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_priv = obj->o_index;
++ if (file->f_flags&FASYNC)
++ v->cpt_fown_fd = cpt_socket_fasync(file, ctx);
++ }
++ if (file->f_op == &eventpoll_fops) {
++ v->cpt_priv = file->f_dentry->d_inode->i_ino;
++ v->cpt_lflags |= CPT_DENTRY_EPOLL;
++ }
++ if (file->f_dentry->d_inode->i_sb->s_magic == FSMAGIC_INOTIFY) {
++ v->cpt_priv = file->f_dentry->d_inode->i_ino;
++ v->cpt_lflags |= CPT_DENTRY_INOTIFY;
++ }
++
++ v->cpt_fown_pid = (file->f_owner.pid == NULL ?
++ CPT_FOWN_STRAY_PID : pid_vnr(file->f_owner.pid));
++ v->cpt_fown_uid = file->f_owner.uid;
++ v->cpt_fown_euid = file->f_owner.euid;
++ v->cpt_fown_signo = file->f_owner.signum;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ if (!S_ISSOCK(v->cpt_i_mode)) {
++ err = cpt_dump_filename(file, replaced, ctx);
++ if (err)
++ return err;
++ if ((file->f_mode & FMODE_WRITE) &&
++ file->f_dentry->d_inode->i_sb->s_magic == FSMAGIC_VEFS)
++ vefs_track_notify(file->f_dentry, 1);
++ }
++
++ if (file->f_dentry->d_inode->i_flock)
++ err = cpt_dump_flock(file, ctx);
++
++ cpt_close_object(ctx);
++
++ return err;
++}
++
++/* About this weird function... Crappy code dealing with SYSV shared memory
++ * defines TMPFS inode and file with f_op doing only mmap. So...
++ * Maybe, this is wrong and leaks something. It is clear access to
++ * SYSV shmem via mmap is quite unusual and impossible from user space.
++ */
++static int dump_content_shm(struct file *file, struct cpt_context *ctx)
++{
++ struct cpt_obj_bits *v;
++ loff_t saved_pos;
++ unsigned long addr;
++
++ addr = do_mmap_pgoff(file, 0, file->f_dentry->d_inode->i_size,
++ PROT_READ, MAP_SHARED, 0);
++ if (IS_ERR((void*)addr))
++ return PTR_ERR((void*)addr);
++
++ cpt_push_object(&saved_pos, ctx);
++ cpt_open_object(NULL, ctx);
++ v = cpt_get_buf(ctx);
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_BITS;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_DATA;
++ v->cpt_size = file->f_dentry->d_inode->i_size;
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ ctx->write((void*)addr, file->f_dentry->d_inode->i_size, ctx);
++ ctx->align(ctx);
++ do_munmap(current->mm, addr, file->f_dentry->d_inode->i_size);
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_pos, ctx);
++ return 0;
++}
++
++static int data_is_zero(char *addr, int len)
++{
++ int i;
++ unsigned long zerolong = 0;
++
++ for (i=0; i<len/sizeof(unsigned long); i++) {
++ if (((unsigned long*)(addr))[i] != 0)
++ return 0;
++ }
++ i = len % sizeof(unsigned long);
++ if (!i)
++ return 1;
++ return memcmp(addr + len - i, &zerolong, i) == 0;
++}
++
++
++static int dump_content_regular(struct file *file, struct cpt_context *ctx)
++{
++ loff_t saved_pos;
++ loff_t pos = 0;
++ loff_t obj_opened = CPT_NULL;
++ struct cpt_page_block pgb;
++ ssize_t (*do_read)(struct file *, char __user *, size_t, loff_t *);
++
++ if (file->f_op == NULL)
++ return -EINVAL;
++
++ do_read = file->f_op->read;
++ if (file->f_op == &shm_file_operations) {
++ struct shm_file_data *sfd = file->private_data;
++
++ cpt_dump_content_sysvshm(sfd->file, ctx);
++
++ return 0;
++ }
++ if (file->f_op == &shmem_file_operations) {
++ do_read = file->f_dentry->d_inode->i_fop->read;
++ cpt_dump_content_sysvshm(file, ctx);
++ if (!do_read) {
++ wprintk_ctx("TMPFS is not configured?\n");
++ return dump_content_shm(file, ctx);
++ }
++ }
++
++ if (!(file->f_mode & FMODE_READ) ||
++ (file->f_flags & O_DIRECT)) {
++ file = dentry_open(dget(file->f_dentry),
++ mntget(file->f_vfsmnt), O_RDONLY);
++ if (IS_ERR(file)) {
++ cpt_printk_dentry(file->f_dentry, file->f_vfsmnt);
++ eprintk_ctx("cannot reopen file for read %ld\n", PTR_ERR(file));
++ return PTR_ERR(file);
++ }
++ } else {
++ atomic_long_inc(&file->f_count);
++ }
++
++ for (;;) {
++ mm_segment_t oldfs;
++ int err;
++
++ (void)cpt_get_buf(ctx);
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = do_read(file, ctx->tmpbuf, PAGE_SIZE, &pos);
++ set_fs(oldfs);
++ if (err < 0) {
++ eprintk_ctx("dump_content_regular: do_read: %d", err);
++ fput(file);
++ __cpt_release_buf(ctx);
++ return err;
++ }
++ if (err == 0) {
++ __cpt_release_buf(ctx);
++ break;
++ }
++ if (data_is_zero(ctx->tmpbuf, err)) {
++ if (obj_opened != CPT_NULL) {
++ ctx->pwrite(&pgb.cpt_end, 8, ctx, obj_opened + offsetof(struct cpt_page_block, cpt_end));
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_pos, ctx);
++ obj_opened = CPT_NULL;
++ }
++ } else {
++ if (obj_opened == CPT_NULL) {
++ cpt_push_object(&saved_pos, ctx);
++ cpt_open_object(NULL, ctx);
++ obj_opened = ctx->file->f_pos;
++ pgb.cpt_next = CPT_NULL;
++ pgb.cpt_object = CPT_OBJ_PAGES;
++ pgb.cpt_hdrlen = sizeof(pgb);
++ pgb.cpt_content = CPT_CONTENT_DATA;
++ pgb.cpt_start = pos - err;
++ pgb.cpt_end = pgb.cpt_start;
++ ctx->write(&pgb, sizeof(pgb), ctx);
++ }
++ ctx->write(ctx->tmpbuf, err, ctx);
++ pgb.cpt_end += err;
++ }
++ __cpt_release_buf(ctx);
++ }
++
++ fput(file);
++
++ if (obj_opened != CPT_NULL) {
++ ctx->pwrite(&pgb.cpt_end, 8, ctx, obj_opened + offsetof(struct cpt_page_block, cpt_end));
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_pos, ctx);
++ obj_opened = CPT_NULL;
++ }
++ return 0;
++}
++
++
++static int dump_content_chrdev(struct file *file, struct cpt_context *ctx)
++{
++ struct inode *ino = file->f_dentry->d_inode;
++ int maj;
++
++ maj = imajor(ino);
++ if (maj == MEM_MAJOR) {
++ /* Well, OK. */
++ return 0;
++ }
++ if (maj == PTY_MASTER_MAJOR ||
++ (maj >= UNIX98_PTY_MASTER_MAJOR &&
++ maj < UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT) ||
++ maj == PTY_SLAVE_MAJOR ||
++ maj == UNIX98_PTY_SLAVE_MAJOR ||
++ maj == TTYAUX_MAJOR) {
++ return cpt_dump_content_tty(file, ctx);
++ }
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ if (file->f_op && file->f_op->open == tun_chr_open)
++ return 0;
++#endif
++ eprintk_ctx("unsupported chrdev %d/%d\n", maj, iminor(ino));
++ return -EINVAL;
++}
++
++static int dump_content_blkdev(struct file *file, struct cpt_context *ctx)
++{
++ struct inode *ino = file->f_dentry->d_inode;
++
++ /* We are not going to transfer them. */
++ eprintk_ctx("unsupported blkdev %d/%d\n", imajor(ino), iminor(ino));
++ return -EINVAL;
++}
++
++static int dump_content_fifo(struct file *file, struct cpt_context *ctx)
++{
++ struct inode *ino = file->f_dentry->d_inode;
++ cpt_object_t *obj;
++ loff_t saved_pos;
++ int readers;
++ int writers;
++ int anon = 0;
++
++ mutex_lock(&ino->i_mutex);
++ readers = ino->i_pipe->readers;
++ writers = ino->i_pipe->writers;
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file1 = obj->o_obj;
++ if (file1->f_dentry->d_inode == ino) {
++ if (file1->f_mode & FMODE_READ)
++ readers--;
++ if (file1->f_mode & FMODE_WRITE)
++ writers--;
++ }
++ }
++ mutex_unlock(&ino->i_mutex);
++ if (readers || writers) {
++ struct dentry *dr = file->f_dentry->d_sb->s_root;
++ if (dr->d_name.len == 7 && memcmp(dr->d_name.name,"pipefs:",7) == 0)
++ anon = 1;
++
++ if (anon) {
++ eprintk_ctx("pipe has %d/%d external readers/writers\n", readers, writers);
++ return -EBUSY;
++ }
++ /* If fifo has external readers/writers, we are in troubles.
++ * If the buffer is not empty, we must move its content.
++ * But if the fifo is owned by a service, we cannot do
++ * this. See?
++ *
++ * For now we assume, that if fifo is opened by another
++ * process, we do not own it and, hence, migrate without
++ * data.
++ */
++ return 0;
++ }
++
++ /* OK, we must save fifo state. No semaphores required. */
++
++ if (ino->i_pipe->nrbufs) {
++ struct cpt_obj_bits *v = cpt_get_buf(ctx);
++ struct pipe_inode_info *info;
++ int count, buf, nrbufs;
++
++ mutex_lock(&ino->i_mutex);
++ info = ino->i_pipe;
++ count = 0;
++ buf = info->curbuf;
++ nrbufs = info->nrbufs;
++ while (--nrbufs >= 0) {
++ if (!info->bufs[buf].ops->can_merge) {
++ mutex_unlock(&ino->i_mutex);
++ eprintk_ctx("unknown format of pipe buffer\n");
++ return -EINVAL;
++ }
++ count += info->bufs[buf].len;
++ buf = (buf+1) & (PIPE_BUFFERS-1);
++ }
++
++ if (!count) {
++ mutex_unlock(&ino->i_mutex);
++ return 0;
++ }
++
++ cpt_push_object(&saved_pos, ctx);
++ cpt_open_object(NULL, ctx);
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_BITS;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_DATA;
++ v->cpt_size = count;
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ count = 0;
++ buf = info->curbuf;
++ nrbufs = info->nrbufs;
++ while (--nrbufs >= 0) {
++ struct pipe_buffer *b = info->bufs + buf;
++ /* need to ->pin first? */
++ void * addr = b->ops->map(info, b, 0);
++ ctx->write(addr + b->offset, b->len, ctx);
++ b->ops->unmap(info, b, addr);
++ buf = (buf+1) & (PIPE_BUFFERS-1);
++ }
++
++ mutex_unlock(&ino->i_mutex);
++
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_pos, ctx);
++ }
++
++ return 0;
++}
++
++static int dump_content_socket(struct file *file, struct cpt_context *ctx)
++{
++ return 0;
++}
++
++struct cpt_dirent {
++ unsigned long ino;
++ char *name;
++ int namelen;
++ int found;
++};
++
++static int cpt_filldir(void * __buf, const char * name, int namelen,
++ loff_t offset, u64 ino, unsigned int d_type)
++{
++ struct cpt_dirent * dirent = __buf;
++
++ if ((ino == dirent->ino) && (namelen < PAGE_SIZE - 1)) {
++ memcpy(dirent->name, name, namelen);
++ dirent->name[namelen] = '\0';
++ dirent->namelen = namelen;
++ dirent->found = 1;
++ return 1;
++ }
++ return 0;
++}
++
++static int find_linked_dentry(struct dentry *d, struct vfsmount *mnt,
++ struct inode *ino, struct cpt_context *ctx)
++{
++ int err = -EBUSY;
++ struct file *f = NULL;
++ struct cpt_dirent entry;
++ struct dentry *de, *found = NULL;
++
++ dprintk_ctx("deleted reference to existing inode, try to find file\n");
++ /* 1. Try to find not deleted dentry in ino->i_dentry list */
++ spin_lock(&dcache_lock);
++ list_for_each_entry(de, &ino->i_dentry, d_alias) {
++ if (!IS_ROOT(de) && d_unhashed(de))
++ continue;
++ found = de;
++ dget_locked(found);
++ break;
++ }
++ spin_unlock(&dcache_lock);
++ if (found) {
++ err = cpt_dump_dentry(found, mnt, 0, ctx);
++ dput(found);
++ if (!err) {
++ dprintk_ctx("dentry found in aliases\n");
++ return 0;
++ }
++ }
++
++ /* 2. Try to find file in current dir */
++ de = dget_parent(d);
++ if (!de)
++ return -EINVAL;
++
++ mntget(mnt);
++ f = dentry_open(de, mnt, O_RDONLY);
++ if (IS_ERR(f))
++ return PTR_ERR(f);
++
++ entry.ino = ino->i_ino;
++ entry.name = cpt_get_buf(ctx);
++ entry.found = 0;
++ err = vfs_readdir(f, cpt_filldir, &entry);
++ if (err || !entry.found) {
++ err = err ? err : -ENOENT;
++ goto err_readdir;
++ }
++
++ found = lookup_one_len(entry.name, de, entry.namelen);
++ if (IS_ERR(found)) {
++ err = PTR_ERR(found);
++ goto err_readdir;
++ }
++
++ err = -ENOENT;
++ if (found->d_inode != ino)
++ goto err_lookup;
++
++ dprintk_ctx("dentry found in dir\n");
++ __cpt_release_buf(ctx);
++ err = cpt_dump_dentry(found, mnt, 0, ctx);
++
++err_lookup:
++ dput(found);
++err_readdir:
++ fput(f);
++ __cpt_release_buf(ctx);
++ return err;
++}
++
++static int dump_one_inode(struct file *file, struct dentry *d,
++ struct vfsmount *mnt, struct cpt_context *ctx)
++{
++ int err = 0;
++ struct inode *ino = d->d_inode;
++ cpt_object_t *iobj;
++ int dump_it = 0;
++
++ iobj = lookup_cpt_object(CPT_OBJ_INODE, ino, ctx);
++ if (!iobj)
++ return -EINVAL;
++
++ if (iobj->o_pos >= 0)
++ return 0;
++
++ if ((!IS_ROOT(d) && d_unhashed(d)) &&
++ !cpt_replaced(d, mnt, ctx))
++ dump_it = 1;
++ if (!S_ISREG(ino->i_mode) && !S_ISDIR(ino->i_mode)) {
++ if (file->f_op == &eventpoll_fops)
++ return 0;
++ dump_it = 1;
++ }
++
++ if (!dump_it)
++ return 0;
++
++ cpt_open_object(iobj, ctx);
++ cpt_dump_inode(d, mnt, ctx);
++
++ if (!IS_ROOT(d) && d_unhashed(d)) {
++ struct file *parent;
++ parent = iobj->o_parent;
++ if (!parent ||
++ (!IS_ROOT(parent->f_dentry) && d_unhashed(parent->f_dentry))) {
++ /* Inode is not deleted, but it does not
++ * have references from inside checkpointed
++ * process group. */
++ if (ino->i_nlink != 0) {
++ err = find_linked_dentry(d, mnt, ino, ctx);
++ if (err) {
++ eprintk_ctx("deleted reference to existing inode, checkpointing is impossible: %d\n", err);
++ return -EBUSY;
++ }
++ if (S_ISREG(ino->i_mode) || S_ISDIR(ino->i_mode))
++ dump_it = 0;
++ }
++ } else {
++ /* Refer to _another_ file name. */
++ err = cpt_dump_filename(parent, 0, ctx);
++ if (err)
++ return err;
++ if (S_ISREG(ino->i_mode) || S_ISDIR(ino->i_mode))
++ dump_it = 0;
++ }
++ }
++ if (dump_it) {
++ if (S_ISREG(ino->i_mode)) {
++ if ((err = dump_content_regular(file, ctx)) != 0) {
++ eprintk_ctx("dump_content_regular ");
++ cpt_printk_dentry(d, mnt);
++ }
++ } else if (S_ISDIR(ino->i_mode)) {
++ /* We cannot do anything. The directory should be
++ * empty, so it is not a big deal.
++ */
++ } else if (S_ISCHR(ino->i_mode)) {
++ err = dump_content_chrdev(file, ctx);
++ } else if (S_ISBLK(ino->i_mode)) {
++ err = dump_content_blkdev(file, ctx);
++ } else if (S_ISFIFO(ino->i_mode)) {
++ err = dump_content_fifo(file, ctx);
++ } else if (S_ISSOCK(ino->i_mode)) {
++ err = dump_content_socket(file, ctx);
++ } else {
++ eprintk_ctx("unknown inode mode %o, magic 0x%lx\n", ino->i_mode & S_IFMT, ino->i_sb->s_magic);
++ err = -EINVAL;
++ }
++ }
++ cpt_close_object(ctx);
++
++ return err;
++}
++
++int cpt_dump_files(struct cpt_context *ctx)
++{
++ int epoll_nr, inotify_nr;
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_TTY);
++ for_each_object(obj, CPT_OBJ_TTY) {
++ int err;
++
++ if ((err = cpt_dump_tty(obj, ctx)) != 0)
++ return err;
++ }
++ cpt_close_section(ctx);
++
++ cpt_open_section(ctx, CPT_SECT_INODE);
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ int err;
++
++ if ((err = dump_one_inode(file, file->f_dentry,
++ file->f_vfsmnt, ctx)) != 0)
++ return err;
++ }
++ for_each_object(obj, CPT_OBJ_FS) {
++ struct fs_struct *fs = obj->o_obj;
++ int err;
++
++ if (fs->root.dentry &&
++ (err = dump_one_inode(NULL, fs->root.dentry, fs->root.mnt, ctx)) != 0)
++ return err;
++ if (fs->pwd.dentry &&
++ (err = dump_one_inode(NULL, fs->pwd.dentry, fs->pwd.mnt, ctx)) != 0)
++ return err;
++ }
++ cpt_close_section(ctx);
++
++ epoll_nr = 0;
++ inotify_nr = 0;
++ cpt_open_section(ctx, CPT_SECT_FILES);
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ int err;
++
++ if ((err = dump_one_file(obj, file, ctx)) != 0)
++ return err;
++ if (file->f_op == &eventpoll_fops)
++ epoll_nr++;
++ if (file->f_dentry->d_inode->i_sb->s_magic == FSMAGIC_INOTIFY)
++ inotify_nr++;
++ }
++ cpt_close_section(ctx);
++
++ if (epoll_nr) {
++ cpt_open_section(ctx, CPT_SECT_EPOLL);
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ if (file->f_op == &eventpoll_fops) {
++ int err;
++ if ((err = cpt_dump_epolldev(obj, ctx)) != 0)
++ return err;
++ }
++ }
++ cpt_close_section(ctx);
++ }
++
++ if (inotify_nr) {
++ cpt_open_section(ctx, CPT_SECT_INOTIFY);
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ if (file->f_dentry->d_inode->i_sb->s_magic == FSMAGIC_INOTIFY) {
++ int err = -EINVAL;
++#ifdef CONFIG_INOTIFY_USER
++ if ((err = cpt_dump_inotify(obj, ctx)) != 0)
++#endif
++ return err;
++ }
++ }
++ cpt_close_section(ctx);
++ }
++
++ cpt_open_section(ctx, CPT_SECT_SOCKET);
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ int err;
++
++ if ((err = cpt_dump_socket(obj, obj->o_obj, obj->o_index, -1, ctx)) != 0)
++ return err;
++ }
++ cpt_close_section(ctx);
++
++ return 0;
++}
++
++static int dump_filedesc(int fd, struct file *file,
++ struct files_struct *f, struct cpt_context *ctx)
++{
++ struct cpt_fd_image *v = cpt_get_buf(ctx);
++ cpt_object_t *obj;
++
++ cpt_open_object(NULL, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_FILEDESC;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ v->cpt_fd = fd;
++ obj = lookup_cpt_object(CPT_OBJ_FILE, file, ctx);
++ if (!obj) BUG();
++ v->cpt_file = obj->o_pos;
++ v->cpt_flags = 0;
++ if (FD_ISSET(fd, f->fdt->close_on_exec))
++ v->cpt_flags = CPT_FD_FLAG_CLOSEEXEC;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ cpt_close_object(ctx);
++
++ return 0;
++}
++
++static int dump_one_file_struct(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct files_struct *f = obj->o_obj;
++ struct cpt_files_struct_image *v = cpt_get_buf(ctx);
++ int fd;
++ loff_t saved_obj;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_FILES;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_index = obj->o_index;
++ v->cpt_max_fds = f->fdt->max_fds;
++ v->cpt_next_fd = f->next_fd;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ for (fd = 0; fd < f->fdt->max_fds; fd++) {
++ struct file *file = fcheck_files(f, fd);
++ if (file)
++ dump_filedesc(fd, file, f, ctx);
++ }
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++
++ return 0;
++}
++
++int cpt_dump_files_struct(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_FILES_STRUCT);
++
++ for_each_object(obj, CPT_OBJ_FILES) {
++ int err;
++
++ if ((err = dump_one_file_struct(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++ return 0;
++}
++
++int cpt_collect_fs(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->fs) {
++ if (cpt_object_add(CPT_OBJ_FS, tsk->fs, ctx) == NULL)
++ return -ENOMEM;
++ if (tsk->fs->pwd.dentry &&
++ cpt_object_add(CPT_OBJ_INODE, tsk->fs->pwd.dentry->d_inode, ctx) == NULL)
++ return -ENOMEM;
++ if (tsk->fs->root.dentry &&
++ cpt_object_add(CPT_OBJ_INODE, tsk->fs->root.dentry->d_inode, ctx) == NULL)
++ return -ENOMEM;
++ }
++ }
++ return 0;
++}
++
++int cpt_dump_dir(struct dentry *d, struct vfsmount *mnt, struct cpt_context *ctx)
++{
++ struct file file;
++
++ memset(&file, 0, sizeof(file));
++
++ file.f_dentry = d;
++ file.f_vfsmnt = mnt;
++ file.f_mode = FMODE_READ|FMODE_PREAD|FMODE_LSEEK;
++ return dump_one_file(NULL, &file, ctx);
++}
++
++static int dump_one_fs(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct fs_struct *fs = obj->o_obj;
++ struct cpt_fs_struct_image *v = cpt_get_buf(ctx);
++ loff_t saved_obj;
++ int err;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_FS;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_umask = fs->umask;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ err = cpt_dump_dir(fs->root.dentry, fs->root.mnt, ctx);
++ if (!err)
++ err = cpt_dump_dir(fs->pwd.dentry, fs->pwd.mnt, ctx);
++
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++
++ return err;
++}
++
++int cpt_dump_fs_struct(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_FS);
++
++ for_each_object(obj, CPT_OBJ_FS) {
++ int err;
++
++ if ((err = dump_one_fs(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++ return 0;
++}
++
++static int check_one_namespace(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ int err = 0;
++ struct mnt_namespace *n = obj->o_obj;
++ struct list_head *p;
++ char *path_buf, *path;
++
++ path_buf = (char *) __get_free_page(GFP_KERNEL);
++ if (!path_buf)
++ return -ENOMEM;
++
++ down_read(&namespace_sem);
++ list_for_each(p, &n->list) {
++ struct path pt;
++ struct vfsmount *mnt = list_entry(p, struct vfsmount, mnt_list);
++
++ pt.dentry = mnt->mnt_root;
++ pt.mnt = mnt;
++ path = d_path(&pt, path_buf, PAGE_SIZE);
++ if (IS_ERR(path))
++ continue;
++
++ if (check_one_vfsmount(mnt)) {
++ eprintk_ctx("unsupported fs type %s\n", mnt->mnt_sb->s_type->name);
++ err = -EINVAL;
++ break;
++ }
++ }
++ up_read(&namespace_sem);
++
++ free_page((unsigned long) path_buf);
++
++ return err;
++}
++
++int cpt_collect_namespace(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->nsproxy && tsk->nsproxy->mnt_ns &&
++ cpt_object_add(CPT_OBJ_NAMESPACE,
++ tsk->nsproxy->mnt_ns, ctx) == NULL)
++ return -ENOMEM;
++ }
++
++ for_each_object(obj, CPT_OBJ_NAMESPACE) {
++ int err;
++ if ((err = check_one_namespace(obj, ctx)) != 0)
++ return err;
++ }
++
++ return 0;
++}
++
++struct args_t
++{
++ int* pfd;
++ char* path;
++};
++
++static int dumptmpfs(void *arg)
++{
++ int i;
++ struct args_t *args = arg;
++ int *pfd = args->pfd;
++ int fd0, fd2;
++ char *path = args->path;
++ char *argv[] = { "tar", "-c", "-S", "--numeric-owner", path, NULL };
++
++ i = real_env_create(VEID(get_exec_env()), VE_ENTER|VE_SKIPLOCK, 2, NULL, 0);
++ if (i < 0) {
++ eprintk("cannot enter ve to dump tmpfs\n");
++ module_put(THIS_MODULE);
++ return 255 << 8;
++ }
++
++ if (pfd[1] != 1)
++ sc_dup2(pfd[1], 1);
++ set_fs(KERNEL_DS);
++ fd0 = sc_open("/dev/null", O_RDONLY, 0);
++ fd2 = sc_open("/dev/null", O_WRONLY, 0);
++ if (fd0 < 0 || fd2 < 0) {
++ eprintk("can not open /dev/null for tar: %d %d\n", fd0, fd2);
++ module_put(THIS_MODULE);
++ return 255 << 8;
++ }
++ if (fd0 != 0)
++ sc_dup2(fd0, 0);
++ if (fd2 != 2)
++ sc_dup2(fd2, 2);
++
++ for (i = 3; i < current->files->fdt->max_fds; i++) {
++ sc_close(i);
++ }
++
++ module_put(THIS_MODULE);
++
++ i = sc_execve("/bin/tar", argv, NULL);
++ eprintk("failed to exec /bin/tar: %d\n", i);
++ return 255 << 8;
++}
++
++static int cpt_dump_tmpfs(char *path, struct cpt_context *ctx)
++{
++ int err;
++ int pid;
++ int pfd[2];
++ struct file *f;
++ struct cpt_object_hdr v;
++ char buf[16];
++ int n;
++ loff_t saved_obj;
++ struct args_t args;
++ int status;
++ mm_segment_t oldfs;
++ sigset_t ignore, blocked;
++
++ err = sc_pipe(pfd);
++ if (err < 0)
++ return err;
++ args.pfd = pfd;
++ args.path = path;
++ ignore.sig[0] = CPT_SIG_IGNORE_MASK;
++ sigprocmask(SIG_BLOCK, &ignore, &blocked);
++ err = pid = local_kernel_thread(dumptmpfs, (void*)&args,
++ SIGCHLD | CLONE_VFORK, 0);
++ if (err < 0) {
++ eprintk_ctx("tmpfs local_kernel_thread: %d\n", err);
++ goto out;
++ }
++ f = fget(pfd[0]);
++ sc_close(pfd[1]);
++ sc_close(pfd[0]);
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NAME;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_NAME;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ do {
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ n = f->f_op->read(f, buf, sizeof(buf), &f->f_pos);
++ set_fs(oldfs);
++ if (n > 0)
++ ctx->write(buf, n, ctx);
++ } while (n > 0);
++
++ fput(f);
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if ((err = sc_waitx(pid, 0, &status)) < 0)
++ eprintk_ctx("wait4: %d\n", err);
++ else if ((status & 0x7f) == 0) {
++ err = (status & 0xff00) >> 8;
++ if (err != 0) {
++ eprintk_ctx("tar exited with %d\n", err);
++ err = -EINVAL;
++ }
++ } else {
++ eprintk_ctx("tar terminated\n");
++ err = -EINVAL;
++ }
++ set_fs(oldfs);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++
++ buf[0] = 0;
++ ctx->write(buf, 1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ return n ? : err;
++
++out:
++ if (pfd[1] >= 0)
++ sc_close(pfd[1]);
++ if (pfd[0] >= 0)
++ sc_close(pfd[0]);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++ return err;
++}
++
++static int loopy_root(struct vfsmount *mnt)
++{
++ struct list_head *p;
++
++ list_for_each(p, &mnt->mnt_ns->list) {
++ struct vfsmount * m = list_entry(p, struct vfsmount, mnt_list);
++ if (m == mnt)
++ return 0;
++ if (m->mnt_sb == mnt->mnt_sb)
++ return 1;
++ }
++ /* Cannot happen */
++ return 0;
++}
++
++static int cpt_dump_bind_mnt(struct vfsmount * mnt, cpt_context_t * ctx)
++{
++ struct list_head *p;
++ int err = -EINVAL;
++
++ /* One special case: mount --bind /a /a */
++ if (mnt->mnt_root == mnt->mnt_mountpoint)
++ return cpt_dump_dentry(mnt->mnt_root, mnt, 0, ctx);
++
++ list_for_each_prev(p, &mnt->mnt_list) {
++ struct vfsmount * m;
++
++ if (p == &mnt->mnt_ns->list)
++ break;
++
++ m = list_entry(p, struct vfsmount, mnt_list);
++
++ if (m->mnt_sb != mnt->mnt_sb)
++ continue;
++
++ err = cpt_dump_dentry(mnt->mnt_root, m, 0, ctx);
++ if (err == 0)
++ break;
++ }
++ return err;
++}
++
++static int dump_vfsmount(struct vfsmount *mnt, struct cpt_context *ctx)
++{
++ int err = 0;
++ struct cpt_vfsmount_image v;
++ loff_t saved_obj;
++ char *path_buf, *path;
++ struct path p;
++
++ path_buf = (char *) __get_free_page(GFP_KERNEL);
++ if (!path_buf)
++ return -ENOMEM;
++
++ p.dentry = mnt->mnt_root;
++ p.mnt = mnt;
++ path = d_path(&p, path_buf, PAGE_SIZE);
++ if (IS_ERR(path)) {
++ free_page((unsigned long) path_buf);
++ return PTR_ERR(path) == -EINVAL ? 0 : PTR_ERR(path);
++ }
++
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_VFSMOUNT;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_ARRAY;
++
++ v.cpt_mntflags = mnt->mnt_flags;
++ if (top_beancounter(slab_ub(mnt)) != top_beancounter(get_exec_ub())) {
++ v.cpt_mntflags |= CPT_MNT_EXT;
++ } else {
++ if (mnt->mnt_root != mnt->mnt_sb->s_root || loopy_root(mnt))
++ v.cpt_mntflags |= CPT_MNT_BIND;
++ }
++ v.cpt_flags = mnt->mnt_sb->s_flags;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_dump_string(mnt->mnt_devname ? : "none", ctx);
++ cpt_dump_string(path, ctx);
++ cpt_dump_string(mnt->mnt_sb->s_type->name, ctx);
++
++ if (v.cpt_mntflags & CPT_MNT_BIND)
++ err = cpt_dump_bind_mnt(mnt, ctx);
++ else if (!(v.cpt_mntflags & CPT_MNT_EXT) &&
++ strcmp(mnt->mnt_sb->s_type->name, "tmpfs") == 0) {
++ mntget(mnt);
++ up_read(&namespace_sem);
++ err = cpt_dump_tmpfs(path, ctx);
++ down_read(&namespace_sem);
++ if (!err) {
++ if (list_empty(&mnt->mnt_list))
++ err = -EBUSY;
++ }
++ mntput(mnt);
++ }
++
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++ if (!err && mnt->mnt_sb->s_magic == FSMAGIC_VEFS)
++ vefs_track_force_stop(mnt->mnt_sb);
++
++ free_page((unsigned long) path_buf);
++
++ return err;
++}
++
++static int dump_one_namespace(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct mnt_namespace *n = obj->o_obj;
++ struct cpt_object_hdr v;
++ struct list_head *p;
++ loff_t saved_obj;
++ int err = 0;
++
++ cpt_open_object(obj, ctx);
++
++ v.cpt_next = -1;
++ v.cpt_object = CPT_OBJ_NAMESPACE;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_ARRAY;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++
++ down_read(&namespace_sem);
++ list_for_each(p, &n->list) {
++ err = dump_vfsmount(list_entry(p, struct vfsmount, mnt_list), ctx);
++ if (err)
++ break;
++ }
++ up_read(&namespace_sem);
++
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++
++ return err;
++}
++
++int cpt_dump_namespace(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_NAMESPACE);
++
++ for_each_object(obj, CPT_OBJ_NAMESPACE) {
++ int err;
++
++ if ((err = dump_one_namespace(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_files.h b/kernel/cpt/cpt_files.h
+new file mode 100644
+index 0000000..7770ab2
+--- /dev/null
++++ b/kernel/cpt/cpt_files.h
+@@ -0,0 +1,71 @@
++int cpt_collect_files(cpt_context_t *);
++int cpt_collect_fs(cpt_context_t *);
++int cpt_collect_namespace(cpt_context_t *);
++int cpt_collect_sysvsem_undo(cpt_context_t *);
++int cpt_collect_tty(struct file *, cpt_context_t *);
++int cpt_dump_files(struct cpt_context *ctx);
++int cpt_dump_files_struct(struct cpt_context *ctx);
++int cpt_dump_fs_struct(struct cpt_context *ctx);
++int cpt_dump_content_sysvshm(struct file *file, struct cpt_context *ctx);
++int cpt_dump_content_tty(struct file *file, struct cpt_context *ctx);
++int cpt_dump_tty(cpt_object_t *, struct cpt_context *ctx);
++struct file * rst_sysv_shm_vma(struct cpt_vma_image *vmai, struct cpt_context *ctx);
++struct file * rst_sysv_shm_itself(loff_t pos, struct cpt_context *ctx);
++struct file * rst_open_tty(struct cpt_file_image *fi, struct cpt_inode_image *ii, unsigned flags, struct cpt_context *ctx);
++__u32 cpt_tty_fasync(struct file *file, struct cpt_context *ctx);
++
++int rst_posix_locks(struct cpt_context *ctx);
++
++struct file *rst_file(loff_t pos, int fd, struct cpt_context *ctx);
++int rst_files_complete(struct cpt_task_image *ti, struct cpt_context *ctx);
++__u32 rst_files_flag(struct cpt_task_image *ti, struct cpt_context *ctx);
++int rst_fs_complete(struct cpt_task_image *ti, struct cpt_context *ctx);
++int rst_restore_fs(struct cpt_context *ctx);
++
++int cpt_collect_sysv(cpt_context_t *);
++int cpt_dump_sysvsem(struct cpt_context *ctx);
++int cpt_dump_sysvmsg(struct cpt_context *ctx);
++int rst_sysv_ipc(struct cpt_context *ctx);
++int rst_semundo_complete(struct cpt_task_image *ti, struct cpt_context *ctx);
++__u32 rst_semundo_flag(struct cpt_task_image *ti, struct cpt_context *ctx);
++
++int cpt_dump_namespace(struct cpt_context *ctx);
++int rst_root_namespace(struct cpt_context *ctx);
++
++int rst_stray_files(struct cpt_context *ctx);
++int rst_tty_jobcontrol(struct cpt_context *ctx);
++
++void rst_flush_filejobs(struct cpt_context *);
++int rst_do_filejobs(struct cpt_context *);
++
++extern struct file_operations eventpoll_fops;
++int rst_eventpoll(struct cpt_context *);
++struct file *cpt_open_epolldev(struct cpt_file_image *fi,
++ unsigned flags,
++ struct cpt_context *ctx);
++int cpt_dump_epolldev(cpt_object_t *obj, struct cpt_context *);
++
++int cpt_dump_dir(struct dentry *d, struct vfsmount *mnt, struct cpt_context *ctx);
++int cpt_get_dentry(struct dentry **dp, struct vfsmount **mp,
++ loff_t *pos, struct cpt_context *ctx);
++
++int cpt_dump_inotify(cpt_object_t *obj, cpt_context_t *ctx);
++int rst_inotify(cpt_context_t *ctx);
++struct file *rst_open_inotify(struct cpt_file_image *fi,
++ unsigned flags,
++ struct cpt_context *ctx);
++
++
++int cpt_verify_overmount(char *path, struct dentry *d, struct vfsmount *mnt,
++ cpt_context_t *ctx);
++
++#define check_one_vfsmount(mnt) \
++ (strcmp(mnt->mnt_sb->s_type->name, "rootfs") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "ext3") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "ext2") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "simfs") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "unionfs") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "tmpfs") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "devpts") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "proc") != 0 && \
++ strcmp(mnt->mnt_sb->s_type->name, "sysfs") != 0)
+diff --git a/kernel/cpt/cpt_fsmagic.h b/kernel/cpt/cpt_fsmagic.h
+new file mode 100644
+index 0000000..142e539
+--- /dev/null
++++ b/kernel/cpt/cpt_fsmagic.h
+@@ -0,0 +1,16 @@
++/* Collected from kernel sources. */
++
++#define FSMAGIC_TMPFS 0x01021994
++#define FSMAGIC_PIPEFS 0x50495045
++#define FSMAGIC_SOCKFS 0x534F434B
++#define FSMAGIC_PFMFS 0xa0b4d889
++#define FSMAGIC_BDEV 0x62646576
++#define FSMAGIC_FUTEX 0x0BAD1DEA
++#define FSMAGIC_INOTIFY 0x2BAD1DEA
++#define FSMAGIC_MQUEUE 0x19800202
++#define FSMAGIC_PROC 0x9fa0
++#define FSMAGIC_DEVPTS 0x1CD1
++#define FSMAGIC_AUTOFS 0x0187
++#define FSMAGIC_EXT2 0xEF53
++#define FSMAGIC_REISER 0x52654973
++#define FSMAGIC_VEFS 0x565a4653
+diff --git a/kernel/cpt/cpt_inotify.c b/kernel/cpt/cpt_inotify.c
+new file mode 100644
+index 0000000..4d4637e
+--- /dev/null
++++ b/kernel/cpt/cpt_inotify.c
+@@ -0,0 +1,144 @@
++/*
++ *
++ * kernel/cpt/cpt_inotify.c
++ *
++ * Copyright (C) 2000-2007 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mnt_namespace.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/vzcalluser.h>
++#include <linux/inotify.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#include "cpt_syscalls.h"
++
++extern struct file_operations inotify_fops;
++
++int cpt_dump_inotify(cpt_object_t *obj, cpt_context_t *ctx)
++{
++ int err = 0;
++ struct file *file = obj->o_obj;
++ struct inotify_device *dev;
++ struct inotify_watch *watch;
++ struct inotify_kernel_event *kev;
++ struct cpt_inotify_image ii;
++
++ if (file->f_op != &inotify_fops) {
++ eprintk_ctx("bad inotify file\n");
++ return -EINVAL;
++ }
++
++ dev = file->private_data;
++
++ /* inotify_user.c does not protect open /proc/N/fd, silly.
++ * Opener will get an invalid file with uninitialized private_data
++ */
++ if (unlikely(dev == NULL)) {
++ eprintk_ctx("bad inotify dev\n");
++ return -EINVAL;
++ }
++
++ cpt_open_object(NULL, ctx);
++
++ ii.cpt_next = CPT_NULL;
++ ii.cpt_object = CPT_OBJ_INOTIFY;
++ ii.cpt_hdrlen = sizeof(ii);
++ ii.cpt_content = CPT_CONTENT_ARRAY;
++ ii.cpt_file = obj->o_pos;
++ ii.cpt_user = dev->user->uid;
++ ii.cpt_max_events = dev->max_events;
++ ii.cpt_last_wd = dev->ih->last_wd;
++
++ ctx->write(&ii, sizeof(ii), ctx);
++
++ mutex_lock(&dev->ih->mutex);
++ list_for_each_entry(watch, &dev->ih->watches, h_list) {
++ loff_t saved_obj;
++ loff_t saved_obj2;
++ struct cpt_inotify_wd_image wi;
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ wi.cpt_next = CPT_NULL;
++ wi.cpt_object = CPT_OBJ_INOTIFY_WATCH;
++ wi.cpt_hdrlen = sizeof(wi);
++ wi.cpt_content = CPT_CONTENT_ARRAY;
++ wi.cpt_wd = watch->wd;
++ wi.cpt_mask = watch->mask;
++
++ ctx->write(&wi, sizeof(wi), ctx);
++
++ cpt_push_object(&saved_obj2, ctx);
++ err = cpt_dump_dir(watch->path.dentry, watch->path.mnt, ctx);
++ cpt_pop_object(&saved_obj2, ctx);
++ if (err)
++ break;
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ mutex_unlock(&dev->ih->mutex);
++
++ if (err)
++ return err;
++
++ mutex_lock(&dev->ev_mutex);
++ list_for_each_entry(kev, &dev->events, list) {
++ loff_t saved_obj;
++ struct cpt_inotify_ev_image ei;
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ ei.cpt_next = CPT_NULL;
++ ei.cpt_object = CPT_OBJ_INOTIFY_EVENT;
++ ei.cpt_hdrlen = sizeof(ei);
++ ei.cpt_content = CPT_CONTENT_NAME;
++ ei.cpt_wd = kev->event.wd;
++ ei.cpt_mask = kev->event.mask;
++ ei.cpt_cookie = kev->event.cookie;
++ ei.cpt_namelen = kev->name ? strlen(kev->name) : 0;
++
++ ctx->write(&ei, sizeof(ei), ctx);
++
++ if (kev->name) {
++ ctx->write(kev->name, ei.cpt_namelen+1, ctx);
++ ctx->align(ctx);
++ }
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ mutex_unlock(&dev->ev_mutex);
++
++ cpt_close_object(ctx);
++
++ return err;
++}
+diff --git a/kernel/cpt/cpt_kernel.c b/kernel/cpt/cpt_kernel.c
+new file mode 100644
+index 0000000..5eb7f1c
+--- /dev/null
++++ b/kernel/cpt/cpt_kernel.c
+@@ -0,0 +1,177 @@
++/*
++ *
++ * kernel/cpt/cpt_kernel.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#define __KERNEL_SYSCALLS__ 1
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/kernel.h>
++#ifdef CONFIG_X86
++#include <asm/cpufeature.h>
++#endif
++#include <linux/cpt_image.h>
++
++#include "cpt_kernel.h"
++#include "cpt_syscalls.h"
++
++int debug_level = 1;
++
++#ifdef CONFIG_X86_32
++
++/*
++ * Create a kernel thread
++ */
++extern void kernel_thread_helper(void);
++int asm_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags, pid_t pid)
++{
++ struct pt_regs regs;
++
++ memset(&regs, 0, sizeof(regs));
++
++ regs.bx = (unsigned long) fn;
++ regs.dx = (unsigned long) arg;
++
++ regs.ds = __USER_DS;
++ regs.es = __USER_DS;
++ regs.fs = __KERNEL_PERCPU;
++ regs.orig_ax = -1;
++ regs.ip = (unsigned long) kernel_thread_helper;
++ regs.cs = __KERNEL_CS | get_kernel_rpl();
++ regs.flags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
++
++ /* Ok, create the new process.. */
++ return do_fork_pid(flags | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL, pid);
++}
++#endif
++
++#ifdef CONFIG_IA64
++pid_t
++asm_kernel_thread (int (*fn)(void *), void *arg, unsigned long flags, pid_t pid)
++{
++ extern void start_kernel_thread (void);
++ unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
++ struct {
++ struct switch_stack sw;
++ struct pt_regs pt;
++ } regs;
++
++ memset(&regs, 0, sizeof(regs));
++ regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
++ regs.pt.r1 = helper_fptr[1]; /* set GP */
++ regs.pt.r9 = (unsigned long) fn; /* 1st argument */
++ regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
++ /* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
++ regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
++ regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
++ regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
++ regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
++ regs.sw.pr = (1 << 2 /*PRED_KERNEL_STACK*/);
++ return do_fork_pid(flags | CLONE_UNTRACED, 0, &regs.pt, 0, NULL, NULL, pid);
++}
++#endif
++
++int local_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags, pid_t pid)
++{
++ pid_t ret;
++
++ if (current->fs == NULL) {
++ /* do_fork_pid() hates processes without fs, oopses. */
++ printk("CPT BUG: local_kernel_thread: current->fs==NULL\n");
++ return -EINVAL;
++ }
++ if (!try_module_get(THIS_MODULE))
++ return -EBUSY;
++ ret = asm_kernel_thread(fn, arg, flags, pid);
++ if (ret < 0)
++ module_put(THIS_MODULE);
++ return ret;
++}
++
++#ifdef __i386__
++int __execve(const char *file, char **argv, char **envp)
++{
++ long res;
++ __asm__ volatile ("int $0x80"
++ : "=a" (res)
++ : "0" (__NR_execve),"b" ((long)(file)),"c" ((long)(argv)),
++ "d" ((long)(envp)) : "memory");
++ return (int)res;
++}
++#endif
++
++int sc_execve(char *cmd, char **argv, char **env)
++{
++ int ret;
++#ifndef __i386__
++ ret = kernel_execve(cmd, argv, env);
++#else
++ ret = __execve(cmd, argv, env);
++#endif
++ return ret;
++}
++
++unsigned int test_cpu_caps(void)
++{
++ unsigned int flags = 0;
++
++#ifdef CONFIG_X86
++ if (boot_cpu_has(X86_FEATURE_CMOV))
++ flags |= 1 << CPT_CPU_X86_CMOV;
++ if (cpu_has_fxsr)
++ flags |= 1 << CPT_CPU_X86_FXSR;
++ if (cpu_has_xmm)
++ flags |= 1 << CPT_CPU_X86_SSE;
++#ifndef CONFIG_X86_64
++ if (cpu_has_xmm2)
++#endif
++ flags |= 1 << CPT_CPU_X86_SSE2;
++ if (cpu_has_mmx)
++ flags |= 1 << CPT_CPU_X86_MMX;
++ if (boot_cpu_has(X86_FEATURE_3DNOW))
++ flags |= 1 << CPT_CPU_X86_3DNOW;
++ if (boot_cpu_has(X86_FEATURE_3DNOWEXT))
++ flags |= 1 << CPT_CPU_X86_3DNOW2;
++ if (boot_cpu_has(X86_FEATURE_SYSCALL))
++ flags |= 1 << CPT_CPU_X86_SYSCALL;
++#ifdef CONFIG_X86_64
++ if (boot_cpu_has(X86_FEATURE_SYSCALL) &&
++ boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
++ flags |= 1 << CPT_CPU_X86_SYSCALL32;
++#endif
++ if (boot_cpu_has(X86_FEATURE_SEP)
++#ifdef CONFIG_X86_64
++ && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL
++#endif
++ )
++ flags |= ((1 << CPT_CPU_X86_SEP) | (1 << CPT_CPU_X86_SEP32));
++#ifdef CONFIG_X86_64
++ flags |= 1 << CPT_CPU_X86_EMT64;
++#endif
++#endif
++#ifdef CONFIG_IA64
++ flags |= 1 << CPT_CPU_X86_IA64;
++ flags |= 1 << CPT_CPU_X86_FXSR;
++#endif
++ return flags;
++}
++
++unsigned int test_kernel_config(void)
++{
++ unsigned int flags = 0;
++#ifdef CONFIG_X86
++#if defined(CONFIG_X86_PAE) || defined(CONFIG_X86_64)
++ flags |= 1 << CPT_KERNEL_CONFIG_PAE;
++#endif
++#endif
++ return flags;
++}
+diff --git a/kernel/cpt/cpt_kernel.h b/kernel/cpt/cpt_kernel.h
+new file mode 100644
+index 0000000..9254778
+--- /dev/null
++++ b/kernel/cpt/cpt_kernel.h
+@@ -0,0 +1,99 @@
++/* Interface to kernel vars which we had to _add_. */
++
++#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
++#define TASK_TRACED TASK_STOPPED
++#define unix_peer(sk) ((sk)->sk_pair)
++#define page_mapcount(pg) ((pg)->mapcount)
++#else
++#define unix_peer(sk) (unix_sk(sk)->peer)
++#endif
++
++#ifdef CONFIG_IA64
++#define cpu_has_fxsr 1
++#endif
++
++#define CPT_SIG_IGNORE_MASK (\
++ (1 << (SIGCONT - 1)) | (1 << (SIGCHLD - 1)) | \
++ (1 << (SIGWINCH - 1)) | (1 << (SIGURG - 1)))
++
++static inline void do_gettimespec(struct timespec *ts)
++{
++ struct timeval tv;
++ do_gettimeofday(&tv);
++ ts->tv_sec = tv.tv_sec;
++ ts->tv_nsec = tv.tv_usec*1000;
++}
++
++int local_kernel_thread(int (*fn)(void *),
++ void * arg,
++ unsigned long flags,
++ pid_t pid);
++int asm_kernel_thread(int (*fn)(void *),
++ void * arg,
++ unsigned long flags,
++ pid_t pid);
++
++#if defined(CONFIG_VZFS_FS) || defined(CONFIG_VZFS_FS_MODULE)
++void vefs_track_force_stop(struct super_block *super);
++
++void vefs_track_notify(struct dentry *vdentry, int track_cow);
++
++struct dentry * vefs_replaced_dentry(struct dentry *de);
++int vefs_is_renamed_dentry(struct dentry *vde, struct dentry *pde);
++#else
++static inline void vefs_track_force_stop(struct super_block *super) { };
++
++static inline void vefs_track_notify(struct dentry *vdentry, int track_cow) { };
++#endif
++
++unsigned int test_cpu_caps(void);
++unsigned int test_kernel_config(void);
++
++#define test_one_flag_old(src, dst, flag, message, ret) \
++if (src & (1 << flag)) \
++ if (!(dst & (1 << flag))) { \
++ wprintk("Destination cpu does not have " message "\n"); \
++ ret = 1; \
++ }
++#define test_one_flag(src, dst, flag, message, ret) \
++if (src & (1 << flag)) \
++ if (!(dst & (1 << flag))) { \
++ eprintk_ctx("Destination cpu does not have " message "\n"); \
++ ret = 1; \
++ }
++
++static inline void
++_set_normalized_timespec(struct timespec *ts, time_t sec, long nsec)
++{
++ while (nsec >= NSEC_PER_SEC) {
++ nsec -= NSEC_PER_SEC;
++ ++sec;
++ }
++ while (nsec < 0) {
++ nsec += NSEC_PER_SEC;
++ --sec;
++ }
++ ts->tv_sec = sec;
++ ts->tv_nsec = nsec;
++}
++
++static inline struct timespec
++_ns_to_timespec(const s64 nsec)
++{
++ struct timespec ts;
++ s32 rem;
++
++ if (!nsec)
++ return (struct timespec) {0, 0};
++
++ ts.tv_sec = div_s64_rem(nsec, NSEC_PER_SEC, &rem);
++ if (unlikely(rem < 0)) {
++ ts.tv_sec--;
++ rem += NSEC_PER_SEC;
++ }
++ ts.tv_nsec = rem;
++
++ return ts;
++}
+diff --git a/kernel/cpt/cpt_mm.c b/kernel/cpt/cpt_mm.c
+new file mode 100644
+index 0000000..4e98a8e
+--- /dev/null
++++ b/kernel/cpt/cpt_mm.c
+@@ -0,0 +1,923 @@
++/*
++ *
++ * kernel/cpt/cpt_mm.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/hugetlb.h>
++#include <linux/errno.h>
++#include <linux/ve.h>
++#include <linux/pagemap.h>
++#include <linux/rmap.h>
++#ifdef CONFIG_X86
++#include <asm/ldt.h>
++#endif
++#include <asm/mmu.h>
++#include <linux/cpt_image.h>
++#include <linux/shm.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++#include "cpt_pagein.h"
++#endif
++#include "cpt_ubc.h"
++
++static int collect_one_aio_ctx(struct mm_struct *mm, struct kioctx *aio_ctx,
++ cpt_context_t *ctx)
++{
++ if (!list_empty(&aio_ctx->run_list)) {
++ /* This is impossible at least with kernel 2.6.8.1 or 2.6.16 */
++ eprintk_ctx("run list is not empty, cannot suspend AIO\n");
++ return -EBUSY;
++ }
++
++ /* Wait for pending IOCBs. Linux AIO is mostly _fake_.
++ * It is actually synchronous, except for direct IO and
++ * some funny raw USB things, which cannot happen inside VE.
++ * However, we do this for future.
++ *
++ * Later note: in 2.6.16 we may allow O_DIRECT, so that
++ * it is not meaningless code.
++ */
++ wait_for_all_aios(aio_ctx);
++
++ if (!list_empty(&aio_ctx->run_list) ||
++ !list_empty(&aio_ctx->active_reqs) ||
++ aio_ctx->reqs_active) {
++ eprintk_ctx("were not able to suspend AIO\n");
++ return -EBUSY;
++ }
++
++ return 0;
++}
++
++static int collect_one_mm(struct mm_struct *mm, cpt_context_t * ctx)
++{
++ struct vm_area_struct *vma;
++
++ for (vma = mm->mmap; vma; vma = vma->vm_next) {
++ if (vma->vm_file) {
++ if (cpt_object_add(CPT_OBJ_FILE, vma->vm_file, ctx) == NULL)
++ return -ENOMEM;
++ }
++ }
++
++ if (mm->exe_file &&
++ cpt_object_add(CPT_OBJ_FILE, mm->exe_file, ctx) == NULL)
++ return -ENOMEM;
++
++#ifdef CONFIG_BEANCOUNTERS
++ if (cpt_add_ubc(mm->mm_ub, ctx) == NULL)
++ return -ENOMEM;
++#endif
++
++ if (mm->ioctx_list) {
++ struct kioctx *aio_ctx;
++ int err;
++
++ for (aio_ctx = mm->ioctx_list; aio_ctx; aio_ctx = aio_ctx->next)
++ if ((err = collect_one_aio_ctx(mm, aio_ctx, ctx)) != 0)
++ return err;
++ }
++
++ return 0;
++}
++
++int cpt_collect_mm(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++ int err;
++ int index;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->mm && cpt_object_add(CPT_OBJ_MM, tsk->mm, ctx) == NULL)
++ return -ENOMEM;
++ }
++
++ index = 1;
++ for_each_object(obj, CPT_OBJ_MM) {
++ struct mm_struct *mm = obj->o_obj;
++ if (obj->o_count != atomic_read(&mm->mm_users)) {
++ eprintk_ctx("mm_struct is referenced outside %d %d\n", obj->o_count, atomic_read(&mm->mm_users));
++ return -EAGAIN;
++ }
++ cpt_obj_setindex(obj, index++, ctx);
++
++ if ((err = collect_one_mm(mm, ctx)) != 0)
++ return err;
++ }
++
++ return 0;
++}
++
++static int zcnt, scnt, scnt0, ucnt;
++
++/* Function where_is_anon_page() returns address of a anonymous page in mm
++ * of already dumped process. This happens f.e. after fork(). We do not use
++ * this right now, just keep statistics, it is diffucult to restore such state,
++ * but the most direct use is to save space in dumped image. */
++
++
++static inline unsigned long
++vma_address0(struct page *page, struct vm_area_struct *vma)
++{
++ pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
++ unsigned long address;
++
++ address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
++ if (unlikely(address < vma->vm_start || address >= vma->vm_end))
++ address |= 1;
++ return address;
++}
++
++static int really_this_one(struct vm_area_struct *vma, unsigned long address,
++ struct page *page)
++{
++ struct mm_struct *mm = vma->vm_mm;
++ pgd_t *pgd;
++ pud_t *pud;
++ pmd_t *pmd;
++ pte_t *pte;
++ spinlock_t *ptl;
++ int result;
++
++ pgd = pgd_offset(mm, address);
++ if (unlikely(!pgd_present(*pgd)))
++ return 0;
++
++ pud = pud_offset(pgd, address);
++ if (!pud_present(*pud))
++ return 0;
++
++ pmd = pmd_offset(pud, address);
++ if (unlikely(!pmd_present(*pmd)))
++ return 0;
++
++ result = 0;
++ pte = pte_offset_map(pmd, address);
++ if (!pte_present(*pte)) {
++ pte_unmap(pte);
++ return 0;
++ }
++
++ ptl = pte_lockptr(mm, pmd);
++ spin_lock(ptl);
++ if (pte_present(*pte) && page_to_pfn(page) == pte_pfn(*pte))
++ result = 1;
++ pte_unmap_unlock(pte, ptl);
++ return result;
++}
++
++static loff_t where_is_anon_page(cpt_object_t *mmobj, unsigned long mapaddr,
++ struct page *page, cpt_context_t * ctx)
++{
++ loff_t mmptr = CPT_NULL;
++ struct anon_vma *anon_vma;
++ struct vm_area_struct *vma;
++ int idx = mmobj->o_index;
++
++ if (!PageAnon(page))
++ return CPT_NULL;
++
++ anon_vma = page_lock_anon_vma(page);
++ if (!anon_vma)
++ return CPT_NULL;
++
++ list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
++ unsigned long addr = vma_address0(page, vma);
++ cpt_object_t *obj;
++
++ /* We do not try to support mremapped regions (addr != mapaddr),
++ * only mmaps directly inherited via fork().
++ * With this limitation we may check self-consistency of
++ * vmas (vm_start, vm_pgoff, anon_vma) before
++ * doing __copy_page_range() in rst_mm.
++ */
++ if (mmobj->o_obj != vma->vm_mm && addr == mapaddr) {
++ obj = lookup_cpt_object(CPT_OBJ_MM, vma->vm_mm, ctx);
++ if (obj && obj->o_pos != CPT_NULL && obj->o_index < idx) {
++ if (really_this_one(vma, addr, page)) {
++ mmptr = obj->o_pos;
++ idx = obj->o_index;
++ }
++ }
++ }
++ }
++ page_unlock_anon_vma(anon_vma);
++
++ return mmptr;
++}
++
++struct page_area
++{
++ int type;
++ unsigned long start;
++ unsigned long end;
++ pgoff_t pgoff;
++ loff_t mm;
++ __u64 list[16];
++};
++
++struct page_desc
++{
++ int type;
++ pgoff_t index;
++ loff_t mm;
++ int shared;
++};
++
++enum {
++ PD_ABSENT,
++ PD_COPY,
++ PD_ZERO,
++ PD_CLONE,
++ PD_FUNKEY,
++ PD_LAZY,
++ PD_ITER,
++ PD_ITERYOUNG,
++};
++
++/* 0: page can be obtained from backstore, or still not mapped anonymous page,
++ or something else, which does not requre copy.
++ 1: page requires copy
++ 2: page requres copy but its content is zero. Quite useless.
++ 3: wp page is shared after fork(). It is to be COWed when modified.
++ 4: page is something unsupported... We copy it right now.
++ */
++
++
++
++static void page_get_desc(cpt_object_t *mmobj,
++ struct vm_area_struct *vma, unsigned long addr,
++ struct page_desc *pdesc, cpt_context_t * ctx)
++{
++ struct mm_struct *mm = vma->vm_mm;
++ pgd_t *pgd;
++ pud_t *pud;
++ pmd_t *pmd;
++ pte_t *ptep, pte;
++ spinlock_t *ptl;
++ struct page *pg = NULL;
++ pgoff_t linear_index = (addr - vma->vm_start)/PAGE_SIZE + vma->vm_pgoff;
++
++ pdesc->index = linear_index;
++ pdesc->shared = 0;
++ pdesc->mm = CPT_NULL;
++
++ if (vma->vm_flags & VM_IO) {
++ pdesc->type = PD_ABSENT;
++ return;
++ }
++
++ pgd = pgd_offset(mm, addr);
++ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
++ goto out_absent;
++ pud = pud_offset(pgd, addr);
++ if (pud_none(*pud) || unlikely(pud_bad(*pud)))
++ goto out_absent;
++ pmd = pmd_offset(pud, addr);
++ if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
++ goto out_absent;
++#ifdef CONFIG_X86
++ if (pmd_huge(*pmd)) {
++ eprintk_ctx("page_huge\n");
++ goto out_unsupported;
++ }
++#endif
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++retry:
++#endif
++ ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
++ pte = *ptep;
++ pte_unmap(ptep);
++
++ if (pte_none(pte))
++ goto out_absent_unlock;
++
++ if (!pte_present(pte)) {
++ if (pte_file(pte)) {
++ pdesc->index = pte_to_pgoff(pte);
++ goto out_absent_unlock;
++ }
++ if (vma->vm_flags & VM_SHARED) {
++ /* It is impossible: shared mappings cannot be in swap */
++ eprintk_ctx("shared mapping is not present: %08lx@%Ld\n", addr, mmobj->o_pos);
++ goto out_unsupported_unlock;
++ }
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ /* Otherwise it is in swap. */
++ if (!ctx->lazy_vm) {
++ int err;
++ /* If lazy transfer is not enabled,
++ * raise it from swap now, so that we
++ * save at least when the page is shared.
++ */
++ spin_unlock(ptl);
++ err = handle_mm_fault(mm, vma, addr, 0);
++ if (err == VM_FAULT_SIGBUS)
++ goto out_absent;
++ if (err == VM_FAULT_OOM)
++ goto out_absent;
++ err = 0;
++ goto retry;
++ }
++#endif
++ pdesc->type = PD_LAZY;
++ goto out_unlock;
++ }
++
++ if ((pg = vm_normal_page(vma, addr, pte)) == NULL) {
++ pdesc->type = PD_COPY;
++ goto out_unlock;
++ }
++
++ get_page(pg);
++ spin_unlock(ptl);
++
++ if (pg->mapping && !PageAnon(pg)) {
++ if (vma->vm_file == NULL) {
++ eprintk_ctx("pg->mapping!=NULL for fileless vma: %08lx\n", addr);
++ goto out_unsupported;
++ }
++ if (vma->vm_file->f_mapping != pg->mapping) {
++ eprintk_ctx("pg->mapping!=f_mapping: %08lx %p %p %Ld\n",
++ addr, vma->vm_file->f_mapping, pg->mapping,
++ mmobj->o_pos);
++ goto out_unsupported;
++ }
++ pdesc->index = (pg->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT));
++ /* Page is in backstore. For us it is like
++ * it is not present.
++ */
++ goto out_absent;
++ }
++
++ if (PageReserved(pg)) {
++ /* Special case: ZERO_PAGE is used, when an
++ * anonymous page is accessed but not written. */
++ if (pg == ZERO_PAGE(addr)) {
++ if (pte_write(pte)) {
++ eprintk_ctx("not funny already, writable ZERO_PAGE\n");
++ goto out_unsupported;
++ }
++ zcnt++;
++ goto out_absent;
++ }
++ eprintk_ctx("reserved page %lu at %08lx@%Ld\n", pg->index,
++ addr, mmobj->o_pos);
++ goto out_unsupported;
++ }
++
++ if (pg == ZERO_PAGE(addr)) {
++ wprintk_ctx("that's how it works now\n");
++ }
++
++ if (!pg->mapping) {
++ eprintk_ctx("page without mapping at %08lx@%Ld\n", addr,
++ mmobj->o_pos);
++ goto out_unsupported;
++ }
++
++ if (pg->mapping && page_mapcount(pg) > 1) {
++ pdesc->shared = 1;
++ pdesc->mm = where_is_anon_page(mmobj, addr, pg, ctx);
++ if (pdesc->mm != CPT_NULL) {
++ scnt0++;
++ pdesc->type = PD_CLONE;
++ goto out_put;
++ } else {
++ scnt++;
++ }
++ }
++#ifdef CONFIG_VZ_CHECKPOINT_ITER
++ if (ctx->iter_done &&
++ test_bit(PG_checkpointed, &pg->flags)) {
++ if (pte_write(pte)) {
++ wprintk_ctx("writable PG_checkpointed page\n");
++ }
++ pdesc->index = page_to_pfn(pg);
++ pdesc->type = pte_young(pte) ? PD_ITERYOUNG : PD_ITER;
++ goto out_put;
++ }
++#endif
++ pdesc->type = pte_young(pte) ? PD_COPY : PD_LAZY;
++
++out_put:
++ if (pg)
++ put_page(pg);
++ return;
++
++out_unlock:
++ spin_unlock(ptl);
++ goto out_put;
++
++out_absent_unlock:
++ spin_unlock(ptl);
++out_absent:
++ pdesc->type = PD_ABSENT;
++ goto out_put;
++
++out_unsupported_unlock:
++ spin_unlock(ptl);
++out_unsupported:
++ ucnt++;
++ pdesc->type = PD_FUNKEY;
++ goto out_put;
++}
++
++/* ATTN: We give "current" to get_user_pages(). This is wrong, but get_user_pages()
++ * does not really need this thing. It just stores some page fault stats there.
++ *
++ * BUG: some archs (f.e. sparc64, but not Intel*) require flush cache pages
++ * before accessing vma.
++ */
++void dump_pages(struct vm_area_struct *vma, unsigned long start,
++ unsigned long end, struct cpt_context *ctx)
++{
++#define MAX_PAGE_BATCH 16
++ struct page *pg[MAX_PAGE_BATCH];
++ int npages = (end - start)/PAGE_SIZE;
++ int count = 0;
++
++ while (count < npages) {
++ int copy = npages - count;
++ int n;
++
++ if (copy > MAX_PAGE_BATCH)
++ copy = MAX_PAGE_BATCH;
++ n = get_user_pages(current, vma->vm_mm, start, copy,
++ 0, 1, pg, NULL);
++ if (n == copy) {
++ int i;
++ for (i=0; i<n; i++) {
++ char *maddr = kmap(pg[i]);
++ ctx->write(maddr, PAGE_SIZE, ctx);
++ kunmap(pg[i]);
++ }
++ } else {
++ eprintk_ctx("get_user_pages fault");
++ for ( ; n > 0; n--)
++ page_cache_release(pg[n-1]);
++ return;
++ }
++ start += n*PAGE_SIZE;
++ count += n;
++ for ( ; n > 0; n--)
++ page_cache_release(pg[n-1]);
++ }
++ return;
++}
++
++int dump_page_block(struct vm_area_struct *vma, struct cpt_page_block *pgb,
++ int copy,
++ struct cpt_context *ctx)
++{
++ loff_t saved_object;
++
++ cpt_push_object(&saved_object, ctx);
++
++ pgb->cpt_object = (copy != PD_LAZY) ? CPT_OBJ_PAGES : CPT_OBJ_LAZYPAGES;
++ pgb->cpt_hdrlen = sizeof(*pgb);
++ pgb->cpt_content = (copy == PD_COPY || copy == PD_LAZY) ? CPT_CONTENT_DATA : CPT_CONTENT_VOID;
++
++ ctx->write(pgb, sizeof(*pgb), ctx);
++ if (copy == PD_COPY || copy == PD_LAZY)
++ dump_pages(vma, pgb->cpt_start, pgb->cpt_end, ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++int dump_remappage_block(struct vm_area_struct *vma, struct page_area *pa,
++ struct cpt_context *ctx)
++{
++ struct cpt_remappage_block pgb;
++ loff_t saved_object;
++
++ cpt_push_object(&saved_object, ctx);
++
++ pgb.cpt_object = CPT_OBJ_REMAPPAGES;
++ pgb.cpt_hdrlen = sizeof(pgb);
++ pgb.cpt_content = CPT_CONTENT_VOID;
++ pgb.cpt_start = pa->start;
++ pgb.cpt_end = pa->end;
++ pgb.cpt_pgoff = pa->pgoff - (pa->end-pa->start)/PAGE_SIZE + 1;
++
++ ctx->write(&pgb, sizeof(pgb), ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++int dump_copypage_block(struct vm_area_struct *vma, struct page_area *pa,
++ struct cpt_context *ctx)
++{
++ struct cpt_copypage_block pgb;
++ loff_t saved_object;
++
++ cpt_push_object(&saved_object, ctx);
++
++ pgb.cpt_object = CPT_OBJ_COPYPAGES;
++ pgb.cpt_hdrlen = sizeof(pgb);
++ pgb.cpt_content = CPT_CONTENT_VOID;
++ pgb.cpt_start = pa->start;
++ pgb.cpt_end = pa->end;
++ pgb.cpt_source = pa->mm;
++
++ ctx->write(&pgb, sizeof(pgb), ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++int dump_lazypage_block(struct vm_area_struct *vma, struct page_area *pa,
++ cpt_context_t *ctx)
++{
++ struct cpt_lazypage_block pgb;
++ loff_t saved_object;
++
++ cpt_push_object(&saved_object, ctx);
++
++ pgb.cpt_object = CPT_OBJ_LAZYPAGES;
++ pgb.cpt_hdrlen = sizeof(pgb);
++ pgb.cpt_content = CPT_CONTENT_VOID;
++ pgb.cpt_start = pa->start;
++ pgb.cpt_end = pa->end;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ pgb.cpt_index = cpt_alloc_pgin_index(vma, pa->start,
++ (pa->end-pa->start)/PAGE_SIZE, ctx);
++#endif
++ ctx->write(&pgb, sizeof(pgb), ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++int dump_iterpage_block(struct vm_area_struct *vma, struct page_area *pa,
++ cpt_context_t *ctx)
++{
++ struct cpt_iterpage_block pgb;
++ loff_t saved_object;
++
++ cpt_push_object(&saved_object, ctx);
++
++ pgb.cpt_object = pa->type == PD_ITER ? CPT_OBJ_ITERPAGES :
++ CPT_OBJ_ITERYOUNGPAGES;
++ pgb.cpt_hdrlen = sizeof(pgb);
++ pgb.cpt_content = CPT_CONTENT_VOID;
++ pgb.cpt_start = pa->start;
++ pgb.cpt_end = pa->end;
++ ctx->write(&pgb, sizeof(pgb), ctx);
++
++ ctx->write(pa->list, 8*((pa->end-pa->start)/PAGE_SIZE), ctx);
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++
++static int can_expand(struct page_area *pa, struct page_desc *pd)
++{
++ if (pa->start == pa->end)
++ return 1;
++ if (pa->type != pd->type)
++ return 0;
++ if (pa->type == PD_ITER || pa->type == PD_ITERYOUNG) {
++ if (pa->end - pa->start >= PAGE_SIZE*16)
++ return 0;
++ pa->list[(pa->end - pa->start)/PAGE_SIZE] = pd->index;
++ }
++ if (pa->type == PD_ABSENT)
++ return pd->index == pa->pgoff + 1;
++ if (pa->type == PD_CLONE)
++ return pd->mm == pa->mm;
++ return 1;
++}
++
++static int dump_one_vma(cpt_object_t *mmobj,
++ struct vm_area_struct *vma, struct cpt_context *ctx)
++{
++ struct cpt_vma_image *v = cpt_get_buf(ctx);
++ unsigned long addr;
++ loff_t saved_object;
++ struct cpt_page_block pgb;
++ struct page_area pa;
++ int cloned_pages = 0;
++
++ cpt_push_object(&saved_object, ctx);
++
++ v->cpt_object = CPT_OBJ_VMA;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_start = vma->vm_start;
++ v->cpt_end = vma->vm_end;
++ v->cpt_flags = vma->vm_flags;
++ if (vma->vm_flags&VM_HUGETLB) {
++ eprintk_ctx("huge TLB VMAs are still not supported\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_pgprot = vma->vm_page_prot.pgprot;
++ v->cpt_pgoff = vma->vm_pgoff;
++ v->cpt_file = CPT_NULL;
++#ifndef CONFIG_IA64
++ if ((void *)vma->vm_start == vma->vm_mm->context.vdso &&
++ vma->vm_ops == &special_mapping_vmops)
++ v->cpt_type = CPT_VMA_VDSO;
++ else
++#endif
++ v->cpt_type = CPT_VMA_TYPE_0;
++ v->cpt_anonvma = 0;
++
++ /* We have to remember what VMAs are bound to one anon_vma.
++ * So, we store an identifier of group of VMAs. It is handy
++ * to use absolute address of anon_vma as this identifier. */
++ v->cpt_anonvmaid = (unsigned long)vma->anon_vma;
++
++ if (vma->vm_file) {
++ struct file *filp;
++ cpt_object_t *obj = lookup_cpt_object(CPT_OBJ_FILE, vma->vm_file, ctx);
++ if (obj == NULL) BUG();
++ filp = obj->o_obj;
++ if (filp->f_op == &shm_file_operations) {
++ struct shm_file_data *sfd = filp->private_data;
++
++ v->cpt_type = CPT_VMA_TYPE_SHM;
++ obj = lookup_cpt_object(CPT_OBJ_FILE, sfd->file, ctx);
++ }
++ v->cpt_file = obj->o_pos;
++ }
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ if (v->cpt_type == CPT_VMA_VDSO)
++ goto out;
++
++ pa.type = PD_ABSENT;
++ pa.pgoff = vma->vm_pgoff;
++ pa.mm = CPT_NULL;
++ pa.start = vma->vm_start;
++ pa.end = vma->vm_start;
++
++ for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
++ struct page_desc pd;
++
++ page_get_desc(mmobj, vma, addr, &pd, ctx);
++ cloned_pages += pd.shared;
++
++ if (pd.type == PD_FUNKEY) {
++ eprintk_ctx("dump_one_vma: funkey page\n");
++ return -EINVAL;
++ }
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ if (pd.type == PD_LAZY &&
++ (ctx->lazy_vm == 0 || (vma->vm_flags&VM_LOCKED)))
++ pd.type = PD_COPY;
++#else
++ if (pd.type == PD_LAZY)
++ pd.type = PD_COPY;
++#endif
++
++ if (!can_expand(&pa, &pd)) {
++ if (pa.type == PD_COPY ||
++ pa.type == PD_ZERO) {
++ pgb.cpt_start = pa.start;
++ pgb.cpt_end = pa.end;
++ dump_page_block(vma, &pgb, pa.type, ctx);
++ } else if (pa.type == PD_CLONE) {
++ dump_copypage_block(vma, &pa, ctx);
++ cloned_pages++;
++ } else if (pa.type == PD_LAZY) {
++ dump_lazypage_block(vma, &pa, ctx);
++ } else if (pa.type == PD_ITER || pa.type == PD_ITERYOUNG) {
++ dump_iterpage_block(vma, &pa, ctx);
++ cloned_pages++;
++ } else if (pa.type == PD_ABSENT &&
++ pa.pgoff != (pa.end - vma->vm_start)/PAGE_SIZE + vma->vm_pgoff - 1) {
++ dump_remappage_block(vma, &pa, ctx);
++ }
++ pa.start = addr;
++ }
++ pa.type = pd.type;
++ pa.end = addr + PAGE_SIZE;
++ pa.pgoff = pd.index;
++ if (addr == pa.start)
++ pa.list[0] = pd.index;
++ pa.mm = pd.mm;
++ }
++
++ if (pa.end > pa.start) {
++ if (pa.type == PD_COPY ||
++ pa.type == PD_ZERO) {
++ pgb.cpt_start = pa.start;
++ pgb.cpt_end = pa.end;
++ dump_page_block(vma, &pgb, pa.type, ctx);
++ } else if (pa.type == PD_CLONE) {
++ dump_copypage_block(vma, &pa, ctx);
++ cloned_pages++;
++ } else if (pa.type == PD_LAZY) {
++ dump_lazypage_block(vma, &pa, ctx);
++ } else if (pa.type == PD_ITER || pa.type == PD_ITERYOUNG) {
++ dump_iterpage_block(vma, &pa, ctx);
++ cloned_pages++;
++ } else if (pa.type == PD_ABSENT &&
++ pa.pgoff != (pa.end - vma->vm_start)/PAGE_SIZE + vma->vm_pgoff - 1) {
++ dump_remappage_block(vma, &pa, ctx);
++ }
++ }
++
++ if (cloned_pages) {
++ __u32 anonvma = 1;
++ loff_t anonpos = ctx->current_object + offsetof(struct cpt_vma_image, cpt_anonvma);
++ ctx->pwrite(&anonvma, 4, ctx, anonpos);
++ }
++
++out:
++ cpt_close_object(ctx);
++
++ cpt_pop_object(&saved_object, ctx);
++
++ return 0;
++}
++
++static int dump_one_aio_ctx(struct mm_struct *mm, struct kioctx *aio_ctx,
++ cpt_context_t *ctx)
++{
++ loff_t saved_object;
++ struct cpt_aio_ctx_image aimg;
++
++ if (!list_empty(&aio_ctx->run_list) ||
++ !list_empty(&aio_ctx->active_reqs) ||
++ aio_ctx->reqs_active) {
++ eprintk_ctx("AIO is active after suspend\n");
++ return -EBUSY;
++ }
++
++ cpt_push_object(&saved_object, ctx);
++
++ aimg.cpt_next = CPT_ALIGN(sizeof(aimg));
++ aimg.cpt_object = CPT_OBJ_AIO_CONTEXT;
++ aimg.cpt_hdrlen = sizeof(aimg);
++ aimg.cpt_content = CPT_CONTENT_ARRAY;
++
++ aimg.cpt_max_reqs = aio_ctx->max_reqs;
++ aimg.cpt_ring_pages = aio_ctx->ring_info.nr_pages;
++ aimg.cpt_nr = aio_ctx->ring_info.nr;
++ aimg.cpt_tail = aio_ctx->ring_info.tail;
++ aimg.cpt_mmap_base = aio_ctx->ring_info.mmap_base;
++
++ ctx->write(&aimg, sizeof(aimg), ctx);
++
++ cpt_pop_object(&saved_object, ctx);
++ return 0;
++}
++
++static int dump_one_mm(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct mm_struct *mm = obj->o_obj;
++ struct vm_area_struct *vma;
++ struct cpt_mm_image *v = cpt_get_buf(ctx);
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = -1;
++ v->cpt_object = CPT_OBJ_MM;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_start_code = mm->start_code;
++ v->cpt_end_code = mm->end_code;
++ v->cpt_start_data = mm->start_data;
++ v->cpt_end_data = mm->end_data;
++ v->cpt_start_brk = mm->start_brk;
++ v->cpt_brk = mm->brk;
++ v->cpt_start_stack = mm->start_stack;
++ v->cpt_start_arg = mm->arg_start;
++ v->cpt_end_arg = mm->arg_end;
++ v->cpt_start_env = mm->env_start;
++ v->cpt_end_env = mm->env_end;
++ v->cpt_def_flags = mm->def_flags;
++#ifdef CONFIG_BEANCOUNTERS
++ v->cpt_mmub = cpt_lookup_ubc(mm->mm_ub, ctx);
++#endif
++ /* FIXME when coredump mask exceeds 8 bits */
++ WARN_ON(mm->flags >> 8);
++ v->cpt_dumpable = mm->flags;
++ v->cpt_vps_dumpable = mm->vps_dumpable;
++ v->cpt_used_hugetlb = 0; /* not used */
++#ifndef CONFIG_IA64
++ v->cpt_vdso = (__u32)(unsigned long)mm->context.vdso;
++#endif
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++#ifdef CONFIG_X86
++ if (mm->context.size) {
++ loff_t saved_object;
++ struct cpt_obj_bits b;
++ int size;
++
++ dprintk_ctx("nontrivial LDT\n");
++
++ cpt_push_object(&saved_object, ctx);
++
++ cpt_open_object(NULL, ctx);
++ b.cpt_next = CPT_NULL;
++ b.cpt_object = CPT_OBJ_BITS;
++ b.cpt_hdrlen = sizeof(b);
++ b.cpt_content = CPT_CONTENT_MM_CONTEXT;
++ b.cpt_size = mm->context.size*LDT_ENTRY_SIZE;
++
++ ctx->write(&b, sizeof(b), ctx);
++
++ size = mm->context.size*LDT_ENTRY_SIZE;
++
++#if defined(CONFIG_X86_64) || defined(CONFIG_XEN) || \
++ LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
++ ctx->write(mm->context.ldt, size, ctx);
++#else
++ for (i = 0; i < size; i += PAGE_SIZE) {
++ int nr = i / PAGE_SIZE, bytes;
++ char *kaddr = kmap(mm->context.ldt_pages[nr]);
++
++ bytes = size - i;
++ if (bytes > PAGE_SIZE)
++ bytes = PAGE_SIZE;
++ ctx->write(kaddr, bytes, ctx);
++ kunmap(mm->context.ldt_pages[nr]);
++ }
++#endif
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_object, ctx);
++ }
++#endif
++
++ for (vma = mm->mmap; vma; vma = vma->vm_next) {
++ int err;
++
++ if ((err = dump_one_vma(obj, vma, ctx)) != 0)
++ return err;
++ }
++
++ if (mm->ioctx_list) {
++ struct kioctx *aio_ctx;
++ int err;
++
++ for (aio_ctx = mm->ioctx_list; aio_ctx; aio_ctx = aio_ctx->next)
++ if ((err = dump_one_aio_ctx(mm, aio_ctx, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_object(ctx);
++
++ return 0;
++}
++
++int cpt_dump_vm(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ scnt = scnt0 = zcnt = 0;
++
++ cpt_open_section(ctx, CPT_SECT_MM);
++
++ for_each_object(obj, CPT_OBJ_MM) {
++ int err;
++
++ if ((err = dump_one_mm(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++
++ if (scnt)
++ dprintk_ctx("cpt_dump_vm: %d shared private anon pages\n", scnt);
++ if (scnt0)
++ dprintk_ctx("cpt_dump_vm: %d anon pages are cloned\n", scnt0);
++ if (zcnt)
++ dprintk_ctx("cpt_dump_vm: %d silly pages canceled\n", zcnt);
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_mm.h b/kernel/cpt/cpt_mm.h
+new file mode 100644
+index 0000000..dc2c483
+--- /dev/null
++++ b/kernel/cpt/cpt_mm.h
+@@ -0,0 +1,35 @@
++int cpt_collect_mm(cpt_context_t *);
++
++int cpt_dump_vm(struct cpt_context *ctx);
++
++__u32 rst_mm_flag(struct cpt_task_image *ti, struct cpt_context *ctx);
++int rst_mm_basic(cpt_object_t *obj, struct cpt_task_image *ti, struct cpt_context *ctx);
++int rst_mm_complete(struct cpt_task_image *ti, struct cpt_context *ctx);
++
++int cpt_mm_prepare(unsigned long veid);
++
++int cpt_free_pgin_dir(struct cpt_context *);
++int cpt_start_pagein(struct cpt_context *);
++int rst_setup_pagein(struct cpt_context *);
++int rst_complete_pagein(struct cpt_context *, int);
++int rst_pageind(struct cpt_context *);
++int cpt_iteration(cpt_context_t *ctx);
++int rst_iteration(cpt_context_t *ctx);
++void rst_drop_iter_dir(cpt_context_t *ctx);
++int rst_iter(struct vm_area_struct *vma, u64 pfn,
++ unsigned long addr, cpt_context_t * ctx);
++
++int rst_swapoff(struct cpt_context *);
++
++#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
++struct linux_binprm;
++extern int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack,
++ unsigned long map_address);
++#endif
++
++#ifdef CONFIG_X86
++extern struct page *vdso32_pages[1];
++#define vsyscall_addr page_address(vdso32_pages[0])
++#endif
++
++extern struct vm_operations_struct special_mapping_vmops;
+diff --git a/kernel/cpt/cpt_net.c b/kernel/cpt/cpt_net.c
+new file mode 100644
+index 0000000..eafbc8b
+--- /dev/null
++++ b/kernel/cpt/cpt_net.c
+@@ -0,0 +1,614 @@
++/*
++ *
++ * kernel/cpt/cpt_net.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/nsproxy.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <net/addrconf.h>
++#include <linux/rtnetlink.h>
++#include <linux/ve.h>
++#include <linux/ve_proto.h>
++#include <linux/vzcalluser.h>
++#include <linux/cpt_image.h>
++#include <linux/nfcalls.h>
++#include <linux/if_tun.h>
++#include <linux/veth.h>
++#include <linux/fdtable.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_kernel.h"
++#include "cpt_syscalls.h"
++
++static void cpt_dump_veth(struct net_device *dev, struct cpt_context * ctx)
++{
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++ struct cpt_veth_image v;
++ struct veth_struct *veth;
++
++ if (!KSYMREF(veth_open) || dev->open != KSYMREF(veth_open))
++ return;
++
++ veth = veth_from_netdev(dev);
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_VETH;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_VOID;
++
++ v.cpt_allow_mac_change = veth->allow_mac_change;
++
++ ctx->write(&v, sizeof(v), ctx);
++ cpt_close_object(ctx);
++#endif
++ return;
++}
++
++static void cpt_dump_netstats(struct net_device *dev, struct cpt_context * ctx)
++{
++ struct cpt_netstats_image *n;
++ struct net_device_stats *stats;
++
++ if (!dev->get_stats)
++ return;
++
++ n = cpt_get_buf(ctx);
++ stats = dev->get_stats(dev);
++ cpt_open_object(NULL, ctx);
++
++ n->cpt_next = CPT_NULL;
++ n->cpt_object = CPT_OBJ_NET_STATS;
++ n->cpt_hdrlen = sizeof(*n);
++ n->cpt_content = CPT_CONTENT_VOID;
++
++ n->cpt_rx_packets = stats->rx_packets;
++ n->cpt_tx_packets = stats->tx_packets;
++ n->cpt_rx_bytes = stats->rx_bytes;
++ n->cpt_tx_bytes = stats->tx_bytes;
++ n->cpt_rx_errors = stats->rx_errors;
++ n->cpt_tx_errors = stats->tx_errors;
++ n->cpt_rx_dropped = stats->rx_dropped;
++ n->cpt_tx_dropped = stats->tx_dropped;
++ n->cpt_multicast = stats->multicast;
++ n->cpt_collisions = stats->collisions;
++ n->cpt_rx_length_errors = stats->rx_length_errors;
++ n->cpt_rx_over_errors = stats->rx_over_errors;
++ n->cpt_rx_crc_errors = stats->rx_crc_errors;
++ n->cpt_rx_frame_errors = stats->rx_frame_errors;
++ n->cpt_rx_fifo_errors = stats->rx_fifo_errors;
++ n->cpt_rx_missed_errors = stats->rx_missed_errors;
++ n->cpt_tx_aborted_errors = stats->tx_aborted_errors;
++ n->cpt_tx_carrier_errors = stats->tx_carrier_errors;
++ n->cpt_tx_fifo_errors = stats->tx_fifo_errors;
++ n->cpt_tx_heartbeat_errors = stats->tx_heartbeat_errors;
++ n->cpt_tx_window_errors = stats->tx_window_errors;
++ n->cpt_rx_compressed = stats->rx_compressed;
++ n->cpt_tx_compressed = stats->tx_compressed;
++
++ ctx->write(n, sizeof(*n), ctx);
++ cpt_close_object(ctx);
++ cpt_release_buf(ctx);
++ return;
++}
++
++static void cpt_dump_tuntap(struct net_device *dev, struct cpt_context * ctx)
++{
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ struct cpt_tuntap_image v;
++ struct tun_struct *tun;
++ cpt_object_t *obj;
++
++ if (dev->open != tun_net_open)
++ return;
++
++ tun = netdev_priv(dev);
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_TUNTAP;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_VOID;
++
++ v.cpt_owner = tun->owner;
++ v.cpt_flags = tun->flags;
++ v.cpt_attached = tun->attached;
++
++ if (tun->bind_file) {
++ obj = lookup_cpt_object(CPT_OBJ_FILE, tun->bind_file, ctx);
++ BUG_ON(!obj);
++ v.cpt_bindfile = obj->o_pos;
++ }
++
++ BUG_ON(tun->txflt.count != 0); /* FIXME (f271b2cc) */
++
++ v.cpt_if_flags = 0;
++ memset(v.cpt_dev_addr, 0, sizeof(v.cpt_dev_addr));
++ memset(v.cpt_chr_filter, 0, sizeof(v.cpt_chr_filter));
++ memset(v.cpt_net_filter, 0, sizeof(v.cpt_net_filter));
++
++ ctx->write(&v, sizeof(v), ctx);
++ cpt_close_object(ctx);
++#endif
++ return;
++}
++
++int cpt_dump_link(struct cpt_context * ctx)
++{
++ struct net *net = get_exec_env()->ve_netns;
++ struct net_device *dev;
++
++ cpt_open_section(ctx, CPT_SECT_NET_DEVICE);
++ for_each_netdev(net, dev) {
++ struct cpt_netdev_image v;
++ struct cpt_hwaddr_image hw;
++ loff_t saved_obj;
++
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_DEVICE;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_ARRAY;
++
++ v.cpt_index = dev->ifindex;
++ v.cpt_flags = dev->flags;
++ memcpy(v.cpt_name, dev->name, IFNAMSIZ);
++ ctx->write(&v, sizeof(v), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++
++ cpt_dump_tuntap(dev, ctx);
++
++ cpt_dump_veth(dev, ctx);
++
++ /* Dump hardware address */
++ cpt_open_object(NULL, ctx);
++ hw.cpt_next = CPT_NULL;
++ hw.cpt_object = CPT_OBJ_NET_HWADDR;
++ hw.cpt_hdrlen = sizeof(hw);
++ hw.cpt_content = CPT_CONTENT_VOID;
++ BUILD_BUG_ON(sizeof(hw.cpt_dev_addr) != sizeof(dev->dev_addr));
++ memcpy(hw.cpt_dev_addr, dev->dev_addr, sizeof(hw.cpt_dev_addr));
++ ctx->write(&hw, sizeof(hw), ctx);
++ cpt_close_object(ctx);
++
++ cpt_dump_netstats(dev, ctx);
++
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++
++ if (dev != net->loopback_dev
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++ && !(KSYMREF(veth_open) && dev->open == KSYMREF(veth_open))
++#endif
++#if defined(CONFIG_VE_NETDEV) || defined(CONFIG_VE_NETDEV_MODULE)
++ && dev != get_exec_env()->_venet_dev
++#endif
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ && dev->open != tun_net_open
++#endif
++ ) {
++ eprintk_ctx("unsupported netdevice %s\n", dev->name);
++ cpt_close_section(ctx);
++ return -EBUSY;
++ }
++ }
++ cpt_close_section(ctx);
++ return 0;
++}
++
++int cpt_suspend_network(struct cpt_context *ctx)
++{
++ get_exec_env()->disable_net = 1;
++ synchronize_net();
++ return 0;
++}
++
++int cpt_resume_network(struct cpt_context *ctx)
++{
++ struct ve_struct *env;
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++ env->disable_net = 0;
++ put_ve(env);
++ return 0;
++}
++
++int cpt_dump_ifaddr(struct cpt_context * ctx)
++{
++ struct net *net = get_exec_env()->ve_netns;
++ struct net_device *dev;
++
++ cpt_open_section(ctx, CPT_SECT_NET_IFADDR);
++ for_each_netdev(net, dev) {
++ struct in_device *idev = in_dev_get(dev);
++ struct in_ifaddr *ifa;
++
++ if (!idev)
++ continue;
++
++ for (ifa = idev->ifa_list; ifa; ifa = ifa->ifa_next) {
++ struct cpt_ifaddr_image v;
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_IFADDR;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_VOID;
++
++ v.cpt_index = dev->ifindex;
++ v.cpt_family = AF_INET;
++ v.cpt_masklen = ifa->ifa_prefixlen;
++ v.cpt_flags = ifa->ifa_flags;
++ v.cpt_scope = ifa->ifa_scope;
++ memset(&v.cpt_address, 0, sizeof(v.cpt_address));
++ memset(&v.cpt_peer, 0, sizeof(v.cpt_peer));
++ memset(&v.cpt_broadcast, 0, sizeof(v.cpt_broadcast));
++ v.cpt_address[0] = ifa->ifa_local;
++ v.cpt_peer[0] = ifa->ifa_address;
++ v.cpt_broadcast[0] = ifa->ifa_broadcast;
++ memcpy(v.cpt_label, ifa->ifa_label, IFNAMSIZ);
++ ctx->write(&v, sizeof(v), ctx);
++ cpt_close_object(ctx);
++ }
++ in_dev_put(idev);
++ }
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ for_each_netdev(net, dev) {
++ struct inet6_dev *idev = in6_dev_get(dev);
++ struct inet6_ifaddr *ifa;
++
++ if (!idev)
++ continue;
++
++ for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) {
++ struct cpt_ifaddr_image v;
++
++ if (dev == net->loopback_dev &&
++ ifa->prefix_len == 128 &&
++ ifa->addr.s6_addr32[0] == 0 &&
++ ifa->addr.s6_addr32[1] == 0 &&
++ ifa->addr.s6_addr32[2] == 0 &&
++ ifa->addr.s6_addr32[3] == htonl(1))
++ continue;
++
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_IFADDR;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_VOID;
++
++ v.cpt_index = dev->ifindex;
++ v.cpt_family = AF_INET6;
++ v.cpt_masklen = ifa->prefix_len;
++ v.cpt_flags = ifa->flags;
++ v.cpt_scope = ifa->scope;
++ v.cpt_valid_lft = ifa->valid_lft;
++ v.cpt_prefered_lft = ifa->prefered_lft;
++ memcpy(&v.cpt_address, &ifa->addr, 16);
++ memcpy(&v.cpt_peer, &ifa->addr, 16);
++ memset(&v.cpt_broadcast, 0, sizeof(v.cpt_broadcast));
++ memcpy(v.cpt_label, dev->name, IFNAMSIZ);
++ ctx->write(&v, sizeof(v), ctx);
++ cpt_close_object(ctx);
++ }
++ in6_dev_put(idev);
++ }
++#endif
++ cpt_close_section(ctx);
++ return 0;
++}
++
++#ifdef CONFIG_IP_FIB_TRIE
++#error "Trie fib rules are known not to be restored proprly yet"
++#endif
++
++static int cpt_dump_route(struct cpt_context * ctx)
++{
++ int err;
++ struct socket *sock;
++ struct msghdr msg;
++ struct iovec iov;
++ struct {
++ struct nlmsghdr nlh;
++ struct rtgenmsg g;
++ } req;
++ struct sockaddr_nl nladdr;
++ struct cpt_object_hdr v;
++ mm_segment_t oldfs;
++ char *pg;
++
++ err = sock_create(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, &sock);
++ if (err)
++ return err;
++
++ memset(&nladdr, 0, sizeof(nladdr));
++ nladdr.nl_family = AF_NETLINK;
++
++ req.nlh.nlmsg_len = sizeof(req);
++ req.nlh.nlmsg_type = RTM_GETROUTE;
++ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
++ req.nlh.nlmsg_pid = 0;
++ req.g.rtgen_family = AF_INET;
++
++ iov.iov_base=&req;
++ iov.iov_len=sizeof(req);
++ msg.msg_name=&nladdr;
++ msg.msg_namelen=sizeof(nladdr);
++ msg.msg_iov=&iov;
++ msg.msg_iovlen=1;
++ msg.msg_control=NULL;
++ msg.msg_controllen=0;
++ msg.msg_flags=MSG_DONTWAIT;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = sock_sendmsg(sock, &msg, sizeof(req));
++ set_fs(oldfs);
++
++ if (err < 0)
++ goto out_sock;
++
++ pg = (char*)__get_free_page(GFP_KERNEL);
++ if (pg == NULL) {
++ err = -ENOMEM;
++ goto out_sock;
++ }
++
++ cpt_open_section(ctx, CPT_SECT_NET_ROUTE);
++ cpt_open_object(NULL, ctx);
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NET_ROUTE;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_NLMARRAY;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++restart:
++#endif
++ for (;;) {
++ struct nlmsghdr *h;
++
++ iov.iov_base = pg;
++ iov.iov_len = PAGE_SIZE;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = sock_recvmsg(sock, &msg, PAGE_SIZE, MSG_DONTWAIT);
++ set_fs(oldfs);
++
++ if (err < 0)
++ goto out_sock_pg;
++ if (msg.msg_flags & MSG_TRUNC) {
++ err = -ENOBUFS;
++ goto out_sock_pg;
++ }
++
++ h = (struct nlmsghdr*)pg;
++ while (NLMSG_OK(h, err)) {
++ if (h->nlmsg_type == NLMSG_DONE) {
++ err = 0;
++ goto done;
++ }
++ if (h->nlmsg_type == NLMSG_ERROR) {
++ struct nlmsgerr *errm = (struct nlmsgerr*)NLMSG_DATA(h);
++ err = errm->error;
++ eprintk_ctx("NLMSG error: %d\n", errm->error);
++ goto done;
++ }
++ if (h->nlmsg_type != RTM_NEWROUTE) {
++ eprintk_ctx("NLMSG: %d\n", h->nlmsg_type);
++ err = -EINVAL;
++ goto done;
++ }
++ ctx->write(h, NLMSG_ALIGN(h->nlmsg_len), ctx);
++ h = NLMSG_NEXT(h, err);
++ }
++ if (err) {
++ eprintk_ctx("!!!Remnant of size %d %d %d\n", err, h->nlmsg_len, h->nlmsg_type);
++ err = -EINVAL;
++ break;
++ }
++ }
++done:
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ if (!err && req.g.rtgen_family == AF_INET) {
++ req.g.rtgen_family = AF_INET6;
++ iov.iov_base=&req;
++ iov.iov_len=sizeof(req);
++ msg.msg_name=&nladdr;
++ msg.msg_namelen=sizeof(nladdr);
++ msg.msg_iov=&iov;
++ msg.msg_iovlen=1;
++ msg.msg_control=NULL;
++ msg.msg_controllen=0;
++ msg.msg_flags=MSG_DONTWAIT;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = sock_sendmsg(sock, &msg, sizeof(req));
++ set_fs(oldfs);
++
++ if (err > 0)
++ goto restart;
++ }
++#endif
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_close_section(ctx);
++
++out_sock_pg:
++ free_page((unsigned long)pg);
++out_sock:
++ sock_release(sock);
++ return err;
++}
++
++static int dumpfn(void *arg)
++{
++ int i;
++ int *pfd = arg;
++ char *argv[] = { "iptables-save", "-c", NULL };
++
++ i = real_env_create(VEID(get_exec_env()), VE_ENTER|VE_SKIPLOCK, 2, NULL, 0);
++ if (i < 0) {
++ eprintk("cannot enter ve to dump iptables\n");
++ module_put(THIS_MODULE);
++ return 255 << 8;
++ }
++
++ if (pfd[1] != 1)
++ sc_dup2(pfd[1], 1);
++
++ for (i=0; i<current->files->fdt->max_fds; i++) {
++ if (i != 1)
++ sc_close(i);
++ }
++
++ module_put(THIS_MODULE);
++
++ set_fs(KERNEL_DS);
++ i = sc_execve("/sbin/iptables-save", argv, NULL);
++ if (i == -ENOENT)
++ i = sc_execve("/usr/sbin/iptables-save", argv, NULL);
++ eprintk("failed to exec iptables-save: %d\n", i);
++ return 255 << 8;
++}
++
++
++static int cpt_dump_iptables(struct cpt_context * ctx)
++{
++ int err = 0;
++#ifdef CONFIG_VE_IPTABLES
++ int pid;
++ int pfd[2];
++ struct file *f;
++ struct cpt_object_hdr v;
++ char buf[16];
++ loff_t pos;
++ int n;
++ int status;
++ mm_segment_t oldfs;
++ sigset_t ignore, blocked;
++
++ if (!(get_exec_env()->_iptables_modules & VE_IP_IPTABLES_MOD))
++ return 0;
++
++ err = sc_pipe(pfd);
++ if (err < 0) {
++ eprintk_ctx("sc_pipe: %d\n", err);
++ return err;
++ }
++ ignore.sig[0] = CPT_SIG_IGNORE_MASK;
++ sigprocmask(SIG_BLOCK, &ignore, &blocked);
++ err = pid = local_kernel_thread(dumpfn, (void*)pfd, SIGCHLD, 0);
++ if (err < 0) {
++ eprintk_ctx("local_kernel_thread: %d\n", err);
++ goto out;
++ }
++
++ f = fget(pfd[0]);
++ sc_close(pfd[1]);
++ sc_close(pfd[0]);
++
++ cpt_open_section(ctx, CPT_SECT_NET_IPTABLES);
++
++ cpt_open_object(NULL, ctx);
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_NAME;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_NAME;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ pos = ctx->file->f_pos;
++ do {
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ n = f->f_op->read(f, buf, sizeof(buf), &f->f_pos);
++ set_fs(oldfs);
++ if (n > 0)
++ ctx->write(buf, n, ctx);
++ } while (n > 0);
++
++ if (n < 0)
++ eprintk_ctx("read: %d\n", n);
++
++ fput(f);
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if ((err = sc_waitx(pid, 0, &status)) < 0)
++ eprintk_ctx("wait4: %d\n", err);
++ else if ((status & 0x7f) == 0) {
++ err = (status & 0xff00) >> 8;
++ if (err != 0) {
++ eprintk_ctx("iptables-save exited with %d\n", err);
++ err = -EINVAL;
++ }
++ } else {
++ eprintk_ctx("iptables-save terminated\n");
++ err = -EINVAL;
++ }
++ set_fs(oldfs);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++
++ if (ctx->file->f_pos != pos) {
++ buf[0] = 0;
++ ctx->write(buf, 1, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_close_section(ctx);
++ } else {
++ pos = ctx->current_section;
++ cpt_close_object(ctx);
++ cpt_close_section(ctx);
++ ctx->sections[CPT_SECT_NET_IPTABLES] = CPT_NULL;
++ ctx->file->f_pos = pos;
++ }
++ return n ? : err;
++
++out:
++ if (pfd[1] >= 0)
++ sc_close(pfd[1]);
++ if (pfd[0] >= 0)
++ sc_close(pfd[0]);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++#endif
++ return err;
++}
++
++int cpt_dump_ifinfo(struct cpt_context * ctx)
++{
++ int err;
++
++ rtnl_lock();
++ err = cpt_dump_link(ctx);
++ if (!err)
++ err = cpt_dump_ifaddr(ctx);
++ rtnl_unlock();
++ if (!err)
++ err = cpt_dump_route(ctx);
++ if (!err)
++ err = cpt_dump_iptables(ctx);
++ return err;
++}
+diff --git a/kernel/cpt/cpt_net.h b/kernel/cpt/cpt_net.h
+new file mode 100644
+index 0000000..5d33877
+--- /dev/null
++++ b/kernel/cpt/cpt_net.h
+@@ -0,0 +1,7 @@
++int cpt_dump_ifinfo(struct cpt_context *ctx);
++int rst_restore_net(struct cpt_context *ctx);
++int cpt_suspend_network(struct cpt_context *ctx);
++int cpt_resume_network(struct cpt_context *ctx);
++int rst_resume_network(struct cpt_context *ctx);
++int cpt_dump_ip_conntrack(struct cpt_context *ctx);
++int rst_restore_ip_conntrack(struct cpt_context * ctx);
+diff --git a/kernel/cpt/cpt_obj.c b/kernel/cpt/cpt_obj.c
+new file mode 100644
+index 0000000..7ab23d7
+--- /dev/null
++++ b/kernel/cpt/cpt_obj.c
+@@ -0,0 +1,162 @@
++/*
++ *
++ * kernel/cpt/cpt_obj.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++cpt_object_t *alloc_cpt_object(int gfp, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = kmalloc(sizeof(cpt_object_t), gfp);
++ if (obj) {
++ INIT_LIST_HEAD(&obj->o_list);
++ INIT_LIST_HEAD(&obj->o_hash);
++ INIT_LIST_HEAD(&obj->o_alist);
++ obj->o_count = 1;
++ obj->o_pos = CPT_NULL;
++ obj->o_lock = 0;
++ obj->o_parent = NULL;
++ obj->o_index = CPT_NOINDEX;
++ obj->o_obj = NULL;
++ obj->o_image = NULL;
++ ctx->objcount++;
++ }
++ return obj;
++}
++
++void free_cpt_object(cpt_object_t *obj, cpt_context_t *ctx)
++{
++ list_del(&obj->o_alist);
++ kfree(obj);
++ ctx->objcount--;
++}
++
++void intern_cpt_object(enum _cpt_object_type type, cpt_object_t *obj, cpt_context_t *ctx)
++{
++ list_add_tail(&obj->o_list, &ctx->object_array[type]);
++}
++
++void insert_cpt_object(enum _cpt_object_type type, cpt_object_t *obj,
++ cpt_object_t *head, cpt_context_t *ctx)
++{
++ list_add(&obj->o_list, &head->o_list);
++}
++
++cpt_object_t * __cpt_object_add(enum _cpt_object_type type, void *p,
++ unsigned gfp_mask, cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = lookup_cpt_object(type, p, ctx);
++
++ if (obj) {
++ obj->o_count++;
++ return obj;
++ }
++
++ if ((obj = alloc_cpt_object(gfp_mask, ctx)) != NULL) {
++ if (p)
++ cpt_obj_setobj(obj, p, ctx);
++ intern_cpt_object(type, obj, ctx);
++ return obj;
++ }
++ return NULL;
++}
++
++cpt_object_t * cpt_object_add(enum _cpt_object_type type, void *p, cpt_context_t *ctx)
++{
++ return __cpt_object_add(type, p, GFP_KERNEL, ctx);
++}
++
++cpt_object_t * cpt_object_get(enum _cpt_object_type type, void *p, cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = lookup_cpt_object(type, p, ctx);
++
++ if (obj)
++ obj->o_count++;
++
++ return obj;
++}
++
++int cpt_object_init(cpt_context_t *ctx)
++{
++ int i;
++
++ for (i=0; i<CPT_OBJ_MAX; i++) {
++ INIT_LIST_HEAD(&ctx->object_array[i]);
++ }
++ return 0;
++}
++
++int cpt_object_destroy(cpt_context_t *ctx)
++{
++ int i;
++
++ for (i=0; i<CPT_OBJ_MAX; i++) {
++ while (!list_empty(&ctx->object_array[i])) {
++ struct list_head *head = ctx->object_array[i].next;
++ cpt_object_t *obj = list_entry(head, cpt_object_t, o_list);
++ list_del(head);
++ if (obj->o_image)
++ kfree(obj->o_image);
++ free_cpt_object(obj, ctx);
++ }
++ }
++ if (ctx->objcount != 0)
++ eprintk_ctx("BUG: ctx->objcount=%d\n", ctx->objcount);
++ return 0;
++}
++
++cpt_object_t *lookup_cpt_object(enum _cpt_object_type type, void *p, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, type) {
++ if (obj->o_obj == p)
++ return obj;
++ }
++ return NULL;
++}
++
++cpt_object_t *lookup_cpt_obj_bypos(enum _cpt_object_type type, loff_t pos, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, type) {
++ if (obj->o_pos == pos)
++ return obj;
++ }
++ return NULL;
++}
++
++cpt_object_t *lookup_cpt_obj_byindex(enum _cpt_object_type type, __u32 index, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, type) {
++ if (obj->o_index == index)
++ return obj;
++ }
++ return NULL;
++}
+diff --git a/kernel/cpt/cpt_obj.h b/kernel/cpt/cpt_obj.h
+new file mode 100644
+index 0000000..7762623
+--- /dev/null
++++ b/kernel/cpt/cpt_obj.h
+@@ -0,0 +1,62 @@
++#ifndef __CPT_OBJ_H_
++#define __CPT_OBJ_H_ 1
++
++#include <linux/list.h>
++#include <linux/cpt_image.h>
++
++typedef struct _cpt_object
++{
++ struct list_head o_list;
++ struct list_head o_hash;
++ int o_count;
++ int o_index;
++ int o_lock;
++ loff_t o_pos;
++ loff_t o_ppos;
++ void *o_obj;
++ void *o_image;
++ void *o_parent;
++ struct list_head o_alist;
++} cpt_object_t;
++
++struct cpt_context;
++
++#define for_each_object(obj, type) list_for_each_entry(obj, &ctx->object_array[type], o_list)
++
++
++extern cpt_object_t *alloc_cpt_object(int gfp, struct cpt_context *ctx);
++extern void free_cpt_object(cpt_object_t *obj, struct cpt_context *ctx);
++
++cpt_object_t *lookup_cpt_object(enum _cpt_object_type type, void *p, struct cpt_context *ctx);
++cpt_object_t *lookup_cpt_obj_bypos(enum _cpt_object_type type, loff_t pos, struct cpt_context *ctx);
++cpt_object_t *lookup_cpt_obj_byindex(enum _cpt_object_type type, __u32 index, struct cpt_context *ctx);
++
++static inline void cpt_obj_setpos(cpt_object_t *cpt, loff_t pos, struct cpt_context *ctx)
++{
++ cpt->o_pos = pos;
++ /* Add to pos hash table */
++}
++
++static inline void cpt_obj_setobj(cpt_object_t *cpt, void *ptr, struct cpt_context *ctx)
++{
++ cpt->o_obj = ptr;
++ /* Add to hash table */
++}
++
++static inline void cpt_obj_setindex(cpt_object_t *cpt, __u32 index, struct cpt_context *ctx)
++{
++ cpt->o_index = index;
++ /* Add to index hash table */
++}
++
++
++extern void intern_cpt_object(enum _cpt_object_type type, cpt_object_t *obj, struct cpt_context *ctx);
++extern void insert_cpt_object(enum _cpt_object_type type, cpt_object_t *obj, cpt_object_t *head, struct cpt_context *ctx);
++extern cpt_object_t *cpt_object_add(enum _cpt_object_type type, void *p, struct cpt_context *ctx);
++extern cpt_object_t *__cpt_object_add(enum _cpt_object_type type, void *p, unsigned int gfp_mask, struct cpt_context *ctx);
++extern cpt_object_t *cpt_object_get(enum _cpt_object_type type, void *p, struct cpt_context *ctx);
++
++extern int cpt_object_init(struct cpt_context *ctx);
++extern int cpt_object_destroy(struct cpt_context *ctx);
++
++#endif /* __CPT_OBJ_H_ */
+diff --git a/kernel/cpt/cpt_proc.c b/kernel/cpt/cpt_proc.c
+new file mode 100644
+index 0000000..08d5fd4
+--- /dev/null
++++ b/kernel/cpt/cpt_proc.c
+@@ -0,0 +1,595 @@
++/*
++ *
++ * kernel/cpt/cpt_proc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/list.h>
++#include <linux/proc_fs.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/cpt_ioctl.h>
++#include <linux/delay.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_dump.h"
++#include "cpt_mm.h"
++#include "cpt_kernel.h"
++
++MODULE_AUTHOR("Alexey Kuznetsov <alexey@sw.ru>");
++MODULE_LICENSE("GPL");
++
++/* List of contexts and lock protecting the list */
++static struct list_head cpt_context_list;
++static spinlock_t cpt_context_lock;
++
++static int proc_read(char *buffer, char **start, off_t offset,
++ int length, int *eof, void *data)
++{
++ off_t pos = 0;
++ off_t begin = 0;
++ int len = 0;
++ cpt_context_t *ctx;
++
++ len += sprintf(buffer, "Ctx Id VE State\n");
++
++ spin_lock(&cpt_context_lock);
++
++ list_for_each_entry(ctx, &cpt_context_list, ctx_list) {
++ len += sprintf(buffer+len,"%p %08x %-8u %d",
++ ctx,
++ ctx->contextid,
++ ctx->ve_id,
++ ctx->ctx_state
++ );
++
++ buffer[len++] = '\n';
++
++ pos = begin+len;
++ if (pos < offset) {
++ len = 0;
++ begin = pos;
++ }
++ if (pos > offset+length)
++ goto done;
++ }
++ *eof = 1;
++
++done:
++ spin_unlock(&cpt_context_lock);
++ *start = buffer + (offset - begin);
++ len -= (offset - begin);
++ if(len > length)
++ len = length;
++ if(len < 0)
++ len = 0;
++ return len;
++}
++
++void cpt_context_release(cpt_context_t *ctx)
++{
++ list_del(&ctx->ctx_list);
++ spin_unlock(&cpt_context_lock);
++
++ if (ctx->ctx_state > 0)
++ cpt_resume(ctx);
++ ctx->ctx_state = CPT_CTX_ERROR;
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ if (ctx->pgin_task)
++ put_task_struct(ctx->pgin_task);
++ if (ctx->pgin_dir)
++ cpt_free_pgin_dir(ctx);
++ if (ctx->pagein_file_out)
++ fput(ctx->pagein_file_out);
++ if (ctx->pagein_file_in)
++ fput(ctx->pagein_file_in);
++#endif
++ if (ctx->objcount)
++ eprintk_ctx("%d objects leaked\n", ctx->objcount);
++ if (ctx->file)
++ fput(ctx->file);
++ cpt_flush_error(ctx);
++ if (ctx->errorfile) {
++ fput(ctx->errorfile);
++ ctx->errorfile = NULL;
++ }
++ if (ctx->error_msg) {
++ free_page((unsigned long)ctx->error_msg);
++ ctx->error_msg = NULL;
++ }
++ if (ctx->statusfile)
++ fput(ctx->statusfile);
++ if (ctx->lockfile)
++ fput(ctx->lockfile);
++ kfree(ctx);
++
++ spin_lock(&cpt_context_lock);
++}
++
++static void __cpt_context_put(cpt_context_t *ctx)
++{
++ if (!--ctx->refcount)
++ cpt_context_release(ctx);
++}
++
++static void cpt_context_put(cpt_context_t *ctx)
++{
++ spin_lock(&cpt_context_lock);
++ __cpt_context_put(ctx);
++ spin_unlock(&cpt_context_lock);
++}
++
++cpt_context_t * cpt_context_open(void)
++{
++ cpt_context_t *ctx;
++
++ if ((ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)) != NULL) {
++ cpt_context_init(ctx);
++ spin_lock(&cpt_context_lock);
++ list_add_tail(&ctx->ctx_list, &cpt_context_list);
++ spin_unlock(&cpt_context_lock);
++ ctx->error_msg = (char*)__get_free_page(GFP_KERNEL);
++ if (ctx->error_msg != NULL)
++ ctx->error_msg[0] = 0;
++ }
++ return ctx;
++}
++
++static cpt_context_t * cpt_context_lookup(unsigned int contextid)
++{
++ cpt_context_t *ctx;
++
++ spin_lock(&cpt_context_lock);
++ list_for_each_entry(ctx, &cpt_context_list, ctx_list) {
++ if (ctx->contextid == contextid) {
++ ctx->refcount++;
++ spin_unlock(&cpt_context_lock);
++ return ctx;
++ }
++ }
++ spin_unlock(&cpt_context_lock);
++ return NULL;
++}
++
++int cpt_context_lookup_veid(unsigned int veid)
++{
++ cpt_context_t *ctx;
++
++ spin_lock(&cpt_context_lock);
++ list_for_each_entry(ctx, &cpt_context_list, ctx_list) {
++ if (ctx->ve_id == veid && ctx->ctx_state > 0) {
++ spin_unlock(&cpt_context_lock);
++ return 1;
++ }
++ }
++ spin_unlock(&cpt_context_lock);
++ return 0;
++}
++
++static int cpt_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
++{
++ int err = 0;
++ cpt_context_t *ctx;
++ struct file *dfile = NULL;
++ int try;
++
++ unlock_kernel();
++
++ if (cmd == CPT_VMPREP) {
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ err = cpt_mm_prepare(arg);
++#else
++ err = -EINVAL;
++#endif
++ goto out_lock;
++ }
++
++ if (cmd == CPT_TEST_CAPS) {
++ unsigned int src_flags, dst_flags = arg;
++
++ err = 0;
++ src_flags = test_cpu_caps();
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_CMOV, "cmov", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_FXSR, "fxsr", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_SSE, "sse", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_SSE2, "sse2", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_MMX, "mmx", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_3DNOW, "3dnow", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_3DNOW2, "3dnowext", err);
++ test_one_flag_old(src_flags, dst_flags, CPT_CPU_X86_SEP, "sysenter", err);
++ goto out_lock;
++ }
++
++ if (cmd == CPT_JOIN_CONTEXT || cmd == CPT_PUT_CONTEXT) {
++ cpt_context_t *old_ctx;
++
++ ctx = NULL;
++ if (cmd == CPT_JOIN_CONTEXT) {
++ err = -ENOENT;
++ ctx = cpt_context_lookup(arg);
++ if (!ctx)
++ goto out_lock;
++ }
++
++ spin_lock(&cpt_context_lock);
++ old_ctx = (cpt_context_t*)file->private_data;
++ file->private_data = ctx;
++
++ if (old_ctx) {
++ if (cmd == CPT_PUT_CONTEXT && old_ctx->sticky) {
++ old_ctx->sticky = 0;
++ old_ctx->refcount--;
++ }
++ __cpt_context_put(old_ctx);
++ }
++ spin_unlock(&cpt_context_lock);
++ err = 0;
++ goto out_lock;
++ }
++
++ spin_lock(&cpt_context_lock);
++ ctx = (cpt_context_t*)file->private_data;
++ if (ctx)
++ ctx->refcount++;
++ spin_unlock(&cpt_context_lock);
++
++ if (!ctx) {
++ cpt_context_t *old_ctx;
++
++ err = -ENOMEM;
++ ctx = cpt_context_open();
++ if (!ctx)
++ goto out_lock;
++
++ spin_lock(&cpt_context_lock);
++ old_ctx = (cpt_context_t*)file->private_data;
++ if (!old_ctx) {
++ ctx->refcount++;
++ file->private_data = ctx;
++ } else {
++ old_ctx->refcount++;
++ }
++ if (old_ctx) {
++ __cpt_context_put(ctx);
++ ctx = old_ctx;
++ }
++ spin_unlock(&cpt_context_lock);
++ }
++
++ if (cmd == CPT_GET_CONTEXT) {
++ unsigned int contextid = (unsigned int)arg;
++
++ if (ctx->contextid && ctx->contextid != contextid) {
++ err = -EINVAL;
++ goto out_nosem;
++ }
++ if (!ctx->contextid) {
++ cpt_context_t *c1 = cpt_context_lookup(contextid);
++ if (c1) {
++ cpt_context_put(c1);
++ err = -EEXIST;
++ goto out_nosem;
++ }
++ ctx->contextid = contextid;
++ }
++ spin_lock(&cpt_context_lock);
++ if (!ctx->sticky) {
++ ctx->sticky = 1;
++ ctx->refcount++;
++ }
++ spin_unlock(&cpt_context_lock);
++ goto out_nosem;
++ }
++
++ down(&ctx->main_sem);
++
++ err = -EBUSY;
++ if (ctx->ctx_state < 0)
++ goto out;
++
++ err = 0;
++ switch (cmd) {
++ case CPT_SET_DUMPFD:
++ if (ctx->ctx_state == CPT_CTX_DUMPING) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ err = -EBADF;
++ dfile = fget(arg);
++ if (dfile == NULL)
++ break;
++ if (dfile->f_op == NULL ||
++ dfile->f_op->write == NULL) {
++ fput(dfile);
++ break;
++ }
++ err = 0;
++ }
++ if (ctx->file)
++ fput(ctx->file);
++ ctx->file = dfile;
++ break;
++ case CPT_SET_ERRORFD:
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->errorfile)
++ fput(ctx->errorfile);
++ ctx->errorfile = dfile;
++ break;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ case CPT_SET_PAGEINFDIN:
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->pagein_file_in)
++ fput(ctx->pagein_file_in);
++ ctx->pagein_file_in = dfile;
++ break;
++ case CPT_SET_PAGEINFDOUT:
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->pagein_file_out)
++ fput(ctx->pagein_file_out);
++ ctx->pagein_file_out = dfile;
++ break;
++ case CPT_SET_LAZY:
++ ctx->lazy_vm = arg;
++ break;
++ case CPT_ITER:
++ err = cpt_iteration(ctx);
++ break;
++ case CPT_PAGEIND:
++ err = cpt_start_pagein(ctx);
++ break;
++#endif
++ case CPT_SET_VEID:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ ctx->ve_id = arg;
++ break;
++ case CPT_SET_CPU_FLAGS:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ ctx->dst_cpu_flags = arg;
++ ctx->src_cpu_flags = test_cpu_caps();
++ break;
++ case CPT_SUSPEND:
++ if (cpt_context_lookup_veid(ctx->ve_id) ||
++ ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ ctx->ctx_state = CPT_CTX_SUSPENDING;
++ try = 0;
++ do {
++ err = cpt_vps_suspend(ctx);
++ if (err)
++ cpt_resume(ctx);
++ if (err == -EAGAIN)
++ msleep(1000);
++ try++;
++ } while (err == -EAGAIN && try < 3);
++ if (err) {
++ ctx->ctx_state = CPT_CTX_IDLE;
++ } else {
++ ctx->ctx_state = CPT_CTX_SUSPENDED;
++ }
++ break;
++ case CPT_DUMP:
++ if (!ctx->ctx_state) {
++ err = -ENOENT;
++ break;
++ }
++ if (!ctx->file) {
++ err = -EBADF;
++ break;
++ }
++ err = cpt_dump(ctx);
++ break;
++ case CPT_RESUME:
++ if (ctx->ctx_state == CPT_CTX_IDLE) {
++ err = -ENOENT;
++ break;
++ }
++ err = cpt_resume(ctx);
++ if (!err)
++ ctx->ctx_state = CPT_CTX_IDLE;
++ break;
++ case CPT_KILL:
++ if (ctx->ctx_state == CPT_CTX_IDLE) {
++ err = -ENOENT;
++ break;
++ }
++ err = cpt_kill(ctx);
++ if (!err)
++ ctx->ctx_state = CPT_CTX_IDLE;
++ break;
++ case CPT_TEST_VECAPS:
++ {
++ __u32 dst_flags = arg;
++ __u32 src_flags;
++
++ err = cpt_vps_caps(ctx, &src_flags);
++ if (err)
++ break;
++
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_CMOV, "cmov", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_FXSR, "fxsr", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_SSE, "sse", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_SSE2, "sse2", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_MMX, "mmx", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_3DNOW, "3dnow", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_3DNOW2, "3dnowext", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_SEP, "sysenter", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_EMT64, "emt64", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_IA64, "ia64", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_SYSCALL, "syscall", err);
++ test_one_flag(src_flags, dst_flags, CPT_CPU_X86_SYSCALL32, "syscall32", err);
++ if (src_flags & CPT_UNSUPPORTED_MASK)
++ err = 2;
++ break;
++ }
++ default:
++ err = -EINVAL;
++ break;
++ }
++
++out:
++ cpt_flush_error(ctx);
++ up(&ctx->main_sem);
++out_nosem:
++ cpt_context_put(ctx);
++out_lock:
++ lock_kernel();
++ if (err == -ERESTARTSYS || err == -ERESTARTNOINTR ||
++ err == -ERESTARTNOHAND || err == -ERESTART_RESTARTBLOCK)
++ err = -EINTR;
++ return err;
++}
++
++static int cpt_open(struct inode *inode, struct file *file)
++{
++ if (!try_module_get(THIS_MODULE))
++ return -EBUSY;
++
++ return 0;
++}
++
++static int cpt_release(struct inode * inode, struct file * file)
++{
++ cpt_context_t *ctx;
++
++ spin_lock(&cpt_context_lock);
++ ctx = (cpt_context_t*)file->private_data;
++ file->private_data = NULL;
++
++ if (ctx)
++ __cpt_context_put(ctx);
++ spin_unlock(&cpt_context_lock);
++
++ module_put(THIS_MODULE);
++ return 0;
++}
++
++
++static struct file_operations cpt_fops = {
++ .owner = THIS_MODULE,
++ .open = cpt_open,
++ .release = cpt_release,
++ .ioctl = cpt_ioctl,
++};
++
++static struct proc_dir_entry *proc_ent;
++
++static struct ctl_table_header *ctl_header;
++
++static ctl_table debug_table[] = {
++ {
++ .procname = "cpt",
++ .data = &debug_level,
++ .maxlen = sizeof(debug_level),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ { .ctl_name = 0 }
++};
++static ctl_table root_table[] = {
++ {
++ .ctl_name = CTL_DEBUG,
++ .procname = "debug",
++ .mode = 0555,
++ .child = debug_table,
++ },
++ { .ctl_name = 0 }
++};
++
++static int __init init_cpt(void)
++{
++ int err;
++
++ err = -ENOMEM;
++ ctl_header = register_sysctl_table(root_table);
++ if (!ctl_header)
++ goto err_mon;
++
++ spin_lock_init(&cpt_context_lock);
++ INIT_LIST_HEAD(&cpt_context_list);
++
++ err = -EINVAL;
++ proc_ent = proc_create("cpt", 0600, NULL, NULL);
++ if (!proc_ent)
++ goto err_out;
++
++ cpt_fops.read = proc_ent->proc_fops->read;
++ cpt_fops.write = proc_ent->proc_fops->write;
++ cpt_fops.llseek = proc_ent->proc_fops->llseek;
++ proc_ent->proc_fops = &cpt_fops;
++
++ proc_ent->read_proc = proc_read;
++ proc_ent->data = NULL;
++ proc_ent->owner = THIS_MODULE;
++ return 0;
++
++err_out:
++ unregister_sysctl_table(ctl_header);
++err_mon:
++ return err;
++}
++module_init(init_cpt);
++
++static void __exit exit_cpt(void)
++{
++ remove_proc_entry("cpt", NULL);
++ unregister_sysctl_table(ctl_header);
++
++ spin_lock(&cpt_context_lock);
++ while (!list_empty(&cpt_context_list)) {
++ cpt_context_t *ctx;
++ ctx = list_entry(cpt_context_list.next, cpt_context_t, ctx_list);
++
++ if (!ctx->sticky)
++ ctx->refcount++;
++ ctx->sticky = 0;
++
++ BUG_ON(ctx->refcount != 1);
++
++ __cpt_context_put(ctx);
++ }
++ spin_unlock(&cpt_context_lock);
++}
++module_exit(exit_cpt);
+diff --git a/kernel/cpt/cpt_process.c b/kernel/cpt/cpt_process.c
+new file mode 100644
+index 0000000..8b6d4bf
+--- /dev/null
++++ b/kernel/cpt/cpt_process.c
+@@ -0,0 +1,1369 @@
++/*
++ *
++ * kernel/cpt/cpt_process.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/compat.h>
++#include <linux/cpt_image.h>
++#include <linux/nsproxy.h>
++#include <linux/futex.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_ubc.h"
++#include "cpt_process.h"
++#include "cpt_kernel.h"
++
++#ifdef CONFIG_X86_32
++#undef task_pt_regs
++#define task_pt_regs(t) ((struct pt_regs *)((t)->thread.sp0) - 1)
++#endif
++
++int check_task_state(struct task_struct *tsk, struct cpt_context *ctx)
++{
++#ifdef CONFIG_X86_64
++ if (!(task_thread_info(tsk)->flags&_TIF_IA32)) {
++ if (task_pt_regs(tsk)->ip >= VSYSCALL_START &&
++ task_pt_regs(tsk)->ip < VSYSCALL_END) {
++ eprintk_ctx(CPT_FID "cannot be checkpointied while vsyscall, try later\n", CPT_TID(tsk));
++ return -EAGAIN;
++ }
++ }
++#endif
++ return 0;
++}
++
++#ifdef CONFIG_X86
++
++static u32 encode_segment(u32 segreg)
++{
++ segreg &= 0xFFFF;
++
++ if (segreg == 0)
++ return CPT_SEG_ZERO;
++ if ((segreg & 3) != 3) {
++ wprintk("Invalid RPL of a segment reg %x\n", segreg);
++ return CPT_SEG_ZERO;
++ }
++
++ /* LDT descriptor, it is just an index to LDT array */
++ if (segreg & 4)
++ return CPT_SEG_LDT + (segreg >> 3);
++
++ /* TLS descriptor. */
++ if ((segreg >> 3) >= GDT_ENTRY_TLS_MIN &&
++ (segreg >> 3) <= GDT_ENTRY_TLS_MAX)
++ return CPT_SEG_TLS1 + ((segreg>>3) - GDT_ENTRY_TLS_MIN);
++
++ /* One of standard desriptors */
++#ifdef CONFIG_X86_64
++ if (segreg == __USER32_DS)
++ return CPT_SEG_USER32_DS;
++ if (segreg == __USER32_CS)
++ return CPT_SEG_USER32_CS;
++ if (segreg == __USER_DS)
++ return CPT_SEG_USER64_DS;
++ if (segreg == __USER_CS)
++ return CPT_SEG_USER64_CS;
++#else
++ if (segreg == __USER_DS)
++ return CPT_SEG_USER32_DS;
++ if (segreg == __USER_CS)
++ return CPT_SEG_USER32_CS;
++#endif
++ wprintk("Invalid segment reg %x\n", segreg);
++ return CPT_SEG_ZERO;
++}
++
++#ifdef CONFIG_X86_64
++static void xlate_ptregs_64_to_32(struct cpt_x86_regs *d, struct pt_regs *s,
++ struct task_struct *tsk)
++{
++ d->cpt_ebp = s->bp;
++ d->cpt_ebx = s->bx;
++ d->cpt_eax = s->ax;
++ d->cpt_ecx = s->cx;
++ d->cpt_edx = s->dx;
++ d->cpt_esi = s->si;
++ d->cpt_edi = s->di;
++ d->cpt_orig_eax = s->orig_ax;
++ d->cpt_eip = s->ip;
++ d->cpt_xcs = encode_segment(s->cs);
++ d->cpt_eflags = s->flags;
++ d->cpt_esp = s->sp;
++ d->cpt_xss = encode_segment(s->ss);
++ d->cpt_xds = encode_segment(tsk->thread.ds);
++ d->cpt_xes = encode_segment(tsk->thread.es);
++}
++
++static int dump_registers(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ cpt_open_object(NULL, ctx);
++
++ if (task_thread_info(tsk)->flags & _TIF_IA32) {
++ struct cpt_x86_regs ri;
++ ri.cpt_next = sizeof(ri);
++ ri.cpt_object = CPT_OBJ_X86_REGS;
++ ri.cpt_hdrlen = sizeof(ri);
++ ri.cpt_content = CPT_CONTENT_VOID;
++
++ ri.cpt_debugreg[0] = tsk->thread.debugreg0;
++ ri.cpt_debugreg[1] = tsk->thread.debugreg1;
++ ri.cpt_debugreg[2] = tsk->thread.debugreg2;
++ ri.cpt_debugreg[3] = tsk->thread.debugreg3;
++ ri.cpt_debugreg[4] = 0;
++ ri.cpt_debugreg[5] = 0;
++ ri.cpt_debugreg[6] = tsk->thread.debugreg6;
++ ri.cpt_debugreg[7] = tsk->thread.debugreg7;
++ ri.cpt_fs = encode_segment(tsk->thread.fsindex);
++ ri.cpt_gs = encode_segment(tsk->thread.gsindex);
++
++ xlate_ptregs_64_to_32(&ri, task_pt_regs(tsk), tsk);
++
++ ctx->write(&ri, sizeof(ri), ctx);
++ } else {
++ struct cpt_x86_64_regs ri;
++ ri.cpt_next = sizeof(ri);
++ ri.cpt_object = CPT_OBJ_X86_64_REGS;
++ ri.cpt_hdrlen = sizeof(ri);
++ ri.cpt_content = CPT_CONTENT_VOID;
++
++ ri.cpt_fsbase = tsk->thread.fs;
++ ri.cpt_gsbase = tsk->thread.gs;
++ ri.cpt_fsindex = encode_segment(tsk->thread.fsindex);
++ ri.cpt_gsindex = encode_segment(tsk->thread.gsindex);
++ ri.cpt_ds = encode_segment(tsk->thread.ds);
++ ri.cpt_es = encode_segment(tsk->thread.es);
++ ri.cpt_debugreg[0] = tsk->thread.debugreg0;
++ ri.cpt_debugreg[1] = tsk->thread.debugreg1;
++ ri.cpt_debugreg[2] = tsk->thread.debugreg2;
++ ri.cpt_debugreg[3] = tsk->thread.debugreg3;
++ ri.cpt_debugreg[4] = 0;
++ ri.cpt_debugreg[5] = 0;
++ ri.cpt_debugreg[6] = tsk->thread.debugreg6;
++ ri.cpt_debugreg[7] = tsk->thread.debugreg7;
++
++ memcpy(&ri.cpt_r15, task_pt_regs(tsk), sizeof(struct pt_regs));
++
++ ri.cpt_cs = encode_segment(task_pt_regs(tsk)->cs);
++ ri.cpt_ss = encode_segment(task_pt_regs(tsk)->ss);
++
++ ctx->write(&ri, sizeof(ri), ctx);
++
++ }
++ cpt_close_object(ctx);
++
++ return 0;
++}
++
++#else
++
++static int dump_registers(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ struct cpt_x86_regs ri;
++ struct pt_regs *pt_regs;
++
++ cpt_open_object(NULL, ctx);
++
++ ri.cpt_next = sizeof(ri);
++ ri.cpt_object = CPT_OBJ_X86_REGS;
++ ri.cpt_hdrlen = sizeof(ri);
++ ri.cpt_content = CPT_CONTENT_VOID;
++
++ ri.cpt_debugreg[0] = tsk->thread.debugreg0;
++ ri.cpt_debugreg[1] = tsk->thread.debugreg1;
++ ri.cpt_debugreg[2] = tsk->thread.debugreg2;
++ ri.cpt_debugreg[3] = tsk->thread.debugreg3;
++ ri.cpt_debugreg[6] = tsk->thread.debugreg6;
++ ri.cpt_debugreg[7] = tsk->thread.debugreg7;
++
++ pt_regs = task_pt_regs(tsk);
++
++ ri.cpt_fs = encode_segment(pt_regs->fs);
++ ri.cpt_gs = encode_segment(tsk->thread.gs);
++
++ ri.cpt_ebx = pt_regs->bx;
++ ri.cpt_ecx = pt_regs->cx;
++ ri.cpt_edx = pt_regs->dx;
++ ri.cpt_esi = pt_regs->si;
++ ri.cpt_edi = pt_regs->di;
++ ri.cpt_ebp = pt_regs->bp;
++ ri.cpt_eax = pt_regs->ax;
++ ri.cpt_xds = pt_regs->ds;
++ ri.cpt_xes = pt_regs->es;
++ ri.cpt_orig_eax = pt_regs->orig_ax;
++ ri.cpt_eip = pt_regs->ip;
++ ri.cpt_xcs = pt_regs->cs;
++ ri.cpt_eflags = pt_regs->flags;
++ ri.cpt_esp = pt_regs->sp;
++ ri.cpt_xss = pt_regs->ss;
++
++ ri.cpt_xcs = encode_segment(pt_regs->cs);
++ ri.cpt_xss = encode_segment(pt_regs->ss);
++ ri.cpt_xds = encode_segment(pt_regs->ds);
++ ri.cpt_xes = encode_segment(pt_regs->es);
++
++ ctx->write(&ri, sizeof(ri), ctx);
++ cpt_close_object(ctx);
++
++ return 0;
++}
++#endif
++#endif
++
++#ifdef CONFIG_IA64
++
++/*
++ PMD?
++ */
++
++#define _C(x) do { if ((err = (x)) < 0) { printk("atm:" CPT_FID #x " %d\n", \
++ CPT_TID(tsk), err); return -EINVAL; } } while (0)
++
++static int ass_to_mouth(struct cpt_ia64_regs *r, struct task_struct *tsk,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct unw_frame_info info;
++ struct ia64_fpreg fpval;
++ int i;
++
++ unw_init_from_blocked_task(&info, tsk);
++ _C(unw_unwind_to_user(&info));
++
++ /* NAT_BITS */
++ do {
++ unsigned long scratch_unat;
++
++ scratch_unat = info.sw->caller_unat;
++ if (info.pri_unat_loc)
++ scratch_unat = *info.pri_unat_loc;
++
++ r->nat[0] = ia64_get_scratch_nat_bits(task_pt_regs(tsk), scratch_unat);
++ /* Just to be on safe side. */
++ r->nat[0] &= 0xFFFFFFFFUL;
++ } while (0);
++
++ /* R4-R7 */
++ for (i = 4; i <= 7; i++) {
++ char nat = 0;
++ _C(unw_access_gr(&info, i, &r->gr[i], &nat, 0));
++ r->nat[0] |= (nat != 0) << i;
++ }
++
++ /* B1-B5 */
++ for (i = 1; i <= 5; i++) {
++ _C(unw_access_br(&info, i, &r->br[i], 0));
++ }
++
++ /* AR_EC, AR_LC */
++ _C(unw_access_ar(&info, UNW_AR_EC, &r->ar_ec, 0));
++ _C(unw_access_ar(&info, UNW_AR_LC, &r->ar_lc, 0));
++
++ /* F2..F5, F16..F31 */
++ for (i = 2; i <= 5; i++) {
++ _C(unw_get_fr(&info, i, &fpval));
++ memcpy(&r->fr[i*2], &fpval, 16);
++ }
++ for (i = 16; i <= 31; i++) {
++ _C(unw_get_fr(&info, i, &fpval));
++ memcpy(&r->fr[i*2], &fpval, 16);
++ }
++ return 0;
++}
++
++#undef _C
++
++static int dump_registers(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ int err;
++ unsigned long pg;
++ struct cpt_ia64_regs *r;
++ struct ia64_psr *psr;
++ struct switch_stack *sw;
++ struct pt_regs *pt;
++ void *krbs = (void *)tsk + IA64_RBS_OFFSET;
++ unsigned long reg;
++
++ if (tsk->exit_state)
++ return 0;
++
++ pt = task_pt_regs(tsk);
++
++ sw = (struct switch_stack *) (tsk->thread.ksp + 16);
++
++ if ((pg = __get_free_page(GFP_KERNEL)) == 0)
++ return -ENOMEM;
++
++ r = (void*)pg;
++ /* To catch if we forgot some register */
++ memset(r, 0xA5, sizeof(*r));
++
++ r->gr[0] = 0;
++ r->fr[0] = r->fr[1] = 0;
++ r->fr[2] = 0x8000000000000000UL;
++ r->fr[3] = 0xffff;
++
++ r->nat[0] = r->nat[1] = 0;
++
++ err = ass_to_mouth(r, tsk, ctx);
++ if (err) {
++ printk("ass_to_mouth error %d\n", err);
++ goto out;
++ }
++
++ /* gr 1,2-3,8-11,12-13,14,15,16-31 are on pt_regs */
++ memcpy(&r->gr[1], &pt->r1, 8*(2-1));
++ memcpy(&r->gr[2], &pt->r2, 8*(4-2));
++ memcpy(&r->gr[8], &pt->r8, 8*(12-8));
++ memcpy(&r->gr[12], &pt->r12, 8*(14-12));
++ memcpy(&r->gr[14], &pt->r14, 8*(15-14));
++ memcpy(&r->gr[15], &pt->r15, 8*(16-15));
++ memcpy(&r->gr[16], &pt->r16, 8*(32-16));
++
++ r->br[0] = pt->b0;
++ r->br[6] = pt->b6;
++ r->br[7] = pt->b7;
++
++ r->ar_bspstore = pt->ar_bspstore;
++ r->ar_unat = pt->ar_unat;
++ r->ar_pfs = pt->ar_pfs;
++ r->ar_ccv = pt->ar_ccv;
++ r->ar_fpsr = pt->ar_fpsr;
++ r->ar_csd = pt->ar_csd;
++ r->ar_ssd = pt->ar_ssd;
++ r->ar_rsc = pt->ar_rsc;
++
++ r->cr_iip = pt->cr_iip;
++ r->cr_ipsr = pt->cr_ipsr;
++
++ r->pr = pt->pr;
++
++ r->cfm = pt->cr_ifs;
++ r->ar_rnat = pt->ar_rnat;
++
++ /* fpregs 6..9,10..11 are in pt_regs */
++ memcpy(&r->fr[2*6], &pt->f6, 16*(10-6));
++ memcpy(&r->fr[2*10], &pt->f10, 16*(12-10));
++ /* fpreg 12..15 are on switch stack */
++ memcpy(&r->fr[2*12], &sw->f12, 16*(16-12));
++ /* fpregs 32...127 */
++ psr = ia64_psr(task_pt_regs(tsk));
++ preempt_disable();
++ if (ia64_is_local_fpu_owner(tsk) && psr->mfh) {
++ psr->mfh = 0;
++ tsk->thread.flags |= IA64_THREAD_FPH_VALID;
++ ia64_save_fpu(&tsk->thread.fph[0]);
++ }
++ preempt_enable();
++ memcpy(&r->fr[32*2], tsk->thread.fph, 16*(128-32));
++
++ if (tsk->thread.flags & IA64_THREAD_DBG_VALID) {
++ memcpy(r->ibr, tsk->thread.ibr, sizeof(r->ibr));
++ memcpy(r->dbr, tsk->thread.dbr, sizeof(r->ibr));
++ } else {
++ memset(r->ibr, 0, sizeof(r->ibr));
++ memset(r->dbr, 0, sizeof(r->dbr));
++ }
++
++ r->loadrs = pt->loadrs;
++ r->num_regs = ia64_rse_num_regs(krbs, krbs + 8*(pt->loadrs >> 19));
++ if ((long)pt->cr_ifs > 0)
++ r->num_regs += (pt->cr_ifs & 0x7f);
++
++ if (r->num_regs > 96) {
++ eprintk_ctx(CPT_FID " too much RSE regs %lu\n",
++ CPT_TID(tsk), r->num_regs);
++ return -EINVAL;
++ }
++
++ for (reg = 0; reg < r->num_regs; reg++) {
++ unsigned long *ptr = ia64_rse_skip_regs(krbs, reg);
++ unsigned long *rnatp = ia64_rse_rnat_addr(ptr);
++
++ r->gr[32+reg] = *ptr;
++
++ if ((unsigned long)rnatp >= sw->ar_bspstore)
++ rnatp = &sw->ar_rnat;
++ if (*rnatp & (1UL<<ia64_rse_slot_num(ptr))) {
++ if (reg < 32)
++ r->nat[0] |= (1UL<<(reg+32));
++ else
++ r->nat[1] |= (1UL<<(reg-32));
++ }
++ }
++ if (r->nat[0] | r->nat[1])
++ wprintk_ctx(CPT_FID " nat bits %lx%016lx\n", CPT_TID(tsk),
++ r->nat[1], r->nat[0]);
++
++ cpt_open_object(NULL, ctx);
++ r->cpt_next = sizeof(*r);
++ r->cpt_object = CPT_OBJ_IA64_REGS;
++ r->cpt_hdrlen = sizeof(*r);
++ r->cpt_content = CPT_CONTENT_VOID;
++ ctx->write(r, sizeof(*r), ctx);
++ cpt_close_object(ctx);
++ err = 0;
++
++out:
++ free_page(pg);
++ return err;
++}
++#endif
++
++static int dump_kstack(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ struct cpt_obj_bits hdr;
++ unsigned long size;
++ void *start;
++
++ cpt_open_object(NULL, ctx);
++
++#ifdef CONFIG_X86_64
++ size = tsk->thread.sp0 - tsk->thread.sp;
++ start = (void*)tsk->thread.sp;
++#elif defined(CONFIG_X86_32)
++ size = tsk->thread.sp0 - tsk->thread.sp;
++ start = (void*)tsk->thread.sp;
++#elif defined(CONFIG_IA64)
++ size = (unsigned long)(task_pt_regs(tsk)+1) - tsk->thread.ksp;
++ start = (void*)tsk->thread.ksp;
++#else
++#error Arch is not supported
++#endif
++
++ hdr.cpt_next = sizeof(hdr) + CPT_ALIGN(size);
++ hdr.cpt_object = CPT_OBJ_BITS;
++ hdr.cpt_hdrlen = sizeof(hdr);
++ hdr.cpt_content = CPT_CONTENT_STACK;
++ hdr.cpt_size = size;
++
++ ctx->write(&hdr, sizeof(hdr), ctx);
++ ctx->write(start, size, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ return 0;
++}
++
++#ifdef CONFIG_X86
++/* Formats of i387_fxsave_struct are the same for x86_64
++ * and i386. Plain luck. */
++
++static int dump_fpustate(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ struct cpt_obj_bits hdr;
++ unsigned long size;
++ int type;
++
++ if (!tsk->thread.xstate)
++ return 0;
++
++ cpt_open_object(NULL, ctx);
++
++ type = CPT_CONTENT_X86_FPUSTATE;
++ size = sizeof(struct i387_fxsave_struct);
++#ifndef CONFIG_X86_64
++ if (!cpu_has_fxsr) {
++ size = sizeof(struct i387_fsave_struct);
++ type = CPT_CONTENT_X86_FPUSTATE_OLD;
++ }
++#endif
++
++ hdr.cpt_next = sizeof(hdr) + CPT_ALIGN(size);
++ hdr.cpt_object = CPT_OBJ_BITS;
++ hdr.cpt_hdrlen = sizeof(hdr);
++ hdr.cpt_content = type;
++ hdr.cpt_size = size;
++
++ ctx->write(&hdr, sizeof(hdr), ctx);
++ ctx->write(tsk->thread.xstate, size, ctx);
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_IA64
++
++static int dump_fpustate(struct task_struct *tsk, struct cpt_context *ctx)
++{
++ return 0;
++}
++#endif
++
++static int encode_siginfo(struct cpt_siginfo_image *si, siginfo_t *info)
++{
++ si->cpt_signo = info->si_signo;
++ si->cpt_errno = info->si_errno;
++ si->cpt_code = info->si_code;
++
++ switch(si->cpt_code & __SI_MASK) {
++ case __SI_TIMER:
++ si->cpt_pid = info->si_tid;
++ si->cpt_uid = info->si_overrun;
++ si->cpt_sigval = cpt_ptr_export(info->_sifields._timer._sigval.sival_ptr);
++ si->cpt_utime = info->si_sys_private;
++ break;
++ case __SI_POLL:
++ si->cpt_pid = info->si_band;
++ si->cpt_uid = info->si_fd;
++ break;
++ case __SI_FAULT:
++ si->cpt_sigval = cpt_ptr_export(info->si_addr);
++#ifdef __ARCH_SI_TRAPNO
++ si->cpt_pid = info->si_trapno;
++#endif
++ break;
++ case __SI_CHLD:
++ si->cpt_pid = info->si_pid;
++ si->cpt_uid = info->si_uid;
++ si->cpt_sigval = info->si_status;
++ si->cpt_stime = info->si_stime;
++ si->cpt_utime = info->si_utime;
++ break;
++ case __SI_KILL:
++ case __SI_RT:
++ case __SI_MESGQ:
++ default:
++ si->cpt_pid = info->si_pid;
++ si->cpt_uid = info->si_uid;
++ si->cpt_sigval = cpt_ptr_export(info->si_ptr);
++ break;
++ }
++ return 0;
++}
++
++static int dump_sigqueue(struct sigpending *list, struct cpt_context *ctx)
++{
++ struct sigqueue *q;
++ loff_t saved_obj;
++
++ if (list_empty(&list->list))
++ return 0;
++
++ cpt_push_object(&saved_obj, ctx);
++ list_for_each_entry(q, &list->list, list) {
++ struct cpt_siginfo_image si;
++
++ si.cpt_next = sizeof(si);
++ si.cpt_object = CPT_OBJ_SIGINFO;
++ si.cpt_hdrlen = sizeof(si);
++ si.cpt_content = CPT_CONTENT_VOID;
++
++ si.cpt_qflags = q->flags;
++ si.cpt_user = q->user->uid;
++
++ if (encode_siginfo(&si, &q->info))
++ return -EINVAL;
++
++ ctx->write(&si, sizeof(si), ctx);
++ }
++ cpt_pop_object(&saved_obj, ctx);
++ return 0;
++}
++
++
++
++static int dump_one_signal_struct(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct signal_struct *sig = obj->o_obj;
++ struct cpt_signal_image *v = cpt_get_buf(ctx);
++ struct task_struct *tsk;
++ int i;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_SIGNAL_STRUCT;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ if (sig->__pgrp <= 0) {
++ eprintk_ctx("bad pgid\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_pgrp_type = CPT_PGRP_NORMAL;
++ read_lock(&tasklist_lock);
++ tsk = find_task_by_pid_ns(sig->__pgrp, &init_pid_ns);
++ if (tsk == NULL)
++ v->cpt_pgrp_type = CPT_PGRP_ORPHAN;
++ read_unlock(&tasklist_lock);
++ v->cpt_pgrp = pid_to_vpid(sig->__pgrp);
++
++ v->cpt_old_pgrp = 0;
++/* if (!sig->tty_old_pgrp) {
++ eprintk_ctx("bad tty_old_pgrp\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }*/
++ if (sig->tty_old_pgrp) {
++ v->cpt_old_pgrp_type = CPT_PGRP_NORMAL;
++ read_lock(&tasklist_lock);
++ tsk = pid_task(sig->tty_old_pgrp, PIDTYPE_PID);
++ if (tsk == NULL) {
++ v->cpt_old_pgrp_type = CPT_PGRP_ORPHAN;
++ tsk = pid_task(sig->tty_old_pgrp, PIDTYPE_PGID);
++ }
++ read_unlock(&tasklist_lock);
++ if (tsk == NULL) {
++ eprintk_ctx("tty_old_pgrp does not exist anymore\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_old_pgrp = pid_vnr(sig->tty_old_pgrp);
++ if ((int)v->cpt_old_pgrp < 0) {
++ dprintk_ctx("stray tty_old_pgrp %d\n", pid_nr(sig->tty_old_pgrp));
++ v->cpt_old_pgrp = -1;
++ v->cpt_old_pgrp_type = CPT_PGRP_STRAY;
++ }
++ }
++
++ if (sig->__session <= 0) {
++ eprintk_ctx("bad session\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_session_type = CPT_PGRP_NORMAL;
++ read_lock(&tasklist_lock);
++ tsk = find_task_by_pid_ns(sig->__session, &init_pid_ns);
++ if (tsk == NULL)
++ v->cpt_session_type = CPT_PGRP_ORPHAN;
++ read_unlock(&tasklist_lock);
++ v->cpt_session = pid_to_vpid(sig->__session);
++
++ v->cpt_leader = sig->leader;
++ v->cpt_ctty = CPT_NULL;
++ if (sig->tty) {
++ cpt_object_t *cobj = lookup_cpt_object(CPT_OBJ_TTY, sig->tty, ctx);
++ if (cobj)
++ v->cpt_ctty = cobj->o_pos;
++ else {
++ eprintk_ctx("controlling tty is not found\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ memcpy(&v->cpt_sigpending, &sig->shared_pending.signal, 8);
++
++ v->cpt_curr_target = 0;
++ if (sig->curr_target)
++ v->cpt_curr_target = task_pid_vnr(sig->curr_target);
++ v->cpt_group_exit = ((sig->flags & SIGNAL_GROUP_EXIT) != 0);
++ v->cpt_group_exit_code = sig->group_exit_code;
++ v->cpt_group_exit_task = 0;
++ if (sig->group_exit_task)
++ v->cpt_group_exit_task = task_pid_vnr(sig->group_exit_task);
++ v->cpt_notify_count = sig->notify_count;
++ v->cpt_group_stop_count = sig->group_stop_count;
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
++ v->cpt_utime = sig->utime;
++ v->cpt_stime = sig->stime;
++ v->cpt_cutime = sig->cutime;
++ v->cpt_cstime = sig->cstime;
++ v->cpt_nvcsw = sig->nvcsw;
++ v->cpt_nivcsw = sig->nivcsw;
++ v->cpt_cnvcsw = sig->cnvcsw;
++ v->cpt_cnivcsw = sig->cnivcsw;
++ v->cpt_min_flt = sig->min_flt;
++ v->cpt_maj_flt = sig->maj_flt;
++ v->cpt_cmin_flt = sig->cmin_flt;
++ v->cpt_cmaj_flt = sig->cmaj_flt;
++
++ if (RLIM_NLIMITS > CPT_RLIM_NLIMITS)
++ __asm__("undefined\n");
++
++ for (i=0; i<CPT_RLIM_NLIMITS; i++) {
++ if (i < RLIM_NLIMITS) {
++ v->cpt_rlim_cur[i] = sig->rlim[i].rlim_cur;
++ v->cpt_rlim_max[i] = sig->rlim[i].rlim_max;
++ } else {
++ v->cpt_rlim_cur[i] = CPT_NULL;
++ v->cpt_rlim_max[i] = CPT_NULL;
++ }
++ }
++#endif
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ dump_sigqueue(&sig->shared_pending, ctx);
++
++ cpt_close_object(ctx);
++ return 0;
++}
++
++int cpt_check_unsupported(struct task_struct *tsk, cpt_context_t *ctx)
++{
++ if (tsk->splice_pipe) {
++ eprintk_ctx("splice is used by " CPT_FID "\n", CPT_TID(tsk));
++ return -EBUSY;
++ }
++#ifdef CONFIG_KEYS
++ if (tsk->request_key_auth || tsk->thread_keyring) {
++ eprintk_ctx("keys are used by " CPT_FID "\n", CPT_TID(tsk));
++ return -EBUSY;
++ }
++#endif
++#ifdef CONFIG_NUMA
++ if (tsk->mempolicy) {
++ eprintk_ctx("NUMA mempolicy is used by " CPT_FID "\n", CPT_TID(tsk));
++ return -EBUSY;
++ }
++#endif
++#ifdef CONFIG_TUX
++ if (tsk->tux_info) {
++ eprintk_ctx("TUX is used by " CPT_FID "\n", CPT_TID(tsk));
++ return -EBUSY;
++ }
++#endif
++ return 0;
++}
++
++static int dump_one_process(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct task_struct *tsk = obj->o_obj;
++ int last_thread;
++ struct cpt_task_image *v = cpt_get_buf(ctx);
++ cpt_object_t *tobj;
++ cpt_object_t *tg_obj;
++ loff_t saved_obj;
++ int i;
++ int err;
++ struct timespec delta;
++ struct mm_struct * tsk_mm;
++ struct files_struct * tsk_files;
++ struct fs_struct * tsk_fs;
++ struct mnt_namespace * tsk_ns;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_signal = CPT_NULL;
++ tg_obj = lookup_cpt_object(CPT_OBJ_SIGNAL_STRUCT, tsk->signal, ctx);
++ if (!tg_obj) BUG();
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_TASK;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_state = tsk->state;
++ if (tsk->state == EXIT_ZOMBIE) {
++ eprintk_ctx("invalid zombie state on" CPT_FID "\n", CPT_TID(tsk));
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ } else if (tsk->state == EXIT_DEAD) {
++ if (tsk->exit_state != EXIT_DEAD &&
++ tsk->exit_state != EXIT_ZOMBIE) {
++ eprintk_ctx("invalid exit_state %d on" CPT_FID "\n", tsk->exit_state, CPT_TID(tsk));
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ if (tsk->exit_state) {
++ v->cpt_state = tsk->exit_state;
++ if (tsk->state != TASK_DEAD) {
++ eprintk_ctx("invalid tsk->state %ld/%d on" CPT_FID "\n",
++ tsk->state, tsk->exit_state, CPT_TID(tsk));
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ if (cpt_check_unsupported(tsk, ctx)) {
++ cpt_release_buf(ctx);
++ return -EBUSY;
++ }
++
++ v->cpt_flags = tsk->flags&~(PF_FROZEN|PF_EXIT_RESTART);
++ v->cpt_ptrace = tsk->ptrace;
++ v->cpt_prio = tsk->prio;
++ v->cpt_exit_code = tsk->exit_code;
++ v->cpt_exit_signal = tsk->exit_signal;
++ v->cpt_pdeath_signal = tsk->pdeath_signal;
++ v->cpt_static_prio = tsk->static_prio;
++ v->cpt_rt_priority = tsk->rt_priority;
++ v->cpt_policy = tsk->policy;
++ if (v->cpt_policy != SCHED_NORMAL) {
++ eprintk_ctx("scheduler policy is not supported %d/%d(%s)\n", task_pid_vnr(tsk), tsk->pid, tsk->comm);
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++
++ /* Unpleasant moment. When leader of thread group exits,
++ * it remains in zombie state until all the group exits.
++ * We save not-NULL pointers to process mm/files/fs, so
++ * that we can restore this thread group.
++ */
++ tsk_mm = tsk->mm;
++ tsk_files = tsk->files;
++ tsk_fs = tsk->fs;
++ tsk_ns = tsk->nsproxy ? tsk->nsproxy->mnt_ns : NULL;
++
++ if (tsk->exit_state && !thread_group_empty(tsk) &&
++ thread_group_leader(tsk)) {
++ struct task_struct * p = tsk;
++
++ read_lock(&tasklist_lock);
++ do {
++ if (p->mm)
++ tsk_mm = p->mm;
++ if (p->files)
++ tsk_files = p->files;
++ if (p->fs)
++ tsk_fs = p->fs;
++ if (p->nsproxy && p->nsproxy->mnt_ns)
++ tsk_ns = p->nsproxy->mnt_ns;
++ p = next_thread(p);
++ } while (p != tsk);
++ read_unlock(&tasklist_lock);
++ }
++
++ v->cpt_mm = CPT_NULL;
++ if (tsk_mm) {
++ tobj = lookup_cpt_object(CPT_OBJ_MM, tsk_mm, ctx);
++ if (!tobj) BUG();
++ v->cpt_mm = tobj->o_pos;
++ }
++ v->cpt_files = CPT_NULL;
++ if (tsk_files) {
++ tobj = lookup_cpt_object(CPT_OBJ_FILES, tsk_files, ctx);
++ if (!tobj) BUG();
++ v->cpt_files = tobj->o_pos;
++ }
++ v->cpt_fs = CPT_NULL;
++ if (tsk_fs) {
++ tobj = lookup_cpt_object(CPT_OBJ_FS, tsk_fs, ctx);
++ if (!tobj) BUG();
++ v->cpt_fs = tobj->o_pos;
++ }
++ v->cpt_namespace = CPT_NULL;
++ if (tsk_ns) {
++ tobj = lookup_cpt_object(CPT_OBJ_NAMESPACE, tsk_ns, ctx);
++ if (!tobj) BUG();
++ v->cpt_namespace = tobj->o_pos;
++
++ if (tsk_ns != current->nsproxy->mnt_ns)
++ eprintk_ctx("namespaces are not supported:"
++ "process " CPT_FID "\n", CPT_TID(tsk));
++ }
++ v->cpt_sysvsem_undo = CPT_NULL;
++ if (tsk->sysvsem.undo_list && !tsk->exit_state) {
++ tobj = lookup_cpt_object(CPT_OBJ_SYSVSEM_UNDO, tsk->sysvsem.undo_list, ctx);
++ if (!tobj) BUG();
++ v->cpt_sysvsem_undo = tobj->o_pos;
++ }
++ v->cpt_sighand = CPT_NULL;
++ if (tsk->sighand) {
++ tobj = lookup_cpt_object(CPT_OBJ_SIGHAND_STRUCT, tsk->sighand, ctx);
++ if (!tobj) BUG();
++ v->cpt_sighand = tobj->o_pos;
++ }
++ v->cpt_sigblocked = cpt_sigset_export(&tsk->blocked);
++ v->cpt_sigrblocked = cpt_sigset_export(&tsk->real_blocked);
++ v->cpt_sigsuspend_blocked = cpt_sigset_export(&tsk->saved_sigmask);
++
++ v->cpt_pid = task_pid_vnr(tsk);
++ v->cpt_tgid = task_tgid_vnr(tsk);
++ v->cpt_ppid = 0;
++ if (tsk->parent) {
++ if (tsk->parent != tsk->real_parent &&
++ !lookup_cpt_object(CPT_OBJ_TASK, tsk->parent, ctx)) {
++ eprintk_ctx("task %d/%d(%s) is ptraced from ve0\n", tsk->pid, task_pid_vnr(tsk), tsk->comm);
++ cpt_release_buf(ctx);
++ return -EBUSY;
++ }
++ v->cpt_ppid = task_pid_vnr(tsk->parent);
++ }
++ v->cpt_rppid = tsk->real_parent ? task_pid_vnr(tsk->real_parent) : 0;
++ v->cpt_pgrp = task_pgrp_vnr(tsk);
++ v->cpt_session = task_session_vnr(tsk);
++ v->cpt_old_pgrp = 0;
++ if (tsk->signal->tty_old_pgrp)
++ v->cpt_old_pgrp = pid_vnr(tsk->signal->tty_old_pgrp);
++ v->cpt_leader = tsk->group_leader ? task_pid_vnr(tsk->group_leader) : 0;
++ v->cpt_set_tid = (unsigned long)tsk->set_child_tid;
++ v->cpt_clear_tid = (unsigned long)tsk->clear_child_tid;
++ memcpy(v->cpt_comm, tsk->comm, 16);
++ v->cpt_user = tsk->user->uid;
++ v->cpt_uid = tsk->uid;
++ v->cpt_euid = tsk->euid;
++ v->cpt_suid = tsk->suid;
++ v->cpt_fsuid = tsk->fsuid;
++ v->cpt_gid = tsk->gid;
++ v->cpt_egid = tsk->egid;
++ v->cpt_sgid = tsk->sgid;
++ v->cpt_fsgid = tsk->fsgid;
++ v->cpt_ngids = 0;
++ if (tsk->group_info && tsk->group_info->ngroups != 0) {
++ int i = tsk->group_info->ngroups;
++ if (i > 32) {
++ /* Shame... I did a simplified version and _forgot_
++ * about this. Later, later. */
++ eprintk_ctx("too many of groups " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++ v->cpt_ngids = i;
++ for (i--; i>=0; i--)
++ v->cpt_gids[i] = tsk->group_info->small_block[i];
++ }
++ v->cpt_prctl_uac = 0;
++ v->cpt_prctl_fpemu = 0;
++ v->__cpt_pad1 = 0;
++#ifdef CONFIG_IA64
++ v->cpt_prctl_uac = (tsk->thread.flags & IA64_THREAD_UAC_MASK) >> IA64_THREAD_UAC_SHIFT;
++ v->cpt_prctl_fpemu = (tsk->thread.flags & IA64_THREAD_FPEMU_MASK) >> IA64_THREAD_FPEMU_SHIFT;
++#endif
++ memcpy(&v->cpt_ecap, &tsk->cap_effective, 8);
++ memcpy(&v->cpt_icap, &tsk->cap_inheritable, 8);
++ memcpy(&v->cpt_pcap, &tsk->cap_permitted, 8);
++ v->cpt_keepcap = tsk->securebits;
++
++ v->cpt_did_exec = tsk->did_exec;
++ v->cpt_exec_domain = -1;
++ v->cpt_thrflags = task_thread_info(tsk)->flags & ~(1<<TIF_FREEZE);
++ v->cpt_64bit = 0;
++#ifdef CONFIG_X86_64
++ /* Clear x86_64 specific flags */
++ v->cpt_thrflags &= ~(_TIF_FORK|_TIF_ABI_PENDING|_TIF_IA32);
++ if (!(task_thread_info(tsk)->flags & _TIF_IA32)) {
++ ctx->tasks64++;
++ v->cpt_64bit = 1;
++ }
++#endif
++#ifdef CONFIG_IA64
++ /* Clear ia64 specific flags */
++ //// v->cpt_thrflags &= ~(_TIF_FORK|_TIF_ABI_PENDING|_TIF_IA32);
++ if (!IS_IA32_PROCESS(task_pt_regs(tsk))) {
++ ctx->tasks64++;
++ v->cpt_64bit = 1;
++ }
++#endif
++ v->cpt_thrstatus = task_thread_info(tsk)->status;
++ v->cpt_addr_limit = -1;
++
++ v->cpt_personality = tsk->personality;
++
++#ifdef CONFIG_X86
++ for (i=0; i<GDT_ENTRY_TLS_ENTRIES; i++) {
++ if (i>=3) {
++ eprintk_ctx("too many tls descs\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_tls[i] = (((u64)tsk->thread.tls_array[i].b)<<32) + tsk->thread.tls_array[i].a;
++ }
++#endif
++
++ v->cpt_restart.fn = CPT_RBL_0;
++ if (task_thread_info(tsk)->restart_block.fn != task_thread_info(current)->restart_block.fn) {
++ struct restart_block *rb = &task_thread_info(tsk)->restart_block;
++ ktime_t e;
++
++ if (rb->fn == hrtimer_nanosleep_restart) {
++ v->cpt_restart.fn = CPT_RBL_NANOSLEEP;
++
++ e.tv64 = ((u64)rb->arg3 << 32) | (u64)rb->arg2;
++ e = ktime_sub(e, timespec_to_ktime(ctx->cpt_monotonic_time));
++ v->cpt_restart.arg0 = rb->arg0;
++ v->cpt_restart.arg1 = rb->arg1;
++ v->cpt_restart.arg2 = ktime_to_ns(e);
++ v->cpt_restart.arg3 = 0;
++ dprintk_ctx(CPT_FID " %Lu\n", CPT_TID(tsk), (unsigned long long)v->cpt_restart.arg0);
++ goto continue_dump;
++ }
++#if defined(CONFIG_X86_64) && defined(CONFIG_COMPAT)
++ if (rb->fn == compat_nanosleep_restart) {
++ v->cpt_restart.fn = CPT_RBL_COMPAT_NANOSLEEP;
++
++ e.tv64 = ((u64)rb->arg3 << 32) | (u64)rb->arg2;
++ e = ktime_sub(e, timespec_to_ktime(ctx->cpt_monotonic_time));
++ v->cpt_restart.arg0 = rb->arg0;
++ v->cpt_restart.arg1 = rb->arg1;
++ v->cpt_restart.arg2 = ktime_to_ns(e);
++ v->cpt_restart.arg3 = 0;
++ dprintk_ctx(CPT_FID " %Lu\n", CPT_TID(tsk), (unsigned long long)v->cpt_restart.arg0);
++ goto continue_dump;
++ }
++#endif
++ if (rb->fn == do_restart_poll) {
++ u64 timeout_jiffies;
++
++ timeout_jiffies = ((u64)rb->arg3 << 32)|(u64)rb->arg2;
++ e.tv64 = timeout_jiffies * TICK_NSEC;
++
++ v->cpt_restart.fn = CPT_RBL_POLL;
++ v->cpt_restart.arg0 = rb->arg0;
++ v->cpt_restart.arg1 = rb->arg1;
++ v->cpt_restart.arg2 = ktime_to_ns(e);
++ v->cpt_restart.arg3 = 0;
++ dprintk_ctx(CPT_FID " %Lu\n", CPT_TID(tsk), (unsigned long long)v->cpt_restart.arg0);
++ goto continue_dump;
++ }
++ if (rb->fn == futex_wait_restart) {
++ v->cpt_restart.fn = CPT_RBL_FUTEX_WAIT;
++
++ e.tv64 = rb->futex.time;
++ e = ktime_sub(e, timespec_to_ktime(ctx->cpt_monotonic_time));
++ v->cpt_restart.arg0 = (unsigned long)rb->futex.uaddr;
++ v->cpt_restart.arg1 = rb->futex.val;
++ v->cpt_restart.arg2 = ktime_to_ns(e);
++ v->cpt_restart.arg3 = rb->futex.flags;
++ goto continue_dump;
++ }
++ eprintk_ctx("unknown restart block %p\n", rb->fn);
++ return -EINVAL;
++ }
++
++continue_dump:
++ v->cpt_it_real_incr = 0;
++ v->cpt_it_prof_incr = 0;
++ v->cpt_it_virt_incr = 0;
++ v->cpt_it_real_value = 0;
++ v->cpt_it_prof_value = 0;
++ v->cpt_it_virt_value = 0;
++ if (thread_group_leader(tsk) && tsk->exit_state == 0) {
++ ktime_t rem;
++
++ v->cpt_it_real_incr = ktime_to_ns(tsk->signal->it_real_incr);
++ v->cpt_it_prof_incr = tsk->signal->it_prof_incr;
++ v->cpt_it_virt_incr = tsk->signal->it_virt_incr;
++
++ rem = hrtimer_get_remaining(&tsk->signal->real_timer);
++
++ if (hrtimer_active(&tsk->signal->real_timer)) {
++ if (rem.tv64 <= 0)
++ rem.tv64 = NSEC_PER_USEC;
++ v->cpt_it_real_value = ktime_to_ns(rem);
++ dprintk("cpt itimer " CPT_FID " %Lu\n", CPT_TID(tsk), (unsigned long long)v->cpt_it_real_value);
++ }
++ v->cpt_it_prof_value = tsk->signal->it_prof_expires;
++ v->cpt_it_virt_value = tsk->signal->it_virt_expires;
++ }
++ v->cpt_used_math = (tsk_used_math(tsk) != 0);
++
++ if (tsk->notifier) {
++ eprintk_ctx("task notifier is in use: process %d/%d(%s)\n", task_pid_vnr(tsk), tsk->pid, tsk->comm);
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++
++ v->cpt_utime = tsk->utime;
++ v->cpt_stime = tsk->stime;
++ delta = tsk->start_time;
++ _set_normalized_timespec(&delta,
++ delta.tv_sec - get_exec_env()->start_timespec.tv_sec,
++ delta.tv_nsec - get_exec_env()->start_timespec.tv_nsec);
++ v->cpt_starttime = cpt_timespec_export(&delta);
++ v->cpt_nvcsw = tsk->nvcsw;
++ v->cpt_nivcsw = tsk->nivcsw;
++ v->cpt_min_flt = tsk->min_flt;
++ v->cpt_maj_flt = tsk->maj_flt;
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
++ v->cpt_cutime = tsk->cutime;
++ v->cpt_cstime = tsk->cstime;
++ v->cpt_cnvcsw = tsk->cnvcsw;
++ v->cpt_cnivcsw = tsk->cnivcsw;
++ v->cpt_cmin_flt = tsk->cmin_flt;
++ v->cpt_cmaj_flt = tsk->cmaj_flt;
++
++ if (RLIM_NLIMITS > CPT_RLIM_NLIMITS)
++ __asm__("undefined\n");
++
++ for (i=0; i<CPT_RLIM_NLIMITS; i++) {
++ if (i < RLIM_NLIMITS) {
++ v->cpt_rlim_cur[i] = tsk->rlim[i].rlim_cur;
++ v->cpt_rlim_max[i] = tsk->rlim[i].rlim_max;
++ } else {
++ v->cpt_rlim_cur[i] = CPT_NULL;
++ v->cpt_rlim_max[i] = CPT_NULL;
++ }
++ }
++#else
++ v->cpt_cutime = tsk->signal->cutime;
++ v->cpt_cstime = tsk->signal->cstime;
++ v->cpt_cnvcsw = tsk->signal->cnvcsw;
++ v->cpt_cnivcsw = tsk->signal->cnivcsw;
++ v->cpt_cmin_flt = tsk->signal->cmin_flt;
++ v->cpt_cmaj_flt = tsk->signal->cmaj_flt;
++
++ if (RLIM_NLIMITS > CPT_RLIM_NLIMITS)
++ __asm__("undefined\n");
++
++ for (i=0; i<CPT_RLIM_NLIMITS; i++) {
++ if (i < RLIM_NLIMITS) {
++ v->cpt_rlim_cur[i] = tsk->signal->rlim[i].rlim_cur;
++ v->cpt_rlim_max[i] = tsk->signal->rlim[i].rlim_max;
++ } else {
++ v->cpt_rlim_cur[i] = CPT_NULL;
++ v->cpt_rlim_max[i] = CPT_NULL;
++ }
++ }
++#endif
++
++#ifdef CONFIG_BEANCOUNTERS
++ if (tsk->mm)
++ v->cpt_mm_ub = cpt_lookup_ubc(tsk->mm->mm_ub, ctx);
++ else
++ v->cpt_mm_ub = CPT_NULL;
++ v->cpt_task_ub = cpt_lookup_ubc(tsk->task_bc.task_ub, ctx);
++ v->cpt_exec_ub = cpt_lookup_ubc(tsk->task_bc.exec_ub, ctx);
++ v->cpt_fork_sub = cpt_lookup_ubc(tsk->task_bc.fork_sub, ctx);
++#endif
++
++ v->cpt_ptrace_message = tsk->ptrace_message;
++ v->cpt_pn_state = tsk->pn_state;
++ v->cpt_stopped_state = tsk->stopped_state;
++ v->cpt_sigsuspend_state = 0;
++
++#ifdef CONFIG_X86_32
++ if (tsk->thread.vm86_info) {
++ eprintk_ctx("vm86 task is running\n");
++ cpt_release_buf(ctx);
++ return -EBUSY;
++ }
++#endif
++
++ v->cpt_sigpending = cpt_sigset_export(&tsk->pending.signal);
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ dump_kstack(tsk, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ err = dump_registers(tsk, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ if (err)
++ return err;
++
++ if (tsk_used_math(tsk)) {
++ cpt_push_object(&saved_obj, ctx);
++ dump_fpustate(tsk, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++
++ if (tsk->last_siginfo) {
++ struct cpt_siginfo_image si;
++ cpt_push_object(&saved_obj, ctx);
++
++ si.cpt_next = sizeof(si);
++ si.cpt_object = CPT_OBJ_LASTSIGINFO;
++ si.cpt_hdrlen = sizeof(si);
++ si.cpt_content = CPT_CONTENT_VOID;
++
++ if (encode_siginfo(&si, tsk->last_siginfo))
++ return -EINVAL;
++
++ ctx->write(&si, sizeof(si), ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++
++ if (tsk->sas_ss_size) {
++ struct cpt_sigaltstack_image si;
++ cpt_push_object(&saved_obj, ctx);
++
++ si.cpt_next = sizeof(si);
++ si.cpt_object = CPT_OBJ_SIGALTSTACK;
++ si.cpt_hdrlen = sizeof(si);
++ si.cpt_content = CPT_CONTENT_VOID;
++
++ si.cpt_stack = tsk->sas_ss_sp;
++ si.cpt_stacksize = tsk->sas_ss_size;
++
++ ctx->write(&si, sizeof(si), ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++
++ if (tsk->robust_list
++#ifdef CONFIG_COMPAT
++ || tsk->compat_robust_list
++#endif
++ ) {
++ struct cpt_task_aux_image ai;
++ cpt_push_object(&saved_obj, ctx);
++
++ ai.cpt_next = sizeof(ai);
++ ai.cpt_object = CPT_OBJ_TASK_AUX;
++ ai.cpt_hdrlen = sizeof(ai);
++ ai.cpt_content = CPT_CONTENT_VOID;
++
++ ai.cpt_robust_list = (unsigned long)tsk->robust_list;
++#ifdef CONFIG_X86_64
++#ifdef CONFIG_COMPAT
++ if (task_thread_info(tsk)->flags & _TIF_IA32)
++ ai.cpt_robust_list = (unsigned long)tsk->compat_robust_list;
++#endif
++#endif
++ ctx->write(&ai, sizeof(ai), ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++
++ dump_sigqueue(&tsk->pending, ctx);
++
++ last_thread = 1;
++ read_lock(&tasklist_lock);
++ do {
++ struct task_struct * next = next_thread(tsk);
++ if (next != tsk && !thread_group_leader(next))
++ last_thread = 0;
++ } while (0);
++ read_unlock(&tasklist_lock);
++
++ if (last_thread) {
++ struct task_struct *prev_tsk;
++ int err;
++ loff_t pos = ctx->file->f_pos;
++
++ cpt_push_object(&saved_obj, ctx);
++ err = dump_one_signal_struct(tg_obj, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ if (err)
++ return err;
++
++ prev_tsk = tsk;
++ for (;;) {
++ if (prev_tsk->tgid == tsk->tgid) {
++ loff_t tg_pos;
++
++ tg_pos = obj->o_pos + offsetof(struct cpt_task_image, cpt_signal);
++ ctx->pwrite(&pos, sizeof(pos), ctx, tg_pos);
++ if (thread_group_leader(prev_tsk))
++ break;
++ }
++
++ if (obj->o_list.prev == &ctx->object_array[CPT_OBJ_TASK]) {
++ eprintk_ctx("bug: thread group leader is lost\n");
++ return -EINVAL;
++ }
++
++ obj = list_entry(obj->o_list.prev, cpt_object_t, o_list);
++ prev_tsk = obj->o_obj;
++ }
++ }
++
++ cpt_close_object(ctx);
++ return 0;
++}
++
++int cpt_dump_tasks(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_TASKS);
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ int err;
++
++ if ((err = dump_one_process(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++ return 0;
++}
++
++int cpt_collect_signals(cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++
++ /* Collect process fd sets */
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->signal && !list_empty(&tsk->signal->posix_timers)) {
++ eprintk_ctx("task %d/%d(%s) uses posix timers\n", tsk->pid, task_pid_vnr(tsk), tsk->comm);
++ return -EBUSY;
++ }
++ if (tsk->signal && cpt_object_add(CPT_OBJ_SIGNAL_STRUCT, tsk->signal, ctx) == NULL)
++ return -ENOMEM;
++ if (tsk->sighand && cpt_object_add(CPT_OBJ_SIGHAND_STRUCT, tsk->sighand, ctx) == NULL)
++ return -ENOMEM;
++ }
++ return 0;
++}
++
++
++static int dump_one_sighand_struct(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct sighand_struct *sig = obj->o_obj;
++ struct cpt_sighand_image *v = cpt_get_buf(ctx);
++ int i;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_SIGHAND_STRUCT;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ for (i=0; i< _NSIG; i++) {
++ if (sig->action[i].sa.sa_handler != SIG_DFL ||
++ sig->action[i].sa.sa_flags) {
++ loff_t saved_obj;
++ struct cpt_sighandler_image *o = cpt_get_buf(ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ o->cpt_next = CPT_NULL;
++ o->cpt_object = CPT_OBJ_SIGHANDLER;
++ o->cpt_hdrlen = sizeof(*o);
++ o->cpt_content = CPT_CONTENT_VOID;
++
++ o->cpt_signo = i;
++ o->cpt_handler = (unsigned long)sig->action[i].sa.sa_handler;
++ o->cpt_restorer = 0;
++#ifdef CONFIG_X86
++ o->cpt_restorer = (unsigned long)sig->action[i].sa.sa_restorer;
++#endif
++ o->cpt_flags = sig->action[i].sa.sa_flags;
++ memcpy(&o->cpt_mask, &sig->action[i].sa.sa_mask, 8);
++ ctx->write(o, sizeof(*o), ctx);
++ cpt_release_buf(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ }
++
++ cpt_close_object(ctx);
++ return 0;
++}
++
++int cpt_dump_sighand(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ cpt_open_section(ctx, CPT_SECT_SIGHAND_STRUCT);
++
++ for_each_object(obj, CPT_OBJ_SIGHAND_STRUCT) {
++ int err;
++
++ if ((err = dump_one_sighand_struct(obj, ctx)) != 0)
++ return err;
++ }
++
++ cpt_close_section(ctx);
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_process.h b/kernel/cpt/cpt_process.h
+new file mode 100644
+index 0000000..b9f28af
+--- /dev/null
++++ b/kernel/cpt/cpt_process.h
+@@ -0,0 +1,13 @@
++int cpt_collect_signals(cpt_context_t *);
++int cpt_dump_signal(struct cpt_context *);
++int cpt_dump_sighand(struct cpt_context *);
++int cpt_dump_tasks(struct cpt_context *);
++
++int rst_signal_complete(struct cpt_task_image *ti, int *exiting, struct cpt_context *ctx);
++__u32 rst_signal_flag(struct cpt_task_image *ti, struct cpt_context *ctx);
++
++int rst_restore_process(struct cpt_context *ctx);
++int rst_process_linkage(struct cpt_context *ctx);
++
++int check_task_state(struct task_struct *tsk, struct cpt_context *ctx);
++struct pid *alloc_vpid_safe(pid_t vnr);
+diff --git a/kernel/cpt/cpt_socket.c b/kernel/cpt/cpt_socket.c
+new file mode 100644
+index 0000000..4878df1
+--- /dev/null
++++ b/kernel/cpt/cpt_socket.c
+@@ -0,0 +1,790 @@
++/*
++ *
++ * kernel/cpt/cpt_socket.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/un.h>
++#include <linux/tcp.h>
++#include <net/sock.h>
++#include <net/scm.h>
++#include <net/af_unix.h>
++#include <net/tcp.h>
++#include <net/netlink_sock.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_socket.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++
++static int dump_rqueue(int owner, struct sock *sk, struct cpt_context *ctx);
++
++
++/* Sockets are quite different of another kinds of files.
++ * There is one simplification: only one struct file can refer to a socket,
++ * so we could store information about socket directly in section FILES as
++ * a description of a file and append f.e. array of not-yet-accepted
++ * connections of listening socket as array of auxiliary data.
++ *
++ * Complications are:
++ * 1. TCP sockets can be orphans. We have to relocate orphans as well,
++ * so we have to create special section for orphans.
++ * 2. AF_UNIX sockets are distinguished objects: set of links between
++ * AF_UNIX sockets is quite arbitrary.
++ * A. Each socket can refers to many of files due to FD passing.
++ * B. Each socket except for connected ones can have in queue skbs
++ * sent by any of sockets.
++ *
++ * 2A is relatively easy: after our tasks are frozen we make an additional
++ * recursive pass throgh set of collected files and get referenced to
++ * FD passed files. After end of recursion, all the files are treated
++ * in the same way. All they will be stored in section FILES.
++ *
++ * 2B. We have to resolve all those references at some point.
++ * It is the place where pipe-like approach to image fails.
++ *
++ * All this makes socket checkpointing quite chumbersome.
++ * Right now we collect all the sockets and assign some numeric index value
++ * to each of them. The socket section is separate and put after section FILES,
++ * so section FILES refers to sockets by index, section SOCKET refers to FILES
++ * as usual by position in image. All the refs inside socket section are
++ * by index. When restoring we read socket section, create objects to hold
++ * mappings index <-> pos. At the second pass we open sockets (simultaneosly
++ * with their pairs) and create FILE objects.
++ */
++
++
++/* ====== FD passing ====== */
++
++/* Almost nobody does FD passing via AF_UNIX sockets, nevertheless we
++ * have to implement this. A problem is that in general case we receive
++ * skbs from an unknown context, so new files can arrive to checkpointed
++ * set of processes even after they are stopped. Well, we are going just
++ * to ignore unknown fds while doing real checkpointing. It is fair because
++ * links outside checkpointed set are going to fail anyway.
++ *
++ * ATTN: the procedure is recursive. We linearize the recursion adding
++ * newly found files to the end of file list, so they will be analyzed
++ * in the same loop.
++ */
++
++static int collect_one_passedfd(struct file *file, cpt_context_t * ctx)
++{
++ struct inode *inode = file->f_dentry->d_inode;
++ struct socket *sock;
++ struct sock *sk;
++ struct sk_buff *skb;
++
++ if (!S_ISSOCK(inode->i_mode))
++ return -ENOTSOCK;
++
++ sock = &container_of(inode, struct socket_alloc, vfs_inode)->socket;
++
++ if (sock->ops->family != AF_UNIX)
++ return 0;
++
++ sk = sock->sk;
++
++ /* Subtle locking issue. skbs cannot be removed while
++ * we are scanning, because all the processes are stopped.
++ * They still can be added to tail of queue. Locking while
++ * we dereference skb->next is enough to resolve this.
++ * See above about collision with skbs added after we started
++ * checkpointing.
++ */
++
++ skb = skb_peek(&sk->sk_receive_queue);
++ while (skb && skb != (struct sk_buff*)&sk->sk_receive_queue) {
++ if (UNIXCB(skb).fp && skb->sk &&
++ (!sock_flag(skb->sk, SOCK_DEAD) || unix_peer(sk) == skb->sk)) {
++ struct scm_fp_list *fpl = UNIXCB(skb).fp;
++ int i;
++
++ for (i = fpl->count-1; i >= 0; i--) {
++ if (cpt_object_add(CPT_OBJ_FILE, fpl->fp[i], ctx) == NULL)
++ return -ENOMEM;
++ }
++ }
++
++ spin_lock_irq(&sk->sk_receive_queue.lock);
++ skb = skb->next;
++ spin_unlock_irq(&sk->sk_receive_queue.lock);
++ }
++
++ return 0;
++}
++
++int cpt_collect_passedfds(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++
++ if (S_ISSOCK(file->f_dentry->d_inode->i_mode)) {
++ int err;
++
++ if ((err = collect_one_passedfd(file, ctx)) < 0)
++ return err;
++ }
++ }
++
++ return 0;
++}
++
++/* ====== End of FD passing ====== */
++
++/* Must be called under bh_lock_sock() */
++
++void clear_backlog(struct sock *sk)
++{
++ struct sk_buff *skb = sk->sk_backlog.head;
++
++ sk->sk_backlog.head = sk->sk_backlog.tail = NULL;
++ while (skb) {
++ struct sk_buff *next = skb->next;
++
++ skb->next = NULL;
++ kfree_skb(skb);
++ skb = next;
++ }
++}
++
++void release_sock_nobacklog(struct sock *sk)
++{
++ spin_lock_bh(&(sk->sk_lock.slock));
++ clear_backlog(sk);
++ sk->sk_lock.owned = 0;
++ if (waitqueue_active(&(sk->sk_lock.wq)))
++ wake_up(&(sk->sk_lock.wq));
++ spin_unlock_bh(&(sk->sk_lock.slock));
++}
++
++int cpt_dump_skb(int type, int owner, struct sk_buff *skb,
++ struct cpt_context *ctx)
++{
++ struct cpt_skb_image *v = cpt_get_buf(ctx);
++ loff_t saved_obj;
++ struct timeval tmptv;
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_SKB;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_owner = owner;
++ v->cpt_queue = type;
++ skb_get_timestamp(skb, &tmptv);
++ v->cpt_stamp = cpt_timeval_export(&tmptv);
++ v->cpt_hspace = skb->data - skb->head;
++ v->cpt_tspace = skb->end - skb->tail;
++ v->cpt_h = skb_transport_header(skb) - skb->head;
++ v->cpt_nh = skb_network_header(skb) - skb->head;
++ v->cpt_mac = skb_mac_header(skb) - skb->head;
++ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(v->cpt_cb));
++ memcpy(v->cpt_cb, skb->cb, sizeof(v->cpt_cb));
++ if (sizeof(skb->cb) > sizeof(v->cpt_cb)) {
++ int i;
++ for (i=sizeof(v->cpt_cb); i<sizeof(skb->cb); i++) {
++ if (skb->cb[i]) {
++ wprintk_ctx("dirty skb cb");
++ break;
++ }
++ }
++ }
++ v->cpt_len = skb->len;
++ v->cpt_mac_len = skb->mac_len;
++ v->cpt_csum = skb->csum;
++ v->cpt_local_df = skb->local_df;
++ v->cpt_pkt_type = skb->pkt_type;
++ v->cpt_ip_summed = skb->ip_summed;
++ v->cpt_priority = skb->priority;
++ v->cpt_protocol = skb->protocol;
++ v->cpt_security = 0;
++ v->cpt_gso_segs = skb_shinfo(skb)->gso_segs;
++ v->cpt_gso_size = skb_shinfo(skb)->gso_size;
++ if (skb_shinfo(skb)->gso_type) {
++ eprintk_ctx("skb ufo is not supported\n");
++ return -EINVAL;
++ }
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ if (skb->len + (skb->data - skb->head) > 0) {
++ struct cpt_obj_bits ob;
++ loff_t saved_obj2;
++
++ cpt_push_object(&saved_obj2, ctx);
++ cpt_open_object(NULL, ctx);
++ ob.cpt_next = CPT_NULL;
++ ob.cpt_object = CPT_OBJ_BITS;
++ ob.cpt_hdrlen = sizeof(ob);
++ ob.cpt_content = CPT_CONTENT_DATA;
++ ob.cpt_size = skb->len + v->cpt_hspace;
++
++ ctx->write(&ob, sizeof(ob), ctx);
++
++ ctx->write(skb->head, (skb->data-skb->head) + (skb->len-skb->data_len), ctx);
++ if (skb->data_len) {
++ int offset = skb->len - skb->data_len;
++ while (offset < skb->len) {
++ int copy = skb->len - offset;
++ if (copy > PAGE_SIZE)
++ copy = PAGE_SIZE;
++ (void)cpt_get_buf(ctx);
++ if (skb_copy_bits(skb, offset, ctx->tmpbuf, copy))
++ BUG();
++ ctx->write(ctx->tmpbuf, copy, ctx);
++ __cpt_release_buf(ctx);
++ offset += copy;
++ }
++ }
++
++ ctx->align(ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj2, ctx);
++ }
++
++ if (skb->sk && skb->sk->sk_family == AF_UNIX) {
++ struct scm_fp_list *fpl = UNIXCB(skb).fp;
++
++ if (fpl) {
++ int i;
++
++ for (i = 0; i < fpl->count; i++) {
++ struct cpt_fd_image v;
++ cpt_object_t *obj;
++ loff_t saved_obj2;
++
++ obj = lookup_cpt_object(CPT_OBJ_FILE, fpl->fp[i], ctx);
++
++ if (!obj) {
++ eprintk_ctx("lost passed FD\n");
++ return -EINVAL;
++ }
++
++ cpt_push_object(&saved_obj2, ctx);
++ cpt_open_object(NULL, ctx);
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_FILEDESC;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_VOID;
++
++ v.cpt_fd = i;
++ v.cpt_file = obj->o_pos;
++ v.cpt_flags = 0;
++ ctx->write(&v, sizeof(v), ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj2, ctx);
++ }
++ }
++ }
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ return 0;
++}
++
++static int dump_rqueue(int idx, struct sock *sk, struct cpt_context *ctx)
++{
++ struct sk_buff *skb;
++ struct sock *sk_cache = NULL;
++
++ skb = skb_peek(&sk->sk_receive_queue);
++ while (skb && skb != (struct sk_buff*)&sk->sk_receive_queue) {
++ int err;
++
++ if (sk->sk_family == AF_UNIX) {
++ cpt_object_t *obj;
++ if (skb->sk != sk_cache) {
++ idx = -1;
++ sk_cache = NULL;
++ obj = lookup_cpt_object(CPT_OBJ_SOCKET, skb->sk, ctx);
++ if (obj) {
++ idx = obj->o_index;
++ sk_cache = skb->sk;
++ } else if (unix_peer(sk) != skb->sk)
++ goto next_skb;
++ }
++ }
++
++ err = cpt_dump_skb(CPT_SKB_RQ, idx, skb, ctx);
++ if (err)
++ return err;
++
++next_skb:
++ spin_lock_irq(&sk->sk_receive_queue.lock);
++ skb = skb->next;
++ spin_unlock_irq(&sk->sk_receive_queue.lock);
++ }
++ return 0;
++}
++
++static int dump_wqueue(int idx, struct sock *sk, struct cpt_context *ctx)
++{
++ struct sk_buff *skb;
++
++ skb = skb_peek(&sk->sk_write_queue);
++ while (skb && skb != (struct sk_buff*)&sk->sk_write_queue) {
++ int err = cpt_dump_skb(CPT_SKB_WQ, idx, skb, ctx);
++ if (err)
++ return err;
++
++ spin_lock_irq(&sk->sk_write_queue.lock);
++ skb = skb->next;
++ spin_unlock_irq(&sk->sk_write_queue.lock);
++ }
++ return 0;
++}
++
++void cpt_dump_sock_attr(struct sock *sk, cpt_context_t *ctx)
++{
++ loff_t saved_obj;
++ if (sk->sk_filter) {
++ struct cpt_obj_bits v;
++
++ cpt_push_object(&saved_obj, ctx);
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_SKFILTER;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_DATA;
++ v.cpt_size = sk->sk_filter->len*sizeof(struct sock_filter);
++
++ ctx->write(&v, sizeof(v), ctx);
++ ctx->write(sk->sk_filter->insns, v.cpt_size, ctx);
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ if (sk->sk_family == AF_INET || sk->sk_family == AF_INET6) {
++ cpt_push_object(&saved_obj, ctx);
++ cpt_dump_mcfilter(sk, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++}
++
++/* Dump socket content */
++
++int cpt_dump_socket(cpt_object_t *obj, struct sock *sk, int index, int parent, struct cpt_context *ctx)
++{
++ struct cpt_sock_image *v = cpt_get_buf(ctx);
++ struct socket *sock;
++ struct timeval tmptv;
++
++ cpt_open_object(obj, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_SOCKET;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_file = CPT_NULL;
++ sock = sk->sk_socket;
++ if (sock && sock->file) {
++ cpt_object_t *tobj;
++ tobj = lookup_cpt_object(CPT_OBJ_FILE, sock->file, ctx);
++ if (tobj)
++ v->cpt_file = tobj->o_pos;
++ }
++ v->cpt_index = index;
++ v->cpt_parent = parent;
++
++ if (sk->sk_family == AF_INET || sk->sk_family == AF_INET6) {
++ if (sock && !obj->o_lock) {
++ lockdep_off();
++ lock_sock(sk);
++ lockdep_on();
++ obj->o_lock = 1;
++ }
++ }
++
++ /* Some bits stored in inode */
++ v->cpt_ssflags = sock ? sock->flags : 0;
++ v->cpt_sstate = sock ? sock->state : 0;
++ v->cpt_passcred = sock ? test_bit(SOCK_PASSCRED, &sock->flags) : 0;
++
++ /* Common data */
++ v->cpt_family = sk->sk_family;
++ v->cpt_type = sk->sk_type;
++ v->cpt_state = sk->sk_state;
++ v->cpt_reuse = sk->sk_reuse;
++ v->cpt_zapped = sock_flag(sk, SOCK_ZAPPED);
++ v->cpt_shutdown = sk->sk_shutdown;
++ v->cpt_userlocks = sk->sk_userlocks;
++ v->cpt_no_check = sk->sk_no_check;
++ v->cpt_zapped = sock_flag(sk, SOCK_DBG);
++ v->cpt_rcvtstamp = sock_flag(sk, SOCK_RCVTSTAMP);
++ v->cpt_localroute = sock_flag(sk, SOCK_LOCALROUTE);
++ v->cpt_protocol = sk->sk_protocol;
++ v->cpt_err = sk->sk_err;
++ v->cpt_err_soft = sk->sk_err_soft;
++ v->cpt_max_ack_backlog = sk->sk_max_ack_backlog;
++ v->cpt_priority = sk->sk_priority;
++ v->cpt_rcvlowat = sk->sk_rcvlowat;
++ v->cpt_rcvtimeo = CPT_NULL;
++ if (sk->sk_rcvtimeo != MAX_SCHEDULE_TIMEOUT)
++ v->cpt_rcvtimeo = sk->sk_rcvtimeo > INT_MAX ? INT_MAX : sk->sk_rcvtimeo;
++ v->cpt_sndtimeo = CPT_NULL;
++ if (sk->sk_sndtimeo != MAX_SCHEDULE_TIMEOUT)
++ v->cpt_sndtimeo = sk->sk_sndtimeo > INT_MAX ? INT_MAX : sk->sk_sndtimeo;
++ v->cpt_rcvbuf = sk->sk_rcvbuf;
++ v->cpt_sndbuf = sk->sk_sndbuf;
++ v->cpt_bound_dev_if = sk->sk_bound_dev_if;
++ v->cpt_flags = sk->sk_flags;
++ v->cpt_lingertime = CPT_NULL;
++ if (sk->sk_lingertime != MAX_SCHEDULE_TIMEOUT)
++ v->cpt_lingertime = sk->sk_lingertime > INT_MAX ? INT_MAX : sk->sk_lingertime;
++ v->cpt_peer_pid = sk->sk_peercred.pid;
++ v->cpt_peer_uid = sk->sk_peercred.uid;
++ v->cpt_peer_gid = sk->sk_peercred.gid;
++ tmptv = ktime_to_timeval(sk->sk_stamp);
++ v->cpt_stamp = cpt_timeval_export(&tmptv);
++
++ v->cpt_peer = -1;
++ v->cpt_socketpair = 0;
++ v->cpt_deleted = 0;
++
++ v->cpt_laddrlen = 0;
++ if (sock) {
++ int alen = sizeof(v->cpt_laddr);
++ int err = sock->ops->getname(sock, (struct sockaddr*)&v->cpt_laddr, &alen, 0);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++ v->cpt_laddrlen = alen;
++ }
++ v->cpt_raddrlen = 0;
++ if (sock) {
++ int alen = sizeof(v->cpt_raddr);
++ int err = sock->ops->getname(sock, (struct sockaddr*)&v->cpt_raddr, &alen, 2);
++ if (!err)
++ v->cpt_raddrlen = alen;
++ }
++
++ if (sk->sk_family == AF_UNIX) {
++ if (unix_sk(sk)->dentry) {
++ struct dentry *d = unix_sk(sk)->dentry;
++ v->cpt_deleted = !IS_ROOT(d) && d_unhashed(d);
++ if (!v->cpt_deleted) {
++ int err = 0;
++ char *path;
++ struct path p;
++ unsigned long pg = __get_free_page(GFP_KERNEL);
++
++ if (!pg) {
++ cpt_release_buf(ctx);
++ return -ENOMEM;
++ }
++
++ p.dentry = d;
++ p.mnt = unix_sk(sk)->mnt;
++ path = d_path(&p, (char *)pg, PAGE_SIZE);
++
++ if (!IS_ERR(path)) {
++ int len = strlen(path);
++ if (len < 126) {
++ strcpy(((char*)v->cpt_laddr)+2, path);
++ v->cpt_laddrlen = len + 2;
++ } else {
++ wprintk_ctx("af_unix path is too long: %s (%s)\n", path, ((char*)v->cpt_laddr)+2);
++ }
++ err = cpt_verify_overmount(path, d, unix_sk(sk)->mnt, ctx);
++ } else {
++ eprintk_ctx("cannot get path of an af_unix socket\n");
++ err = PTR_ERR(path);
++ }
++ free_page(pg);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++ }
++ }
++
++ /* If the socket is connected, find its peer. If peer is not
++ * in our table, the socket is connected to external process
++ * and we consider it disconnected.
++ */
++ if (unix_peer(sk)) {
++ cpt_object_t *pobj;
++ pobj = lookup_cpt_object(CPT_OBJ_SOCKET, unix_peer(sk), ctx);
++ if (pobj)
++ v->cpt_peer = pobj->o_index;
++ else
++ v->cpt_shutdown = SHUTDOWN_MASK;
++
++ if (unix_peer(unix_peer(sk)) == sk)
++ v->cpt_socketpair = 1;
++ }
++
++ /* If the socket shares address with another socket it is
++ * child of some listening socket. Find and record it. */
++ if (unix_sk(sk)->addr &&
++ atomic_read(&unix_sk(sk)->addr->refcnt) > 1 &&
++ sk->sk_state != TCP_LISTEN) {
++ cpt_object_t *pobj;
++ for_each_object(pobj, CPT_OBJ_SOCKET) {
++ struct sock *psk = pobj->o_obj;
++ if (psk->sk_family == AF_UNIX &&
++ psk->sk_state == TCP_LISTEN &&
++ unix_sk(psk)->addr == unix_sk(sk)->addr) {
++ v->cpt_parent = pobj->o_index;
++ break;
++ }
++ }
++ }
++ }
++
++ if (sk->sk_family == AF_INET || sk->sk_family == AF_INET6)
++ cpt_dump_socket_in(v, sk, ctx);
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ cpt_dump_sock_attr(sk, ctx);
++
++ dump_rqueue(index, sk, ctx);
++ if (sk->sk_family == AF_INET || sk->sk_family == AF_INET6) {
++ dump_wqueue(index, sk, ctx);
++ cpt_dump_ofo_queue(index, sk, ctx);
++ }
++
++ if ((sk->sk_family == AF_INET || sk->sk_family == AF_INET6)
++ && sk->sk_state == TCP_LISTEN)
++ cpt_dump_synwait_queue(sk, index, ctx);
++
++ cpt_close_object(ctx);
++
++ if ((sk->sk_family == AF_INET || sk->sk_family == AF_INET6)
++ && sk->sk_state == TCP_LISTEN)
++ cpt_dump_accept_queue(sk, index, ctx);
++
++ return 0;
++}
++
++int cpt_dump_orphaned_sockets(struct cpt_context *ctx)
++{
++ int i;
++
++ cpt_open_section(ctx, CPT_SECT_ORPHANS);
++
++ for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
++ struct sock *sk;
++ struct hlist_node *node;
++ rwlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, i);
++retry:
++ read_lock_bh(lock);
++ sk_for_each(sk, node, &tcp_hashinfo.ehash[i].chain) {
++
++ if (sk->owner_env != get_exec_env())
++ continue;
++ if (sk->sk_socket)
++ continue;
++ if (!sock_flag(sk, SOCK_DEAD))
++ continue;
++ if (lookup_cpt_object(CPT_OBJ_SOCKET, sk, ctx))
++ continue;
++ sock_hold(sk);
++ read_unlock_bh(lock);
++
++ local_bh_disable();
++ bh_lock_sock(sk);
++ if (sock_owned_by_user(sk))
++ eprintk_ctx("BUG: sk locked by whom?\n");
++ sk->sk_lock.owned = 1;
++ bh_unlock_sock(sk);
++ local_bh_enable();
++
++ cpt_dump_socket(NULL, sk, -1, -1, ctx);
++
++ local_bh_disable();
++ bh_lock_sock(sk);
++ sk->sk_lock.owned = 0;
++ clear_backlog(sk);
++ tcp_done(sk);
++ bh_unlock_sock(sk);
++ local_bh_enable();
++ sock_put(sk);
++
++ goto retry;
++ }
++ read_unlock_bh(lock);
++ }
++ cpt_close_section(ctx);
++ return 0;
++}
++
++static int can_dump(struct sock *sk, cpt_context_t *ctx)
++{
++ switch (sk->sk_family) {
++ case AF_NETLINK:
++ if (((struct netlink_sock *)sk)->cb) {
++ eprintk_ctx("netlink socket has active callback\n");
++ return 0;
++ }
++ break;
++ }
++ return 1;
++}
++
++/* We are not going to block suspend when we have external AF_UNIX connections.
++ * But we cannot stop feed of new packets/connections to our environment
++ * from outside. Taking into account that it is intrincically unreliable,
++ * we collect some amount of data, but when checkpointing/restoring we
++ * are going to drop everything, which does not make sense: skbs sent
++ * by outside processes, connections from outside etc. etc.
++ */
++
++/* The first pass. When we see socket referenced by a file, we just
++ * add it to socket table */
++int cpt_collect_socket(struct file *file, cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++ struct socket *sock;
++ struct sock *sk;
++
++ if (!S_ISSOCK(file->f_dentry->d_inode->i_mode))
++ return -ENOTSOCK;
++ sock = &container_of(file->f_dentry->d_inode, struct socket_alloc, vfs_inode)->socket;
++ sk = sock->sk;
++ if (!can_dump(sk, ctx))
++ return -EAGAIN;
++ if ((obj = cpt_object_add(CPT_OBJ_SOCKET, sk, ctx)) == NULL)
++ return -ENOMEM;
++ obj->o_parent = file;
++
++ return 0;
++}
++
++/*
++ * We should end with table containing:
++ * * all sockets opened by our processes in the table.
++ * * all the sockets queued in listening queues on _our_ listening sockets,
++ * which are connected to our opened sockets.
++ */
++
++static int collect_one_unix_listening_sock(cpt_object_t *obj, cpt_context_t * ctx)
++{
++ struct sock *sk = obj->o_obj;
++ cpt_object_t *cobj;
++ struct sk_buff *skb;
++
++ skb = skb_peek(&sk->sk_receive_queue);
++ while (skb && skb != (struct sk_buff*)&sk->sk_receive_queue) {
++ struct sock *lsk = skb->sk;
++ if (unix_peer(lsk) &&
++ lookup_cpt_object(CPT_OBJ_SOCKET, unix_peer(lsk), ctx)) {
++ if ((cobj = cpt_object_add(CPT_OBJ_SOCKET, lsk, ctx)) == NULL)
++ return -ENOMEM;
++ cobj->o_parent = obj->o_parent;
++ }
++ spin_lock_irq(&sk->sk_receive_queue.lock);
++ skb = skb->next;
++ spin_unlock_irq(&sk->sk_receive_queue.lock);
++ }
++
++ return 0;
++}
++
++int cpt_index_sockets(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++ unsigned long index = 0;
++
++ /* Collect not-yet-accepted children of listening sockets. */
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct sock *sk = obj->o_obj;
++
++ if (sk->sk_state != TCP_LISTEN)
++ continue;
++
++ if (sk->sk_family == AF_UNIX)
++ collect_one_unix_listening_sock(obj, ctx);
++ }
++
++ /* Assign indices to all the sockets. */
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct sock *sk = obj->o_obj;
++ cpt_obj_setindex(obj, index++, ctx);
++
++ if (sk->sk_socket && sk->sk_socket->file) {
++ cpt_object_t *tobj;
++ tobj = lookup_cpt_object(CPT_OBJ_FILE, sk->sk_socket->file, ctx);
++ if (tobj)
++ cpt_obj_setindex(tobj, obj->o_index, ctx);
++ }
++ }
++
++ return 0;
++}
++
++void cpt_unlock_sockets(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++
++ lockdep_off();
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct sock *sk = obj->o_obj;
++ if (sk && obj->o_lock) {
++ if (sk->sk_socket)
++ release_sock(sk);
++ }
++ }
++ lockdep_on();
++}
++
++void cpt_kill_sockets(cpt_context_t * ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct sock *sk = obj->o_obj;
++ if (sk && obj->o_lock) {
++ struct ve_struct *old_env;
++ old_env = set_exec_env(sk->owner_env);
++ cpt_kill_socket(sk, ctx);
++ if (sk->sk_socket)
++ release_sock_nobacklog(sk);
++ set_exec_env(old_env);
++ }
++ }
++}
++
++__u32 cpt_socket_fasync(struct file *file, struct cpt_context *ctx)
++{
++ struct fasync_struct *fa;
++ struct inode *inode = file->f_dentry->d_inode;
++ struct socket *sock;
++
++ sock = &container_of(inode, struct socket_alloc, vfs_inode)->socket;
++
++ for (fa = sock->fasync_list; fa; fa = fa->fa_next) {
++ if (fa->fa_file == file)
++ return fa->fa_fd;
++ }
++ return -1;
++}
+diff --git a/kernel/cpt/cpt_socket.h b/kernel/cpt/cpt_socket.h
+new file mode 100644
+index 0000000..6489184
+--- /dev/null
++++ b/kernel/cpt/cpt_socket.h
+@@ -0,0 +1,33 @@
++struct sock;
++
++int cpt_collect_passedfds(cpt_context_t *);
++int cpt_index_sockets(cpt_context_t *);
++int cpt_collect_socket(struct file *, cpt_context_t *);
++int cpt_dump_socket(cpt_object_t *obj, struct sock *sk, int index, int parent, struct cpt_context *ctx);
++int cpt_dump_accept_queue(struct sock *sk, int index, struct cpt_context *ctx);
++int cpt_dump_synwait_queue(struct sock *sk, int index, struct cpt_context *ctx);
++int rst_sockets(struct cpt_context *ctx);
++int rst_sockets_complete(struct cpt_context *ctx);
++int cpt_dump_orphaned_sockets(struct cpt_context *ctx);
++
++int rst_sock_attr(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx);
++struct sk_buff * rst_skb(loff_t *pos_p, __u32 *owner, __u32 *queue, struct cpt_context *ctx);
++
++void cpt_unlock_sockets(cpt_context_t *);
++void cpt_kill_sockets(cpt_context_t *);
++
++
++int cpt_kill_socket(struct sock *, cpt_context_t *);
++int cpt_dump_socket_in(struct cpt_sock_image *, struct sock *, struct cpt_context*);
++int rst_socket_in(struct cpt_sock_image *si, loff_t pos, struct sock *, struct cpt_context *ctx);
++__u32 cpt_socket_fasync(struct file *file, struct cpt_context *ctx);
++int cpt_attach_accept(struct sock *lsk, struct sock *sk, cpt_context_t *);
++int rst_restore_synwait_queue(struct sock *sk, struct cpt_sock_image *si, loff_t pos, struct cpt_context *ctx);
++int cpt_dump_ofo_queue(int idx, struct sock *sk, struct cpt_context *ctx);
++int cpt_dump_skb(int type, int owner, struct sk_buff *skb, struct cpt_context *ctx);
++int cpt_dump_mcfilter(struct sock *sk, struct cpt_context *ctx);
++
++int rst_sk_mcfilter_in(struct sock *sk, struct cpt_sockmc_image *v,
++ loff_t pos, cpt_context_t *ctx);
++int rst_sk_mcfilter_in6(struct sock *sk, struct cpt_sockmc_image *v,
++ loff_t pos, cpt_context_t *ctx);
+diff --git a/kernel/cpt/cpt_socket_in.c b/kernel/cpt/cpt_socket_in.c
+new file mode 100644
+index 0000000..c02d459
+--- /dev/null
++++ b/kernel/cpt/cpt_socket_in.c
+@@ -0,0 +1,450 @@
++/*
++ *
++ * kernel/cpt/cpt_socket_in.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/tcp.h>
++#include <net/sock.h>
++#include <net/tcp.h>
++#include <net/if_inet6.h>
++#include <linux/igmp.h>
++#include <linux/ipv6.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_socket.h"
++#include "cpt_kernel.h"
++
++static inline __u32 jiffies_export(unsigned long tmo)
++{
++ __s32 delta = (long)(tmo - jiffies);
++ return delta;
++}
++
++static inline __u32 tcp_jiffies_export(__u32 tmo)
++{
++ __s32 delta = tmo - tcp_time_stamp;
++ return delta;
++}
++
++int cpt_dump_ofo_queue(int idx, struct sock *sk, struct cpt_context *ctx)
++{
++ struct sk_buff *skb;
++ struct tcp_sock *tp;
++
++ if (sk->sk_type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP)
++ return 0;
++
++ tp = tcp_sk(sk);
++
++ skb = skb_peek(&tp->out_of_order_queue);
++ while (skb && skb != (struct sk_buff*)&tp->out_of_order_queue) {
++ int err;
++
++ err = cpt_dump_skb(CPT_SKB_OFOQ, idx, skb, ctx);
++ if (err)
++ return err;
++
++ spin_lock_irq(&tp->out_of_order_queue.lock);
++ skb = skb->next;
++ spin_unlock_irq(&tp->out_of_order_queue.lock);
++ }
++ return 0;
++}
++
++static int cpt_dump_socket_tcp(struct cpt_sock_image *si, struct sock *sk,
++ struct cpt_context *ctx)
++{
++ struct tcp_sock *tp = tcp_sk(sk);
++
++ si->cpt_pred_flags = tp->pred_flags;
++ si->cpt_rcv_nxt = tp->rcv_nxt;
++ si->cpt_snd_nxt = tp->snd_nxt;
++ si->cpt_snd_una = tp->snd_una;
++ si->cpt_snd_sml = tp->snd_sml;
++ si->cpt_rcv_tstamp = tcp_jiffies_export(tp->rcv_tstamp);
++ si->cpt_lsndtime = tcp_jiffies_export(tp->lsndtime);
++ si->cpt_tcp_header_len = tp->tcp_header_len;
++ si->cpt_ack_pending = inet_csk(sk)->icsk_ack.pending;
++ si->cpt_quick = inet_csk(sk)->icsk_ack.quick;
++ si->cpt_pingpong = inet_csk(sk)->icsk_ack.pingpong;
++ si->cpt_blocked = inet_csk(sk)->icsk_ack.blocked;
++ si->cpt_ato = inet_csk(sk)->icsk_ack.ato;
++ si->cpt_ack_timeout = jiffies_export(inet_csk(sk)->icsk_ack.timeout);
++ si->cpt_lrcvtime = tcp_jiffies_export(inet_csk(sk)->icsk_ack.lrcvtime);
++ si->cpt_last_seg_size = inet_csk(sk)->icsk_ack.last_seg_size;
++ si->cpt_rcv_mss = inet_csk(sk)->icsk_ack.rcv_mss;
++ si->cpt_snd_wl1 = tp->snd_wl1;
++ si->cpt_snd_wnd = tp->snd_wnd;
++ si->cpt_max_window = tp->max_window;
++ si->cpt_pmtu_cookie = inet_csk(sk)->icsk_pmtu_cookie;
++ si->cpt_mss_cache = tp->mss_cache;
++ si->cpt_mss_cache_std = tp->mss_cache; /* FIXMW was tp->mss_cache_std */
++ si->cpt_mss_clamp = tp->rx_opt.mss_clamp;
++ si->cpt_ext_header_len = inet_csk(sk)->icsk_ext_hdr_len;
++ si->cpt_ext2_header_len = 0;
++ si->cpt_ca_state = inet_csk(sk)->icsk_ca_state;
++ si->cpt_retransmits = inet_csk(sk)->icsk_retransmits;
++ si->cpt_reordering = tp->reordering;
++ si->cpt_frto_counter = tp->frto_counter;
++ si->cpt_frto_highmark = tp->frto_highmark;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)
++ // // si->cpt_adv_cong = tp->adv_cong;
++#endif
++ si->cpt_defer_accept = inet_csk(sk)->icsk_accept_queue.rskq_defer_accept;
++ si->cpt_backoff = inet_csk(sk)->icsk_backoff;
++ si->cpt_srtt = tp->srtt;
++ si->cpt_mdev = tp->mdev;
++ si->cpt_mdev_max = tp->mdev_max;
++ si->cpt_rttvar = tp->rttvar;
++ si->cpt_rtt_seq = tp->rtt_seq;
++ si->cpt_rto = inet_csk(sk)->icsk_rto;
++ si->cpt_packets_out = tp->packets_out;
++ si->cpt_left_out = tp->sacked_out + tp->lost_out;
++ si->cpt_retrans_out = tp->retrans_out;
++ si->cpt_lost_out = tp->lost_out;
++ si->cpt_sacked_out = tp->sacked_out;
++ si->cpt_fackets_out = tp->fackets_out;
++ si->cpt_snd_ssthresh = tp->snd_ssthresh;
++ si->cpt_snd_cwnd = tp->snd_cwnd;
++ si->cpt_snd_cwnd_cnt = tp->snd_cwnd_cnt;
++ si->cpt_snd_cwnd_clamp = tp->snd_cwnd_clamp;
++ si->cpt_snd_cwnd_used = tp->snd_cwnd_used;
++ si->cpt_snd_cwnd_stamp = tcp_jiffies_export(tp->snd_cwnd_stamp);
++ si->cpt_timeout = jiffies_export(inet_csk(sk)->icsk_timeout);
++ si->cpt_ka_timeout = 0;
++ si->cpt_rcv_wnd = tp->rcv_wnd;
++ si->cpt_rcv_wup = tp->rcv_wup;
++ si->cpt_write_seq = tp->write_seq;
++ si->cpt_pushed_seq = tp->pushed_seq;
++ si->cpt_copied_seq = tp->copied_seq;
++ si->cpt_tstamp_ok = tp->rx_opt.tstamp_ok;
++ si->cpt_wscale_ok = tp->rx_opt.wscale_ok;
++ si->cpt_sack_ok = tp->rx_opt.sack_ok;
++ si->cpt_saw_tstamp = tp->rx_opt.saw_tstamp;
++ si->cpt_snd_wscale = tp->rx_opt.snd_wscale;
++ si->cpt_rcv_wscale = tp->rx_opt.rcv_wscale;
++ si->cpt_nonagle = tp->nonagle;
++ si->cpt_keepalive_probes = tp->keepalive_probes;
++ si->cpt_rcv_tsval = tp->rx_opt.rcv_tsval;
++ si->cpt_rcv_tsecr = tp->rx_opt.rcv_tsecr;
++ si->cpt_ts_recent = tp->rx_opt.ts_recent;
++ si->cpt_ts_recent_stamp = tp->rx_opt.ts_recent_stamp;
++ si->cpt_user_mss = tp->rx_opt.user_mss;
++ si->cpt_dsack = tp->rx_opt.dsack;
++ si->cpt_eff_sacks = tp->rx_opt.eff_sacks;
++ si->cpt_sack_array[0] = tp->duplicate_sack[0].start_seq;
++ si->cpt_sack_array[1] = tp->duplicate_sack[0].end_seq;
++ si->cpt_sack_array[2] = tp->selective_acks[0].start_seq;
++ si->cpt_sack_array[3] = tp->selective_acks[0].end_seq;
++ si->cpt_sack_array[4] = tp->selective_acks[1].start_seq;
++ si->cpt_sack_array[5] = tp->selective_acks[1].end_seq;
++ si->cpt_sack_array[6] = tp->selective_acks[2].start_seq;
++ si->cpt_sack_array[7] = tp->selective_acks[2].end_seq;
++ si->cpt_sack_array[8] = tp->selective_acks[3].start_seq;
++ si->cpt_sack_array[9] = tp->selective_acks[3].end_seq;
++ si->cpt_window_clamp = tp->window_clamp;
++ si->cpt_rcv_ssthresh = tp->rcv_ssthresh;
++ si->cpt_probes_out = inet_csk(sk)->icsk_probes_out;
++ si->cpt_num_sacks = tp->rx_opt.num_sacks;
++ si->cpt_advmss = tp->advmss;
++ si->cpt_syn_retries = inet_csk(sk)->icsk_syn_retries;
++ si->cpt_ecn_flags = tp->ecn_flags;
++ si->cpt_prior_ssthresh = tp->prior_ssthresh;
++ si->cpt_high_seq = tp->high_seq;
++ si->cpt_retrans_stamp = tp->retrans_stamp;
++ si->cpt_undo_marker = tp->undo_marker;
++ si->cpt_undo_retrans = tp->undo_retrans;
++ si->cpt_urg_seq = tp->urg_seq;
++ si->cpt_urg_data = tp->urg_data;
++ si->cpt_pending = inet_csk(sk)->icsk_pending;
++ si->cpt_urg_mode = tp->urg_mode;
++ si->cpt_snd_up = tp->snd_up;
++ si->cpt_keepalive_time = tp->keepalive_time;
++ si->cpt_keepalive_intvl = tp->keepalive_intvl;
++ si->cpt_linger2 = tp->linger2;
++
++ if (sk->sk_state != TCP_LISTEN &&
++ sk->sk_state != TCP_CLOSE &&
++ sock_flag(sk, SOCK_KEEPOPEN)) {
++ si->cpt_ka_timeout = jiffies_export(sk->sk_timer.expires);
++ }
++
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ {
++ extern struct inet_connection_sock_af_ops ipv6_mapped;
++ if (sk->sk_family == AF_INET6 &&
++ inet_csk(sk)->icsk_af_ops == &ipv6_mapped)
++ si->cpt_mapped = 1;
++ }
++#endif
++
++ return 0;
++}
++
++
++int cpt_dump_socket_in(struct cpt_sock_image *si, struct sock *sk,
++ struct cpt_context *ctx)
++{
++ struct inet_sock *inet = inet_sk(sk);
++ struct ipv6_pinfo *np = inet6_sk(sk);
++
++ if (sk->sk_family == AF_INET) {
++ struct sockaddr_in *sin = ((struct sockaddr_in*)si->cpt_laddr);
++ sin->sin_family = AF_INET;
++ sin->sin_port = inet->sport;
++ sin->sin_addr.s_addr = inet->rcv_saddr;
++ si->cpt_laddrlen = sizeof(*sin);
++ } else if (sk->sk_family == AF_INET6) {
++ struct sockaddr_in6 *sin6 = ((struct sockaddr_in6*)si->cpt_laddr);
++ sin6->sin6_family = AF_INET6;
++ sin6->sin6_port = inet->sport;
++ memcpy(&sin6->sin6_addr, &np->rcv_saddr, 16);
++ si->cpt_laddrlen = sizeof(*sin6);
++ }
++ if (!inet->num)
++ si->cpt_laddrlen = 0;
++
++ si->cpt_daddr = inet->daddr;
++ si->cpt_dport = inet->dport;
++ si->cpt_saddr = inet->saddr;
++ si->cpt_rcv_saddr = inet->rcv_saddr;
++ si->cpt_sport = inet->sport;
++ si->cpt_uc_ttl = inet->uc_ttl;
++ si->cpt_tos = inet->tos;
++ si->cpt_cmsg_flags = inet->cmsg_flags;
++ si->cpt_mc_index = inet->mc_index;
++ si->cpt_mc_addr = inet->mc_addr;
++ si->cpt_hdrincl = inet->hdrincl;
++ si->cpt_mc_ttl = inet->mc_ttl;
++ si->cpt_mc_loop = inet->mc_loop;
++ si->cpt_pmtudisc = inet->pmtudisc;
++ si->cpt_recverr = inet->recverr;
++ si->cpt_freebind = inet->freebind;
++ si->cpt_idcounter = inet->id;
++
++ si->cpt_cork_flags = inet->cork.flags;
++ si->cpt_cork_fragsize = 0;
++ si->cpt_cork_length = inet->cork.length;
++ si->cpt_cork_addr = inet->cork.addr;
++ si->cpt_cork_saddr = inet->cork.fl.fl4_src;
++ si->cpt_cork_daddr = inet->cork.fl.fl4_dst;
++ si->cpt_cork_oif = inet->cork.fl.oif;
++ if (inet->cork.dst) {
++ struct rtable *rt = (struct rtable *)inet->cork.dst;
++ si->cpt_cork_fragsize = inet->cork.fragsize;
++ si->cpt_cork_saddr = rt->fl.fl4_src;
++ si->cpt_cork_daddr = rt->fl.fl4_dst;
++ si->cpt_cork_oif = rt->fl.oif;
++ }
++
++ if (sk->sk_type == SOCK_DGRAM && sk->sk_protocol == IPPROTO_UDP) {
++ struct udp_sock *up = udp_sk(sk);
++ si->cpt_udp_pending = up->pending;
++ si->cpt_udp_corkflag = up->corkflag;
++ si->cpt_udp_encap = up->encap_type;
++ si->cpt_udp_len = up->len;
++ }
++
++ if (sk->sk_family == AF_INET6) {
++ memcpy(si->cpt_saddr6, &np->saddr, 16);
++ memcpy(si->cpt_rcv_saddr6, &np->rcv_saddr, 16);
++ memcpy(si->cpt_daddr6, &np->daddr, 16);
++ si->cpt_flow_label6 = np->flow_label;
++ si->cpt_frag_size6 = np->frag_size;
++ si->cpt_hop_limit6 = np->hop_limit;
++ si->cpt_mcast_hops6 = np->mcast_hops;
++ si->cpt_mcast_oif6 = np->mcast_oif;
++ si->cpt_rxopt6 = np->rxopt.all;
++ si->cpt_mc_loop6 = np->mc_loop;
++ si->cpt_recverr6 = np->recverr;
++ si->cpt_sndflow6 = np->sndflow;
++ si->cpt_pmtudisc6 = np->pmtudisc;
++ si->cpt_ipv6only6 = np->ipv6only;
++ si->cpt_mapped = 0;
++ }
++
++ if (sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP)
++ cpt_dump_socket_tcp(si, sk, ctx);
++
++ return 0;
++}
++
++int cpt_dump_accept_queue(struct sock *sk, int index, struct cpt_context *ctx)
++{
++ struct request_sock *req;
++
++ for (req=inet_csk(sk)->icsk_accept_queue.rskq_accept_head; req; req=req->dl_next)
++ cpt_dump_socket(NULL, req->sk, -1, index, ctx);
++ return 0;
++}
++
++
++static int dump_openreq(struct request_sock *req, struct sock *sk, int index,
++ struct cpt_context *ctx)
++{
++ struct cpt_openreq_image *v = cpt_get_buf(ctx);
++
++ cpt_open_object(NULL, ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_OPENREQ;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ v->cpt_rcv_isn = tcp_rsk(req)->rcv_isn;
++ v->cpt_snt_isn = tcp_rsk(req)->snt_isn;
++ v->cpt_rmt_port = inet_rsk(req)->rmt_port;
++ v->cpt_mss = req->mss;
++ // // v->cpt_family = (req->class == &or_ipv4 ? AF_INET : AF_INET6);
++ v->cpt_retrans = req->retrans;
++ v->cpt_snd_wscale = inet_rsk(req)->snd_wscale;
++ v->cpt_rcv_wscale = inet_rsk(req)->rcv_wscale;
++ v->cpt_tstamp_ok = inet_rsk(req)->tstamp_ok;
++ v->cpt_sack_ok = inet_rsk(req)->sack_ok;
++ v->cpt_wscale_ok = inet_rsk(req)->wscale_ok;
++ v->cpt_ecn_ok = inet_rsk(req)->ecn_ok;
++ v->cpt_acked = inet_rsk(req)->acked;
++ v->cpt_window_clamp = req->window_clamp;
++ v->cpt_rcv_wnd = req->rcv_wnd;
++ v->cpt_ts_recent = req->ts_recent;
++ v->cpt_expires = jiffies_export(req->expires);
++
++ if (v->cpt_family == AF_INET) {
++ memcpy(v->cpt_loc_addr, &inet_rsk(req)->loc_addr, 4);
++ memcpy(v->cpt_rmt_addr, &inet_rsk(req)->rmt_addr, 4);
++ } else {
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ memcpy(v->cpt_loc_addr, &inet6_rsk(req)->loc_addr, 16);
++ memcpy(v->cpt_rmt_addr, &inet6_rsk(req)->rmt_addr, 16);
++ v->cpt_iif = inet6_rsk(req)->iif;
++#endif
++ }
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ cpt_close_object(ctx);
++ return 0;
++}
++
++int cpt_dump_synwait_queue(struct sock *sk, int index, struct cpt_context *ctx)
++{
++ struct inet_connection_sock *icsk;
++ struct listen_sock *lopt;
++ struct request_sock *req;
++ int nr_entries;
++ int i;
++
++ icsk = inet_csk(sk);
++ lopt = icsk->icsk_accept_queue.listen_opt;
++ nr_entries = icsk->icsk_accept_queue.listen_opt->nr_table_entries;
++
++ for (i=0; i < nr_entries; i++) {
++ for (req=lopt->syn_table[i]; req; req=req->dl_next) {
++ loff_t saved_obj;
++ cpt_push_object(&saved_obj, ctx);
++ dump_openreq(req, sk, index, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ }
++ }
++ return 0;
++}
++
++
++int cpt_kill_socket(struct sock *sk, cpt_context_t * ctx)
++{
++ if (sk->sk_state != TCP_CLOSE &&
++ (sk->sk_family == AF_INET || sk->sk_family == AF_INET6) &&
++ sk->sk_protocol == IPPROTO_TCP) {
++ if (sk->sk_state != TCP_LISTEN)
++ tcp_set_state(sk, TCP_CLOSE);
++ else
++ sk->sk_prot->disconnect(sk, 0);
++ }
++ return 0;
++}
++
++int cpt_dump_mcfilter(struct sock *sk, cpt_context_t *ctx)
++{
++ struct inet_sock *inet = inet_sk(sk);
++ struct ip_mc_socklist *iml;
++
++ for (iml = inet->mc_list; iml; iml = iml->next) {
++ struct cpt_sockmc_image smi;
++ int scnt = 0;
++ int i;
++
++ if (iml->sflist)
++ scnt = iml->sflist->sl_count*16;
++
++ smi.cpt_next = sizeof(smi) + scnt;
++ smi.cpt_object = CPT_OBJ_SOCK_MCADDR;
++ smi.cpt_hdrlen = sizeof(smi);
++ smi.cpt_content = CPT_CONTENT_DATA;
++
++ smi.cpt_family = AF_INET;
++ smi.cpt_mode = iml->sfmode;
++ smi.cpt_ifindex = iml->multi.imr_ifindex;
++ memset(&smi.cpt_mcaddr, 0, sizeof(smi.cpt_mcaddr));
++ smi.cpt_mcaddr[0] = iml->multi.imr_multiaddr.s_addr;
++
++ ctx->write(&smi, sizeof(smi), ctx);
++
++ for (i = 0; i < scnt; i++) {
++ u32 addr[4];
++ memset(&addr, 0, sizeof(addr));
++ addr[0] = iml->sflist->sl_addr[i];
++ ctx->write(&addr, sizeof(addr), ctx);
++ }
++ }
++
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ if (sk->sk_family == AF_INET6) {
++ struct ipv6_mc_socklist *mcl;
++ struct ipv6_pinfo *np = inet6_sk(sk);
++
++ for (mcl = np->ipv6_mc_list; mcl; mcl = mcl->next) {
++ struct cpt_sockmc_image smi;
++ int scnt = 0;
++ int i;
++
++ if (mcl->sflist)
++ scnt = mcl->sflist->sl_count*16;
++
++ smi.cpt_next = sizeof(smi) + scnt;
++ smi.cpt_object = CPT_OBJ_SOCK_MCADDR;
++ smi.cpt_hdrlen = sizeof(smi);
++ smi.cpt_content = CPT_CONTENT_DATA;
++
++ smi.cpt_family = AF_INET6;
++ smi.cpt_mode = mcl->sfmode;
++ smi.cpt_ifindex = mcl->ifindex;
++ memcpy(&smi.cpt_mcaddr, &mcl->addr, sizeof(smi.cpt_mcaddr));
++
++ ctx->write(&smi, sizeof(smi), ctx);
++ for (i = 0; i < scnt; i++)
++ ctx->write(&mcl->sflist->sl_addr[i], 16, ctx);
++ }
++ }
++#endif
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_syscalls.h b/kernel/cpt/cpt_syscalls.h
+new file mode 100644
+index 0000000..ba69cb5
+--- /dev/null
++++ b/kernel/cpt/cpt_syscalls.h
+@@ -0,0 +1,101 @@
++#include <linux/unistd.h>
++#include <linux/syscalls.h>
++#include <linux/fs.h>
++#include <asm/uaccess.h>
++
++#define WRAP(c, args) return sys_##c args
++#define WRAP2(c, args) int err; mm_segment_t oldfs; \
++ oldfs = get_fs(); set_fs(KERNEL_DS); \
++ err = sys_##c args ;\
++ set_fs(oldfs); \
++ return err
++
++static inline int sc_close(int fd)
++{
++ WRAP(close, (fd));
++}
++
++static inline int sc_dup2(int fd1, int fd2)
++{
++ WRAP(dup2, (fd1, fd2));
++}
++
++static inline int sc_unlink(char *name)
++{
++ WRAP2(unlink, (name));
++}
++
++static inline int sc_pipe(int *pfd)
++{
++ return do_pipe(pfd);
++}
++
++static inline int sc_mknod(char *name, int mode, int dev)
++{
++ WRAP2(mknod, (name, mode, dev));
++}
++
++static inline int sc_chmod(char *name, int mode)
++{
++ WRAP2(mkdir, (name, mode));
++}
++
++static inline int sc_chown(char *name, int uid, int gid)
++{
++ WRAP2(chown, (name, uid, gid));
++}
++
++static inline int sc_mkdir(char *name, int mode)
++{
++ WRAP2(mkdir, (name, mode));
++}
++
++static inline int sc_rmdir(char *name)
++{
++ WRAP2(rmdir, (name));
++}
++
++static inline int sc_mount(char *mntdev, char *mntpnt, char *type, unsigned long flags)
++{
++ WRAP2(mount, (mntdev ? : "none", mntpnt, type, flags, NULL));
++}
++
++static inline int sc_mprotect(unsigned long start, size_t len,
++ unsigned long prot)
++{
++ WRAP(mprotect, (start, len, prot));
++}
++
++static inline int sc_mlock(unsigned long start, size_t len)
++{
++ WRAP(mlock, (start, len));
++}
++
++static inline int sc_munlock(unsigned long start, size_t len)
++{
++ WRAP(munlock, (start, len));
++}
++
++static inline int sc_remap_file_pages(unsigned long start, size_t len,
++ unsigned long prot, unsigned long pgoff,
++ unsigned long flags)
++{
++ WRAP(remap_file_pages, (start, len, prot, pgoff, flags));
++}
++
++static inline int sc_waitx(int pid, int opt, int *stat_addr)
++{
++ WRAP(wait4, (pid, stat_addr, opt, NULL));
++}
++
++static inline int sc_flock(int fd, int flags)
++{
++ WRAP(flock, (fd, flags));
++}
++
++static inline int sc_open(char* path, int flags, int mode)
++{
++ WRAP(open, (path, flags, mode));
++}
++
++extern int sc_execve(char *cms, char **argv, char **env);
+diff --git a/kernel/cpt/cpt_sysvipc.c b/kernel/cpt/cpt_sysvipc.c
+new file mode 100644
+index 0000000..820f1ac
+--- /dev/null
++++ b/kernel/cpt/cpt_sysvipc.c
+@@ -0,0 +1,403 @@
++/*
++ *
++ * kernel/cpt/cpt_sysvipc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/shm.h>
++#include <linux/sem.h>
++#include <linux/msg.h>
++#include <asm/uaccess.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_kernel.h"
++
++struct _warg {
++ struct file *file;
++ struct cpt_sysvshm_image *v;
++};
++
++static int dump_one_shm(struct shmid_kernel *shp, void *arg)
++{
++ struct _warg *warg = arg;
++ struct cpt_sysvshm_image *v = (struct cpt_sysvshm_image *)warg->v;
++
++ if (shp->shm_file != warg->file)
++ return 0;
++
++ v->cpt_key = shp->shm_perm.key;
++ v->cpt_uid = shp->shm_perm.uid;
++ v->cpt_gid = shp->shm_perm.gid;
++ v->cpt_cuid = shp->shm_perm.cuid;
++ v->cpt_cgid = shp->shm_perm.cgid;
++ v->cpt_mode = shp->shm_perm.mode;
++ v->cpt_seq = shp->shm_perm.seq;
++
++ v->cpt_id = shp->shm_perm.id;
++ v->cpt_segsz = shp->shm_segsz;
++ v->cpt_atime = shp->shm_atim;
++ v->cpt_ctime = shp->shm_ctim;
++ v->cpt_dtime = shp->shm_dtim;
++ v->cpt_creator = shp->shm_cprid;
++ v->cpt_last = shp->shm_lprid;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)
++ v->cpt_mlockuser = shp->mlock_user ? shp->mlock_user->uid : -1;
++#else
++ v->cpt_mlockuser = -1;
++#endif
++ return 1;
++}
++
++int cpt_dump_content_sysvshm(struct file *file, struct cpt_context *ctx)
++{
++ struct cpt_sysvshm_image *v = cpt_get_buf(ctx);
++ struct _warg warg;
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_SYSV_SHM;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ warg.file = file;
++ warg.v = v;
++ if (sysvipc_walk_shm(dump_one_shm, &warg) == 0) {
++ cpt_release_buf(ctx);
++ return -ESRCH;
++ }
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++ return 0;
++}
++
++
++int match_sem(int id, struct sem_array *sema, void *arg)
++{
++ if (id != (unsigned long)arg)
++ return 0;
++ return sema->sem_nsems + 1;
++}
++
++static int get_sem_nsem(int id, cpt_context_t *ctx)
++{
++ int res;
++ res = sysvipc_walk_sem(match_sem, (void*)(unsigned long)id);
++ if (res > 0)
++ return res - 1;
++ eprintk_ctx("get_sem_nsem: SYSV semaphore %d not found\n", id);
++ return -ESRCH;
++}
++
++static int dump_one_semundo(struct sem_undo *su, struct cpt_context *ctx)
++{
++ struct cpt_sysvsem_undo_image v;
++ loff_t saved_obj;
++
++ cpt_open_object(NULL, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_SYSVSEM_UNDO_REC;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_SEMUNDO;
++ v.cpt_id = su->semid;
++ v.cpt_nsem = get_sem_nsem(su->semid, ctx);
++ if ((int)v.cpt_nsem < 0)
++ return -ESRCH;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ ctx->write(su->semadj, v.cpt_nsem*sizeof(short), ctx);
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++ return 0;
++}
++
++struct sem_warg {
++ int last_id;
++ struct cpt_sysvsem_image *v;
++};
++
++static int dump_one_sem(int id, struct sem_array *sma, void *arg)
++{
++ struct sem_warg * warg = (struct sem_warg *)arg;
++ struct cpt_sysvsem_image *v = warg->v;
++ int i;
++
++ if (warg->last_id != -1) {
++ if ((id % IPCMNI) <= warg->last_id)
++ return 0;
++ }
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_SYSV_SEM;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_SEMARRAY;
++
++ v->cpt_key = sma->sem_perm.key;
++ v->cpt_uid = sma->sem_perm.uid;
++ v->cpt_gid = sma->sem_perm.gid;
++ v->cpt_cuid = sma->sem_perm.cuid;
++ v->cpt_cgid = sma->sem_perm.cgid;
++ v->cpt_mode = sma->sem_perm.mode;
++ v->cpt_seq = sma->sem_perm.seq;
++
++ v->cpt_id = id;
++ v->cpt_ctime = sma->sem_ctime;
++ v->cpt_otime = sma->sem_otime;
++
++ for (i=0; i<sma->sem_nsems; i++) {
++ struct {
++ __u32 semval;
++ __u32 sempid;
++ } *s = (void*)v + v->cpt_next;
++ if (v->cpt_next >= PAGE_SIZE - sizeof(*s))
++ return -EINVAL;
++ s->semval = sma->sem_base[i].semval;
++ s->sempid = sma->sem_base[i].sempid;
++ v->cpt_next += sizeof(*s);
++ }
++
++ warg->last_id = id % IPCMNI;
++ return 1;
++}
++
++
++int cpt_dump_sysvsem(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++ struct sem_warg warg;
++
++ /* Dumping semaphores is quite tricky because we cannot
++ * write to dump file under lock inside sysvipc_walk_sem().
++ */
++ cpt_open_section(ctx, CPT_SECT_SYSV_SEM);
++ warg.last_id = -1;
++ warg.v = cpt_get_buf(ctx);
++ for (;;) {
++ if (sysvipc_walk_sem(dump_one_sem, &warg) <= 0)
++ break;
++ ctx->write(warg.v, warg.v->cpt_next, ctx);
++ }
++ cpt_release_buf(ctx);
++ cpt_close_section(ctx);
++
++ cpt_open_section(ctx, CPT_SECT_SYSVSEM_UNDO);
++ for_each_object(obj, CPT_OBJ_SYSVSEM_UNDO) {
++ struct sem_undo_list *semu = obj->o_obj;
++ struct sem_undo *su;
++ struct cpt_object_hdr v;
++ loff_t saved_obj;
++
++ cpt_open_object(obj, ctx);
++
++ v.cpt_next = CPT_NULL;
++ v.cpt_object = CPT_OBJ_SYSVSEM_UNDO;
++ v.cpt_hdrlen = sizeof(v);
++ v.cpt_content = CPT_CONTENT_ARRAY;
++
++ ctx->write(&v, sizeof(v), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ list_for_each_entry(su, &semu->list_proc, list_proc) {
++ if (su->semid != -1) {
++ int err;
++ err = dump_one_semundo(su, ctx);
++ if (err < 0)
++ return err;
++ }
++ }
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++ }
++ cpt_close_section(ctx);
++ return 0;
++}
++
++struct msg_warg {
++ int last_id;
++ struct msg_queue *msq;
++ struct cpt_sysvmsg_image *v;
++};
++
++static int dump_one_msg(int id, struct msg_queue *msq, void *arg)
++{
++ struct msg_warg * warg = (struct msg_warg *)arg;
++ struct cpt_sysvmsg_image *v = warg->v;
++
++ if (warg->last_id != -1) {
++ if ((id % IPCMNI) <= warg->last_id)
++ return 0;
++ }
++
++ v->cpt_next = sizeof(*v);
++ v->cpt_object = CPT_OBJ_SYSVMSG;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_key = msq->q_perm.key;
++ v->cpt_uid = msq->q_perm.uid;
++ v->cpt_gid = msq->q_perm.gid;
++ v->cpt_cuid = msq->q_perm.cuid;
++ v->cpt_cgid = msq->q_perm.cgid;
++ v->cpt_mode = msq->q_perm.mode;
++ v->cpt_seq = msq->q_perm.seq;
++
++ v->cpt_id = id;
++ v->cpt_stime = msq->q_stime;
++ v->cpt_rtime = msq->q_rtime;
++ v->cpt_ctime = msq->q_ctime;
++ v->cpt_last_sender = msq->q_lspid;
++ v->cpt_last_receiver = msq->q_lrpid;
++ v->cpt_qbytes = msq->q_qbytes;
++
++ warg->msq = msq;
++ warg->last_id = id % IPCMNI;
++ return 1;
++}
++
++static int do_store(void * src, int len, int offset, void * data)
++{
++ cpt_context_t * ctx = data;
++ ctx->write(src, len, ctx);
++ return 0;
++}
++
++static void cpt_dump_one_sysvmsg(struct msg_msg *m, cpt_context_t * ctx)
++{
++ loff_t saved_obj;
++ struct cpt_sysvmsg_msg_image mv;
++
++ cpt_open_object(NULL, ctx);
++ mv.cpt_next = CPT_NULL;
++ mv.cpt_object = CPT_OBJ_SYSVMSG_MSG;
++ mv.cpt_hdrlen = sizeof(mv);
++ mv.cpt_content = CPT_CONTENT_DATA;
++
++ mv.cpt_type = m->m_type;
++ mv.cpt_size = m->m_ts;
++
++ ctx->write(&mv, sizeof(mv), ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ sysv_msg_store(m, do_store, m->m_ts, ctx);
++ cpt_pop_object(&saved_obj, ctx);
++ cpt_close_object(ctx);
++}
++
++int cpt_dump_sysvmsg(struct cpt_context *ctx)
++{
++ struct msg_warg warg;
++
++ /* Dumping msg queues is tricky because we cannot
++ * write to dump file under lock inside sysvipc_walk_msg().
++ *
++ * And even worse, we have to access msg list in an unserialized
++ * context. It is fragile. But VE is still frozen, remember?
++ */
++ cpt_open_section(ctx, CPT_SECT_SYSV_MSG);
++ warg.last_id = -1;
++ warg.v = cpt_get_buf(ctx);
++ for (;;) {
++ loff_t saved_obj;
++ struct msg_msg * m;
++
++ if (sysvipc_walk_msg(dump_one_msg, &warg) <= 0)
++ break;
++
++ cpt_open_object(NULL, ctx);
++
++ ctx->write(warg.v, warg.v->cpt_next, ctx);
++
++ cpt_push_object(&saved_obj, ctx);
++ list_for_each_entry(m, &warg.msq->q_messages, m_list) {
++ cpt_dump_one_sysvmsg(m, ctx);
++ }
++ cpt_pop_object(&saved_obj, ctx);
++
++ cpt_close_object(ctx);
++ }
++ cpt_release_buf(ctx);
++ cpt_close_section(ctx);
++ return 0;
++}
++
++static int cpt_collect_sysvsem_undo(cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ if (tsk->exit_state) {
++ /* ipc/sem.c forgets to clear tsk->sysvsem.undo_list
++ * on exit. Grrr... */
++ continue;
++ }
++ if (tsk->sysvsem.undo_list &&
++ cpt_object_add(CPT_OBJ_SYSVSEM_UNDO, tsk->sysvsem.undo_list, ctx) == NULL)
++ return -ENOMEM;
++ }
++
++ for_each_object(obj, CPT_OBJ_SYSVSEM_UNDO) {
++ struct sem_undo_list *semu = obj->o_obj;
++
++ if (atomic_read(&semu->refcnt) != obj->o_count) {
++ eprintk_ctx("sem_undo_list is referenced outside %d %d\n", obj->o_count, atomic_read(&semu->refcnt));
++ return -EBUSY;
++ }
++ }
++ return 0;
++}
++
++static int collect_one_shm(struct shmid_kernel *shp, void *arg)
++{
++ cpt_context_t *ctx = arg;
++
++ if (__cpt_object_add(CPT_OBJ_FILE, shp->shm_file, GFP_ATOMIC, ctx) == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++int cpt_collect_sysvshm(cpt_context_t * ctx)
++{
++ int err;
++
++ err = sysvipc_walk_shm(collect_one_shm, ctx);
++
++ return err < 0 ? err : 0;
++}
++
++int cpt_collect_sysv(cpt_context_t * ctx)
++{
++ int err;
++
++ err = cpt_collect_sysvsem_undo(ctx);
++ if (err)
++ return err;
++ err = cpt_collect_sysvshm(ctx);
++ if (err)
++ return err;
++
++ return 0;
++}
+diff --git a/kernel/cpt/cpt_tty.c b/kernel/cpt/cpt_tty.c
+new file mode 100644
+index 0000000..8ac9417
+--- /dev/null
++++ b/kernel/cpt/cpt_tty.c
+@@ -0,0 +1,215 @@
++/*
++ *
++ * kernel/cpt/cpt_tty.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/tty.h>
++#include <asm/uaccess.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++/* We must support at least N_TTY. */
++
++int cpt_dump_content_tty(struct file *file, struct cpt_context *ctx)
++{
++ struct tty_struct *tty = file->private_data;
++ cpt_object_t *obj;
++ struct cpt_obj_ref o;
++ loff_t saved_pos;
++
++ obj = lookup_cpt_object(CPT_OBJ_TTY, tty, ctx);
++ if (!obj)
++ return -EINVAL;
++
++ cpt_push_object(&saved_pos, ctx);
++
++ o.cpt_next = sizeof(o);
++ o.cpt_object = CPT_OBJ_REF;
++ o.cpt_hdrlen = sizeof(o);
++ o.cpt_content = CPT_CONTENT_VOID;
++ o.cpt_pos = obj->o_pos;
++ ctx->write(&o, sizeof(o), ctx);
++
++ cpt_pop_object(&saved_pos, ctx);
++
++ return 0;
++}
++
++int cpt_collect_tty(struct file *file, cpt_context_t * ctx)
++{
++ struct tty_struct *tty = file->private_data;
++
++ if (tty) {
++ if (cpt_object_add(CPT_OBJ_TTY, tty, ctx) == NULL)
++ return -ENOMEM;
++ if (tty->link) {
++ cpt_object_t *obj;
++
++ obj = cpt_object_add(CPT_OBJ_TTY, tty->link, ctx);
++ if (obj == NULL)
++ return -ENOMEM;
++ /* Undo o_count, tty->link is not a reference */
++ obj->o_count--;
++ }
++ }
++ return 0;
++}
++
++int cpt_dump_tty(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct tty_struct *tty = obj->o_obj;
++ struct cpt_tty_image *v;
++
++ if (tty->link) {
++ if (lookup_cpt_object(CPT_OBJ_TTY, tty->link, ctx) == NULL) {
++ eprintk_ctx("orphan pty %s %d\n", tty->name, tty->driver->subtype == PTY_TYPE_SLAVE);
++ return -EINVAL;
++ }
++ if (tty->link->link != tty) {
++ eprintk_ctx("bad pty pair\n");
++ return -EINVAL;
++ }
++ if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
++ tty->driver->subtype == PTY_TYPE_SLAVE &&
++ tty->link->count)
++ obj->o_count++;
++ }
++ if (obj->o_count != tty->count) {
++ eprintk_ctx("tty %s is referenced outside %d %d\n", tty->name, obj->o_count, tty->count);
++ return -EBUSY;
++ }
++
++ cpt_open_object(obj, ctx);
++
++ v = cpt_get_buf(ctx);
++ v->cpt_next = -1;
++ v->cpt_object = CPT_OBJ_TTY;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_ARRAY;
++
++ v->cpt_index = tty->index;
++ v->cpt_link = -1;
++ if (tty->link)
++ v->cpt_link = tty->link->index;
++ v->cpt_drv_type = tty->driver->type;
++ v->cpt_drv_subtype = tty->driver->subtype;
++ v->cpt_drv_flags = tty->driver->flags;
++ v->cpt_packet = tty->packet;
++ v->cpt_stopped = tty->stopped;
++ v->cpt_hw_stopped = tty->hw_stopped;
++ v->cpt_flow_stopped = tty->flow_stopped;
++ v->cpt_flags = tty->flags;
++ v->cpt_ctrl_status = tty->ctrl_status;
++ v->cpt_canon_data = tty->canon_data;
++ v->cpt_canon_head = tty->canon_head - tty->read_tail;
++ v->cpt_canon_column = tty->canon_column;
++ v->cpt_column = tty->column;
++ v->cpt_erasing = tty->erasing;
++ v->cpt_lnext = tty->lnext;
++ v->cpt_icanon = tty->icanon;
++ v->cpt_raw = tty->raw;
++ v->cpt_real_raw = tty->real_raw;
++ v->cpt_closing = tty->closing;
++ v->cpt_minimum_to_wake = tty->minimum_to_wake;
++ v->cpt_pgrp = 0;
++ if (tty->pgrp) {
++ v->cpt_pgrp = pid_vnr(tty->pgrp);
++ if ((int)v->cpt_pgrp < 0) {
++ dprintk_ctx("cannot map tty->pgrp %d -> %d\n", pid_vnr(tty->pgrp), (int)v->cpt_pgrp);
++ v->cpt_pgrp = -1;
++ }
++ }
++ v->cpt_session = 0;
++ if (tty->session) {
++ v->cpt_session = pid_vnr(tty->session);
++ if ((int)v->cpt_session < 0) {
++ eprintk_ctx("cannot map tty->session %d -> %d\n", pid_nr(tty->session), (int)v->cpt_session);
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ memcpy(v->cpt_name, tty->name, 64);
++ v->cpt_ws_row = tty->winsize.ws_row;
++ v->cpt_ws_col = tty->winsize.ws_col;
++ v->cpt_ws_prow = tty->winsize.ws_ypixel;
++ v->cpt_ws_pcol = tty->winsize.ws_xpixel;
++ if (tty->termios == NULL) {
++ eprintk_ctx("NULL termios");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ v->cpt_c_line = tty->termios->c_line;
++ v->cpt_c_iflag = tty->termios->c_iflag;
++ v->cpt_c_oflag = tty->termios->c_oflag;
++ v->cpt_c_cflag = tty->termios->c_cflag;
++ v->cpt_c_lflag = tty->termios->c_lflag;
++ memcpy(v->cpt_c_cc, tty->termios->c_cc, NCCS);
++ if (NCCS < 32)
++ memset(v->cpt_c_cc + NCCS, 255, 32 - NCCS);
++ memcpy(v->cpt_read_flags, tty->read_flags, sizeof(v->cpt_read_flags));
++
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ if (tty->read_buf && tty->read_cnt) {
++ struct cpt_obj_bits *v = cpt_get_buf(ctx);
++ loff_t saved_pos;
++
++ cpt_push_object(&saved_pos, ctx);
++ cpt_open_object(NULL, ctx);
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_BITS;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_DATA;
++ v->cpt_size = tty->read_cnt;
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_release_buf(ctx);
++
++ if (tty->read_cnt) {
++ int n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
++ ctx->write(tty->read_buf + tty->read_tail, n, ctx);
++ if (tty->read_cnt > n)
++ ctx->write(tty->read_buf, tty->read_cnt-n, ctx);
++ ctx->align(ctx);
++ }
++
++ cpt_close_object(ctx);
++ cpt_pop_object(&saved_pos, ctx);
++ }
++
++ cpt_close_object(ctx);
++
++ return 0;
++}
++
++__u32 cpt_tty_fasync(struct file *file, struct cpt_context *ctx)
++{
++ struct tty_struct * tty;
++ struct fasync_struct *fa;
++
++ tty = (struct tty_struct *)file->private_data;
++
++ for (fa = tty->fasync; fa; fa = fa->fa_next) {
++ if (fa->fa_file == file)
++ return fa->fa_fd;
++ }
++ return -1;
++}
+diff --git a/kernel/cpt/cpt_ubc.c b/kernel/cpt/cpt_ubc.c
+new file mode 100644
+index 0000000..fc27e74
+--- /dev/null
++++ b/kernel/cpt/cpt_ubc.c
+@@ -0,0 +1,132 @@
++/*
++ *
++ * kernel/cpt/cpt_ubc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/types.h>
++#include <bc/beancounter.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++cpt_object_t *cpt_add_ubc(struct user_beancounter *bc, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = cpt_object_add(CPT_OBJ_UBC, bc, ctx);
++ if (obj != NULL) {
++ if (obj->o_count == 1)
++ get_beancounter(bc);
++ if (bc->parent != NULL && obj->o_parent == NULL)
++ obj->o_parent = cpt_add_ubc(bc->parent, ctx);
++ }
++ return obj;
++}
++
++__u64 cpt_lookup_ubc(struct user_beancounter *bc, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = lookup_cpt_object(CPT_OBJ_UBC, bc, ctx);
++ if (obj == NULL) {
++ char buf[48];
++ print_ub_uid(bc, buf, sizeof(buf));
++ eprintk("CPT: unknown ub %s (%p)\n", buf, bc);
++ dump_stack();
++ return CPT_NULL;
++ }
++ return obj->o_pos;
++}
++
++static void dump_one_bc_parm(struct cpt_ubparm *dmp, struct ubparm *prm,
++ int held)
++{
++ dmp->barrier = (prm->barrier < UB_MAXVALUE ? prm->barrier : CPT_NULL);
++ dmp->limit = (prm->limit < UB_MAXVALUE ? prm->limit : CPT_NULL);
++ dmp->held = (held ? prm->held : CPT_NULL);
++ dmp->maxheld = prm->maxheld;
++ dmp->minheld = prm->minheld;
++ dmp->failcnt = prm->failcnt;
++}
++
++static int dump_one_bc(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct user_beancounter *bc;
++ struct cpt_beancounter_image *v;
++ int i;
++
++ bc = obj->o_obj;
++ v = cpt_get_buf(ctx);
++
++ v->cpt_next = CPT_NULL;
++ v->cpt_object = CPT_OBJ_UBC;
++ v->cpt_hdrlen = sizeof(*v);
++ v->cpt_content = CPT_CONTENT_VOID;
++
++ if (obj->o_parent != NULL)
++ v->cpt_parent = ((cpt_object_t *)obj->o_parent)->o_pos;
++ else
++ v->cpt_parent = CPT_NULL;
++ v->cpt_id = (obj->o_parent != NULL) ? bc->ub_uid : 0;
++ for (i = 0; i < UB_RESOURCES; i++) {
++ dump_one_bc_parm(v->cpt_parms + i * 2, bc->ub_parms + i, 0);
++ dump_one_bc_parm(v->cpt_parms + i * 2 + 1, bc->ub_store + i, 1);
++ }
++ memset(v->cpt_parms + UB_RESOURCES * 2, 0,
++ sizeof(v->cpt_parms)
++ - UB_RESOURCES * 2 * sizeof(v->cpt_parms[0]));
++
++ cpt_open_object(obj, ctx);
++ ctx->write(v, sizeof(*v), ctx);
++ cpt_close_object(ctx);
++
++ cpt_release_buf(ctx);
++ return 0;
++}
++
++int cpt_dump_ubc(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++ int skipped;
++ int top;
++
++ cpt_open_section(ctx, CPT_SECT_UBC);
++
++ do {
++ skipped = 0;
++ top = 0;
++ for_each_object(obj, CPT_OBJ_UBC) {
++ if (obj->o_parent == NULL)
++ top++;
++ if (obj->o_pos != CPT_NULL)
++ continue;
++ if (obj->o_parent != NULL &&
++ ((cpt_object_t *)obj->o_parent)->o_pos == CPT_NULL)
++ skipped++;
++ else
++ dump_one_bc(obj, ctx);
++ }
++ } while (skipped && (top < 2));
++
++ cpt_close_section(ctx);
++ if (top > 1) {
++ eprintk_ctx("More than one top level ub exist");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++void cpt_finish_ubc(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_UBC)
++ put_beancounter(obj->o_obj);
++}
+diff --git a/kernel/cpt/cpt_ubc.h b/kernel/cpt/cpt_ubc.h
+new file mode 100644
+index 0000000..645ba79
+--- /dev/null
++++ b/kernel/cpt/cpt_ubc.h
+@@ -0,0 +1,23 @@
++#ifdef CONFIG_BEANCOUNTERS
++cpt_object_t *cpt_add_ubc(struct user_beancounter *bc, struct cpt_context *ctx);
++__u64 cpt_lookup_ubc(struct user_beancounter *bc, struct cpt_context *ctx);
++int cpt_dump_ubc(struct cpt_context *ctx);
++
++struct user_beancounter *rst_lookup_ubc(__u64 pos, struct cpt_context *ctx);
++int rst_undump_ubc(struct cpt_context *ctx);
++
++void cpt_finish_ubc(struct cpt_context *ctx);
++void rst_finish_ubc(struct cpt_context *ctx);
++void copy_one_ubparm(struct ubparm *from, struct ubparm *to, int bc_parm_id);
++void set_one_ubparm_to_max(struct ubparm *ubprm, int bc_parm_id);
++#else
++static int inline cpt_dump_ubc(struct cpt_context *ctx)
++{ return 0; }
++static int inline rst_undump_ubc(struct cpt_context *ctx)
++{ return 0; }
++static void inline cpt_finish_ubc(struct cpt_context *ctx)
++{ return; }
++static void inline rst_finish_ubc(struct cpt_context *ctx)
++{ return; }
++#endif
++
+diff --git a/kernel/cpt/cpt_x8664.S b/kernel/cpt/cpt_x8664.S
+new file mode 100644
+index 0000000..0d5e361
+--- /dev/null
++++ b/kernel/cpt/cpt_x8664.S
+@@ -0,0 +1,67 @@
++#define ASSEMBLY 1
++
++#include <linux/linkage.h>
++#include <asm/segment.h>
++#include <asm/cache.h>
++#include <asm/errno.h>
++#include <asm/dwarf2.h>
++#include <asm/calling.h>
++#include <asm/msr.h>
++#include <asm/unistd.h>
++#include <asm/thread_info.h>
++#include <asm/hw_irq.h>
++#include <asm/errno.h>
++
++ .code64
++
++ .macro FAKE_STACK_FRAME child_rip
++ /* push in order ss, rsp, eflags, cs, rip */
++ xorq %rax, %rax
++ pushq %rax /* ss */
++ pushq %rax /* rsp */
++ pushq $(1<<9) /* eflags - interrupts on */
++ pushq $__KERNEL_CS /* cs */
++ pushq \child_rip /* rip */
++ pushq %rax /* orig rax */
++ .endm
++
++ .macro UNFAKE_STACK_FRAME
++ addq $8*6, %rsp
++ .endm
++
++ENTRY(asm_kernel_thread)
++ CFI_STARTPROC
++ FAKE_STACK_FRAME $child_rip
++ SAVE_ALL
++
++ # rdi: flags, rsi: usp, rdx: will be &pt_regs
++ movq %rdx,%rdi
++ orq $0x00800000,%rdi
++ movq $-1, %rsi
++ movq %rsp, %rdx
++
++ xorl %r8d,%r8d
++ xorl %r9d,%r9d
++ pushq %rcx
++ call do_fork_pid
++ addq $8, %rsp
++ /* call do_fork */
++ movq %rax,RAX(%rsp)
++ xorl %edi,%edi
++ RESTORE_ALL
++ UNFAKE_STACK_FRAME
++ ret
++ CFI_ENDPROC
++ENDPROC(asm_kernel_thread)
++
++child_rip:
++ pushq $0 # fake return address
++ CFI_STARTPROC
++ movq %rdi, %rax
++ movq %rsi, %rdi
++ call *%rax
++ movq %rax, %rdi
++ call do_exit
++ CFI_ENDPROC
++ENDPROC(child_rip)
++
+diff --git a/kernel/cpt/rst_conntrack.c b/kernel/cpt/rst_conntrack.c
+new file mode 100644
+index 0000000..4c31f32
+--- /dev/null
++++ b/kernel/cpt/rst_conntrack.c
+@@ -0,0 +1,283 @@
++/*
++ *
++ * kernel/cpt/rst_conntrack.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/unistd.h>
++#include <linux/ve.h>
++#include <linux/vzcalluser.h>
++#include <linux/cpt_image.h>
++#include <linux/icmp.h>
++#include <linux/ip.h>
++
++#if defined(CONFIG_VE_IPTABLES) && \
++ (defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE))
++
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/netfilter_ipv4/ip_nat.h>
++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
++#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
++#include <linux/netfilter_ipv4/ip_conntrack_core.h>
++#include <linux/netfilter_ipv4/ip_nat_helper.h>
++#include <linux/netfilter_ipv4/ip_nat_core.h>
++
++#define ASSERT_READ_LOCK(x) do { } while (0)
++#define ASSERT_WRITE_LOCK(x) do { } while (0)
++
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++struct ct_holder
++{
++ struct ct_holder *next;
++ struct ip_conntrack *ct;
++ int index;
++};
++
++static void decode_tuple(struct cpt_ipct_tuple *v, struct ip_conntrack_tuple *tuple, int dir)
++{
++ tuple->dst.ip = v->cpt_dst;
++ tuple->dst.u.all = v->cpt_dstport;
++ tuple->dst.protonum = v->cpt_protonum;
++ tuple->dst.dir = v->cpt_dir;
++ if (dir != tuple->dst.dir)
++ wprintk("dir != tuple->dst.dir\n");
++
++ tuple->src.ip = v->cpt_src;
++ tuple->src.u.all = v->cpt_srcport;
++}
++
++
++static int undump_expect_list(struct ip_conntrack *ct,
++ struct cpt_ip_conntrack_image *ci,
++ loff_t pos, struct ct_holder *ct_list,
++ cpt_context_t *ctx)
++{
++ loff_t end;
++ int err;
++
++ end = pos + ci->cpt_next;
++ pos += ci->cpt_hdrlen;
++ while (pos < end) {
++ struct cpt_ip_connexpect_image v;
++ struct ip_conntrack_expect *exp;
++ struct ip_conntrack *sibling;
++
++ err = rst_get_object(CPT_OBJ_NET_CONNTRACK_EXPECT, pos, &v, ctx);
++ if (err)
++ return err;
++
++ sibling = NULL;
++ if (v.cpt_sibling_conntrack) {
++ struct ct_holder *c;
++
++ for (c = ct_list; c; c = c->next) {
++ if (c->index == v.cpt_sibling_conntrack) {
++ sibling = c->ct;
++ break;
++ }
++ }
++ if (!sibling) {
++ eprintk_ctx("lost sibling of expectation\n");
++ return -EINVAL;
++ }
++ }
++
++ write_lock_bh(&ip_conntrack_lock);
++
++ /* It is possible. Helper module could be just unregistered,
++ * if expectation were on the list, it would be destroyed. */
++ if (ct->helper == NULL) {
++ write_unlock_bh(&ip_conntrack_lock);
++ dprintk_ctx("conntrack: no helper and non-trivial expectation\n");
++ continue;
++ }
++
++ exp = ip_conntrack_expect_alloc(NULL);
++ if (exp == NULL) {
++ write_unlock_bh(&ip_conntrack_lock);
++ return -ENOMEM;
++ }
++
++ if (ct->helper->timeout && !del_timer(&exp->timeout)) {
++ /* Dying already. We can do nothing. */
++ write_unlock_bh(&ip_conntrack_lock);
++ dprintk_ctx("conntrack expectation is dying\n");
++ continue;
++ }
++
++ decode_tuple(&v.cpt_tuple, &exp->tuple, 0);
++ decode_tuple(&v.cpt_mask, &exp->mask, 0);
++
++ exp->master = ct;
++ nf_conntrack_get(&ct->ct_general);
++ ip_conntrack_expect_insert(exp);
++#if 0
++ if (sibling) {
++ exp->sibling = sibling;
++ sibling->master = exp;
++ LIST_DELETE(&ve_ip_conntrack_expect_list, exp);
++ ct->expecting--;
++ nf_conntrack_get(&master_ct(sibling)->infos[0]);
++ } else
++#endif
++ if (ct->helper->timeout) {
++ exp->timeout.expires = jiffies + v.cpt_timeout;
++ add_timer(&exp->timeout);
++ }
++ write_unlock_bh(&ip_conntrack_lock);
++
++ pos += v.cpt_next;
++ }
++ return 0;
++}
++
++static int undump_one_ct(struct cpt_ip_conntrack_image *ci, loff_t pos,
++ struct ct_holder **ct_list, cpt_context_t *ctx)
++{
++ int err = 0;
++ struct ip_conntrack *conntrack;
++ struct ct_holder *c;
++ struct ip_conntrack_tuple orig, repl;
++
++ c = kmalloc(sizeof(struct ct_holder), GFP_KERNEL);
++ if (c == NULL)
++ return -ENOMEM;
++
++ decode_tuple(&ci->cpt_tuple[0], &orig, 0);
++ decode_tuple(&ci->cpt_tuple[1], &repl, 1);
++
++ conntrack = ip_conntrack_alloc(&orig, &repl, get_exec_env()->_ip_conntrack->ub);
++ if (!conntrack || IS_ERR(conntrack)) {
++ kfree(c);
++ return -ENOMEM;
++ }
++
++ c->ct = conntrack;
++ c->next = *ct_list;
++ *ct_list = c;
++ c->index = ci->cpt_index;
++
++ decode_tuple(&ci->cpt_tuple[0], &conntrack->tuplehash[0].tuple, 0);
++ decode_tuple(&ci->cpt_tuple[1], &conntrack->tuplehash[1].tuple, 1);
++
++ conntrack->status = ci->cpt_status;
++
++ memcpy(&conntrack->proto, ci->cpt_proto_data, sizeof(conntrack->proto));
++ memcpy(&conntrack->help, ci->cpt_help_data, sizeof(conntrack->help));
++
++#ifdef CONFIG_IP_NF_NAT_NEEDED
++#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
++ defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
++ conntrack->nat.masq_index = ci->cpt_masq_index;
++#endif
++ if (ci->cpt_initialized) {
++ conntrack->nat.info.seq[0].correction_pos = ci->cpt_nat_seq[0].cpt_correction_pos;
++ conntrack->nat.info.seq[0].offset_before = ci->cpt_nat_seq[0].cpt_offset_before;
++ conntrack->nat.info.seq[0].offset_after = ci->cpt_nat_seq[0].cpt_offset_after;
++ conntrack->nat.info.seq[1].correction_pos = ci->cpt_nat_seq[1].cpt_correction_pos;
++ conntrack->nat.info.seq[1].offset_before = ci->cpt_nat_seq[1].cpt_offset_before;
++ conntrack->nat.info.seq[1].offset_after = ci->cpt_nat_seq[1].cpt_offset_after;
++ }
++ if (conntrack->status & IPS_NAT_DONE_MASK)
++ ip_nat_hash_conntrack(conntrack);
++#endif
++
++ if (ci->cpt_ct_helper) {
++ conntrack->helper = ip_conntrack_helper_find_get(&conntrack->tuplehash[1].tuple);
++ if (conntrack->helper == NULL) {
++ eprintk_ctx("conntrack: cannot find helper, some module is not loaded\n");
++ err = -EINVAL;
++ }
++ }
++
++ ip_conntrack_hash_insert(conntrack);
++ conntrack->timeout.expires = jiffies + ci->cpt_timeout;
++
++ if (err == 0 && ci->cpt_next > ci->cpt_hdrlen)
++ err = undump_expect_list(conntrack, ci, pos, *ct_list, ctx);
++
++ return err;
++}
++
++int rst_restore_ip_conntrack(struct cpt_context * ctx)
++{
++ int err = 0;
++ loff_t sec = ctx->sections[CPT_SECT_NET_CONNTRACK];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_ip_conntrack_image ci;
++ struct ct_holder *c;
++ struct ct_holder *ct_list = NULL;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ if (sizeof(ci.cpt_proto_data) != sizeof(union ip_conntrack_proto)) {
++ eprintk_ctx("conntrack module ct->proto version mismatch\n");
++ return -EINVAL;
++ }
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NET_CONNTRACK || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ err = rst_get_object(CPT_OBJ_NET_CONNTRACK, sec, &ci, ctx);
++ if (err)
++ break;
++ err = undump_one_ct(&ci, sec, &ct_list, ctx);
++ if (err)
++ break;
++ sec += ci.cpt_next;
++ }
++
++ while ((c = ct_list) != NULL) {
++ ct_list = c->next;
++ if (c->ct)
++ add_timer(&c->ct->timeout);
++ kfree(c);
++ }
++
++ return err;
++}
++
++#else
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++int rst_restore_ip_conntrack(struct cpt_context * ctx)
++{
++ if (ctx->sections[CPT_SECT_NET_CONNTRACK] != CPT_NULL)
++ return -EINVAL;
++ return 0;
++}
++
++#endif
+diff --git a/kernel/cpt/rst_context.c b/kernel/cpt/rst_context.c
+new file mode 100644
+index 0000000..f74b81c
+--- /dev/null
++++ b/kernel/cpt/rst_context.c
+@@ -0,0 +1,323 @@
++/*
++ *
++ * kernel/cpt/rst_context.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++static ssize_t file_read(void *addr, size_t count, struct cpt_context *ctx)
++{
++ mm_segment_t oldfs;
++ ssize_t err = -EBADF;
++ struct file *file = ctx->file;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (file)
++ err = file->f_op->read(file, addr, count, &file->f_pos);
++ set_fs(oldfs);
++ if (err != count)
++ return err >= 0 ? -EIO : err;
++ return 0;
++}
++
++static ssize_t file_pread(void *addr, size_t count, struct cpt_context *ctx, loff_t pos)
++{
++ mm_segment_t oldfs;
++ ssize_t err = -EBADF;
++ struct file *file = ctx->file;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (file)
++ err = file->f_op->read(file, addr, count, &pos);
++ set_fs(oldfs);
++ if (err != count)
++ return err >= 0 ? -EIO : err;
++ return 0;
++}
++
++static void file_align(struct cpt_context *ctx)
++{
++ struct file *file = ctx->file;
++
++ if (file)
++ file->f_pos = CPT_ALIGN(file->f_pos);
++}
++
++int rst_get_section(int type, struct cpt_context *ctx, loff_t *start, loff_t *end)
++{
++ struct cpt_section_hdr hdr;
++ int err;
++ loff_t pos;
++
++ pos = ctx->sections[type];
++ *start = *end = pos;
++
++ if (pos != CPT_NULL) {
++ if ((err = ctx->pread(&hdr, sizeof(hdr), ctx, pos)) != 0)
++ return err;
++ if (hdr.cpt_section != type || hdr.cpt_hdrlen < sizeof(hdr))
++ return -EINVAL;
++ *start = pos + hdr.cpt_hdrlen;
++ *end = pos + hdr.cpt_next;
++ }
++ return 0;
++}
++EXPORT_SYMBOL(rst_get_section);
++
++void rst_context_init(struct cpt_context *ctx)
++{
++ int i;
++
++ memset(ctx, 0, sizeof(*ctx));
++
++ init_MUTEX(&ctx->main_sem);
++ ctx->refcount = 1;
++
++ ctx->current_section = -1;
++ ctx->current_object = -1;
++ ctx->pagesize = PAGE_SIZE;
++ ctx->read = file_read;
++ ctx->pread = file_pread;
++ ctx->align = file_align;
++ for (i=0; i < CPT_SECT_MAX; i++)
++ ctx->sections[i] = CPT_NULL;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ init_completion(&ctx->pgin_notify);
++#endif
++ cpt_object_init(ctx);
++}
++
++static int parse_sections(loff_t start, loff_t end, cpt_context_t *ctx)
++{
++ struct cpt_section_hdr h;
++
++ while (start < end) {
++ int err;
++
++ err = ctx->pread(&h, sizeof(h), ctx, start);
++ if (err)
++ return err;
++ if (h.cpt_hdrlen < sizeof(h) ||
++ h.cpt_next < h.cpt_hdrlen ||
++ start + h.cpt_next > end)
++ return -EINVAL;
++ if (h.cpt_section >= CPT_SECT_MAX)
++ return -EINVAL;
++ ctx->sections[h.cpt_section] = start;
++ start += h.cpt_next;
++ }
++ return 0;
++}
++
++int rst_open_dumpfile(struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_major_tail *v;
++ struct cpt_major_hdr h;
++ unsigned long size;
++
++ err = -EBADF;
++ if (!ctx->file)
++ goto err_out;
++
++ err = -ENOMEM;
++ ctx->tmpbuf = (char*)__get_free_page(GFP_KERNEL);
++ if (ctx->tmpbuf == NULL)
++ goto err_out;
++ __cpt_release_buf(ctx);
++
++ size = ctx->file->f_dentry->d_inode->i_size;
++
++ if (size & 7) {
++ err = -EINVAL;
++ goto err_out;
++ }
++ if (size < sizeof(struct cpt_major_hdr) +
++ sizeof(struct cpt_major_tail)) {
++ err = -EINVAL;
++ goto err_out;
++ }
++ err = ctx->pread(&h, sizeof(h), ctx, 0);
++ if (err) {
++ eprintk_ctx("too short image 1 %d\n", err);
++ goto err_out;
++ }
++ if (h.cpt_signature[0] != CPT_SIGNATURE0 ||
++ h.cpt_signature[1] != CPT_SIGNATURE1 ||
++ h.cpt_signature[2] != CPT_SIGNATURE2 ||
++ h.cpt_signature[3] != CPT_SIGNATURE3) {
++ err = -EINVAL;
++ goto err_out;
++ }
++ if (h.cpt_hz != HZ) {
++ err = -EINVAL;
++ eprintk_ctx("HZ mismatch: %d != %d\n", h.cpt_hz, HZ);
++ goto err_out;
++ }
++ ctx->virt_jiffies64 = h.cpt_start_jiffies64;
++ ctx->start_time.tv_sec = h.cpt_start_sec;
++ ctx->start_time.tv_nsec = h.cpt_start_nsec;
++ ctx->kernel_config_flags = h.cpt_kernel_config[0];
++ ctx->iptables_mask = h.cpt_iptables_mask;
++ if (h.cpt_image_version > CPT_VERSION_27 ||
++ CPT_VERSION_MINOR(h.cpt_image_version) > 1) {
++ eprintk_ctx("Unknown image version: %x. Can't restore.\n",
++ h.cpt_image_version);
++ err = -EINVAL;
++ goto err_out;
++ }
++ ctx->image_version = h.cpt_image_version;
++ ctx->features = (__u64)((__u64)h.cpt_ve_features2<<32 | h.cpt_ve_features);
++ ctx->image_arch = h.cpt_os_arch;
++
++ v = cpt_get_buf(ctx);
++ err = ctx->pread(v, sizeof(*v), ctx, size - sizeof(*v));
++ if (err) {
++ eprintk_ctx("too short image 2 %d\n", err);
++ cpt_release_buf(ctx);
++ goto err_out;
++ }
++ if (v->cpt_signature[0] != CPT_SIGNATURE0 ||
++ v->cpt_signature[1] != CPT_SIGNATURE1 ||
++ v->cpt_signature[2] != CPT_SIGNATURE2 ||
++ v->cpt_signature[3] != CPT_SIGNATURE3 ||
++ v->cpt_nsect != CPT_SECT_MAX_INDEX) {
++ err = -EINVAL;
++ cpt_release_buf(ctx);
++ goto err_out;
++ }
++ if ((err = parse_sections(h.cpt_hdrlen, size - sizeof(*v) - sizeof(struct cpt_section_hdr), ctx)) < 0) {
++ cpt_release_buf(ctx);
++ goto err_out;
++ }
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ ctx->lazypages = v->cpt_lazypages;
++#endif
++ ctx->tasks64 = v->cpt_64bit;
++ cpt_release_buf(ctx);
++ return 0;
++
++err_out:
++ if (ctx->tmpbuf) {
++ free_page((unsigned long)ctx->tmpbuf);
++ ctx->tmpbuf = NULL;
++ }
++ return err;
++}
++
++void rst_close_dumpfile(struct cpt_context *ctx)
++{
++ if (ctx->file) {
++ fput(ctx->file);
++ ctx->file = NULL;
++ }
++ if (ctx->tmpbuf) {
++ free_page((unsigned long)ctx->tmpbuf);
++ ctx->tmpbuf = NULL;
++ }
++}
++
++int _rst_get_object(int type, loff_t pos, void *tmp, int size, struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_object_hdr *hdr = tmp;
++ err = ctx->pread(hdr, sizeof(struct cpt_object_hdr), ctx, pos);
++ if (err)
++ return err;
++ if (type > 0 && type != hdr->cpt_object)
++ return -EINVAL;
++ if (hdr->cpt_hdrlen > hdr->cpt_next)
++ return -EINVAL;
++ if (hdr->cpt_hdrlen < sizeof(struct cpt_object_hdr))
++ return -EINVAL;
++ if (size < sizeof(*hdr))
++ return -EINVAL;
++ if (size > hdr->cpt_hdrlen)
++ size = hdr->cpt_hdrlen;
++ if (size > sizeof(*hdr))
++ err = ctx->pread(hdr+1, size - sizeof(*hdr),
++ ctx, pos + sizeof(*hdr));
++ return err;
++}
++EXPORT_SYMBOL(_rst_get_object);
++
++void * __rst_get_object(int type, loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ void *tmp;
++ struct cpt_object_hdr hdr;
++ err = ctx->pread(&hdr, sizeof(hdr), ctx, pos);
++ if (err)
++ return NULL;
++ if (type > 0 && type != hdr.cpt_object)
++ return NULL;
++ if (hdr.cpt_hdrlen > hdr.cpt_next)
++ return NULL;
++ if (hdr.cpt_hdrlen < sizeof(struct cpt_object_hdr))
++ return NULL;
++ tmp = kmalloc(hdr.cpt_hdrlen, GFP_KERNEL);
++ if (!tmp)
++ return NULL;
++ err = ctx->pread(tmp, hdr.cpt_hdrlen, ctx, pos);
++ if (!err)
++ return tmp;
++ kfree(tmp);
++ return NULL;
++}
++EXPORT_SYMBOL(__rst_get_object);
++
++__u8 *__rst_get_name(loff_t *pos_p, struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_object_hdr hdr;
++ __u8 *name;
++
++ err = rst_get_object(CPT_OBJ_NAME, *pos_p, &hdr, ctx);
++ if (err)
++ return NULL;
++ if (hdr.cpt_next - hdr.cpt_hdrlen > PAGE_SIZE)
++ return NULL;
++ name = (void*)__get_free_page(GFP_KERNEL);
++ if (!name)
++ return NULL;
++ err = ctx->pread(name, hdr.cpt_next - hdr.cpt_hdrlen,
++ ctx, *pos_p + hdr.cpt_hdrlen);
++ if (err) {
++ free_page((unsigned long)name);
++ return NULL;
++ }
++ *pos_p += hdr.cpt_next;
++ return name;
++}
++
++__u8 *rst_get_name(loff_t pos, struct cpt_context *ctx)
++{
++ return __rst_get_name(&pos, ctx);
++}
++
++void rst_put_name(__u8 *name, struct cpt_context *ctx)
++{
++ unsigned long addr = (unsigned long)name;
++
++ if (addr)
++ free_page(addr&~(PAGE_SIZE-1));
++}
+diff --git a/kernel/cpt/rst_epoll.c b/kernel/cpt/rst_epoll.c
+new file mode 100644
+index 0000000..0ac4cae
+--- /dev/null
++++ b/kernel/cpt/rst_epoll.c
+@@ -0,0 +1,169 @@
++/*
++ *
++ * kernel/cpt/rst_epoll.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mnt_namespace.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/vzcalluser.h>
++#include <linux/eventpoll.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#include "cpt_syscalls.h"
++
++/* Those funcations are static in fs/eventpoll.c */
++extern int ep_insert(struct eventpoll *ep, struct epoll_event *event,
++ struct file *tfile, int fd);
++extern struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd);
++extern void ep_release_epitem(struct epitem *epi);
++
++
++struct file *cpt_open_epolldev(struct cpt_file_image *fi,
++ unsigned flags,
++ struct cpt_context *ctx)
++{
++ struct file *file;
++ int efd;
++
++ /* Argument "size" is ignored, use just 1 */
++ efd = sys_epoll_create(1);
++ if (efd < 0)
++ return ERR_PTR(efd);
++
++ file = fget(efd);
++ sys_close(efd);
++ return file;
++}
++
++static int restore_one_epoll(cpt_object_t *obj,
++ loff_t pos,
++ struct cpt_epoll_image *ebuf,
++ cpt_context_t *ctx)
++{
++ int err = 0;
++ loff_t endpos;
++ struct file *file = obj->o_obj;
++ struct eventpoll *ep;
++
++ if (file->f_op != &eventpoll_fops) {
++ eprintk_ctx("bad epoll file\n");
++ return -EINVAL;
++ }
++
++ ep = file->private_data;
++
++ if (unlikely(ep == NULL)) {
++ eprintk_ctx("bad epoll device\n");
++ return -EINVAL;
++ }
++
++ endpos = pos + ebuf->cpt_next;
++ pos += ebuf->cpt_hdrlen;
++ while (pos < endpos) {
++ struct cpt_epoll_file_image efi;
++ struct epoll_event epds;
++
++ cpt_object_t *tobj;
++
++ err = rst_get_object(CPT_OBJ_EPOLL_FILE, pos, &efi, ctx);
++ if (err)
++ return err;
++ tobj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, efi.cpt_file, ctx);
++ if (!tobj) {
++ eprintk_ctx("epoll file not found\n");
++ return -EINVAL;
++ }
++ epds.events = efi.cpt_events;
++ epds.data = efi.cpt_data;
++ mutex_lock(&ep->mtx);
++ err = ep_insert(ep, &epds, tobj->o_obj, efi.cpt_fd);
++ if (!err) {
++ struct epitem *epi;
++ epi = ep_find(ep, tobj->o_obj, efi.cpt_fd);
++ if (epi) {
++ if (efi.cpt_ready) {
++ unsigned long flags;
++ spin_lock_irqsave(&ep->lock, flags);
++ if (list_empty(&epi->rdllink))
++ list_add_tail(&epi->rdllink, &ep->rdllist);
++ spin_unlock_irqrestore(&ep->lock, flags);
++ }
++ }
++ }
++ mutex_unlock(&ep->mtx);
++ if (err)
++ break;
++ pos += efi.cpt_next;
++ }
++ return err;
++}
++
++int rst_eventpoll(cpt_context_t *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_EPOLL];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_EPOLL || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ cpt_object_t *obj;
++ struct cpt_epoll_image *ebuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_EPOLL, sec, ebuf, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, ebuf->cpt_file, ctx);
++ if (obj == NULL) {
++ eprintk_ctx("cannot find epoll file object\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ err = restore_one_epoll(obj, sec, ebuf, ctx);
++ cpt_release_buf(ctx);
++ if (err)
++ return err;
++ sec += ebuf->cpt_next;
++ }
++
++ return 0;
++
++}
+diff --git a/kernel/cpt/rst_files.c b/kernel/cpt/rst_files.c
+new file mode 100644
+index 0000000..8b4c688
+--- /dev/null
++++ b/kernel/cpt/rst_files.c
+@@ -0,0 +1,1648 @@
++/*
++ *
++ * kernel/cpt/rst_files.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mount.h>
++#include <linux/tty.h>
++#include <linux/namei.h>
++#include <linux/vmalloc.h>
++#include <linux/smp_lock.h>
++#include <linux/vmalloc.h>
++#include <linux/pagemap.h>
++#include <asm/uaccess.h>
++#include <bc/kmem.h>
++#include <linux/cpt_image.h>
++#include <linux/mnt_namespace.h>
++#include <linux/fdtable.h>
++#include <linux/shm.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++
++#include "cpt_syscalls.h"
++
++
++struct filejob {
++ struct filejob *next;
++ int pid;
++ loff_t fdi;
++};
++
++static int rst_filejob_queue(loff_t pos, cpt_context_t *ctx)
++{
++ struct filejob *j;
++
++ j = kmalloc(sizeof(*j), GFP_KERNEL);
++ if (j == NULL)
++ return -ENOMEM;
++ j->pid = current->pid;
++ j->fdi = pos;
++ j->next = ctx->filejob_queue;
++ ctx->filejob_queue = j;
++ return 0;
++}
++
++static void _anon_pipe_buf_release(struct pipe_inode_info *pipe,
++ struct pipe_buffer *buf)
++{
++ struct page *page = buf->page;
++
++ /*
++ * If nobody else uses this page, and we don't already have a
++ * temporary page, let's keep track of it as a one-deep
++ * allocation cache. (Otherwise just release our reference to it)
++ */
++ if (page_count(page) == 1 && !pipe->tmp_page)
++ pipe->tmp_page = page;
++ else
++ page_cache_release(page);
++
++ module_put(THIS_MODULE);
++}
++
++static void *_anon_pipe_buf_map(struct pipe_inode_info *pipe,
++ struct pipe_buffer *buf, int atomic)
++{
++ if (atomic) {
++ buf->flags |= PIPE_BUF_FLAG_ATOMIC;
++ return kmap_atomic(buf->page, KM_USER0);
++ }
++
++ return kmap(buf->page);
++}
++
++static void _anon_pipe_buf_unmap(struct pipe_inode_info *pipe,
++ struct pipe_buffer *buf, void *map_data)
++{
++ if (buf->flags & PIPE_BUF_FLAG_ATOMIC) {
++ buf->flags &= ~PIPE_BUF_FLAG_ATOMIC;
++ kunmap_atomic(map_data, KM_USER0);
++ } else
++ kunmap(buf->page);
++}
++
++static int _anon_pipe_buf_steal(struct pipe_inode_info *pipe,
++ struct pipe_buffer *buf)
++{
++ struct page *page = buf->page;
++
++ if (page_count(page) == 1) {
++ lock_page(page);
++ return 0;
++ }
++
++ return 1;
++}
++
++static void _anon_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf)
++{
++ page_cache_get(buf->page);
++}
++
++static int _anon_pipe_buf_confirm(struct pipe_inode_info *info, struct pipe_buffer *buf)
++{
++ return 0;
++}
++
++static struct pipe_buf_operations _anon_pipe_buf_ops = {
++ .can_merge = 1,
++ .map = _anon_pipe_buf_map,
++ .unmap = _anon_pipe_buf_unmap,
++ .release = _anon_pipe_buf_release,
++ .confirm = _anon_pipe_buf_confirm,
++ .get = _anon_pipe_buf_get,
++ .steal = _anon_pipe_buf_steal,
++};
++
++/* Sorta ugly... Multiple readers/writers of named pipe rewrite buffer
++ * many times. We need to mark it in CPT_OBJ_INODE table in some way.
++ */
++static int fixup_pipe_data(struct file *file, struct cpt_file_image *fi,
++ struct cpt_context *ctx)
++{
++ struct inode *ino = file->f_dentry->d_inode;
++ struct cpt_inode_image ii;
++ struct cpt_obj_bits b;
++ struct pipe_inode_info *info;
++ int err;
++ int count;
++
++ if (!S_ISFIFO(ino->i_mode)) {
++ eprintk_ctx("fixup_pipe_data: not a pipe %Ld\n", (long long)fi->cpt_inode);
++ return -EINVAL;
++ }
++ if (fi->cpt_inode == CPT_NULL)
++ return 0;
++
++ err = rst_get_object(CPT_OBJ_INODE, fi->cpt_inode, &ii, ctx);
++ if (err)
++ return err;
++
++ if (ii.cpt_next <= ii.cpt_hdrlen)
++ return 0;
++
++ err = rst_get_object(CPT_OBJ_BITS, fi->cpt_inode + ii.cpt_hdrlen, &b, ctx);
++ if (err)
++ return err;
++
++ if (b.cpt_size == 0)
++ return 0;
++
++ mutex_lock(&ino->i_mutex);
++ info = ino->i_pipe;
++ if (info->nrbufs) {
++ mutex_unlock(&ino->i_mutex);
++ eprintk("pipe buffer is restored already\n");
++ return -EINVAL;
++ }
++ info->curbuf = 0;
++ count = 0;
++ while (count < b.cpt_size) {
++ struct pipe_buffer *buf = info->bufs + info->nrbufs;
++ void * addr;
++ int chars;
++
++ chars = b.cpt_size - count;
++ if (chars > PAGE_SIZE)
++ chars = PAGE_SIZE;
++ if (!try_module_get(THIS_MODULE)) {
++ err = -EBUSY;
++ break;
++ }
++
++ buf->page = alloc_page(GFP_HIGHUSER);
++ if (buf->page == NULL) {
++ err = -ENOMEM;
++ break;
++ }
++ buf->ops = &_anon_pipe_buf_ops;
++ buf->offset = 0;
++ buf->len = chars;
++ info->nrbufs++;
++ addr = kmap(buf->page);
++ err = ctx->pread(addr, chars, ctx,
++ fi->cpt_inode + ii.cpt_hdrlen + b.cpt_hdrlen + count);
++ if (err)
++ break;
++ count += chars;
++ }
++ mutex_unlock(&ino->i_mutex);
++
++ return err;
++}
++
++static int make_flags(struct cpt_file_image *fi)
++{
++ int flags = O_NOFOLLOW;
++ switch (fi->cpt_mode&(FMODE_READ|FMODE_WRITE)) {
++ case FMODE_READ|FMODE_WRITE:
++ flags |= O_RDWR; break;
++ case FMODE_WRITE:
++ flags |= O_WRONLY; break;
++ case FMODE_READ:
++ flags |= O_RDONLY; break;
++ default: break;
++ }
++ flags |= fi->cpt_flags&~(O_ACCMODE|O_CREAT|O_TRUNC|O_EXCL|FASYNC);
++ flags |= O_NONBLOCK|O_NOCTTY;
++ return flags;
++}
++
++static struct file *open_pipe(char *name,
++ struct cpt_file_image *fi,
++ unsigned flags,
++ struct cpt_context *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++ struct cpt_inode_image ii;
++ struct file *rf, *wf;
++
++ err = rst_get_object(CPT_OBJ_INODE, fi->cpt_inode, &ii, ctx);
++ if (err)
++ return ERR_PTR(err);
++
++ if (ii.cpt_sb == FSMAGIC_PIPEFS) {
++ int pfd[2];
++
++ if ((err = sc_pipe(pfd)) < 0)
++ return ERR_PTR(err);
++
++ rf = fcheck(pfd[0]);
++ wf = fcheck(pfd[1]);
++ get_file(rf);
++ get_file(wf);
++ sc_close(pfd[0]);
++ sc_close(pfd[1]);
++
++ if (fi->cpt_mode&FMODE_READ) {
++ struct file *tf;
++ tf = wf; wf = rf; rf = tf;
++ }
++ } else {
++ if (fi->cpt_mode&FMODE_READ) {
++ rf = filp_open(name, flags, 0);
++ if (IS_ERR(rf)) {
++ dprintk_ctx("filp_open\n");
++ return rf;
++ }
++ dprintk_ctx(CPT_FID "open RDONLY fifo ino %Ld %p %x\n", CPT_TID(current),
++ (long long)fi->cpt_inode, rf, rf->f_dentry->d_inode->i_mode);
++ return rf;
++ }
++
++ dprintk_ctx(CPT_FID "open WRONLY fifo ino %Ld\n", CPT_TID(current), (long long)fi->cpt_inode);
++
++ rf = filp_open(name, O_RDWR|O_NONBLOCK, 0);
++ if (IS_ERR(rf))
++ return rf;
++ wf = dentry_open(dget(rf->f_dentry),
++ mntget(rf->f_vfsmnt), flags);
++ }
++
++ /* Add pipe inode to obj table. */
++ obj = cpt_object_add(CPT_OBJ_INODE, wf->f_dentry->d_inode, ctx);
++ if (obj == NULL) {
++ fput(rf); fput(wf);
++ return ERR_PTR(-ENOMEM);
++ }
++ cpt_obj_setpos(obj, fi->cpt_inode, ctx);
++ obj->o_parent = rf;
++
++ /* Add another side of pipe to obj table, it will not be used
++ * (o_pos = PT_NULL), another processes opeining pipe will find
++ * inode and open it with dentry_open(). */
++ obj = cpt_object_add(CPT_OBJ_FILE, rf, ctx);
++ if (obj == NULL) {
++ fput(wf);
++ return ERR_PTR(-ENOMEM);
++ }
++ return wf;
++}
++
++static struct file *open_special(struct cpt_file_image *fi,
++ unsigned flags,
++ int deleted,
++ struct cpt_context *ctx)
++{
++ struct cpt_inode_image *ii;
++ struct file *file;
++
++ /* Directories and named pipes are not special actually */
++ if (S_ISDIR(fi->cpt_i_mode) || S_ISFIFO(fi->cpt_i_mode))
++ return NULL;
++
++ /* No support for block devices at the moment. */
++ if (S_ISBLK(fi->cpt_i_mode))
++ return ERR_PTR(-EINVAL);
++
++ if (S_ISSOCK(fi->cpt_i_mode)) {
++ eprintk_ctx("bug: socket is not open\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ /* Support only (some) character devices at the moment. */
++ if (!S_ISCHR(fi->cpt_i_mode))
++ return ERR_PTR(-EINVAL);
++
++ ii = __rst_get_object(CPT_OBJ_INODE, fi->cpt_inode, ctx);
++ if (ii == NULL)
++ return ERR_PTR(-ENOMEM);
++
++ /* Do not worry about this right now. /dev/null,zero,*random are here.
++ * To prohibit at least /dev/mem?
++ */
++ if (MAJOR(ii->cpt_rdev) == MEM_MAJOR) {
++ kfree(ii);
++ return NULL;
++ }
++
++ /* /dev/net/tun will be opened by caller */
++ if (fi->cpt_lflags & CPT_DENTRY_TUNTAP) {
++ kfree(ii);
++ return NULL;
++ }
++
++ file = rst_open_tty(fi, ii, flags, ctx);
++ kfree(ii);
++ return file;
++}
++
++static int restore_posix_lock(struct file *file, struct cpt_flock_image *fli, cpt_context_t *ctx)
++{
++ struct file_lock lock;
++ cpt_object_t *obj;
++
++ memset(&lock, 0, sizeof(lock));
++ lock.fl_type = fli->cpt_type;
++ lock.fl_flags = fli->cpt_flags & ~FL_SLEEP;
++ lock.fl_start = fli->cpt_start;
++ lock.fl_end = fli->cpt_end;
++ obj = lookup_cpt_obj_byindex(CPT_OBJ_FILES, fli->cpt_owner, ctx);
++ if (!obj) {
++ eprintk_ctx("unknown lock owner %d\n", (int)fli->cpt_owner);
++ return -EINVAL;
++ }
++ lock.fl_owner = obj->o_obj;
++ lock.fl_pid = vpid_to_pid(fli->cpt_pid);
++ if (lock.fl_pid < 0) {
++ eprintk_ctx("unknown lock pid %d\n", lock.fl_pid);
++ return -EINVAL;
++ }
++ lock.fl_file = file;
++
++ if (lock.fl_owner == NULL)
++ eprintk_ctx("no lock owner\n");
++ return posix_lock_file(file, &lock, NULL);
++}
++
++static int restore_flock(struct file *file, struct cpt_flock_image *fli,
++ cpt_context_t *ctx)
++{
++ int cmd, err, fd;
++ fd = get_unused_fd();
++ if (fd < 0) {
++ eprintk_ctx("BSD flock cannot be restored\n");
++ return fd;
++ }
++ get_file(file);
++ fd_install(fd, file);
++ if (fli->cpt_type == F_RDLCK) {
++ cmd = LOCK_SH;
++ } else if (fli->cpt_type == F_WRLCK) {
++ cmd = LOCK_EX;
++ } else {
++ eprintk_ctx("flock flavor is unknown: %u\n", fli->cpt_type);
++ sc_close(fd);
++ return -EINVAL;
++ }
++
++ err = sc_flock(fd, LOCK_NB | cmd);
++ sc_close(fd);
++ return err;
++}
++
++
++static int fixup_posix_locks(struct file *file,
++ struct cpt_file_image *fi,
++ loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ loff_t end;
++ struct cpt_flock_image fli;
++
++ end = pos + fi->cpt_next;
++ pos += fi->cpt_hdrlen;
++ while (pos < end) {
++ err = rst_get_object(-1, pos, &fli, ctx);
++ if (err)
++ return err;
++ if (fli.cpt_object == CPT_OBJ_FLOCK &&
++ (fli.cpt_flags&FL_POSIX)) {
++ err = restore_posix_lock(file, &fli, ctx);
++ if (err)
++ return err;
++ dprintk_ctx("posix lock restored\n");
++ }
++ pos += fli.cpt_next;
++ }
++ return 0;
++}
++
++int rst_posix_locks(struct cpt_context *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++ struct cpt_file_image fi;
++
++ if (obj->o_pos == CPT_NULL)
++ continue;
++
++ err = rst_get_object(CPT_OBJ_FILE, obj->o_pos, &fi, ctx);
++ if (err < 0)
++ return err;
++ if (fi.cpt_next > fi.cpt_hdrlen)
++ fixup_posix_locks(file, &fi, obj->o_pos, ctx);
++ }
++ return 0;
++}
++
++static int fixup_flocks(struct file *file,
++ struct cpt_file_image *fi,
++ loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ loff_t end;
++ struct cpt_flock_image fli;
++
++ end = pos + fi->cpt_next;
++ pos += fi->cpt_hdrlen;
++ while (pos < end) {
++ err = rst_get_object(-1, pos, &fli, ctx);
++ if (err)
++ return err;
++ if (fli.cpt_object == CPT_OBJ_FLOCK &&
++ (fli.cpt_flags&FL_FLOCK)) {
++ err = restore_flock(file, &fli, ctx);
++ if (err)
++ return err;
++ dprintk_ctx("bsd lock restored\n");
++ }
++ pos += fli.cpt_next;
++ }
++ return 0;
++}
++
++
++static int fixup_reg_data(struct file *file, loff_t pos, loff_t end,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_page_block pgb;
++ ssize_t (*do_write)(struct file *, const char __user *, size_t, loff_t *ppos);
++
++ do_write = file->f_op->write;
++ if (do_write == NULL) {
++ eprintk_ctx("no write method. Cannot restore contents of the file.\n");
++ return -EINVAL;
++ }
++
++ atomic_long_inc(&file->f_count);
++
++ while (pos < end) {
++ loff_t opos;
++ loff_t ipos;
++ int count;
++
++ err = rst_get_object(CPT_OBJ_PAGES, pos, &pgb, ctx);
++ if (err)
++ goto out;
++ dprintk_ctx("restoring file data block: %08x-%08x\n",
++ (__u32)pgb.cpt_start, (__u32)pgb.cpt_end);
++ ipos = pos + pgb.cpt_hdrlen;
++ opos = pgb.cpt_start;
++ count = pgb.cpt_end-pgb.cpt_start;
++ while (count > 0) {
++ mm_segment_t oldfs;
++ int copy = count;
++
++ if (copy > PAGE_SIZE)
++ copy = PAGE_SIZE;
++ (void)cpt_get_buf(ctx);
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = ctx->pread(ctx->tmpbuf, copy, ctx, ipos);
++ set_fs(oldfs);
++ if (err) {
++ __cpt_release_buf(ctx);
++ goto out;
++ }
++ if (!(file->f_mode & FMODE_WRITE) ||
++ (file->f_flags&O_DIRECT)) {
++ fput(file);
++ file = dentry_open(dget(file->f_dentry),
++ mntget(file->f_vfsmnt), O_WRONLY);
++ if (IS_ERR(file)) {
++ __cpt_release_buf(ctx);
++ return PTR_ERR(file);
++ }
++ }
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ ipos += copy;
++ err = do_write(file, ctx->tmpbuf, copy, &opos);
++ set_fs(oldfs);
++ __cpt_release_buf(ctx);
++ if (err != copy) {
++ if (err >= 0)
++ err = -EIO;
++ goto out;
++ }
++ count -= copy;
++ }
++ pos += pgb.cpt_next;
++ }
++ err = 0;
++
++out:
++ fput(file);
++ return err;
++}
++
++
++static int fixup_file_content(struct file **file_p, struct cpt_file_image *fi,
++ struct cpt_inode_image *ii,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct file *file = *file_p;
++ struct iattr newattrs;
++
++ if (!S_ISREG(fi->cpt_i_mode))
++ return 0;
++
++ if (file == NULL) {
++ file = shmem_file_setup("dev/zero", ii->cpt_size, 0);
++ if (IS_ERR(file))
++ return PTR_ERR(file);
++ *file_p = file;
++ }
++
++ if (ii->cpt_next > ii->cpt_hdrlen) {
++ struct cpt_object_hdr hdr;
++ err = ctx->pread(&hdr, sizeof(struct cpt_object_hdr), ctx, fi->cpt_inode+ii->cpt_hdrlen);
++ if (err)
++ return err;
++ if (hdr.cpt_object == CPT_OBJ_PAGES) {
++ err = fixup_reg_data(file, fi->cpt_inode+ii->cpt_hdrlen,
++ fi->cpt_inode+ii->cpt_next, ctx);
++ if (err)
++ return err;
++ }
++ }
++
++ mutex_lock(&file->f_dentry->d_inode->i_mutex);
++ /* stage 1 - update size like do_truncate does */
++ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
++ newattrs.ia_size = ii->cpt_size;
++ cpt_timespec_import(&newattrs.ia_ctime, ii->cpt_ctime);
++ err = notify_change(file->f_dentry, &newattrs);
++ if (err)
++ goto out;
++
++ /* stage 2 - update times, owner and mode */
++ newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME |
++ ATTR_ATIME_SET | ATTR_MTIME_SET |
++ ATTR_MODE | ATTR_UID | ATTR_GID;
++ newattrs.ia_uid = ii->cpt_uid;
++ newattrs.ia_gid = ii->cpt_gid;
++ newattrs.ia_mode = file->f_dentry->d_inode->i_mode & S_IFMT;
++ newattrs.ia_mode |= (ii->cpt_mode & ~S_IFMT);
++ cpt_timespec_import(&newattrs.ia_atime, ii->cpt_atime);
++ cpt_timespec_import(&newattrs.ia_mtime, ii->cpt_mtime);
++ err = notify_change(file->f_dentry, &newattrs);
++
++out:
++ mutex_unlock(&file->f_dentry->d_inode->i_mutex);
++ return err;
++}
++
++static int fixup_file_flags(struct file *file, struct cpt_file_image *fi,
++ int was_dentry_open, loff_t pos,
++ cpt_context_t *ctx)
++{
++ if (fi->cpt_pos != file->f_pos) {
++ int err = -ESPIPE;
++ if (file->f_op->llseek)
++ err = file->f_op->llseek(file, fi->cpt_pos, 0);
++ if (err < 0) {
++ dprintk_ctx("file %Ld lseek %Ld - %Ld\n",
++ (long long)pos,
++ (long long)file->f_pos,
++ (long long)fi->cpt_pos);
++ file->f_pos = fi->cpt_pos;
++ }
++ }
++ file->f_uid = fi->cpt_uid;
++ file->f_gid = fi->cpt_gid;
++ file->f_owner.pid = 0;
++ if (fi->cpt_fown_pid != CPT_FOWN_STRAY_PID) {
++ file->f_owner.pid = find_get_pid(fi->cpt_fown_pid);
++ if (file->f_owner.pid == NULL) {
++ wprintk_ctx("fixup_file_flags: owner %d does not exist anymore\n",
++ fi->cpt_fown_pid);
++ return -EINVAL;
++ }
++ }
++ file->f_owner.uid = fi->cpt_fown_uid;
++ file->f_owner.euid = fi->cpt_fown_euid;
++ file->f_owner.signum = fi->cpt_fown_signo;
++
++ if (file->f_mode != fi->cpt_mode) {
++ if (was_dentry_open &&
++ ((file->f_mode^fi->cpt_mode)&(FMODE_PREAD|FMODE_LSEEK))) {
++ file->f_mode &= ~(FMODE_PREAD|FMODE_LSEEK);
++ file->f_mode |= fi->cpt_mode&(FMODE_PREAD|FMODE_LSEEK);
++ }
++ if (file->f_mode != fi->cpt_mode)
++ wprintk_ctx("file %ld mode mismatch %08x %08x\n", (long)pos, file->f_mode, fi->cpt_mode);
++ }
++ if (file->f_flags != fi->cpt_flags) {
++ if (!(fi->cpt_flags&O_NOFOLLOW))
++ file->f_flags &= ~O_NOFOLLOW;
++ if ((file->f_flags^fi->cpt_flags)&O_NONBLOCK) {
++ file->f_flags &= ~O_NONBLOCK;
++ file->f_flags |= fi->cpt_flags&O_NONBLOCK;
++ }
++ if (fi->cpt_flags&FASYNC) {
++ if (fi->cpt_fown_fd == -1) {
++ wprintk_ctx("No fd for FASYNC\n");
++ return -EINVAL;
++ } else if (file->f_op && file->f_op->fasync) {
++ if (file->f_op->fasync(fi->cpt_fown_fd, file, 1) < 0) {
++ wprintk_ctx("FASYNC problem\n");
++ return -EINVAL;
++ } else {
++ file->f_flags |= FASYNC;
++ }
++ }
++ }
++ if (file->f_flags != fi->cpt_flags) {
++ eprintk_ctx("file %ld flags mismatch %08x %08x\n", (long)pos, file->f_flags, fi->cpt_flags);
++ return -EINVAL;
++ }
++ }
++ return 0;
++}
++
++static struct file *
++open_deleted(char *name, unsigned flags, struct cpt_file_image *fi,
++ struct cpt_inode_image *ii, cpt_context_t *ctx)
++{
++ struct file * file;
++ char *suffix = NULL;
++ int attempt = 0;
++ int tmp_pass = 0;
++ mode_t mode = fi->cpt_i_mode;
++
++ /* Strip (deleted) part... */
++ if (strlen(name) > strlen(" (deleted)")) {
++ if (strcmp(name + strlen(name) - strlen(" (deleted)"), " (deleted)") == 0) {
++ suffix = &name[strlen(name) - strlen(" (deleted)")];
++ *suffix = 0;
++ } else if (memcmp(name, "(deleted) ", strlen("(deleted) ")) == 0) {
++ memmove(name, name + strlen("(deleted) "), strlen(name) - strlen(" (deleted)") + 1);
++ suffix = name + strlen(name);
++ }
++ }
++
++try_again:
++ for (;;) {
++ if (attempt) {
++ if (attempt > 1000) {
++ eprintk_ctx("open_deleted: failed after %d attempts\n", attempt);
++ return ERR_PTR(-EEXIST);
++ }
++ if (suffix == NULL) {
++ eprintk_ctx("open_deleted: no suffix\n");
++ return ERR_PTR(-EEXIST);
++ }
++ sprintf(suffix, ".%08x", (unsigned)((xtime.tv_nsec>>10)+attempt));
++ }
++ attempt++;
++
++ if (S_ISFIFO(mode)) {
++ int err;
++ err = sc_mknod(name, S_IFIFO|(mode&017777), 0);
++ if (err == -EEXIST)
++ continue;
++ if (err < 0 && !tmp_pass)
++ goto change_dir;
++ if (err < 0)
++ return ERR_PTR(err);
++ file = open_pipe(name, fi, flags, ctx);
++ sc_unlink(name);
++ } else if (S_ISCHR(mode)) {
++ int err;
++ err = sc_mknod(name, S_IFCHR|(mode&017777), new_encode_dev(ii->cpt_rdev));
++ if (err == -EEXIST)
++ continue;
++ if (err < 0 && !tmp_pass)
++ goto change_dir;
++ if (err < 0)
++ return ERR_PTR(err);
++ file = filp_open(name, flags, mode&017777);
++ sc_unlink(name);
++ } else if (S_ISDIR(mode)) {
++ int err;
++ err = sc_mkdir(name, mode&017777);
++ if (err == -EEXIST)
++ continue;
++ if (err < 0 && !tmp_pass)
++ goto change_dir;
++ if (err < 0)
++ return ERR_PTR(err);
++ file = filp_open(name, flags, mode&017777);
++ sc_rmdir(name);
++ } else {
++ file = filp_open(name, O_CREAT|O_EXCL|flags, mode&017777);
++ if (IS_ERR(file)) {
++ if (PTR_ERR(file) == -EEXIST)
++ continue;
++ if (!tmp_pass)
++ goto change_dir;
++ } else {
++ sc_unlink(name);
++ }
++ }
++ break;
++ }
++
++ if (IS_ERR(file)) {
++ eprintk_ctx("filp_open %s: %ld\n", name, PTR_ERR(file));
++ return file;
++ } else {
++ dprintk_ctx("deleted file created as %s, %p, %x\n", name, file, file->f_dentry->d_inode->i_mode);
++ }
++ return file;
++
++change_dir:
++ sprintf(name, "/tmp/rst%u", current->pid);
++ suffix = name + strlen(name);
++ attempt = 1;
++ tmp_pass = 1;
++ goto try_again;
++}
++
++struct file *rst_file(loff_t pos, int fd, struct cpt_context *ctx)
++{
++ int err;
++ int was_dentry_open = 0;
++ cpt_object_t *obj;
++ cpt_object_t *iobj;
++ struct cpt_file_image fi;
++ __u8 *name = NULL;
++ struct file *file;
++ int flags;
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, pos, ctx);
++ if (obj) {
++ file = obj->o_obj;
++ if (obj->o_index >= 0) {
++ dprintk_ctx("file is attached to a socket\n");
++ err = rst_get_object(CPT_OBJ_FILE, pos, &fi, ctx);
++ if (err < 0)
++ goto err_out;
++ fixup_file_flags(file, &fi, 0, pos, ctx);
++ }
++ get_file(file);
++ return file;
++ }
++
++ err = rst_get_object(CPT_OBJ_FILE, pos, &fi, ctx);
++ if (err < 0)
++ goto err_out;
++
++ flags = make_flags(&fi);
++
++ /* Easy way, inode has been already open. */
++ if (fi.cpt_inode != CPT_NULL &&
++ !(fi.cpt_lflags & CPT_DENTRY_CLONING) &&
++ (iobj = lookup_cpt_obj_bypos(CPT_OBJ_INODE, fi.cpt_inode, ctx)) != NULL &&
++ iobj->o_parent) {
++ struct file *filp = iobj->o_parent;
++ file = dentry_open(dget(filp->f_dentry),
++ mntget(filp->f_vfsmnt), flags);
++ dprintk_ctx("rst_file: file obtained by dentry_open\n");
++ was_dentry_open = 1;
++ goto map_file;
++ }
++
++ if (fi.cpt_next > fi.cpt_hdrlen)
++ name = rst_get_name(pos + sizeof(fi), ctx);
++
++ if (!name) {
++ eprintk_ctx("no name for file?\n");
++ err = -EINVAL;
++ goto err_out;
++ }
++
++ if (fi.cpt_lflags & CPT_DENTRY_DELETED) {
++ struct cpt_inode_image ii;
++ if (fi.cpt_inode == CPT_NULL) {
++ eprintk_ctx("deleted file and no inode.\n");
++ err = -EINVAL;
++ goto err_out;
++ }
++
++ err = rst_get_object(CPT_OBJ_INODE, fi.cpt_inode, &ii, ctx);
++ if (err)
++ goto err_out;
++
++ if (ii.cpt_next > ii.cpt_hdrlen) {
++ struct cpt_object_hdr hdr;
++ err = ctx->pread(&hdr, sizeof(hdr), ctx,
++ fi.cpt_inode + ii.cpt_hdrlen);
++ if (err)
++ goto err_out;
++ if (hdr.cpt_object == CPT_OBJ_NAME) {
++ rst_put_name(name, ctx);
++ name = rst_get_name(fi.cpt_inode+ii.cpt_hdrlen,
++ ctx);
++ if (!name) {
++ eprintk_ctx("no name for link?\n");
++ err = -EINVAL;
++ goto err_out;
++ }
++ goto open_file;
++ }
++ }
++
++ /* One very special case... */
++ if (S_ISREG(fi.cpt_i_mode) &&
++ (!name[0] || strcmp(name, "/dev/zero (deleted)") == 0)) {
++ /* MAP_ANON|MAP_SHARED mapping.
++ * kernel makes this damn ugly way, when file which
++ * is passed to mmap by user does not match
++ * file finally attached to VMA. Ok, rst_mm
++ * has to take care of this. Otherwise, it will fail.
++ */
++ file = NULL;
++ } else if (S_ISREG(fi.cpt_i_mode) ||
++ S_ISCHR(fi.cpt_i_mode) ||
++ S_ISFIFO(fi.cpt_i_mode) ||
++ S_ISDIR(fi.cpt_i_mode)) {
++ if (S_ISCHR(fi.cpt_i_mode)) {
++ file = open_special(&fi, flags, 1, ctx);
++ if (file != NULL)
++ goto map_file;
++ }
++ file = open_deleted(name, flags, &fi, &ii, ctx);
++ if (IS_ERR(file))
++ goto out;
++ } else {
++ eprintk_ctx("not a regular deleted file.\n");
++ err = -EINVAL;
++ goto err_out;
++ }
++
++ err = fixup_file_content(&file, &fi, &ii, ctx);
++ if (err)
++ goto err_put;
++ goto map_file;
++ } else {
++open_file:
++ if (!name[0]) {
++ eprintk_ctx("empty name for file?\n");
++ err = -EINVAL;
++ goto err_out;
++ }
++ if ((fi.cpt_lflags & CPT_DENTRY_EPOLL) &&
++ (file = cpt_open_epolldev(&fi, flags, ctx)) != NULL)
++ goto map_file;
++#ifdef CONFIG_INOTIFY_USER
++ if ((fi.cpt_lflags & CPT_DENTRY_INOTIFY) &&
++ (file = rst_open_inotify(&fi, flags, ctx)) != NULL)
++ goto map_file;
++#else
++ if (fi.cpt_lflags & CPT_DENTRY_INOTIFY) {
++ err = -EINVAL;
++ goto err_out;
++ }
++#endif
++ if (S_ISFIFO(fi.cpt_i_mode) &&
++ (file = open_pipe(name, &fi, flags, ctx)) != NULL)
++ goto map_file;
++ if (!S_ISREG(fi.cpt_i_mode) &&
++ (file = open_special(&fi, flags, 0, ctx)) != NULL)
++ goto map_file;
++ }
++
++ file = filp_open(name, flags, 0);
++
++map_file:
++ if (!IS_ERR(file)) {
++ fixup_file_flags(file, &fi, was_dentry_open, pos, ctx);
++
++ if (S_ISFIFO(fi.cpt_i_mode) && !was_dentry_open) {
++ err = fixup_pipe_data(file, &fi, ctx);
++ if (err)
++ goto err_put;
++ }
++
++ /* This is very special hack. Logically, cwd/root are
++ * nothing but open directories. Nevertheless, this causes
++ * failures of restores, when number of open files in VE
++ * is close to limit. So, if it is rst_file() of cwd/root
++ * (fd = -2) and the directory is not deleted, we skip
++ * adding files to object table. If the directory is
++ * not unlinked, this cannot cause any problems.
++ */
++ if (fd != -2 ||
++ !S_ISDIR(file->f_dentry->d_inode->i_mode) ||
++ (fi.cpt_lflags & CPT_DENTRY_DELETED)) {
++ obj = cpt_object_get(CPT_OBJ_FILE, file, ctx);
++ if (!obj) {
++ obj = cpt_object_add(CPT_OBJ_FILE, file, ctx);
++ if (obj)
++ get_file(file);
++ }
++ if (obj)
++ cpt_obj_setpos(obj, pos, ctx);
++
++ obj = cpt_object_add(CPT_OBJ_INODE, file->f_dentry->d_inode, ctx);
++ if (obj) {
++ cpt_obj_setpos(obj, fi.cpt_inode, ctx);
++ if (!obj->o_parent || !(fi.cpt_lflags & CPT_DENTRY_DELETED))
++ obj->o_parent = file;
++ }
++ }
++
++ if (fi.cpt_next > fi.cpt_hdrlen) {
++ err = fixup_flocks(file, &fi, pos, ctx);
++ if (err)
++ goto err_put;
++ }
++ } else {
++ if (fi.cpt_lflags & CPT_DENTRY_PROC) {
++ dprintk_ctx("rst_file /proc delayed\n");
++ file = NULL;
++ } else if (name)
++ eprintk_ctx("can't open file %s\n", name);
++ }
++
++out:
++ if (name)
++ rst_put_name(name, ctx);
++ return file;
++
++err_put:
++ if (file)
++ fput(file);
++err_out:
++ if (name)
++ rst_put_name(name, ctx);
++ return ERR_PTR(err);
++}
++
++
++__u32 rst_files_flag(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ __u32 flag = 0;
++
++ if (ti->cpt_files == CPT_NULL ||
++ lookup_cpt_obj_bypos(CPT_OBJ_FILES, ti->cpt_files, ctx))
++ flag |= CLONE_FILES;
++ if (ti->cpt_fs == CPT_NULL ||
++ lookup_cpt_obj_bypos(CPT_OBJ_FS, ti->cpt_fs, ctx))
++ flag |= CLONE_FS;
++ return flag;
++}
++
++static void local_close_files(struct files_struct * files)
++{
++ int i, j;
++
++ j = 0;
++ for (;;) {
++ unsigned long set;
++ i = j * __NFDBITS;
++ if (i >= files->fdt->max_fds)
++ break;
++ set = files->fdt->open_fds->fds_bits[j];
++ while (set) {
++ if (set & 1) {
++ struct file * file = xchg(&files->fdt->fd[i], NULL);
++ if (file)
++ filp_close(file, files);
++ }
++ i++;
++ set >>= 1;
++ }
++ files->fdt->open_fds->fds_bits[j] = 0;
++ files->fdt->close_on_exec->fds_bits[j] = 0;
++ j++;
++ }
++}
++
++extern int expand_fdtable(struct files_struct *files, int nr);
++
++
++int rst_files_complete(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ struct cpt_files_struct_image fi;
++ struct files_struct *f = current->files;
++ cpt_object_t *obj;
++ loff_t pos, endpos;
++ int err;
++
++ if (ti->cpt_files == CPT_NULL) {
++ current->files = NULL;
++ if (f)
++ put_files_struct(f);
++ return 0;
++ }
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FILES, ti->cpt_files, ctx);
++ if (obj) {
++ if (obj->o_obj != f) {
++ put_files_struct(f);
++ f = obj->o_obj;
++ atomic_inc(&f->count);
++ current->files = f;
++ }
++ return 0;
++ }
++
++ err = rst_get_object(CPT_OBJ_FILES, ti->cpt_files, &fi, ctx);
++ if (err)
++ return err;
++
++ local_close_files(f);
++
++ if (fi.cpt_max_fds > f->fdt->max_fds) {
++ spin_lock(&f->file_lock);
++ err = expand_fdtable(f, fi.cpt_max_fds-1);
++ spin_unlock(&f->file_lock);
++ if (err < 0)
++ return err;
++ }
++
++ pos = ti->cpt_files + fi.cpt_hdrlen;
++ endpos = ti->cpt_files + fi.cpt_next;
++ while (pos < endpos) {
++ struct cpt_fd_image fdi;
++ struct file *filp;
++
++ err = rst_get_object(CPT_OBJ_FILEDESC, pos, &fdi, ctx);
++ if (err)
++ return err;
++ filp = rst_file(fdi.cpt_file, fdi.cpt_fd, ctx);
++ if (IS_ERR(filp)) {
++ eprintk_ctx("rst_file: %ld %Lu\n", PTR_ERR(filp),
++ (long long)fdi.cpt_file);
++ return PTR_ERR(filp);
++ }
++ if (filp == NULL) {
++ int err = rst_filejob_queue(pos, ctx);
++ if (err)
++ return err;
++ } else {
++ if (fdi.cpt_fd >= f->fdt->max_fds) BUG();
++ f->fdt->fd[fdi.cpt_fd] = filp;
++ FD_SET(fdi.cpt_fd, f->fdt->open_fds);
++ if (fdi.cpt_flags&CPT_FD_FLAG_CLOSEEXEC)
++ FD_SET(fdi.cpt_fd, f->fdt->close_on_exec);
++ }
++ pos += fdi.cpt_next;
++ }
++ f->next_fd = fi.cpt_next_fd;
++
++ obj = cpt_object_add(CPT_OBJ_FILES, f, ctx);
++ if (obj) {
++ cpt_obj_setpos(obj, ti->cpt_files, ctx);
++ cpt_obj_setindex(obj, fi.cpt_index, ctx);
++ }
++ return 0;
++}
++
++int rst_do_filejobs(cpt_context_t *ctx)
++{
++ struct filejob *j;
++
++ while ((j = ctx->filejob_queue) != NULL) {
++ int err;
++ struct task_struct *tsk;
++ struct cpt_fd_image fdi;
++ struct file *filp;
++
++ read_lock(&tasklist_lock);
++ tsk = find_task_by_vpid(j->pid);
++ if (tsk)
++ get_task_struct(tsk);
++ read_unlock(&tasklist_lock);
++ if (!tsk)
++ return -EINVAL;
++
++ err = rst_get_object(CPT_OBJ_FILEDESC, j->fdi, &fdi, ctx);
++ if (err) {
++ put_task_struct(tsk);
++ return err;
++ }
++
++ if (fdi.cpt_fd >= tsk->files->fdt->max_fds) BUG();
++ if (tsk->files->fdt->fd[fdi.cpt_fd] ||
++ FD_ISSET(fdi.cpt_fd, tsk->files->fdt->open_fds)) {
++ eprintk_ctx("doing filejob %Ld: fd is busy\n", j->fdi);
++ put_task_struct(tsk);
++ return -EBUSY;
++ }
++
++ filp = rst_file(fdi.cpt_file, fdi.cpt_fd, ctx);
++ if (IS_ERR(filp)) {
++ eprintk_ctx("rst_do_filejobs: 1: %ld %Lu\n", PTR_ERR(filp), (unsigned long long)fdi.cpt_file);
++ put_task_struct(tsk);
++ return PTR_ERR(filp);
++ }
++ if (fdi.cpt_fd >= tsk->files->fdt->max_fds) BUG();
++ tsk->files->fdt->fd[fdi.cpt_fd] = filp;
++ FD_SET(fdi.cpt_fd, tsk->files->fdt->open_fds);
++ if (fdi.cpt_flags&CPT_FD_FLAG_CLOSEEXEC)
++ FD_SET(fdi.cpt_fd, tsk->files->fdt->close_on_exec);
++
++ dprintk_ctx("filejob %Ld done\n", j->fdi);
++
++ put_task_struct(tsk);
++ ctx->filejob_queue = j->next;
++ kfree(j);
++ }
++ return 0;
++}
++
++void rst_flush_filejobs(cpt_context_t *ctx)
++{
++ struct filejob *j;
++
++ while ((j = ctx->filejob_queue) != NULL) {
++ ctx->filejob_queue = j->next;
++ kfree(j);
++ }
++}
++
++int rst_fs_complete(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ struct fs_struct *f = current->fs;
++ cpt_object_t *obj;
++
++ if (ti->cpt_fs == CPT_NULL) {
++ exit_fs(current);
++ return 0;
++ }
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FS, ti->cpt_fs, ctx);
++ if (obj) {
++ if (obj->o_obj != f) {
++ exit_fs(current);
++ f = obj->o_obj;
++ atomic_inc(&f->count);
++ current->fs = f;
++ }
++ return 0;
++ }
++
++ /* Do _not_ restore root. Image contains absolute pathnames.
++ * So, we fix it in context of rst process.
++ */
++
++ obj = cpt_object_add(CPT_OBJ_FS, f, ctx);
++ if (obj)
++ cpt_obj_setpos(obj, ti->cpt_fs, ctx);
++
++ return 0;
++}
++
++int cpt_get_dentry(struct dentry **dp, struct vfsmount **mp,
++ loff_t *pos, struct cpt_context *ctx)
++{
++ struct cpt_file_image fi;
++ struct file * file;
++ int err;
++
++ err = rst_get_object(CPT_OBJ_FILE, *pos, &fi, ctx);
++ if (err)
++ return err;
++
++ file = rst_file(*pos, -2, ctx);
++ if (IS_ERR(file))
++ return PTR_ERR(file);
++
++ *dp = dget(file->f_dentry);
++ *mp = mntget(file->f_vfsmnt);
++ *pos += fi.cpt_next;
++ fput(file);
++ return 0;
++}
++
++static void __set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
++ struct dentry *dentry)
++{
++ struct dentry *old_root;
++ struct vfsmount *old_rootmnt;
++ write_lock(&fs->lock);
++ old_root = fs->root.dentry;
++ old_rootmnt = fs->root.mnt;
++ fs->root.mnt = mnt;
++ fs->root.dentry = dentry;
++ write_unlock(&fs->lock);
++ if (old_root) {
++ dput(old_root);
++ mntput(old_rootmnt);
++ }
++}
++
++static void __set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
++ struct dentry *dentry)
++{
++ struct dentry *old_pwd;
++ struct vfsmount *old_pwdmnt;
++
++ write_lock(&fs->lock);
++ old_pwd = fs->pwd.dentry;
++ old_pwdmnt = fs->pwd.mnt;
++ fs->pwd.mnt = mnt;
++ fs->pwd.dentry = dentry;
++ write_unlock(&fs->lock);
++
++ if (old_pwd) {
++ dput(old_pwd);
++ mntput(old_pwdmnt);
++ }
++}
++
++
++int rst_restore_fs(struct cpt_context *ctx)
++{
++ loff_t pos;
++ cpt_object_t *obj;
++ int err = 0;
++
++ for_each_object(obj, CPT_OBJ_FS) {
++ struct cpt_fs_struct_image fi;
++ struct fs_struct *fs = obj->o_obj;
++ int i;
++ struct dentry *d[3];
++ struct vfsmount *m[3];
++
++ err = rst_get_object(CPT_OBJ_FS, obj->o_pos, &fi, ctx);
++ if (err)
++ return err;
++
++ fs->umask = fi.cpt_umask;
++
++ pos = obj->o_pos + fi.cpt_hdrlen;
++ d[0] = d[1] = d[2] = NULL;
++ m[0] = m[1] = m[2] = NULL;
++ i = 0;
++ while (pos < obj->o_pos + fi.cpt_next && i<3) {
++ err = cpt_get_dentry(d+i, m+i, &pos, ctx);
++ if (err) {
++ eprintk_ctx("cannot get_dir: %d", err);
++ for (--i; i >= 0; i--) {
++ if (d[i])
++ dput(d[i]);
++ if (m[i])
++ mntput(m[i]);
++ }
++ return err;
++ }
++ i++;
++ }
++ if (d[0])
++ __set_fs_root(fs, m[0], d[0]);
++ if (d[1])
++ __set_fs_pwd(fs, m[1], d[1]);
++ if (d[2])
++ wprintk_ctx("altroot arrived...\n");
++ }
++ return err;
++}
++
++int do_one_mount(char *mntpnt, char *mnttype, char *mntbind,
++ unsigned long flags, unsigned long mnt_flags,
++ struct cpt_context *ctx)
++{
++ int err;
++
++ if (mntbind && (strcmp(mntbind, "/") == 0 || strcmp(mntbind, "") == 0))
++ mntbind = NULL;
++
++ if (mntbind)
++ flags |= MS_BIND;
++ /* Join per-mountpoint flags with global flags */
++ if (mnt_flags & MNT_NOSUID)
++ flags |= MS_NOSUID;
++ if (mnt_flags & MNT_NODEV)
++ flags |= MS_NODEV;
++ if (mnt_flags & MNT_NOEXEC)
++ flags |= MS_NOEXEC;
++
++ err = sc_mount(mntbind, mntpnt, mnttype, flags);
++ if (err < 0) {
++ eprintk_ctx("%d mounting %s %s %08lx\n", err, mntpnt, mnttype, flags);
++ return err;
++ }
++ return 0;
++}
++
++static int undumptmpfs(void *arg)
++{
++ int i;
++ int *pfd = arg;
++ int fd1, fd2, err;
++ char *argv[] = { "tar", "x", "-C", "/", "-S", NULL };
++
++ if (pfd[0] != 0)
++ sc_dup2(pfd[0], 0);
++
++ set_fs(KERNEL_DS);
++ fd1 = sc_open("/dev/null", O_WRONLY, 0);
++ fd2 = sc_open("/dev/null", O_WRONLY, 0);
++try:
++ if (fd1 < 0 || fd2 < 0) {
++ if (fd1 == -ENOENT && fd2 == -ENOENT) {
++ err = sc_mknod("/dev/null", S_IFCHR|0666,
++ new_encode_dev((MEM_MAJOR<<MINORBITS)|3));
++ if (err < 0) {
++ eprintk("can't create /dev/null: %d\n", err);
++ module_put(THIS_MODULE);
++ return 255 << 8;
++ }
++ fd1 = sc_open("/dev/null", O_WRONLY, 0666);
++ fd2 = sc_open("/dev/null", O_WRONLY, 0666);
++ sc_unlink("/dev/null");
++ goto try;
++ }
++ eprintk("can not open /dev/null for tar: %d %d\n", fd1, fd2);
++ module_put(THIS_MODULE);
++ return 255 << 8;
++ }
++ if (fd1 != 1)
++ sc_dup2(fd1, 1);
++ if (fd2 != 2)
++ sc_dup2(fd2, 2);
++
++ for (i = 3; i < current->files->fdt->max_fds; i++)
++ sc_close(i);
++
++ module_put(THIS_MODULE);
++
++ i = sc_execve("/bin/tar", argv, NULL);
++ eprintk("failed to exec /bin/tar: %d\n", i);
++ return 255 << 8;
++}
++
++static int rst_restore_tmpfs(loff_t *pos, struct cpt_context * ctx)
++{
++ int err;
++ int pfd[2];
++ struct file *f;
++ struct cpt_object_hdr v;
++ int n;
++ loff_t end;
++ int pid;
++ int status;
++ mm_segment_t oldfs;
++ sigset_t ignore, blocked;
++
++ err = rst_get_object(CPT_OBJ_NAME, *pos, &v, ctx);
++ if (err < 0)
++ return err;
++
++ err = sc_pipe(pfd);
++ if (err < 0)
++ return err;
++ ignore.sig[0] = CPT_SIG_IGNORE_MASK;
++ sigprocmask(SIG_BLOCK, &ignore, &blocked);
++ pid = err = local_kernel_thread(undumptmpfs, (void*)pfd, SIGCHLD, 0);
++ if (err < 0) {
++ eprintk_ctx("tmpfs local_kernel_thread: %d\n", err);
++ goto out;
++ }
++ f = fget(pfd[1]);
++ sc_close(pfd[1]);
++ sc_close(pfd[0]);
++
++ ctx->file->f_pos = *pos + v.cpt_hdrlen;
++ end = *pos + v.cpt_next;
++ *pos += v.cpt_next;
++ do {
++ char buf[16];
++
++ n = end - ctx->file->f_pos;
++ if (n > sizeof(buf))
++ n = sizeof(buf);
++
++ if (ctx->read(buf, n, ctx))
++ break;
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ f->f_op->write(f, buf, n, &f->f_pos);
++ set_fs(oldfs);
++ } while (ctx->file->f_pos < end);
++
++ fput(f);
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if ((err = sc_waitx(pid, 0, &status)) < 0)
++ eprintk_ctx("wait4: %d\n", err);
++ else if ((status & 0x7f) == 0) {
++ err = (status & 0xff00) >> 8;
++ if (err != 0) {
++ eprintk_ctx("tar exited with %d\n", err);
++ err = -EINVAL;
++ }
++ } else {
++ eprintk_ctx("tar terminated\n");
++ err = -EINVAL;
++ }
++ set_fs(oldfs);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++
++ return err;
++
++out:
++ if (pfd[1] >= 0)
++ sc_close(pfd[1]);
++ if (pfd[0] >= 0)
++ sc_close(pfd[0]);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++ return err;
++}
++
++int check_ext_mount(char *mntpnt, char *mnttype, struct cpt_context *ctx)
++{
++ struct mnt_namespace *n;
++ struct list_head *p;
++ struct vfsmount *t;
++ char *path, *path_buf;
++ int ret;
++
++ n = current->nsproxy->mnt_ns;
++ ret = -ENOENT;
++ path_buf = cpt_get_buf(ctx);
++ down_read(&namespace_sem);
++ list_for_each(p, &n->list) {
++ struct path pt;
++ t = list_entry(p, struct vfsmount, mnt_list);
++ pt.dentry = t->mnt_root;
++ pt.mnt = t;
++ path = d_path(&pt, path_buf, PAGE_SIZE);
++ if (IS_ERR(path))
++ continue;
++ if (!strcmp(path, mntpnt) &&
++ !strcmp(t->mnt_sb->s_type->name, mnttype)) {
++ ret = 0;
++ break;
++ }
++ }
++ up_read(&namespace_sem);
++ __cpt_release_buf(ctx);
++ return ret;
++}
++
++int restore_one_vfsmount(struct cpt_vfsmount_image *mi, loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ loff_t endpos;
++
++ endpos = pos + mi->cpt_next;
++ pos += mi->cpt_hdrlen;
++
++ while (pos < endpos) {
++ char *mntdev;
++ char *mntpnt;
++ char *mnttype;
++ char *mntbind;
++
++ mntdev = __rst_get_name(&pos, ctx);
++ mntpnt = __rst_get_name(&pos, ctx);
++ mnttype = __rst_get_name(&pos, ctx);
++ mntbind = NULL;
++ if (mi->cpt_mntflags & CPT_MNT_BIND)
++ mntbind = __rst_get_name(&pos, ctx);
++ err = -EINVAL;
++ if (mnttype && mntpnt) {
++ err = 0;
++ if (!(mi->cpt_mntflags & CPT_MNT_EXT) &&
++ strcmp(mntpnt, "/")) {
++ err = do_one_mount(mntpnt, mnttype, mntbind,
++ mi->cpt_flags,
++ mi->cpt_mntflags, ctx);
++ if (!err &&
++ strcmp(mnttype, "tmpfs") == 0 &&
++ !(mi->cpt_mntflags & (CPT_MNT_BIND)))
++ err = rst_restore_tmpfs(&pos, ctx);
++ } else if (mi->cpt_mntflags & CPT_MNT_EXT) {
++ err = check_ext_mount(mntpnt, mnttype, ctx);
++ if (err)
++ eprintk_ctx("mount point is missing: %s\n", mntpnt);
++ }
++ }
++ if (mntdev)
++ rst_put_name(mntdev, ctx);
++ if (mntpnt)
++ rst_put_name(mntpnt, ctx);
++ if (mnttype)
++ rst_put_name(mnttype, ctx);
++ if (mntbind)
++ rst_put_name(mntbind, ctx);
++ if (err)
++ return err;
++ }
++ return 0;
++}
++
++int restore_one_namespace(loff_t pos, loff_t endpos, struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_vfsmount_image mi;
++
++ while (pos < endpos) {
++ err = rst_get_object(CPT_OBJ_VFSMOUNT, pos, &mi, ctx);
++ if (err)
++ return err;
++ err = restore_one_vfsmount(&mi, pos, ctx);
++ if (err)
++ return err;
++ pos += mi.cpt_next;
++ }
++ return 0;
++}
++
++int rst_root_namespace(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_NAMESPACE];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_object_hdr sbuf;
++ int done = 0;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NAMESPACE || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ err = rst_get_object(CPT_OBJ_NAMESPACE, sec, &sbuf, ctx);
++ if (err)
++ return err;
++ if (done) {
++ eprintk_ctx("multiple namespaces are not supported\n");
++ break;
++ }
++ done++;
++ err = restore_one_namespace(sec+sbuf.cpt_hdrlen, sec+sbuf.cpt_next, ctx);
++ if (err)
++ return err;
++ sec += sbuf.cpt_next;
++ }
++
++ return 0;
++}
++
++int rst_stray_files(struct cpt_context *ctx)
++{
++ int err = 0;
++ loff_t sec = ctx->sections[CPT_SECT_FILES];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_FILES || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ struct cpt_object_hdr sbuf;
++ cpt_object_t *obj;
++
++ err = _rst_get_object(CPT_OBJ_FILE, sec, &sbuf, sizeof(sbuf), ctx);
++ if (err)
++ break;
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, sec, ctx);
++ if (!obj) {
++ struct file *file;
++
++ dprintk_ctx("stray file %Ld\n", sec);
++
++ file = rst_sysv_shm_itself(sec, ctx);
++
++ if (IS_ERR(file)) {
++ eprintk_ctx("rst_stray_files: %ld\n", PTR_ERR(file));
++ return PTR_ERR(file);
++ } else {
++ fput(file);
++ }
++ }
++ sec += sbuf.cpt_next;
++ }
++
++ return err;
++}
+diff --git a/kernel/cpt/rst_inotify.c b/kernel/cpt/rst_inotify.c
+new file mode 100644
+index 0000000..0dcaf47
+--- /dev/null
++++ b/kernel/cpt/rst_inotify.c
+@@ -0,0 +1,196 @@
++/*
++ *
++ * kernel/cpt/rst_inotify.c
++ *
++ * Copyright (C) 2000-2007 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mnt_namespace.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/vzcalluser.h>
++#include <linux/inotify.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++#include "cpt_fsmagic.h"
++#include "cpt_syscalls.h"
++
++extern struct file_operations inotify_fops;
++
++struct file *rst_open_inotify(struct cpt_file_image *fi,
++ unsigned flags,
++ struct cpt_context *ctx)
++{
++ struct file *file;
++ int fd;
++
++ fd = sys_inotify_init();
++ if (fd < 0)
++ return ERR_PTR(fd);
++
++ file = fget(fd);
++ sys_close(fd);
++ return file;
++}
++
++static int restore_one_inotify(cpt_object_t *obj,
++ loff_t pos,
++ struct cpt_inotify_image *ibuf,
++ cpt_context_t *ctx)
++{
++ int err = 0;
++ loff_t endpos;
++ struct file *file = obj->o_obj;
++ struct inotify_device *dev;
++
++ if (file->f_op != &inotify_fops) {
++ eprintk_ctx("bad inotify file\n");
++ return -EINVAL;
++ }
++
++ dev = file->private_data;
++
++ if (unlikely(dev == NULL)) {
++ eprintk_ctx("bad inotify device\n");
++ return -EINVAL;
++ }
++
++ endpos = pos + ibuf->cpt_next;
++ pos += ibuf->cpt_hdrlen;
++ while (pos < endpos) {
++ union {
++ struct cpt_inotify_wd_image wi;
++ struct cpt_inotify_ev_image ei;
++ } u;
++
++ err = rst_get_object(-1, pos, &u, ctx);
++ if (err) {
++ eprintk_ctx("rst_get_object: %d\n", err);
++ return err;
++ }
++ if (u.wi.cpt_object == CPT_OBJ_INOTIFY_WATCH) {
++ struct path p;
++ loff_t fpos = pos + u.wi.cpt_hdrlen;
++
++ err = cpt_get_dentry(&p.dentry, &p.mnt, &fpos, ctx);
++ if (err) {
++ eprintk_ctx("cpt_get_dentry: %d\n", err);
++ return err;
++ }
++
++ mutex_lock(&dev->up_mutex);
++ dev->ih->last_wd = u.wi.cpt_wd - 1;
++ err = inotify_create_watch(dev, &p, u.wi.cpt_mask);
++ dev->ih->last_wd = ibuf->cpt_last_wd;
++ if (err != u.wi.cpt_wd) {
++ eprintk_ctx("wrong inotify descriptor %u %u\n", err, u.wi.cpt_wd);
++ if (err >= 0)
++ err = -EINVAL;
++ } else
++ err = 0;
++ mutex_unlock(&dev->up_mutex);
++ path_put(&p);
++ if (err)
++ break;
++ } else if (u.wi.cpt_object == CPT_OBJ_INOTIFY_EVENT) {
++ struct inotify_user_watch dummy_watch;
++ struct inotify_watch *w;
++ char *name = NULL;
++
++ if (u.ei.cpt_namelen) {
++ name = kmalloc(u.ei.cpt_namelen+1, GFP_KERNEL);
++ if (name == NULL) {
++ err = -ENOMEM;
++ break;
++ }
++ name[u.ei.cpt_namelen] = 0;
++ err = ctx->pread(name, u.ei.cpt_namelen, ctx, pos + u.ei.cpt_hdrlen);
++ if (err) {
++ kfree(name);
++ break;
++ }
++ }
++
++ w = &dummy_watch.wdata;
++ dummy_watch.dev = dev;
++ atomic_set(&w->count, 2);
++
++ /* Trick to avoid destruction due to exit event */
++ if (u.ei.cpt_mask & (IN_IGNORED | IN_ONESHOT))
++ atomic_inc(&w->count);
++ dev->ih->in_ops->handle_event(w, u.ei.cpt_wd, u.ei.cpt_mask,
++ u.ei.cpt_cookie, name, NULL);
++ if (name)
++ kfree(name);
++ } else {
++ eprintk_ctx("bad object: %u\n", u.wi.cpt_object);
++ err = -EINVAL;
++ break;
++ }
++ pos += u.wi.cpt_next;
++ }
++ return err;
++}
++
++int rst_inotify(cpt_context_t *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_INOTIFY];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_INOTIFY || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ cpt_object_t *obj;
++ struct cpt_inotify_image ibuf;
++
++ err = rst_get_object(CPT_OBJ_INOTIFY, sec, &ibuf, ctx);
++ if (err)
++ return err;
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_FILE, ibuf.cpt_file, ctx);
++ if (obj == NULL) {
++ eprintk_ctx("cannot find inotify file object\n");
++ return -EINVAL;
++ }
++ err = restore_one_inotify(obj, sec, &ibuf, ctx);
++ if (err)
++ return err;
++ sec += ibuf.cpt_next;
++ }
++
++ return 0;
++
++}
+diff --git a/kernel/cpt/rst_mm.c b/kernel/cpt/rst_mm.c
+new file mode 100644
+index 0000000..377e2e8
+--- /dev/null
++++ b/kernel/cpt/rst_mm.c
+@@ -0,0 +1,1151 @@
++/*
++ *
++ * kernel/cpt/rst_mm.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/virtinfo.h>
++#include <linux/virtinfoscp.h>
++#include <linux/hugetlb.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++#include <linux/mman.h>
++#include <linux/vmalloc.h>
++#include <linux/rmap.h>
++#include <linux/hash.h>
++#include <asm/pgalloc.h>
++#include <asm/tlb.h>
++#include <asm/tlbflush.h>
++#include <asm/pgtable.h>
++#include <asm/mmu.h>
++#ifdef CONFIG_X86
++#include <asm/ldt.h>
++#include <asm/desc.h>
++#endif
++#include <asm/mmu_context.h>
++#include <asm/vsyscall.h>
++#include <linux/swapops.h>
++#include <linux/cpt_image.h>
++
++#ifdef CONFIG_VE
++#include <bc/beancounter.h>
++#include <bc/vmpages.h>
++#endif
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_files.h"
++#include "cpt_ubc.h"
++#include "cpt_mm.h"
++#include "cpt_kernel.h"
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++#include "cpt_pagein.h"
++#endif
++
++#include "cpt_syscalls.h"
++
++#define __PAGE_NX (1ULL<<63)
++
++static unsigned long make_prot(struct cpt_vma_image *vmai)
++{
++ unsigned long prot = 0;
++
++ if (vmai->cpt_flags&VM_READ)
++ prot |= PROT_READ;
++ if (vmai->cpt_flags&VM_WRITE)
++ prot |= PROT_WRITE;
++ if (vmai->cpt_flags&VM_EXEC)
++ prot |= PROT_EXEC;
++ if (vmai->cpt_flags&VM_GROWSDOWN)
++ prot |= PROT_GROWSDOWN;
++ if (vmai->cpt_flags&VM_GROWSUP)
++ prot |= PROT_GROWSUP;
++ return prot;
++}
++
++static unsigned long make_flags(struct cpt_vma_image *vmai)
++{
++ unsigned long flags = MAP_FIXED;
++
++ if (vmai->cpt_flags&(VM_SHARED|VM_MAYSHARE))
++ flags |= MAP_SHARED;
++ else
++ flags |= MAP_PRIVATE;
++
++ if (vmai->cpt_file == CPT_NULL)
++ flags |= MAP_ANONYMOUS;
++ if (vmai->cpt_flags&VM_GROWSDOWN)
++ flags |= MAP_GROWSDOWN;
++#ifdef MAP_GROWSUP
++ if (vmai->cpt_flags&VM_GROWSUP)
++ flags |= MAP_GROWSUP;
++#endif
++ if (vmai->cpt_flags&VM_DENYWRITE)
++ flags |= MAP_DENYWRITE;
++ if (vmai->cpt_flags&VM_EXECUTABLE)
++ flags |= MAP_EXECUTABLE;
++ if (!(vmai->cpt_flags&VM_ACCOUNT))
++ flags |= MAP_NORESERVE;
++ return flags;
++}
++
++#ifdef CONFIG_X86
++#if !defined(CONFIG_X86_64) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) \
++ && !defined(CONFIG_XEN)
++static int __alloc_ldt(mm_context_t *pc, int mincount)
++{
++ int oldsize, newsize, nr;
++
++ if (mincount <= pc->size)
++ return 0;
++ /*
++ * LDT got larger - reallocate if necessary.
++ */
++ oldsize = pc->size;
++ mincount = (mincount+511)&(~511);
++ newsize = mincount*LDT_ENTRY_SIZE;
++ for (nr = 0; nr * PAGE_SIZE < newsize; nr++) {
++ BUG_ON(nr * PAGE_SIZE >= 64*1024);
++ if (!pc->ldt_pages[nr]) {
++ pc->ldt_pages[nr] = alloc_page(GFP_HIGHUSER|__GFP_UBC);
++ if (!pc->ldt_pages[nr])
++ goto nomem;
++ clear_highpage(pc->ldt_pages[nr]);
++ }
++ }
++ pc->size = mincount;
++ return 0;
++
++nomem:
++ while (--nr >= 0)
++ __free_page(pc->ldt_pages[nr]);
++ pc->size = 0;
++ return -ENOMEM;
++}
++
++static int do_rst_ldt(struct cpt_obj_bits *li, loff_t pos, struct cpt_context *ctx)
++{
++ struct mm_struct *mm = current->mm;
++ int i;
++ int err;
++ int size;
++
++ err = __alloc_ldt(&mm->context, li->cpt_size/LDT_ENTRY_SIZE);
++ if (err)
++ return err;
++
++ size = mm->context.size*LDT_ENTRY_SIZE;
++
++ for (i = 0; i < size; i += PAGE_SIZE) {
++ int nr = i / PAGE_SIZE, bytes;
++ char *kaddr = kmap(mm->context.ldt_pages[nr]);
++
++ bytes = size - i;
++ if (bytes > PAGE_SIZE)
++ bytes = PAGE_SIZE;
++ err = ctx->pread(kaddr, bytes, ctx, pos + li->cpt_hdrlen + i);
++ kunmap(mm->context.ldt_pages[nr]);
++ if (err)
++ return err;
++ }
++
++ load_LDT(&mm->context);
++ return 0;
++}
++
++#else
++
++static int do_rst_ldt(struct cpt_obj_bits *li, loff_t pos, struct cpt_context *ctx)
++{
++ struct mm_struct *mm = current->mm;
++ int oldsize = mm->context.size;
++ void *oldldt;
++ void *newldt;
++ int err;
++
++ if (li->cpt_size > PAGE_SIZE)
++ newldt = vmalloc(li->cpt_size);
++ else
++ newldt = kmalloc(li->cpt_size, GFP_KERNEL);
++
++ if (!newldt)
++ return -ENOMEM;
++
++ err = ctx->pread(newldt, li->cpt_size, ctx, pos + li->cpt_hdrlen);
++ if (err)
++ return err;
++
++ oldldt = mm->context.ldt;
++ mm->context.ldt = newldt;
++ mm->context.size = li->cpt_size/LDT_ENTRY_SIZE;
++
++ load_LDT(&mm->context);
++
++ if (oldsize) {
++ if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE)
++ vfree(oldldt);
++ else
++ kfree(oldldt);
++ }
++ return 0;
++}
++#endif
++#endif
++
++static int
++restore_aio_ring(struct kioctx *aio_ctx, struct cpt_aio_ctx_image *aimg)
++{
++ struct aio_ring_info *info = &aio_ctx->ring_info;
++ unsigned nr_events = aio_ctx->max_reqs;
++ unsigned long size;
++ int nr_pages;
++
++ /* We recalculate parameters of the ring exactly like
++ * fs/aio.c does and then compare calculated values
++ * with ones, stored in dump. They must be the same. */
++
++ nr_events += 2;
++
++ size = sizeof(struct aio_ring);
++ size += sizeof(struct io_event) * nr_events;
++ nr_pages = (size + PAGE_SIZE-1) >> PAGE_SHIFT;
++
++ if (nr_pages != aimg->cpt_ring_pages)
++ return -EINVAL;
++
++ info->nr_pages = nr_pages;
++
++ nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) / sizeof(struct io_event);
++
++ if (nr_events != aimg->cpt_nr)
++ return -EINVAL;
++
++ info->nr = 0;
++ info->ring_pages = info->internal_pages;
++ if (nr_pages > AIO_RING_PAGES) {
++ info->ring_pages = kmalloc(sizeof(struct page *) * nr_pages, GFP_KERNEL);
++ if (!info->ring_pages)
++ return -ENOMEM;
++ memset(info->ring_pages, 0, sizeof(struct page *) * nr_pages);
++ }
++
++ info->mmap_size = nr_pages * PAGE_SIZE;
++
++ /* This piece of shit is not entirely my fault. Kernel aio.c makes
++ * something odd mmap()ping some pages and then pinning them.
++ * I guess it is just some mud remained of failed attempt to show ring
++ * to user space. The result is odd. :-) Immediately after
++ * creation of AIO context, kernel shares those pages with user
++ * and user can read and even write there. But after the first
++ * fork, pages are marked COW with evident consequences.
++ * I remember, I did the same mistake in the first version
++ * of mmapped packet socket, luckily that crap never reached
++ * mainstream.
++ *
++ * So, what are we going to do? I can simulate this odd behaviour
++ * exactly, but I am not insane yet. For now just take the pages
++ * from user space. Alternatively, we could keep kernel copy
++ * in AIO context image, which would be more correct.
++ *
++ * What is wrong now? If the pages are COWed, ring is transferred
++ * incorrectly.
++ */
++ down_read(&current->mm->mmap_sem);
++ info->mmap_base = aimg->cpt_mmap_base;
++ info->nr_pages = get_user_pages(current, current->mm,
++ info->mmap_base, nr_pages,
++ 1, 0, info->ring_pages, NULL);
++ up_read(&current->mm->mmap_sem);
++
++ if (unlikely(info->nr_pages != nr_pages)) {
++ int i;
++
++ for (i=0; i<info->nr_pages; i++)
++ put_page(info->ring_pages[i]);
++ if (info->ring_pages && info->ring_pages != info->internal_pages)
++ kfree(info->ring_pages);
++ return -EFAULT;
++ }
++
++ aio_ctx->user_id = info->mmap_base;
++
++ info->nr = nr_events;
++ info->tail = aimg->cpt_tail;
++
++ return 0;
++}
++
++static int do_rst_aio(struct cpt_aio_ctx_image *aimg, loff_t pos, cpt_context_t *ctx)
++{
++ int err;
++ struct kioctx *aio_ctx;
++ extern spinlock_t aio_nr_lock;
++
++ aio_ctx = kmem_cache_alloc(kioctx_cachep, GFP_KERNEL);
++ if (!aio_ctx)
++ return -ENOMEM;
++
++ memset(aio_ctx, 0, sizeof(*aio_ctx));
++ aio_ctx->max_reqs = aimg->cpt_max_reqs;
++
++ if ((err = restore_aio_ring(aio_ctx, aimg)) < 0) {
++ kmem_cache_free(kioctx_cachep, aio_ctx);
++ eprintk_ctx("AIO %Ld restore_aio_ring: %d\n", pos, err);
++ return err;
++ }
++
++ aio_ctx->mm = current->mm;
++ atomic_inc(&aio_ctx->mm->mm_count);
++ atomic_set(&aio_ctx->users, 1);
++ spin_lock_init(&aio_ctx->ctx_lock);
++ spin_lock_init(&aio_ctx->ring_info.ring_lock);
++ init_waitqueue_head(&aio_ctx->wait);
++ INIT_LIST_HEAD(&aio_ctx->active_reqs);
++ INIT_LIST_HEAD(&aio_ctx->run_list);
++ INIT_WORK(&aio_ctx->wq.work, aio_kick_handler);
++
++ spin_lock(&aio_nr_lock);
++ aio_nr += aio_ctx->max_reqs;
++ spin_unlock(&aio_nr_lock);
++
++ write_lock(&aio_ctx->mm->ioctx_list_lock);
++ aio_ctx->next = aio_ctx->mm->ioctx_list;
++ aio_ctx->mm->ioctx_list = aio_ctx;
++ write_unlock(&aio_ctx->mm->ioctx_list_lock);
++
++ return 0;
++}
++
++struct anonvma_map
++{
++ struct hlist_node list;
++ struct anon_vma *avma;
++ __u64 id;
++};
++
++static int verify_create_anonvma(struct mm_struct *mm,
++ struct cpt_vma_image *vmai,
++ cpt_context_t *ctx)
++{
++ struct anon_vma *avma = NULL;
++ struct anon_vma *new_avma;
++ struct vm_area_struct *vma;
++ int h;
++
++ if (!ctx->anonvmas) {
++ if (CPT_ANONVMA_HSIZE*sizeof(struct hlist_head) > PAGE_SIZE)
++ return -EINVAL;
++ if ((ctx->anonvmas = (void*)__get_free_page(GFP_KERNEL)) == NULL)
++ return -ENOMEM;
++ for (h = 0; h < CPT_ANONVMA_HSIZE; h++)
++ INIT_HLIST_HEAD(&ctx->anonvmas[h]);
++ } else {
++ struct anonvma_map *map;
++ struct hlist_node *elem;
++
++ h = hash_long((unsigned long)vmai->cpt_anonvmaid, CPT_ANONVMA_HBITS);
++ hlist_for_each_entry(map, elem, &ctx->anonvmas[h], list) {
++ if (map->id == vmai->cpt_anonvmaid) {
++ avma = map->avma;
++ break;
++ }
++ }
++ }
++
++ down_read(&mm->mmap_sem);
++ if ((vma = find_vma(mm, vmai->cpt_start)) == NULL) {
++ up_read(&mm->mmap_sem);
++ return -ESRCH;
++ }
++ if (vma->vm_start != vmai->cpt_start) {
++ up_read(&mm->mmap_sem);
++ eprintk_ctx("vma start mismatch\n");
++ return -EINVAL;
++ }
++ if (vma->vm_pgoff != vmai->cpt_pgoff) {
++ dprintk_ctx("vma pgoff mismatch, fixing\n");
++ if (vma->vm_file || (vma->vm_flags&(VM_SHARED|VM_MAYSHARE))) {
++ eprintk_ctx("cannot fixup vma pgoff\n");
++ up_read(&mm->mmap_sem);
++ return -EINVAL;
++ }
++ vma->vm_pgoff = vmai->cpt_pgoff;
++ }
++
++ if (!vma->anon_vma) {
++ if (avma) {
++ vma->anon_vma = avma;
++ anon_vma_link(vma);
++ } else {
++ int err;
++
++ err = anon_vma_prepare(vma);
++
++ if (err) {
++ up_read(&mm->mmap_sem);
++ return err;
++ }
++ }
++ } else {
++ /* Note, we _can_ arrive to the situation, when two
++ * different anonvmaid's point to one anon_vma, this happens
++ * f.e. when mmap() merged new area to previous one and
++ * they will share one anon_vma even if they did not on
++ * original host.
++ *
++ * IT IS OK. To all that I understand, we may merge all
++ * the anon_vma's and rmap can scan all the huge list of vmas
++ * searching for page. It is just "suboptimal".
++ *
++ * Real disaster would happen, if vma already got an anon_vma
++ * with different id. It is very rare case, kernel does the
++ * best efforts to merge anon_vmas when some attributes are
++ * different. In this case we will fall to copying memory.
++ */
++ if (avma && vma->anon_vma != avma) {
++ up_read(&mm->mmap_sem);
++ wprintk_ctx("anon_vma mismatch\n");
++ return 0;
++ }
++ }
++
++ new_avma = vma->anon_vma;
++ up_read(&mm->mmap_sem);
++
++ if (!avma) {
++ struct anonvma_map *map;
++
++ if (!new_avma)
++ return -EINVAL;
++
++ if ((map = kmalloc(sizeof(*map), GFP_KERNEL)) == NULL)
++ return -ENOMEM;
++
++ map->id = vmai->cpt_anonvmaid;
++ map->avma = new_avma;
++ h = hash_long((unsigned long)vmai->cpt_anonvmaid, CPT_ANONVMA_HBITS);
++ hlist_add_head(&map->list, &ctx->anonvmas[h]);
++ }
++ return 0;
++}
++
++static int copy_mm_pages(struct mm_struct *src, unsigned long start,
++ unsigned long end)
++{
++ int err;
++
++ for (; start < end; start += PAGE_SIZE) {
++ struct page *page;
++ struct page *spage;
++ void *maddr, *srcaddr;
++
++ err = get_user_pages(current, current->mm,
++ start, 1, 1, 1, &page, NULL);
++ if (err == 0)
++ err = -EFAULT;
++ if (err < 0)
++ return err;
++
++ err = get_user_pages(current, src,
++ start, 1, 0, 1, &spage, NULL);
++
++ if (err == 0)
++ err = -EFAULT;
++ if (err < 0) {
++ page_cache_release(page);
++ return err;
++ }
++
++ srcaddr = kmap(spage);
++ maddr = kmap(page);
++ memcpy(maddr, srcaddr, PAGE_SIZE);
++ set_page_dirty_lock(page);
++ kunmap(page);
++ kunmap(spage);
++ page_cache_release(page);
++ page_cache_release(spage);
++ }
++ return 0;
++}
++
++#include <linux/proc_fs.h>
++
++static int do_rst_vma(struct cpt_vma_image *vmai, loff_t vmapos, loff_t mmpos, struct cpt_context *ctx)
++{
++ int err = 0;
++ unsigned long addr;
++ struct mm_struct *mm = current->mm;
++ struct vm_area_struct *vma;
++ struct file *file = NULL;
++ unsigned long prot;
++ int checked = 0;
++
++ if (vmai->cpt_type == CPT_VMA_VDSO) {
++ if (ctx->vdso == NULL) {
++#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
++ err = arch_setup_additional_pages(NULL, 0,
++ vmai->cpt_start);
++#endif
++ goto out;
++ }
++ }
++
++ prot = make_prot(vmai);
++
++ if (vmai->cpt_file != CPT_NULL) {
++ if (vmai->cpt_type == CPT_VMA_TYPE_0) {
++ file = rst_file(vmai->cpt_file, -1, ctx);
++ if (IS_ERR(file)) {
++ eprintk_ctx("do_rst_vma: rst_file: %Ld\n", (unsigned long long)vmai->cpt_file);
++ return PTR_ERR(file);
++ }
++ } else if (vmai->cpt_type == CPT_VMA_TYPE_SHM) {
++ file = rst_sysv_shm_vma(vmai, ctx);
++ if (IS_ERR(file))
++ return PTR_ERR(file);
++ }
++ }
++
++ down_write(&mm->mmap_sem);
++
++ if ((make_flags(vmai) & VM_EXECUTABLE) && mm->exe_file != file)
++ set_mm_exe_file(mm, file);
++
++ addr = do_mmap_pgoff(file, vmai->cpt_start,
++ vmai->cpt_end-vmai->cpt_start,
++ prot, make_flags(vmai),
++ vmai->cpt_pgoff);
++
++ if (addr != vmai->cpt_start) {
++ up_write(&mm->mmap_sem);
++
++ err = -EINVAL;
++ if (IS_ERR((void*)addr))
++ err = addr;
++ goto out;
++ }
++
++ vma = find_vma(mm, vmai->cpt_start);
++ if (vma == NULL) {
++ up_write(&mm->mmap_sem);
++ eprintk_ctx("cannot find mmapped vma\n");
++ err = -ESRCH;
++ goto out;
++ }
++
++ /* do_mmap_pgoff() can merge new area to previous one (not to the next,
++ * we mmap in order, the rest of mm is still unmapped). This can happen
++ * f.e. if flags are to be adjusted later, or if we had different
++ * anon_vma on two adjacent regions. Split it by brute force. */
++ if (vma->vm_start != vmai->cpt_start) {
++ dprintk_ctx("vma %Ld merged, split\n", vmapos);
++ err = split_vma(mm, vma, (unsigned long)vmai->cpt_start, 0);
++ if (err) {
++ up_write(&mm->mmap_sem);
++ eprintk_ctx("cannot split vma\n");
++ goto out;
++ }
++ }
++ up_write(&mm->mmap_sem);
++
++ if (vmai->cpt_anonvma && vmai->cpt_anonvmaid) {
++ err = verify_create_anonvma(mm, vmai, ctx);
++ if (err) {
++ eprintk_ctx("cannot verify_create_anonvma %Ld\n", vmapos);
++ goto out;
++ }
++ }
++
++ if (vmai->cpt_type == CPT_VMA_VDSO) {
++ struct page *page;
++ void *maddr;
++
++ err = get_user_pages(current, current->mm,
++ (unsigned long)vmai->cpt_start,
++ 1, 1, 1, &page, NULL);
++ if (err == 0)
++ err = -EFAULT;
++ if (err < 0) {
++ eprintk_ctx("can't get vdso: get_user_pages: %d\n", err);
++ goto out;
++ }
++ err = 0;
++ maddr = kmap(page);
++ memcpy(maddr, ctx->vdso, PAGE_SIZE);
++ set_page_dirty_lock(page);
++ kunmap(page);
++ page_cache_release(page);
++ goto out;
++ }
++
++ if (vmai->cpt_next > vmai->cpt_hdrlen) {
++ loff_t offset = vmapos + vmai->cpt_hdrlen;
++
++ do {
++ union {
++ struct cpt_page_block pb;
++ struct cpt_remappage_block rpb;
++ struct cpt_copypage_block cpb;
++ struct cpt_lazypage_block lpb;
++ struct cpt_iterpage_block ipb;
++ } u;
++ loff_t pos;
++
++ err = rst_get_object(-1, offset, &u, ctx);
++ if (err) {
++ eprintk_ctx("vma fix object: %d\n", err);
++ goto out;
++ }
++ if (u.rpb.cpt_object == CPT_OBJ_REMAPPAGES) {
++ err = sc_remap_file_pages(u.rpb.cpt_start,
++ u.rpb.cpt_end-u.rpb.cpt_start,
++ 0, u.rpb.cpt_pgoff, 0);
++ if (err < 0) {
++ eprintk_ctx("remap_file_pages: %d (%08x,%u,%u)\n", err,
++ (__u32)u.rpb.cpt_start, (__u32)(u.rpb.cpt_end-u.rpb.cpt_start),
++ (__u32)u.rpb.cpt_pgoff);
++ goto out;
++ }
++ offset += u.rpb.cpt_next;
++ continue;
++ } else if (u.cpb.cpt_object == CPT_OBJ_LAZYPAGES) {
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ unsigned long ptr = u.lpb.cpt_start;
++
++ down_read(&mm->mmap_sem);
++ if ((vma = find_vma(mm, u.lpb.cpt_start)) == NULL) {
++ up_read(&mm->mmap_sem);
++ eprintk_ctx("lost vm_area_struct\n");
++ err = -ESRCH;
++ goto out;
++ }
++ err = anon_vma_prepare(vma);
++ if (err) {
++ up_read(&mm->mmap_sem);
++ goto out;
++ }
++ while (ptr < u.lpb.cpt_end) {
++ err = rst_pagein(vma, u.lpb.cpt_index + (ptr-u.lpb.cpt_start)/PAGE_SIZE,
++ ptr, ctx);
++ if (err)
++ break;
++ ptr += PAGE_SIZE;
++ }
++ up_read(&mm->mmap_sem);
++#else
++ err = -EINVAL;
++#endif
++ if (err)
++ goto out;
++ offset += u.cpb.cpt_next;
++ continue;
++ } else if (u.cpb.cpt_object == CPT_OBJ_COPYPAGES) {
++ struct vm_area_struct *vma, *vma1;
++ struct mm_struct *src;
++ struct anon_vma *src_anon;
++ cpt_object_t *mobj;
++
++ if (!vmai->cpt_anonvmaid) {
++ err = -EINVAL;
++ eprintk_ctx("CPT_OBJ_COPYPAGES in !anonvma\n");
++ goto out;
++ }
++
++ mobj = lookup_cpt_obj_bypos(CPT_OBJ_MM, u.cpb.cpt_source, ctx);
++ if (!mobj) {
++ eprintk_ctx("lost mm_struct to clone pages from\n");
++ err = -ESRCH;
++ goto out;
++ }
++ src = mobj->o_obj;
++
++ down_read(&src->mmap_sem);
++ src_anon = NULL;
++ vma1 = find_vma(src, u.cpb.cpt_start);
++ if (vma1)
++ src_anon = vma1->anon_vma;
++ up_read(&src->mmap_sem);
++
++ if (!vma1) {
++ eprintk_ctx("lost src vm_area_struct\n");
++ err = -ESRCH;
++ goto out;
++ }
++
++ down_read(&mm->mmap_sem);
++ if ((vma = find_vma(mm, u.cpb.cpt_start)) == NULL) {
++ up_read(&mm->mmap_sem);
++ eprintk_ctx("lost vm_area_struct\n");
++ err = -ESRCH;
++ goto out;
++ }
++
++ if (!src_anon ||
++ !vma->anon_vma ||
++ vma->anon_vma != src_anon ||
++ vma->vm_start - vma1->vm_start !=
++ (vma->vm_pgoff - vma1->vm_pgoff) << PAGE_SHIFT) {
++ up_read(&mm->mmap_sem);
++ wprintk_ctx("anon_vma mismatch in vm_area_struct %Ld\n", vmapos);
++ err = copy_mm_pages(mobj->o_obj,
++ u.cpb.cpt_start,
++ u.cpb.cpt_end);
++ } else {
++ err = __copy_page_range(vma, vma1,
++ u.cpb.cpt_start,
++ u.cpb.cpt_end-u.cpb.cpt_start);
++ up_read(&mm->mmap_sem);
++ }
++ if (err) {
++ eprintk_ctx("clone_page_range: %d (%08x,%u,%ld)\n", err,
++ (__u32)u.cpb.cpt_start, (__u32)(u.cpb.cpt_end-u.cpb.cpt_start),
++ (long)u.cpb.cpt_source);
++ goto out;
++ }
++
++ offset += u.cpb.cpt_next;
++ continue;
++ } else if (u.pb.cpt_object == CPT_OBJ_ITERPAGES ||
++ u.pb.cpt_object == CPT_OBJ_ITERYOUNGPAGES
++ ) {
++#ifdef CONFIG_VZ_CHECKPOINT_ITER
++ unsigned long ptr = u.lpb.cpt_start;
++ u64 page_pos[16];
++ pos = offset + sizeof(u.pb);
++
++ err = ctx->pread(&page_pos,
++ 8*(u.lpb.cpt_end-ptr)/PAGE_SIZE,
++ ctx,
++ pos);
++ if (err) {
++ eprintk_ctx("Oops\n");
++ goto out;
++ }
++
++ down_read(&mm->mmap_sem);
++ if ((vma = find_vma(mm, u.lpb.cpt_start)) == NULL) {
++ up_read(&mm->mmap_sem);
++ eprintk_ctx("lost vm_area_struct\n");
++ err = -ESRCH;
++ goto out;
++ }
++ err = anon_vma_prepare(vma);
++ if (err) {
++ up_read(&mm->mmap_sem);
++ goto out;
++ }
++ while (ptr < u.lpb.cpt_end) {
++ err = rst_iter(vma,
++ page_pos[(ptr-u.lpb.cpt_start)/PAGE_SIZE],
++ ptr,
++ ctx);
++ if (err)
++ break;
++ ptr += PAGE_SIZE;
++ }
++ if (u.pb.cpt_object == CPT_OBJ_ITERYOUNGPAGES) {
++ make_pages_present((unsigned long)u.lpb.cpt_start,
++ (unsigned long)u.lpb.cpt_end);
++ }
++ up_read(&mm->mmap_sem);
++#else
++ err = -EINVAL;
++#endif
++ if (err)
++ goto out;
++ offset += u.cpb.cpt_next;
++ continue;
++ }
++ if (u.pb.cpt_object != CPT_OBJ_PAGES) {
++ eprintk_ctx("unknown vma fix object %d\n", u.pb.cpt_object);
++ err = -EINVAL;
++ goto out;
++ }
++ pos = offset + sizeof(u.pb);
++ if (!(vmai->cpt_flags&VM_ACCOUNT) && !(prot&PROT_WRITE)) {
++ /* I guess this is get_user_pages() messed things,
++ * this happens f.e. when gdb inserts breakpoints.
++ */
++ int i;
++ for (i=0; i<(u.pb.cpt_end-u.pb.cpt_start)/PAGE_SIZE; i++) {
++ struct page *page;
++ void *maddr;
++ err = get_user_pages(current, current->mm,
++ (unsigned long)u.pb.cpt_start + i*PAGE_SIZE,
++ 1, 1, 1, &page, NULL);
++ if (err == 0)
++ err = -EFAULT;
++ if (err < 0) {
++ eprintk_ctx("get_user_pages: %d\n", err);
++ goto out;
++ }
++ err = 0;
++ maddr = kmap(page);
++ if (u.pb.cpt_content == CPT_CONTENT_VOID) {
++ memset(maddr, 0, PAGE_SIZE);
++ } else if (u.pb.cpt_content == CPT_CONTENT_DATA) {
++ err = ctx->pread(maddr, PAGE_SIZE,
++ ctx, pos + i*PAGE_SIZE);
++ if (err) {
++ kunmap(page);
++ goto out;
++ }
++ } else {
++ err = -EINVAL;
++ kunmap(page);
++ goto out;
++ }
++ set_page_dirty_lock(page);
++ kunmap(page);
++ page_cache_release(page);
++ }
++ } else {
++ if (!(prot&PROT_WRITE))
++ sc_mprotect(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start, prot | PROT_WRITE);
++ if (u.pb.cpt_content == CPT_CONTENT_VOID) {
++ int i;
++ for (i=0; i<(u.pb.cpt_end-u.pb.cpt_start)/sizeof(unsigned long); i++) {
++ err = __put_user(0UL, ((unsigned long __user*)(unsigned long)u.pb.cpt_start) + i);
++ if (err) {
++ eprintk_ctx("__put_user 2 %d\n", err);
++ goto out;
++ }
++ }
++ } else if (u.pb.cpt_content == CPT_CONTENT_DATA) {
++ loff_t tpos = pos;
++ err = ctx->file->f_op->read(ctx->file, cpt_ptr_import(u.pb.cpt_start),
++ u.pb.cpt_end-u.pb.cpt_start,
++ &tpos);
++ if (err != u.pb.cpt_end-u.pb.cpt_start) {
++ if (err >= 0)
++ err = -EIO;
++ goto out;
++ }
++ } else {
++ err = -EINVAL;
++ goto out;
++ }
++ if (!(prot&PROT_WRITE))
++ sc_mprotect(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start, prot);
++ }
++ err = 0;
++ offset += u.pb.cpt_next;
++ } while (offset < vmapos + vmai->cpt_next);
++ }
++
++check:
++ do {
++ struct vm_area_struct *vma;
++ down_read(&mm->mmap_sem);
++ vma = find_vma(mm, addr);
++ if (vma) {
++ if ((vma->vm_flags^vmai->cpt_flags)&VM_READHINTMASK) {
++ VM_ClearReadHint(vma);
++ vma->vm_flags |= vmai->cpt_flags&VM_READHINTMASK;
++ }
++ if ((vma->vm_flags^vmai->cpt_flags)&VM_LOCKED) {
++ dprintk_ctx("fixing up VM_LOCKED %Ld\n", vmapos);
++ up_read(&mm->mmap_sem);
++ if (vma->vm_flags&VM_LOCKED)
++ err = sc_munlock(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start);
++ else
++ err = sc_mlock(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start);
++ /* When mlock fails with EFAULT, it means
++ * that it could not bring in pages.
++ * It can happen after mlock() on unreadable
++ * VMAs. But VMA is correctly locked,
++ * so that this error can be ignored. */
++ if (err == -EFAULT)
++ err = 0;
++ if (err)
++ goto out;
++ goto check;
++ }
++ if ((vma->vm_page_prot.pgprot^vmai->cpt_pgprot)&~__PAGE_NX)
++ wprintk_ctx("VMA %08lx@%ld pgprot mismatch %08Lx %08Lx\n", addr, (long)vmapos,
++ (unsigned long long)vma->vm_page_prot.pgprot,
++ (unsigned long long)vmai->cpt_pgprot);
++#if defined(CONFIG_X86_PAE) || defined(CONFIG_X86_64)
++ if (((vma->vm_page_prot.pgprot^vmai->cpt_pgprot)&__PAGE_NX) &&
++ (ctx->kernel_config_flags&CPT_KERNEL_CONFIG_PAE))
++ wprintk_ctx("VMA %08lx@%ld pgprot mismatch %08Lx %08Lx\n", addr, (long)vmapos,
++ (__u64)vma->vm_page_prot.pgprot, (__u64)vmai->cpt_pgprot);
++#endif
++ if (vma->vm_flags != vmai->cpt_flags) {
++ unsigned long x = vma->vm_flags ^ vmai->cpt_flags;
++ if (x & VM_EXEC) {
++ /* Crap. On i386 this is OK.
++ * It is impossible to make via mmap/mprotect
++ * exec.c clears VM_EXEC on stack. */
++ vma->vm_flags &= ~VM_EXEC;
++ } else if ((x & VM_ACCOUNT) && !checked) {
++ checked = 1;
++ if (!(prot&PROT_WRITE)) {
++ up_read(&mm->mmap_sem);
++ sc_mprotect(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start, prot | PROT_WRITE);
++ sc_mprotect(vmai->cpt_start, vmai->cpt_end-vmai->cpt_start, prot);
++ goto check;
++ }
++ wprintk_ctx("VMA %08lx@%ld flag mismatch %08x %08x\n", addr, (long)vmapos,
++ (__u32)vma->vm_flags, (__u32)vmai->cpt_flags);
++ } else {
++ wprintk_ctx("VMA %08lx@%ld flag mismatch %08x %08x\n", addr, (long)vmapos,
++ (__u32)vma->vm_flags, (__u32)vmai->cpt_flags);
++ }
++ }
++ } else {
++ wprintk_ctx("no VMA for %08lx@%ld\n", addr, (long)vmapos);
++ }
++ up_read(&mm->mmap_sem);
++ } while (0);
++
++out:
++ if (file)
++ fput(file);
++ return err;
++}
++
++#ifndef CONFIG_IA64
++#define TASK_UNMAP_START 0
++#else
++/* On IA64 the first page is a special VM_IO|VM_RESERVED mapping
++ * used to accelerate speculative dereferences of NULL pointer. */
++#define TASK_UNMAP_START PAGE_SIZE
++#endif
++
++static int do_rst_mm(struct cpt_mm_image *vmi, loff_t pos, struct cpt_context *ctx)
++{
++ int err = 0;
++ unsigned int def_flags;
++ struct mm_struct *mm = current->mm;
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *bc;
++#endif
++
++ down_write(&mm->mmap_sem);
++ do_munmap(mm, TASK_UNMAP_START, TASK_SIZE-TASK_UNMAP_START);
++
++#ifdef CONFIG_BEANCOUNTERS
++ /*
++ * MM beancounter is usually correct from the fork time,
++ * but not for init, for example.
++ * Luckily, mm_ub can be changed for a completely empty MM.
++ */
++ bc = rst_lookup_ubc(vmi->cpt_mmub, ctx);
++ err = virtinfo_notifier_call(VITYPE_SCP, VIRTINFO_SCP_RSTMM, bc);
++ if (err & NOTIFY_FAIL) {
++ up_write(&mm->mmap_sem);
++ return -ECHRNG;
++ }
++ if ((err & VIRTNOTIFY_CHANGE) && bc != mm->mm_ub) {
++ struct user_beancounter *old_bc;
++
++ old_bc = mm->mm_ub;
++ mm->mm_ub = bc;
++ bc = old_bc;
++ }
++ err = 0;
++ put_beancounter(bc);
++#endif
++
++ mm->start_code = vmi->cpt_start_code;
++ mm->end_code = vmi->cpt_end_code;
++ mm->start_data = vmi->cpt_start_data;
++ mm->end_data = vmi->cpt_end_data;
++ mm->start_brk = vmi->cpt_start_brk;
++ mm->brk = vmi->cpt_brk;
++ mm->start_stack = vmi->cpt_start_stack;
++ mm->arg_start = vmi->cpt_start_arg;
++ mm->arg_end = vmi->cpt_end_arg;
++ mm->env_start = vmi->cpt_start_env;
++ mm->env_end = vmi->cpt_end_env;
++ mm->def_flags = 0;
++ def_flags = vmi->cpt_def_flags;
++
++ mm->flags = vmi->cpt_dumpable;
++ if (ctx->image_version < CPT_VERSION_24)
++ mm->flags |= MMF_DUMP_FILTER_DEFAULT << MMF_DUMPABLE_BITS;
++
++ mm->vps_dumpable = vmi->cpt_vps_dumpable;
++#ifndef CONFIG_IA64
++ if (ctx->image_version >= CPT_VERSION_9) {
++ mm->context.vdso = cpt_ptr_import(vmi->cpt_vdso);
++ current_thread_info()->sysenter_return =
++ VDSO32_SYMBOL(mm->context.vdso, SYSENTER_RETURN);
++ }
++#endif
++
++#if 0 /* def CONFIG_HUGETLB_PAGE*/
++/* NB: ? */
++ int used_hugetlb;
++#endif
++ up_write(&mm->mmap_sem);
++
++ if (vmi->cpt_next > vmi->cpt_hdrlen) {
++ loff_t offset = pos + vmi->cpt_hdrlen;
++ do {
++ union {
++ struct cpt_vma_image vmai;
++ struct cpt_aio_ctx_image aioi;
++ struct cpt_obj_bits bits;
++ } u;
++ err = rst_get_object(-1, offset, &u, ctx);
++ if (err)
++ goto out;
++ if (u.vmai.cpt_object == CPT_OBJ_VMA) {
++#ifdef CONFIG_IA64
++ //// Later...
++ if (u.vmai.cpt_start)
++#endif
++ err = do_rst_vma(&u.vmai, offset, pos, ctx);
++ if (err)
++ goto out;
++#ifdef CONFIG_X86
++ } else if (u.bits.cpt_object == CPT_OBJ_BITS &&
++ u.bits.cpt_content == CPT_CONTENT_MM_CONTEXT) {
++ err = do_rst_ldt(&u.bits, offset, ctx);
++ if (err)
++ goto out;
++#endif
++ } else if (u.aioi.cpt_object == CPT_OBJ_AIO_CONTEXT) {
++ err = do_rst_aio(&u.aioi, offset, ctx);
++ if (err)
++ goto out;
++ } else {
++ eprintk_ctx("unknown object %u in mm image\n", u.vmai.cpt_object);
++ err = -EINVAL;
++ goto out;
++ }
++ offset += u.vmai.cpt_next;
++ } while (offset < pos + vmi->cpt_next);
++ }
++
++ down_write(&mm->mmap_sem);
++ mm->def_flags = def_flags;
++ up_write(&mm->mmap_sem);
++
++
++out:
++ return err;
++}
++
++extern void exit_mm(struct task_struct * tsk);
++
++int rst_mm_complete(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ int err = 0;
++ cpt_object_t *mobj;
++ void *tmp = (void*)__get_free_page(GFP_KERNEL);
++ struct cpt_mm_image *vmi = (struct cpt_mm_image *)tmp;
++
++ if (!tmp)
++ return -ENOMEM;
++
++ if (ti->cpt_mm == CPT_NULL) {
++ if (current->mm) {
++ virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_EXIT,
++ current);
++ exit_mm(current);
++ }
++ goto out;
++ }
++
++ mobj = lookup_cpt_obj_bypos(CPT_OBJ_MM, ti->cpt_mm, ctx);
++ if (mobj) {
++ if (current->mm != mobj->o_obj) BUG();
++ goto out;
++ }
++
++ if (current->mm == NULL) {
++ struct mm_struct *mm = mm_alloc();
++ if (mm == NULL) {
++ err = -ENOMEM;
++ goto out;
++ }
++ err = init_new_context(current, mm);
++ if (err) {
++ mmdrop(mm);
++ goto out;
++ }
++ current->mm = mm;
++ }
++
++ if ((err = rst_get_object(CPT_OBJ_MM, ti->cpt_mm, vmi, ctx)) != 0)
++ goto out;
++ if ((err = do_rst_mm(vmi, ti->cpt_mm, ctx)) != 0) {
++ eprintk_ctx("do_rst_mm %Ld\n", (unsigned long long)ti->cpt_mm);
++ goto out;
++ }
++ err = -ENOMEM;
++ mobj = cpt_object_add(CPT_OBJ_MM, current->mm, ctx);
++ if (mobj != NULL) {
++ err = 0;
++ cpt_obj_setpos(mobj, ti->cpt_mm, ctx);
++ }
++
++out:
++ if (tmp)
++ free_page((unsigned long)tmp);
++ return err;
++}
++
++/* This is part of mm setup, made in parent context. Mostly, it is the place,
++ * where we graft mm of another process to child.
++ */
++
++int rst_mm_basic(cpt_object_t *obj, struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ struct task_struct *tsk = obj->o_obj;
++ cpt_object_t *mobj;
++
++ /* Task without mm. Just get rid of this. */
++ if (ti->cpt_mm == CPT_NULL) {
++ if (tsk->mm) {
++ virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_EXIT,
++ tsk);
++ mmput(tsk->mm);
++ tsk->mm = NULL;
++ }
++ return 0;
++ }
++
++ mobj = lookup_cpt_obj_bypos(CPT_OBJ_MM, ti->cpt_mm, ctx);
++ if (mobj) {
++ struct mm_struct *newmm = mobj->o_obj;
++ /* Good, the MM is already created. */
++ if (newmm == tsk->mm) {
++ /* Already done by clone(). */
++ return 0;
++ }
++ mmput(tsk->mm);
++ atomic_inc(&newmm->mm_users);
++ tsk->mm = newmm;
++ tsk->active_mm = newmm;
++ }
++ return 0;
++}
++
++/* We use CLONE_VM when mm of child is going to be shared with parent.
++ * Otherwise mm is copied.
++ */
++
++__u32 rst_mm_flag(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ if (ti->cpt_mm == CPT_NULL ||
++ lookup_cpt_obj_bypos(CPT_OBJ_MM, ti->cpt_mm, ctx))
++ return CLONE_VM;
++ return 0;
++}
+diff --git a/kernel/cpt/rst_net.c b/kernel/cpt/rst_net.c
+new file mode 100644
+index 0000000..699a052
+--- /dev/null
++++ b/kernel/cpt/rst_net.c
+@@ -0,0 +1,741 @@
++/*
++ *
++ * kernel/cpt/rst_net.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/nsproxy.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/ve.h>
++#include <linux/ve_proto.h>
++#include <net/route.h>
++#include <net/ip_fib.h>
++#include <net/addrconf.h>
++#include <linux/if_tun.h>
++#include <linux/veth.h>
++#include <linux/nfcalls.h>
++#include <linux/venet.h>
++#include <linux/fdtable.h>
++#include <net/net_namespace.h>
++#include <net/netns/generic.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_kernel.h"
++#include "cpt_net.h"
++#include "cpt_files.h"
++
++#include "cpt_syscalls.h"
++
++extern struct in_ifaddr *inet_alloc_ifa(void);
++extern int inet_insert_ifa(struct in_ifaddr *ifa);
++extern struct in_device *inetdev_init(struct net_device *dev);
++
++int rst_restore_ifaddr(struct cpt_context *ctx)
++{
++ struct net *net = get_exec_env()->ve_netns;
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_NET_IFADDR];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_ifaddr_image di;
++ struct net_device *dev;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NET_IFADDR || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ int cindex = -1;
++ int err;
++ err = rst_get_object(CPT_OBJ_NET_IFADDR, sec, &di, ctx);
++ if (err)
++ return err;
++ cindex = di.cpt_index;
++ rtnl_lock();
++ dev = __dev_get_by_index(net, cindex);
++ if (dev && di.cpt_family == AF_INET) {
++ struct in_device *in_dev;
++ struct in_ifaddr *ifa;
++ if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
++ in_dev = inetdev_init(dev);
++ ifa = inet_alloc_ifa();
++ if (ifa) {
++ ifa->ifa_local = di.cpt_address[0];
++ ifa->ifa_address = di.cpt_peer[0];
++ ifa->ifa_broadcast = di.cpt_broadcast[0];
++ ifa->ifa_prefixlen = di.cpt_masklen;
++ ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
++ ifa->ifa_flags = di.cpt_flags;
++ ifa->ifa_scope = di.cpt_scope;
++ memcpy(ifa->ifa_label, di.cpt_label, IFNAMSIZ);
++ in_dev_hold(in_dev);
++ ifa->ifa_dev = in_dev;
++ err = inet_insert_ifa(ifa);
++ if (err && err != -EEXIST) {
++ rtnl_unlock();
++ eprintk_ctx("add ifaddr err %d for %d %s\n", err, di.cpt_index, di.cpt_label);
++ return err;
++ }
++ }
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ } else if (dev && di.cpt_family == AF_INET6) {
++ __u32 prefered_lft;
++ __u32 valid_lft;
++ struct net *net = get_exec_env()->ve_ns->net_ns;
++ prefered_lft = (di.cpt_flags & IFA_F_DEPRECATED) ?
++ 0 : di.cpt_prefered_lft;
++ valid_lft = (di.cpt_flags & IFA_F_PERMANENT) ?
++ 0xFFFFFFFF : di.cpt_valid_lft;
++ err = inet6_addr_add(net, dev->ifindex,
++ (struct in6_addr *)di.cpt_address,
++ di.cpt_masklen, 0,
++ prefered_lft,
++ valid_lft);
++ if (err && err != -EEXIST) {
++ rtnl_unlock();
++ eprintk_ctx("add ifaddr err %d for %d %s\n", err, di.cpt_index, di.cpt_label);
++ return err;
++ }
++#endif
++ } else {
++ rtnl_unlock();
++ eprintk_ctx("unknown ifaddr 2 for %d\n", di.cpt_index);
++ return -EINVAL;
++ }
++ rtnl_unlock();
++ sec += di.cpt_next;
++ }
++ return 0;
++}
++
++static int rewrite_rtmsg(struct nlmsghdr *nlh, struct cpt_context *ctx)
++{
++ int min_len = NLMSG_LENGTH(sizeof(struct rtmsg));
++ struct rtmsg *rtm = NLMSG_DATA(nlh);
++ __u32 prefix0 = 0;
++
++ if (nlh->nlmsg_len > min_len) {
++ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
++ struct rtattr *rta = (void*)nlh + NLMSG_ALIGN(min_len);
++
++ while (RTA_OK(rta, attrlen)) {
++ if (rta->rta_type == RTA_DST) {
++ prefix0 = *(__u32*)RTA_DATA(rta);
++ }
++ rta = RTA_NEXT(rta, attrlen);
++ }
++ }
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ if (rtm->rtm_family == AF_INET6) {
++ if (rtm->rtm_type == RTN_LOCAL)
++ return 2;
++ if (rtm->rtm_flags & RTM_F_CLONED)
++ return 2;
++ if (rtm->rtm_protocol == RTPROT_UNSPEC ||
++ rtm->rtm_protocol == RTPROT_RA ||
++ rtm->rtm_protocol == RTPROT_REDIRECT ||
++ rtm->rtm_protocol == RTPROT_KERNEL)
++ return 2;
++ if (rtm->rtm_protocol == RTPROT_BOOT &&
++ ((rtm->rtm_dst_len == 8 && prefix0 == htonl(0xFF000000)) ||
++ (rtm->rtm_dst_len == 64 && prefix0 == htonl(0xFE800000))))
++ return 2;
++ }
++#endif
++ return rtm->rtm_protocol == RTPROT_KERNEL;
++}
++
++int rst_restore_route(struct cpt_context *ctx)
++{
++ int err;
++ struct socket *sock;
++ struct msghdr msg;
++ struct iovec iov;
++ struct sockaddr_nl nladdr;
++ mm_segment_t oldfs;
++ loff_t sec = ctx->sections[CPT_SECT_NET_ROUTE];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_object_hdr v;
++ char *pg;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NET_ROUTE || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ if (h.cpt_hdrlen >= h.cpt_next)
++ return 0;
++
++ sec += h.cpt_hdrlen;
++ err = rst_get_object(CPT_OBJ_NET_ROUTE, sec, &v, ctx);
++ if (err < 0)
++ return err;
++
++ err = sock_create(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, &sock);
++ if (err)
++ return err;
++
++ pg = (char*)__get_free_page(GFP_KERNEL);
++ if (pg == NULL) {
++ err = -ENOMEM;
++ goto out_sock;
++ }
++
++ memset(&nladdr, 0, sizeof(nladdr));
++ nladdr.nl_family = AF_NETLINK;
++
++ endsec = sec + v.cpt_next;
++ sec += v.cpt_hdrlen;
++
++ while (sec < endsec) {
++ struct nlmsghdr *n;
++ struct nlmsghdr nh;
++ int kernel_flag;
++
++ if (endsec - sec < sizeof(nh))
++ break;
++
++ err = ctx->pread(&nh, sizeof(nh), ctx, sec);
++ if (err)
++ goto out_sock_pg;
++ if (nh.nlmsg_len < sizeof(nh) || nh.nlmsg_len > PAGE_SIZE ||
++ endsec - sec < nh.nlmsg_len) {
++ err = -EINVAL;
++ goto out_sock_pg;
++ }
++ err = ctx->pread(pg, nh.nlmsg_len, ctx, sec);
++ if (err)
++ goto out_sock_pg;
++
++ n = (struct nlmsghdr*)pg;
++ n->nlmsg_flags = NLM_F_REQUEST|NLM_F_APPEND|NLM_F_CREATE;
++
++ err = rewrite_rtmsg(n, ctx);
++ if (err < 0)
++ goto out_sock_pg;
++ kernel_flag = err;
++
++ if (kernel_flag == 2)
++ goto do_next;
++
++ iov.iov_base=n;
++ iov.iov_len=nh.nlmsg_len;
++ msg.msg_name=&nladdr;
++ msg.msg_namelen=sizeof(nladdr);
++ msg.msg_iov=&iov;
++ msg.msg_iovlen=1;
++ msg.msg_control=NULL;
++ msg.msg_controllen=0;
++ msg.msg_flags=MSG_DONTWAIT;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = sock_sendmsg(sock, &msg, nh.nlmsg_len);
++ set_fs(oldfs);
++
++ if (err < 0)
++ goto out_sock_pg;
++ err = 0;
++
++ iov.iov_base=pg;
++ iov.iov_len=PAGE_SIZE;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = sock_recvmsg(sock, &msg, PAGE_SIZE, MSG_DONTWAIT);
++ set_fs(oldfs);
++ if (err != -EAGAIN) {
++ if (err == NLMSG_LENGTH(sizeof(struct nlmsgerr)) &&
++ n->nlmsg_type == NLMSG_ERROR) {
++ struct nlmsgerr *e = NLMSG_DATA(n);
++ if (e->error != -EEXIST || !kernel_flag)
++ eprintk_ctx("NLMERR: %d\n", e->error);
++ } else {
++ eprintk_ctx("Res: %d %d\n", err, n->nlmsg_type);
++ }
++ }
++do_next:
++ err = 0;
++ sec += NLMSG_ALIGN(nh.nlmsg_len);
++ }
++
++out_sock_pg:
++ free_page((unsigned long)pg);
++out_sock:
++ sock_release(sock);
++ return err;
++}
++
++int rst_resume_network(struct cpt_context *ctx)
++{
++ struct ve_struct *env;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++ env->disable_net = 0;
++ put_ve(env);
++ return 0;
++}
++
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++extern unsigned int tun_net_id;
++#endif
++
++/* We do not restore skb queue, just reinit it */
++static int rst_restore_tuntap(loff_t start, struct cpt_netdev_image *di,
++ struct cpt_context *ctx)
++{
++ int err = -ENODEV;
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ struct cpt_tuntap_image ti;
++ struct net_device *dev;
++ struct file *bind_file = NULL;
++ struct net *net;
++ struct tun_struct *tun;
++ struct tun_net *tn;
++ loff_t pos;
++
++ pos = start + di->cpt_hdrlen;
++ err = rst_get_object(CPT_OBJ_NET_TUNTAP, pos, &ti, ctx);
++ if (err)
++ return err;
++
++ pos += ti.cpt_next;
++ if (ti.cpt_bindfile) {
++ bind_file = rst_file(ti.cpt_bindfile, -1, ctx);
++ if (IS_ERR(bind_file)) {
++ eprintk_ctx("rst_restore_tuntap:"
++ "rst_file: %Ld\n",
++ (unsigned long long)ti.cpt_bindfile);
++ return PTR_ERR(bind_file);
++ }
++ }
++
++ rtnl_lock();
++ err = -ENOMEM;
++ dev = alloc_netdev(sizeof(struct tun_struct), di->cpt_name, tun_setup);
++ if (!dev)
++ goto out;
++
++ tun = netdev_priv(dev);
++
++ tun->dev = dev;
++ tun->owner = ti.cpt_owner;
++ tun->flags = ti.cpt_flags;
++ tun->attached = ti.cpt_attached;
++ tun_net_init(dev);
++
++ tun->txflt.count = 0;
++
++ err = register_netdevice(dev);
++ if (err < 0) {
++ free_netdev(dev);
++ eprintk_ctx("failed to register tun/tap net device\n");
++ goto out;
++ }
++ if (pos < start + di->cpt_next) {
++ struct cpt_hwaddr_image hw;
++ /* Restore hardware address */
++ err = rst_get_object(CPT_OBJ_NET_HWADDR, pos,
++ &hw, ctx);
++ if (err)
++ goto out;
++ BUILD_BUG_ON(sizeof(hw.cpt_dev_addr) != sizeof(dev->dev_addr));
++ memcpy(dev->dev_addr, hw.cpt_dev_addr,
++ sizeof(hw.cpt_dev_addr));
++ }
++ net = get_exec_env()->ve_ns->net_ns;
++ tn = net_generic(net, tun_net_id);
++ list_add(&tun->list, &tn->dev_list);
++
++ bind_file->private_data = tun;
++ tun->bind_file = bind_file;
++
++out:
++ fput(bind_file);
++ rtnl_unlock();
++#endif
++ return err;
++}
++
++static int rst_restore_veth(loff_t pos, struct net_device *dev,
++ struct cpt_context *ctx)
++{
++ int err = -ENODEV;
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++ struct cpt_veth_image vi;
++ struct veth_struct *veth;
++
++ if (!KSYMREF(veth_open) || dev->open != KSYMREF(veth_open)) {
++ eprintk_ctx("Module vzethdev is not loaded, "
++ "or device %s is not a veth device\n", dev->name);
++ return -EINVAL;
++ }
++ err = rst_get_object(CPT_OBJ_NET_VETH, pos, &vi, ctx);
++ if (err)
++ return err;
++ veth = veth_from_netdev(dev);
++ veth->allow_mac_change = vi.cpt_allow_mac_change;
++#endif
++ return err;
++}
++
++static int rst_restore_netstats(loff_t pos, struct net_device *dev,
++ struct cpt_context * ctx)
++{
++ struct cpt_netstats_image *n;
++ struct net_device_stats *stats = NULL;
++ struct net_device *lo = get_exec_env()->ve_netns->loopback_dev;
++ int err;
++
++ if (!dev->get_stats)
++ return 0;
++
++ n = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_NET_STATS, pos, n, ctx);
++ if (err)
++ goto out;
++ BUG_ON(sizeof(struct cpt_netstats_image) != n->cpt_hdrlen);
++ preempt_disable();
++ if (dev == lo)
++ stats = &lo->stats;
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++ else if (KSYMREF(veth_open) && dev->open == KSYMREF(veth_open))
++ stats = veth_stats(dev, smp_processor_id());
++#endif
++#if defined(CONFIG_VE_NETDEV) || defined(CONFIG_VE_NETDEV_MODULE)
++ else if (dev == get_exec_env()->_venet_dev)
++ stats = venet_stats(dev, smp_processor_id());
++#endif
++#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
++ if (dev->open == tun_net_open)
++ stats = &dev->stats;
++#endif
++ if (!stats) {
++ err = -ENODEV;
++ eprintk_ctx("Network device %s is not supported\n", dev->name);
++ goto out;
++ }
++
++ stats->rx_packets = n->cpt_rx_packets;
++ stats->tx_packets = n->cpt_tx_packets;
++ stats->rx_bytes = n->cpt_rx_bytes;
++ stats->tx_bytes = n->cpt_tx_bytes;
++ stats->rx_errors = n->cpt_rx_errors;
++ stats->tx_errors = n->cpt_tx_errors;
++ stats->rx_dropped = n->cpt_rx_dropped;
++ stats->tx_dropped = n->cpt_tx_dropped;
++ stats->multicast = n->cpt_multicast;
++ stats->collisions = n->cpt_collisions;
++ stats->rx_length_errors = n->cpt_rx_length_errors;
++ stats->rx_over_errors = n->cpt_rx_over_errors;
++ stats->rx_crc_errors = n->cpt_rx_crc_errors;
++ stats->rx_frame_errors = n->cpt_rx_frame_errors;
++ stats->rx_fifo_errors = n->cpt_rx_fifo_errors;
++ stats->rx_missed_errors = n->cpt_rx_missed_errors;
++ stats->tx_aborted_errors = n->cpt_tx_aborted_errors;
++ stats->tx_carrier_errors = n->cpt_tx_carrier_errors;
++ stats->tx_fifo_errors = n->cpt_tx_fifo_errors;
++ stats->tx_heartbeat_errors = n->cpt_tx_heartbeat_errors;
++ stats->tx_window_errors = n->cpt_tx_window_errors;
++ stats->rx_compressed = n->cpt_rx_compressed;
++ stats->tx_compressed = n->cpt_tx_compressed;
++
++out:
++ preempt_enable();
++ cpt_release_buf(ctx);
++ return err;
++}
++
++int rst_restore_netdev(struct cpt_context *ctx)
++{
++ struct net *net = get_exec_env()->ve_netns;
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_NET_DEVICE];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_netdev_image di;
++ struct net_device *dev;
++
++ get_exec_env()->disable_net = 1;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NET_DEVICE || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ loff_t pos;
++ struct net_device *dev_new;
++ err = rst_get_object(CPT_OBJ_NET_DEVICE, sec, &di, ctx);
++ if (err)
++ return err;
++
++ pos = sec + di.cpt_hdrlen;
++ if (di.cpt_next > sizeof(di)) {
++ struct cpt_object_hdr hdr;
++ err = ctx->pread(&hdr, sizeof(struct cpt_object_hdr),
++ ctx, sec + di.cpt_hdrlen);
++ if (err)
++ return err;
++ if (hdr.cpt_object == CPT_OBJ_NET_TUNTAP) {
++ err = rst_restore_tuntap(sec, &di, ctx);
++ if (err) {
++ eprintk_ctx("restore tuntap %s: %d\n",
++ di.cpt_name, err);
++ return err;
++ }
++ pos += hdr.cpt_next;
++ }
++ }
++
++ rtnl_lock();
++ dev = __dev_get_by_name(net, di.cpt_name);
++ if (dev) {
++ if (dev->ifindex != di.cpt_index) {
++ dev_new = __dev_get_by_index(net, di.cpt_index);
++ if (!dev_new) {
++ write_lock_bh(&dev_base_lock);
++ hlist_del(&dev->index_hlist);
++ if (dev->iflink == dev->ifindex)
++ dev->iflink = di.cpt_index;
++ dev->ifindex = di.cpt_index;
++ hlist_add_head(&dev->index_hlist,
++ dev_index_hash(net, dev->ifindex));
++ write_unlock_bh(&dev_base_lock);
++ } else {
++ write_lock_bh(&dev_base_lock);
++ hlist_del(&dev->index_hlist);
++ hlist_del(&dev_new->index_hlist);
++ if (dev_new->iflink == dev_new->ifindex)
++ dev_new->iflink = dev->ifindex;
++ dev_new->ifindex = dev->ifindex;
++ if (dev->iflink == dev->ifindex)
++ dev->iflink = di.cpt_index;
++ dev->ifindex = di.cpt_index;
++ hlist_add_head(&dev->index_hlist,
++ dev_index_hash(net, dev->ifindex));
++ hlist_add_head(&dev_new->index_hlist,
++ dev_index_hash(net, dev_new->ifindex));
++ write_unlock_bh(&dev_base_lock);
++ }
++ }
++ if (di.cpt_flags^dev->flags) {
++ err = dev_change_flags(dev, di.cpt_flags);
++ if (err)
++ eprintk_ctx("dev_change_flags err: %d\n", err);
++ }
++ while (pos < sec + di.cpt_next) {
++ struct cpt_object_hdr hdr;
++ err = ctx->pread(&hdr, sizeof(struct cpt_object_hdr),
++ ctx, pos);
++ if (err)
++ goto out;
++ if (hdr.cpt_object == CPT_OBJ_NET_VETH) {
++ err = rst_restore_veth(pos, dev, ctx);
++ if (err) {
++ eprintk_ctx("restore veth %s: %d\n",
++ di.cpt_name, err);
++ goto out;
++ }
++ } else if (hdr.cpt_object == CPT_OBJ_NET_HWADDR) {
++ /* Restore hardware address */
++ struct cpt_hwaddr_image hw;
++ err = rst_get_object(CPT_OBJ_NET_HWADDR,
++ pos, &hw, ctx);
++ if (err)
++ goto out;
++ BUILD_BUG_ON(sizeof(hw.cpt_dev_addr) !=
++ sizeof(dev->dev_addr));
++ memcpy(dev->dev_addr, hw.cpt_dev_addr,
++ sizeof(hw.cpt_dev_addr));
++ } else if (hdr.cpt_object == CPT_OBJ_NET_STATS) {
++ err = rst_restore_netstats(pos, dev, ctx);
++ if (err) {
++ eprintk_ctx("rst stats %s: %d\n",
++ di.cpt_name, err);
++ goto out;
++ }
++ }
++ pos += hdr.cpt_next;
++ }
++ } else {
++ eprintk_ctx("unknown interface 2 %s\n", di.cpt_name);
++ }
++ rtnl_unlock();
++ sec += di.cpt_next;
++ }
++ return 0;
++out:
++ rtnl_unlock();
++ return err;
++}
++
++static int dumpfn(void *arg)
++{
++ int i;
++ int *pfd = arg;
++ char *argv[] = { "iptables-restore", "-c", NULL };
++
++ if (pfd[0] != 0)
++ sc_dup2(pfd[0], 0);
++
++ for (i=1; i<current->files->fdt->max_fds; i++)
++ sc_close(i);
++
++ module_put(THIS_MODULE);
++
++ set_fs(KERNEL_DS);
++ i = sc_execve("/sbin/iptables-restore", argv, NULL);
++ if (i == -ENOENT)
++ i = sc_execve("/usr/sbin/iptables-restore", argv, NULL);
++ eprintk("failed to exec iptables-restore: %d\n", i);
++ return 255 << 8;
++}
++
++static int rst_restore_iptables(struct cpt_context * ctx)
++{
++ int err;
++ int pfd[2];
++ struct file *f;
++ struct cpt_object_hdr v;
++ int n;
++ struct cpt_section_hdr h;
++ loff_t sec = ctx->sections[CPT_SECT_NET_IPTABLES];
++ loff_t end;
++ int pid;
++ int status;
++ mm_segment_t oldfs;
++ sigset_t ignore, blocked;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_NET_IPTABLES || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ if (h.cpt_hdrlen == h.cpt_next)
++ return 0;
++ if (h.cpt_hdrlen > h.cpt_next)
++ return -EINVAL;
++ sec += h.cpt_hdrlen;
++ err = rst_get_object(CPT_OBJ_NAME, sec, &v, ctx);
++ if (err < 0)
++ return err;
++
++ err = sc_pipe(pfd);
++ if (err < 0)
++ return err;
++ ignore.sig[0] = CPT_SIG_IGNORE_MASK;
++ sigprocmask(SIG_BLOCK, &ignore, &blocked);
++ pid = err = local_kernel_thread(dumpfn, (void*)pfd, SIGCHLD, 0);
++ if (err < 0) {
++ eprintk_ctx("iptables local_kernel_thread: %d\n", err);
++ goto out;
++ }
++ f = fget(pfd[1]);
++ sc_close(pfd[1]);
++ sc_close(pfd[0]);
++
++ ctx->file->f_pos = sec + v.cpt_hdrlen;
++ end = sec + v.cpt_next;
++ do {
++ char *p;
++ char buf[16];
++
++ n = end - ctx->file->f_pos;
++ if (n > sizeof(buf))
++ n = sizeof(buf);
++
++ if (ctx->read(buf, n, ctx))
++ break;
++ if ((p = memchr(buf, 0, n)) != NULL)
++ n = p - buf;
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ f->f_op->write(f, buf, n, &f->f_pos);
++ set_fs(oldfs);
++ } while (ctx->file->f_pos < end);
++
++ fput(f);
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if ((err = sc_waitx(pid, 0, &status)) < 0)
++ eprintk_ctx("wait4: %d\n", err);
++ else if ((status & 0x7f) == 0) {
++ err = (status & 0xff00) >> 8;
++ if (err != 0) {
++ eprintk_ctx("iptables-restore exited with %d\n", err);
++ err = -EINVAL;
++ }
++ } else {
++ eprintk_ctx("iptables-restore terminated\n");
++ err = -EINVAL;
++ }
++ set_fs(oldfs);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++
++ return err;
++
++out:
++ if (pfd[1] >= 0)
++ sc_close(pfd[1]);
++ if (pfd[0] >= 0)
++ sc_close(pfd[0]);
++ sigprocmask(SIG_SETMASK, &blocked, NULL);
++ return err;
++}
++
++int rst_restore_net(struct cpt_context *ctx)
++{
++ int err;
++
++ err = rst_restore_netdev(ctx);
++ if (!err)
++ err = rst_restore_ifaddr(ctx);
++ if (!err)
++ err = rst_restore_route(ctx);
++ if (!err)
++ err = rst_restore_iptables(ctx);
++ if (!err)
++ err = rst_restore_ip_conntrack(ctx);
++ return err;
++}
+diff --git a/kernel/cpt/rst_proc.c b/kernel/cpt/rst_proc.c
+new file mode 100644
+index 0000000..189649f
+--- /dev/null
++++ b/kernel/cpt/rst_proc.c
+@@ -0,0 +1,580 @@
++/*
++ *
++ * kernel/cpt/rst_proc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/proc_fs.h>
++#include <linux/smp_lock.h>
++#include <asm/uaccess.h>
++#include <linux/cpt_ioctl.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_dump.h"
++#include "cpt_files.h"
++#include "cpt_mm.h"
++#include "cpt_kernel.h"
++
++MODULE_AUTHOR("Alexey Kuznetsov <alexey@sw.ru>");
++MODULE_LICENSE("GPL");
++
++/* List of contexts and lock protecting the list */
++static struct list_head cpt_context_list;
++static spinlock_t cpt_context_lock;
++
++static int proc_read(char *buffer, char **start, off_t offset,
++ int length, int *eof, void *data)
++{
++ off_t pos = 0;
++ off_t begin = 0;
++ int len = 0;
++ cpt_context_t *ctx;
++
++ len += sprintf(buffer, "Ctx Id VE State\n");
++
++ spin_lock(&cpt_context_lock);
++
++ list_for_each_entry(ctx, &cpt_context_list, ctx_list) {
++ len += sprintf(buffer+len,"%p %08x %-8u %d",
++ ctx,
++ ctx->contextid,
++ ctx->ve_id,
++ ctx->ctx_state
++ );
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ len += pagein_info_printf(buffer+len, ctx);
++#endif
++
++ buffer[len++] = '\n';
++
++ pos = begin+len;
++ if (pos < offset) {
++ len = 0;
++ begin = pos;
++ }
++ if (pos > offset+length)
++ goto done;
++ }
++ *eof = 1;
++
++done:
++ spin_unlock(&cpt_context_lock);
++ *start = buffer + (offset - begin);
++ len -= (offset - begin);
++ if(len > length)
++ len = length;
++ if(len < 0)
++ len = 0;
++ return len;
++}
++
++void rst_context_release(cpt_context_t *ctx)
++{
++ list_del(&ctx->ctx_list);
++ spin_unlock(&cpt_context_lock);
++
++ if (ctx->ctx_state > 0)
++ rst_resume(ctx);
++ ctx->ctx_state = CPT_CTX_ERROR;
++
++ rst_close_dumpfile(ctx);
++
++ if (ctx->anonvmas) {
++ int h;
++ for (h = 0; h < CPT_ANONVMA_HSIZE; h++) {
++ while (!hlist_empty(&ctx->anonvmas[h])) {
++ struct hlist_node *elem = ctx->anonvmas[h].first;
++ hlist_del(elem);
++ kfree(elem);
++ }
++ }
++ free_page((unsigned long)ctx->anonvmas);
++ }
++ cpt_flush_error(ctx);
++ if (ctx->errorfile) {
++ fput(ctx->errorfile);
++ ctx->errorfile = NULL;
++ }
++ if (ctx->error_msg) {
++ free_page((unsigned long)ctx->error_msg);
++ ctx->error_msg = NULL;
++ }
++#ifdef CONFIG_VZ_CHECKPOINT_ITER
++ rst_drop_iter_dir(ctx);
++#endif
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ if (ctx->pagein_file_out)
++ fput(ctx->pagein_file_out);
++ if (ctx->pagein_file_in)
++ fput(ctx->pagein_file_in);
++ if (ctx->pgin_task)
++ put_task_struct(ctx->pgin_task);
++#endif
++ if (ctx->filejob_queue)
++ rst_flush_filejobs(ctx);
++ if (ctx->vdso)
++ free_page((unsigned long)ctx->vdso);
++ if (ctx->objcount)
++ eprintk_ctx("%d objects leaked\n", ctx->objcount);
++ kfree(ctx);
++
++ spin_lock(&cpt_context_lock);
++}
++
++static void __cpt_context_put(cpt_context_t *ctx)
++{
++ if (!--ctx->refcount)
++ rst_context_release(ctx);
++}
++
++static void cpt_context_put(cpt_context_t *ctx)
++{
++ spin_lock(&cpt_context_lock);
++ __cpt_context_put(ctx);
++ spin_unlock(&cpt_context_lock);
++}
++
++cpt_context_t * rst_context_open(void)
++{
++ cpt_context_t *ctx;
++
++ if ((ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)) != NULL) {
++ rst_context_init(ctx);
++ spin_lock(&cpt_context_lock);
++ list_add_tail(&ctx->ctx_list, &cpt_context_list);
++ spin_unlock(&cpt_context_lock);
++ ctx->error_msg = (char*)__get_free_page(GFP_KERNEL);
++ if (ctx->error_msg != NULL)
++ ctx->error_msg[0] = 0;
++ }
++ return ctx;
++}
++
++void rst_report_error(int err, cpt_context_t *ctx)
++{
++ if (ctx->statusfile) {
++ mm_segment_t oldfs;
++ int status = 7 /* VZ_ENVCREATE_ERROR */;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (ctx->statusfile->f_op && ctx->statusfile->f_op->write)
++ ctx->statusfile->f_op->write(ctx->statusfile, (char*)&status, sizeof(status), &ctx->statusfile->f_pos);
++ set_fs(oldfs);
++ fput(ctx->statusfile);
++ ctx->statusfile = NULL;
++ }
++}
++
++
++static cpt_context_t * cpt_context_lookup(unsigned int ctxid)
++{
++ cpt_context_t *ctx;
++
++ spin_lock(&cpt_context_lock);
++ list_for_each_entry(ctx, &cpt_context_list, ctx_list) {
++ if (ctx->contextid == ctxid) {
++ ctx->refcount++;
++ spin_unlock(&cpt_context_lock);
++ return ctx;
++ }
++ }
++ spin_unlock(&cpt_context_lock);
++ return NULL;
++}
++
++static int rst_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
++{
++ int err = 0;
++ cpt_context_t *ctx;
++ struct file *dfile = NULL;
++
++ unlock_kernel();
++
++ if (cmd == CPT_TEST_CAPS) {
++ err = test_cpu_caps();
++ goto out_lock;
++ }
++
++ if (cmd == CPT_JOIN_CONTEXT || cmd == CPT_PUT_CONTEXT) {
++ cpt_context_t *old_ctx;
++
++ ctx = NULL;
++ if (cmd == CPT_JOIN_CONTEXT) {
++ err = -ENOENT;
++ ctx = cpt_context_lookup(arg);
++ if (!ctx)
++ goto out_lock;
++ }
++
++ spin_lock(&cpt_context_lock);
++ old_ctx = (cpt_context_t*)file->private_data;
++ file->private_data = ctx;
++
++ if (old_ctx) {
++ if (cmd == CPT_PUT_CONTEXT && old_ctx->sticky) {
++ old_ctx->sticky = 0;
++ old_ctx->refcount--;
++ }
++ __cpt_context_put(old_ctx);
++ }
++ spin_unlock(&cpt_context_lock);
++ err = 0;
++ goto out_lock;
++ }
++
++ spin_lock(&cpt_context_lock);
++ ctx = (cpt_context_t*)file->private_data;
++ if (ctx)
++ ctx->refcount++;
++ spin_unlock(&cpt_context_lock);
++
++ if (!ctx) {
++ cpt_context_t *old_ctx;
++
++ err = -ENOMEM;
++ ctx = rst_context_open();
++ if (!ctx)
++ goto out_lock;
++
++ spin_lock(&cpt_context_lock);
++ old_ctx = (cpt_context_t*)file->private_data;
++ if (!old_ctx) {
++ ctx->refcount++;
++ file->private_data = ctx;
++ } else {
++ old_ctx->refcount++;
++ }
++ if (old_ctx) {
++ __cpt_context_put(ctx);
++ ctx = old_ctx;
++ }
++ spin_unlock(&cpt_context_lock);
++ }
++
++ if (cmd == CPT_GET_CONTEXT) {
++ unsigned int contextid = (unsigned int)arg;
++
++ err = -EINVAL;
++ if (ctx->contextid && ctx->contextid != contextid)
++ goto out_nosem;
++ if (!ctx->contextid) {
++ cpt_context_t *c1 = cpt_context_lookup(contextid);
++ if (c1) {
++ cpt_context_put(c1);
++ err = -EEXIST;
++ goto out_nosem;
++ }
++ ctx->contextid = contextid;
++ }
++ spin_lock(&cpt_context_lock);
++ if (!ctx->sticky) {
++ ctx->sticky = 1;
++ ctx->refcount++;
++ }
++ spin_unlock(&cpt_context_lock);
++ err = 0;
++ goto out_nosem;
++ }
++
++ down(&ctx->main_sem);
++
++ err = -EBUSY;
++ if (ctx->ctx_state < 0)
++ goto out;
++
++ err = 0;
++ switch (cmd) {
++ case CPT_SET_DUMPFD:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ err = -EBADF;
++ dfile = fget(arg);
++ if (dfile == NULL)
++ break;
++ if (dfile->f_op == NULL ||
++ dfile->f_op->read == NULL) {
++ fput(dfile);
++ break;
++ }
++ err = 0;
++ }
++ if (ctx->file)
++ fput(ctx->file);
++ ctx->file = dfile;
++ break;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ case CPT_SET_PAGEINFDIN:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->pagein_file_in)
++ fput(ctx->pagein_file_in);
++ ctx->pagein_file_in = dfile;
++ break;
++ case CPT_SET_PAGEINFDOUT:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->pagein_file_out)
++ fput(ctx->pagein_file_out);
++ ctx->pagein_file_out = dfile;
++ break;
++ case CPT_PAGEIND:
++ err = rst_pageind(ctx);
++ break;
++#endif
++#ifdef CONFIG_VZ_CHECKPOINT_ITER
++ case CPT_ITER:
++ err = rst_iteration(ctx);
++ break;
++#endif
++ case CPT_SET_LOCKFD:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->lockfile)
++ fput(ctx->lockfile);
++ ctx->lockfile = dfile;
++ break;
++ case CPT_SET_STATUSFD:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->statusfile)
++ fput(ctx->statusfile);
++ ctx->statusfile = dfile;
++ break;
++ case CPT_SET_ERRORFD:
++ if (arg >= 0) {
++ dfile = fget(arg);
++ if (dfile == NULL) {
++ err = -EBADF;
++ break;
++ }
++ }
++ if (ctx->errorfile)
++ fput(ctx->errorfile);
++ ctx->errorfile = dfile;
++ break;
++ case CPT_SET_VEID:
++ if (ctx->ctx_state > 0) {
++ err = -EBUSY;
++ break;
++ }
++ ctx->ve_id = arg;
++ break;
++ case CPT_UNDUMP:
++ if (ctx->ctx_state > 0) {
++ err = -ENOENT;
++ break;
++ }
++ ctx->ctx_state = CPT_CTX_UNDUMPING;
++ err = vps_rst_undump(ctx);
++ if (err) {
++ rst_report_error(err, ctx);
++ if (rst_kill(ctx) == 0)
++ ctx->ctx_state = CPT_CTX_IDLE;
++ } else {
++ ctx->ctx_state = CPT_CTX_UNDUMPED;
++ }
++ break;
++ case CPT_RESUME:
++ if (!ctx->ctx_state) {
++ err = -ENOENT;
++ break;
++ }
++ err = rst_resume(ctx);
++ if (!err)
++ ctx->ctx_state = CPT_CTX_IDLE;
++ break;
++ case CPT_KILL:
++ if (!ctx->ctx_state) {
++ err = -ENOENT;
++ break;
++ }
++ err = rst_kill(ctx);
++ if (!err)
++ ctx->ctx_state = CPT_CTX_IDLE;
++ break;
++ default:
++ err = -EINVAL;
++ break;
++ }
++
++out:
++ cpt_flush_error(ctx);
++ up(&ctx->main_sem);
++out_nosem:
++ cpt_context_put(ctx);
++out_lock:
++ lock_kernel();
++ if (err == -ERESTARTSYS || err == -ERESTARTNOINTR ||
++ err == -ERESTARTNOHAND || err == -ERESTART_RESTARTBLOCK)
++ err = -EINTR;
++ return err;
++}
++
++static int rst_open(struct inode * inode, struct file * file)
++{
++ if (!try_module_get(THIS_MODULE))
++ return -EBUSY;
++
++ return 0;
++}
++
++static int rst_release(struct inode * inode, struct file * file)
++{
++ cpt_context_t *ctx;
++
++ spin_lock(&cpt_context_lock);
++ ctx = (cpt_context_t*)file->private_data;
++ file->private_data = NULL;
++ if (ctx)
++ __cpt_context_put(ctx);
++ spin_unlock(&cpt_context_lock);
++
++
++ module_put(THIS_MODULE);
++ return 0;
++}
++
++static struct file_operations rst_fops =
++{
++ .owner = THIS_MODULE,
++ .ioctl = rst_ioctl,
++ .open = rst_open,
++ .release = rst_release,
++};
++
++
++static struct proc_dir_entry *proc_ent;
++extern void *schedule_tail_p;
++extern void schedule_tail_hook(void);
++
++static struct ctl_table_header *ctl_header;
++
++static ctl_table debug_table[] = {
++ {
++ .procname = "rst",
++ .data = &debug_level,
++ .maxlen = sizeof(debug_level),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ { .ctl_name = 0 }
++};
++static ctl_table root_table[] = {
++ {
++ .ctl_name = CTL_DEBUG,
++ .procname = "debug",
++ .mode = 0555,
++ .child = debug_table,
++ },
++ { .ctl_name = 0 }
++};
++
++static int __init init_rst(void)
++{
++ int err;
++
++ err = -ENOMEM;
++ ctl_header = register_sysctl_table(root_table);
++ if (!ctl_header)
++ goto err_mon;
++
++ spin_lock_init(&cpt_context_lock);
++ INIT_LIST_HEAD(&cpt_context_list);
++
++ err = -EINVAL;
++ proc_ent = proc_create("rst", 0600, NULL, NULL);
++ if (!proc_ent)
++ goto err_out;
++
++ rst_fops.read = proc_ent->proc_fops->read;
++ rst_fops.write = proc_ent->proc_fops->write;
++ rst_fops.llseek = proc_ent->proc_fops->llseek;
++ proc_ent->proc_fops = &rst_fops;
++
++ proc_ent->read_proc = proc_read;
++ proc_ent->data = NULL;
++ proc_ent->owner = THIS_MODULE;
++ return 0;
++
++err_out:
++ unregister_sysctl_table(ctl_header);
++err_mon:
++ return err;
++}
++module_init(init_rst);
++
++static void __exit exit_rst(void)
++{
++ remove_proc_entry("rst", NULL);
++ unregister_sysctl_table(ctl_header);
++
++ spin_lock(&cpt_context_lock);
++ while (!list_empty(&cpt_context_list)) {
++ cpt_context_t *ctx;
++ ctx = list_entry(cpt_context_list.next, cpt_context_t, ctx_list);
++
++ if (!ctx->sticky)
++ ctx->refcount++;
++ ctx->sticky = 0;
++
++ BUG_ON(ctx->refcount != 1);
++
++ __cpt_context_put(ctx);
++ }
++ spin_unlock(&cpt_context_lock);
++}
++module_exit(exit_rst);
+diff --git a/kernel/cpt/rst_process.c b/kernel/cpt/rst_process.c
+new file mode 100644
+index 0000000..38e0c38
+--- /dev/null
++++ b/kernel/cpt/rst_process.c
+@@ -0,0 +1,1641 @@
++/*
++ *
++ * kernel/cpt/rst_process.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/virtinfo.h>
++#include <linux/virtinfoscp.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++#include <linux/ptrace.h>
++#include <linux/tty.h>
++#include <linux/nsproxy.h>
++#include <linux/securebits.h>
++#ifdef CONFIG_X86
++#include <asm/desc.h>
++#endif
++#include <asm/unistd.h>
++
++#include <bc/beancounter.h>
++#include <bc/misc.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_files.h"
++#include "cpt_mm.h"
++#include "cpt_ubc.h"
++#include "cpt_process.h"
++#include "cpt_kernel.h"
++
++
++#define HOOK_RESERVE 256
++
++struct resume_info
++{
++ asmlinkage void (*hook)(struct resume_info *);
++ unsigned long hooks;
++#define HOOK_TID 0
++#define HOOK_CONT 1
++#define HOOK_LSI 2
++#define HOOK_RESTART 3
++ unsigned long tid_ptrs[2];
++ siginfo_t last_siginfo;
++};
++
++#ifdef CONFIG_X86_32
++
++#define IN_SYSCALL(regs) ((long)(regs)->orig_ax >= 0)
++#define IN_ERROR(regs) ((long)(regs)->ax < 0)
++#define SYSCALL_ERRNO(regs) (-(long)((regs)->ax))
++#define SYSCALL_RETVAL(regs) ((regs)->ax)
++#define SYSCALL_NR(regs) ((regs)->orig_ax)
++
++#define SYSCALL_SETRET(regs,val) do { (regs)->ax = (val); } while (0)
++
++#define SYSCALL_RESTART2(regs,new) do { (regs)->ax = (new); \
++ (regs)->ip -= 2; } while (0)
++
++#define syscall_is(tsk,regs,name) (SYSCALL_NR(regs) == __NR_##name)
++
++/* In new kernels task_pt_regs() is define to something inappropriate */
++#undef task_pt_regs
++#define task_pt_regs(t) ((struct pt_regs *)((t)->thread.sp0) - 1)
++
++#elif defined(CONFIG_X86_64)
++
++#define IN_SYSCALL(regs) ((long)(regs)->orig_ax >= 0)
++#define IN_ERROR(regs) ((long)(regs)->ax < 0)
++#define SYSCALL_ERRNO(regs) (-(long)((regs)->ax))
++#define SYSCALL_RETVAL(regs) ((regs)->ax)
++#define SYSCALL_NR(regs) ((regs)->orig_ax)
++
++#define SYSCALL_SETRET(regs,val) do { (regs)->ax = (val); } while (0)
++
++#define SYSCALL_RESTART2(regs,new) do { (regs)->ax = (new); \
++ (regs)->ip -= 2; } while (0)
++
++#define __NR32_restart_syscall 0
++#define __NR32_rt_sigtimedwait 177
++#define __NR32_pause 29
++#define __NR32_futex 240
++
++#define syscall_is(tsk,regs,name) ((!(task_thread_info(tsk)->flags&_TIF_IA32) && \
++ SYSCALL_NR(regs) == __NR_##name) || \
++ ((task_thread_info(tsk)->flags&_TIF_IA32) && \
++ SYSCALL_NR(regs) == __NR32_##name))
++
++#elif defined (CONFIG_IA64)
++
++#define IN_SYSCALL(regs) ((long)(regs)->cr_ifs >= 0)
++#define IN_ERROR(regs) ((long)(regs)->r10 == -1)
++#define SYSCALL_ERRNO(regs) ((regs)->r10 == -1 ? (long)((regs)->r8) : 0)
++#define SYSCALL_RETVAL(regs) ((regs)->r8)
++#define SYSCALL_NR(regs) ((regs)->cr_ifs >= 0 ? (regs)->r15 : -1)
++
++#define SYSCALL_SETRET(regs,val) do { (regs)->r8 = (val); } while (0)
++
++#define SYSCALL_RESTART2(regs,new) do { (regs)->r15 = (new); \
++ (regs)->r10 = 0; \
++ ia64_decrement_ip(regs); } while (0)
++
++#define syscall_is(tsk,regs,name) (SYSCALL_NR(regs) == __NR_##name)
++
++#else
++
++#error This arch is not supported
++
++#endif
++
++#define SYSCALL_RESTART(regs) SYSCALL_RESTART2(regs, SYSCALL_NR(regs))
++
++pid_t vpid_to_pid(pid_t nr)
++{
++ pid_t vnr;
++ struct pid *pid;
++
++ rcu_read_lock();
++ pid = find_vpid(nr);
++ vnr = (pid == NULL ? -1 : pid->numbers[0].nr);
++ rcu_read_unlock();
++ return vnr;
++}
++
++static void decode_siginfo(siginfo_t *info, struct cpt_siginfo_image *si)
++{
++ memset(info, 0, sizeof(*info));
++ switch(si->cpt_code & __SI_MASK) {
++ case __SI_TIMER:
++ info->si_tid = si->cpt_pid;
++ info->si_overrun = si->cpt_uid;
++ info->_sifields._timer._sigval.sival_ptr = cpt_ptr_import(si->cpt_sigval);
++ info->si_sys_private = si->cpt_utime;
++ break;
++ case __SI_POLL:
++ info->si_band = si->cpt_pid;
++ info->si_fd = si->cpt_uid;
++ break;
++ case __SI_FAULT:
++ info->si_addr = cpt_ptr_import(si->cpt_sigval);
++#ifdef __ARCH_SI_TRAPNO
++ info->si_trapno = si->cpt_pid;
++#endif
++ break;
++ case __SI_CHLD:
++ info->si_pid = si->cpt_pid;
++ info->si_uid = si->cpt_uid;
++ info->si_status = si->cpt_sigval;
++ info->si_stime = si->cpt_stime;
++ info->si_utime = si->cpt_utime;
++ break;
++ case __SI_KILL:
++ case __SI_RT:
++ case __SI_MESGQ:
++ default:
++ info->si_pid = si->cpt_pid;
++ info->si_uid = si->cpt_uid;
++ info->si_ptr = cpt_ptr_import(si->cpt_sigval);
++ break;
++ }
++ info->si_signo = si->cpt_signo;
++ info->si_errno = si->cpt_errno;
++ info->si_code = si->cpt_code;
++}
++
++static int restore_sigqueue(struct task_struct *tsk,
++ struct sigpending *queue, unsigned long start,
++ unsigned long end)
++{
++ while (start < end) {
++ struct cpt_siginfo_image *si = (struct cpt_siginfo_image *)start;
++ if (si->cpt_object == CPT_OBJ_SIGINFO) {
++ struct sigqueue *q = NULL;
++ struct user_struct *up;
++
++ up = alloc_uid(get_exec_env()->ve_ns->user_ns, si->cpt_user);
++ if (!up)
++ return -ENOMEM;
++ q = kmem_cache_alloc(sigqueue_cachep, GFP_ATOMIC);
++ if (!q) {
++ free_uid(up);
++ return -ENOMEM;
++ }
++ if (ub_siginfo_charge(q, get_exec_ub())) {
++ kmem_cache_free(sigqueue_cachep, q);
++ free_uid(up);
++ return -ENOMEM;
++ }
++
++ INIT_LIST_HEAD(&q->list);
++ /* Preallocated elements (posix timers) are not
++ * supported yet. It is safe to replace them with
++ * a private one. */
++ q->flags = 0;
++ q->user = up;
++ atomic_inc(&q->user->sigpending);
++
++ decode_siginfo(&q->info, si);
++ list_add_tail(&q->list, &queue->list);
++ }
++ start += si->cpt_next;
++ }
++ return 0;
++}
++
++int rst_process_linkage(cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ struct cpt_task_image *ti = obj->o_image;
++
++ if (tsk == NULL) {
++ eprintk_ctx("task %u(%s) is missing\n", ti->cpt_pid, ti->cpt_comm);
++ return -EINVAL;
++ }
++
++ if (task_pgrp_vnr(tsk) != ti->cpt_pgrp) {
++ struct pid *pid;
++
++ rcu_read_lock();
++ pid = find_vpid(ti->cpt_pgrp);
++ if (!pid) {
++ eprintk_ctx("illegal PGRP " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++
++ write_lock_irq(&tasklist_lock);
++ if (task_pgrp_nr(tsk) != pid_nr(pid)) {
++ detach_pid(tsk, PIDTYPE_PGID);
++ set_task_pgrp(tsk, pid_nr(pid));
++ if (thread_group_leader(tsk))
++ attach_pid(tsk, PIDTYPE_PGID, pid);
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (task_pgrp_nr(tsk) != pid_nr(pid)) {
++ eprintk_ctx("cannot set PGRP " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++ rcu_read_unlock();
++ }
++ if (task_session_vnr(tsk) != ti->cpt_session) {
++ struct pid *pid;
++
++ rcu_read_lock();
++ pid = find_vpid(ti->cpt_session);
++ if (!pid) {
++ eprintk_ctx("illegal SID " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++
++ write_lock_irq(&tasklist_lock);
++ if (task_session_nr(tsk) != pid_nr(pid)) {
++ detach_pid(tsk, PIDTYPE_SID);
++ set_task_session(tsk, pid_nr(pid));
++ if (thread_group_leader(tsk))
++ attach_pid(tsk, PIDTYPE_SID, pid);
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (task_session_nr(tsk) != pid_nr(pid)) {
++ eprintk_ctx("cannot set SID " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++ rcu_read_unlock();
++ }
++ if (ti->cpt_old_pgrp > 0 && !tsk->signal->tty_old_pgrp) {
++ struct pid *pid;
++
++ rcu_read_lock();
++ pid = get_pid(find_vpid(ti->cpt_old_pgrp));
++ if (!pid) {
++ eprintk_ctx("illegal OLD_PGRP " CPT_FID "\n", CPT_TID(tsk));
++ return -EINVAL;
++ }
++ tsk->signal->tty_old_pgrp = pid;
++ rcu_read_unlock();
++ }
++ }
++
++ return 0;
++}
++
++struct pid *alloc_vpid_safe(pid_t vnr)
++{
++ struct pid *pid;
++
++ pid = alloc_pid(current->nsproxy->pid_ns, vnr);
++ if (!pid)
++ pid = find_vpid(vnr);
++ return pid;
++}
++
++static int
++restore_one_signal_struct(struct cpt_task_image *ti, int *exiting, cpt_context_t *ctx)
++{
++ int err;
++ struct cpt_signal_image *si = cpt_get_buf(ctx);
++
++ current->signal->tty = NULL;
++
++ err = rst_get_object(CPT_OBJ_SIGNAL_STRUCT, ti->cpt_signal, si, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++
++ if (task_pgrp_vnr(current) != si->cpt_pgrp) {
++ struct pid * pid = NULL, *free = NULL;
++
++ rcu_read_lock();
++ if (si->cpt_pgrp_type == CPT_PGRP_ORPHAN) {
++#if 0
++ if (!is_virtual_pid(si->cpt_pgrp)) {
++ eprintk_ctx("external process group " CPT_FID, CPT_TID(current));
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++#endif
++ pid = alloc_vpid_safe(si->cpt_pgrp);
++ free = pid;
++ }
++ write_lock_irq(&tasklist_lock);
++ if (pid != NULL) {
++ if (task_pgrp_nr(current) != pid_nr(pid)) {
++ detach_pid(current, PIDTYPE_PGID);
++ set_task_pgrp(current, pid_nr(pid));
++ if (thread_group_leader(current)) {
++ attach_pid(current, PIDTYPE_PGID, pid);
++ free = NULL;
++ }
++ }
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (free != NULL)
++ free_pid(free);
++ rcu_read_unlock();
++ }
++
++ current->signal->tty_old_pgrp = NULL;
++ if ((int)si->cpt_old_pgrp > 0) {
++ if (si->cpt_old_pgrp_type == CPT_PGRP_STRAY) {
++ current->signal->tty_old_pgrp =
++ alloc_pid(current->nsproxy->pid_ns, 0);
++ if (!current->signal->tty_old_pgrp) {
++ eprintk_ctx("failed to allocate stray tty_old_pgrp\n");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ } else {
++ rcu_read_lock();
++ current->signal->tty_old_pgrp =
++ get_pid(alloc_vpid_safe(si->cpt_old_pgrp));
++ rcu_read_unlock();
++ if (!current->signal->tty_old_pgrp) {
++ dprintk_ctx("forward old tty PGID\n");
++ current->signal->tty_old_pgrp = NULL;
++ }
++ }
++ }
++
++ if (task_session_vnr(current) != si->cpt_session) {
++ struct pid * pid = NULL, *free = NULL;
++
++ rcu_read_lock();
++ if (si->cpt_session_type == CPT_PGRP_ORPHAN) {
++#if 0
++ if (!is_virtual_pid(si->cpt_session)) {
++ eprintk_ctx("external process session " CPT_FID, CPT_TID(current));
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++#endif
++ pid = alloc_vpid_safe(si->cpt_session);
++ free = pid;
++ }
++ write_lock_irq(&tasklist_lock);
++ if (pid == NULL)
++ pid = find_vpid(si->cpt_session);
++ if (pid != NULL) {
++ if (task_session_nr(current) != pid_nr(pid)) {
++ detach_pid(current, PIDTYPE_SID);
++ set_task_session(current, pid_nr(pid));
++ if (thread_group_leader(current)) {
++ attach_pid(current, PIDTYPE_SID, pid);
++ free = NULL;
++ }
++ }
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (free != NULL)
++ free_pid(free);
++ rcu_read_unlock();
++ }
++
++ cpt_sigset_import(&current->signal->shared_pending.signal, si->cpt_sigpending);
++ current->signal->leader = si->cpt_leader;
++ if (si->cpt_ctty != CPT_NULL) {
++ cpt_object_t *obj = lookup_cpt_obj_bypos(CPT_OBJ_TTY, si->cpt_ctty, ctx);
++ if (obj) {
++ struct tty_struct *tty = obj->o_obj;
++ if (!tty->session || tty->session ==
++ task_session(current)) {
++ tty->session = task_session(current);
++ current->signal->tty = tty;
++ } else {
++ wprintk_ctx("tty session mismatch\n");
++ }
++ }
++ }
++
++ if (si->cpt_curr_target)
++ current->signal->curr_target = find_task_by_vpid(si->cpt_curr_target);
++ current->signal->flags = 0;
++ *exiting = si->cpt_group_exit;
++ current->signal->group_exit_code = si->cpt_group_exit_code;
++ if (si->cpt_group_exit_task) {
++ current->signal->group_exit_task = find_task_by_vpid(si->cpt_group_exit_task);
++ if (current->signal->group_exit_task == NULL) {
++ eprintk_ctx("oops, group_exit_task=NULL, pid=%u\n", si->cpt_group_exit_task);
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ current->signal->notify_count = si->cpt_notify_count;
++ current->signal->group_stop_count = si->cpt_group_stop_count;
++
++ if (si->cpt_next > si->cpt_hdrlen) {
++ char *buf = kmalloc(si->cpt_next - si->cpt_hdrlen, GFP_KERNEL);
++ if (buf == NULL) {
++ cpt_release_buf(ctx);
++ return -ENOMEM;
++ }
++ err = ctx->pread(buf, si->cpt_next - si->cpt_hdrlen, ctx,
++ ti->cpt_signal + si->cpt_hdrlen);
++ if (err) {
++ kfree(buf);
++ cpt_release_buf(ctx);
++ return err;
++ }
++ restore_sigqueue(current,
++ &current->signal->shared_pending, (unsigned long)buf,
++ (unsigned long)buf + si->cpt_next - si->cpt_hdrlen);
++ kfree(buf);
++ }
++ cpt_release_buf(ctx);
++ return 0;
++}
++
++int restore_one_sighand_struct(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_sighand_image si;
++ int i;
++ loff_t pos, endpos;
++
++ err = rst_get_object(CPT_OBJ_SIGHAND_STRUCT, ti->cpt_sighand, &si, ctx);
++ if (err)
++ return err;
++
++ for (i=0; i<_NSIG; i++) {
++ current->sighand->action[i].sa.sa_handler = SIG_DFL;
++#ifndef CONFIG_IA64
++ current->sighand->action[i].sa.sa_restorer = 0;
++#endif
++ current->sighand->action[i].sa.sa_flags = 0;
++ memset(&current->sighand->action[i].sa.sa_mask, 0, sizeof(sigset_t));
++ }
++
++ pos = ti->cpt_sighand + si.cpt_hdrlen;
++ endpos = ti->cpt_sighand + si.cpt_next;
++ while (pos < endpos) {
++ struct cpt_sighandler_image shi;
++
++ err = rst_get_object(CPT_OBJ_SIGHANDLER, pos, &shi, ctx);
++ if (err)
++ return err;
++ current->sighand->action[shi.cpt_signo].sa.sa_handler = (void*)(unsigned long)shi.cpt_handler;
++#ifndef CONFIG_IA64
++ current->sighand->action[shi.cpt_signo].sa.sa_restorer = (void*)(unsigned long)shi.cpt_restorer;
++#endif
++ current->sighand->action[shi.cpt_signo].sa.sa_flags = shi.cpt_flags;
++ cpt_sigset_import(&current->sighand->action[shi.cpt_signo].sa.sa_mask, shi.cpt_mask);
++ pos += shi.cpt_next;
++ }
++
++ return 0;
++}
++
++
++__u32 rst_signal_flag(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ __u32 flag = 0;
++
++ if (lookup_cpt_obj_bypos(CPT_OBJ_SIGNAL_STRUCT, ti->cpt_signal, ctx))
++ flag |= CLONE_THREAD;
++ if (ti->cpt_sighand == CPT_NULL ||
++ lookup_cpt_obj_bypos(CPT_OBJ_SIGHAND_STRUCT, ti->cpt_sighand, ctx))
++ flag |= CLONE_SIGHAND;
++ return flag;
++}
++
++int
++rst_signal_complete(struct cpt_task_image *ti, int * exiting, cpt_context_t *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++
++ if (ti->cpt_signal == CPT_NULL || ti->cpt_sighand == CPT_NULL) {
++ return -EINVAL;
++ }
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_SIGHAND_STRUCT, ti->cpt_sighand, ctx);
++ if (obj) {
++ struct sighand_struct *sig = current->sighand;
++ if (obj->o_obj != sig) {
++ return -EINVAL;
++ }
++ } else {
++ obj = cpt_object_add(CPT_OBJ_SIGHAND_STRUCT, current->sighand, ctx);
++ if (obj == NULL)
++ return -ENOMEM;
++ cpt_obj_setpos(obj, ti->cpt_sighand, ctx);
++ err = restore_one_sighand_struct(ti, ctx);
++ if (err)
++ return err;
++ }
++
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_SIGNAL_STRUCT, ti->cpt_signal, ctx);
++ if (obj) {
++ struct signal_struct *sig = current->signal;
++ if (obj->o_obj != sig) {
++ return -EINVAL;
++ }
++/* if (current->signal) {
++ pid_t session;
++
++ session = process_session(current);
++ set_process_vgroup(current, session);
++ set_signal_vsession(current->signal, session);
++ }*/
++ } else {
++ obj = cpt_object_add(CPT_OBJ_SIGNAL_STRUCT, current->signal, ctx);
++ if (obj == NULL)
++ return -ENOMEM;
++ cpt_obj_setpos(obj, ti->cpt_signal, ctx);
++ err = restore_one_signal_struct(ti, exiting, ctx);
++ if (err)
++ return err;
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_X86
++static u32 decode_segment(u32 segid)
++{
++ if (segid == CPT_SEG_ZERO)
++ return 0;
++
++ /* TLS descriptors */
++ if (segid <= CPT_SEG_TLS3)
++ return ((GDT_ENTRY_TLS_MIN + segid-CPT_SEG_TLS1)<<3) + 3;
++
++ /* LDT descriptor, it is just an index to LDT array */
++ if (segid >= CPT_SEG_LDT)
++ return ((segid - CPT_SEG_LDT) << 3) | 7;
++
++ /* Check for one of standard descriptors */
++#ifdef CONFIG_X86_64
++ if (segid == CPT_SEG_USER32_DS)
++ return __USER32_DS;
++ if (segid == CPT_SEG_USER32_CS)
++ return __USER32_CS;
++ if (segid == CPT_SEG_USER64_DS)
++ return __USER_DS;
++ if (segid == CPT_SEG_USER64_CS)
++ return __USER_CS;
++#else
++ if (segid == CPT_SEG_USER32_DS)
++ return __USER_DS;
++ if (segid == CPT_SEG_USER32_CS)
++ return __USER_CS;
++#endif
++ wprintk("Invalid segment reg %d\n", segid);
++ return 0;
++}
++#endif
++
++#if defined (CONFIG_IA64)
++void ia64_decrement_ip (struct pt_regs *regs)
++{
++ unsigned long w0, ri = ia64_psr(regs)->ri - 1;
++
++ if (ia64_psr(regs)->ri == 0) {
++ regs->cr_iip -= 16;
++ ri = 2;
++ get_user(w0, (char __user *) regs->cr_iip + 0);
++ if (((w0 >> 1) & 0xf) == 2) {
++ /*
++ * rfi'ing to slot 2 of an MLX bundle causes
++ * an illegal operation fault. We don't want
++ * that to happen...
++ */
++ ri = 1;
++ }
++ }
++ ia64_psr(regs)->ri = ri;
++}
++#endif
++
++static void rst_child_tid(unsigned long *child_tids)
++{
++ dprintk("rct: " CPT_FID "\n", CPT_TID(current));
++ current->clear_child_tid = (void*)child_tids[0];
++ current->set_child_tid = (void*)child_tids[1];
++}
++
++static void rst_last_siginfo(void)
++{
++ int signr;
++ siginfo_t *info = current->last_siginfo;
++ struct pt_regs *regs = task_pt_regs(current);
++ struct k_sigaction *ka;
++ int ptrace_id;
++
++ dprintk("rlsi: " CPT_FID "\n", CPT_TID(current));
++
++ spin_lock_irq(&current->sighand->siglock);
++ current->last_siginfo = NULL;
++ recalc_sigpending();
++
++ ptrace_id = current->pn_state;
++ clear_pn_state(current);
++
++ switch (ptrace_id) {
++ case PN_STOP_TF:
++ case PN_STOP_TF_RT:
++ /* frame_*signal */
++ dprintk("SIGTRAP %u/%u(%s) %u/%u %u %ld %u %lu\n",
++ task_pid_vnr(current), current->pid, current->comm,
++ info->si_signo, info->si_code,
++ current->exit_code, SYSCALL_NR(regs),
++ current->ptrace, current->ptrace_message);
++ goto out;
++ case PN_STOP_ENTRY:
++ case PN_STOP_LEAVE:
++ /* do_syscall_trace */
++ spin_unlock_irq(&current->sighand->siglock);
++ dprintk("ptrace do_syscall_trace: %d %d\n", ptrace_id, current->exit_code);
++ if (current->exit_code) {
++ send_sig(current->exit_code, current, 1);
++ current->exit_code = 0;
++ }
++ if (IN_SYSCALL(regs)) {
++ if (ptrace_id == PN_STOP_ENTRY
++#ifdef CONFIG_X86
++ && SYSCALL_ERRNO(regs) == ENOSYS
++#endif
++ )
++ SYSCALL_RESTART(regs);
++ else if (IN_ERROR(regs) &&
++ syscall_is(current, regs, rt_sigtimedwait) &&
++ (SYSCALL_ERRNO(regs) == EAGAIN ||
++ SYSCALL_ERRNO(regs) == EINTR))
++ SYSCALL_RESTART(regs);
++ }
++ return;
++ case PN_STOP_FORK:
++ /* fork */
++ SYSCALL_SETRET(regs, current->ptrace_message);
++ dprintk("ptrace fork returns pid %ld\n", SYSCALL_RETVAL(regs));
++ goto out;
++ case PN_STOP_VFORK:
++ /* after vfork */
++ SYSCALL_SETRET(regs, current->ptrace_message);
++ dprintk("ptrace after vfork returns pid %ld\n", SYSCALL_RETVAL(regs));
++ goto out;
++ case PN_STOP_SIGNAL:
++ /* normal case : dequeue signal */
++ break;
++ case PN_STOP_EXIT:
++ dprintk("ptrace exit caught\n");
++ current->ptrace &= ~PT_TRACE_EXIT;
++ spin_unlock_irq(&current->sighand->siglock);
++ module_put(THIS_MODULE);
++ complete_and_exit(NULL, current->ptrace_message);
++ BUG();
++ case PN_STOP_EXEC:
++ eprintk("ptrace after exec caught: must not happen\n");
++ BUG();
++ default:
++ eprintk("ptrace with unknown identity %d\n", ptrace_id);
++ BUG();
++ }
++
++ signr = current->exit_code;
++ if (signr == 0) {
++ dprintk("rlsi: canceled signal %d\n", info->si_signo);
++ goto out;
++ }
++ current->exit_code = 0;
++
++ if (signr != info->si_signo) {
++ info->si_signo = signr;
++ info->si_errno = 0;
++ info->si_code = SI_USER;
++ info->si_pid = task_pid_vnr(current->parent);
++ info->si_uid = current->parent->uid;
++ }
++
++ /* If the (new) signal is now blocked, requeue it. */
++ if (sigismember(&current->blocked, signr)) {
++ dprintk("going to requeue signal %d\n", signr);
++ goto out_resend_sig;
++ }
++
++ ka = &current->sighand->action[signr-1];
++ if (ka->sa.sa_handler == SIG_IGN) {
++ dprintk("going to resend signal %d (ignored)\n", signr);
++ goto out;
++ }
++ if (ka->sa.sa_handler != SIG_DFL) {
++ dprintk("going to resend signal %d (not SIG_DFL)\n", signr);
++ goto out_resend_sig;
++ }
++ if (signr == SIGCONT ||
++ signr == SIGCHLD ||
++ signr == SIGWINCH ||
++ signr == SIGURG ||
++ current->pid == 1)
++ goto out;
++
++ /* All the rest, which we cannot handle are requeued. */
++ dprintk("going to resend signal %d (sigh)\n", signr);
++out_resend_sig:
++ spin_unlock_irq(&current->sighand->siglock);
++ send_sig_info(signr, info, current);
++ return;
++
++out:
++ spin_unlock_irq(&current->sighand->siglock);
++}
++
++static void rst_finish_stop(void)
++{
++ /* ...
++ * do_signal() ->
++ * get_signal_to_deliver() ->
++ * do_signal_stop() ->
++ * finish_stop()
++ *
++ * Normally after SIGCONT it will dequeue the next signal. If no signal
++ * is found, do_signal restarts syscall unconditionally.
++ * Otherwise signal handler is pushed on user stack.
++ */
++
++ dprintk("rfs: " CPT_FID "\n", CPT_TID(current));
++
++ clear_stop_state(current);
++ current->exit_code = 0;
++}
++
++static void rst_restart_sys(void)
++{
++ struct pt_regs *regs = task_pt_regs(current);
++
++ /* This hook is supposed to be executed, when we have
++ * to complete some interrupted syscall.
++ */
++ dprintk("rrs: " CPT_FID "\n", CPT_TID(current));
++
++ if (!IN_SYSCALL(regs) || !IN_ERROR(regs))
++ return;
++
++#ifdef __NR_pause
++ if (syscall_is(current,regs,pause)) {
++ if (SYSCALL_ERRNO(regs) == ERESTARTNOHAND) {
++ current->state = TASK_INTERRUPTIBLE;
++ schedule();
++ }
++ } else
++#else
++ /* On this arch pause() is simulated with sigsuspend(). */
++ if (syscall_is(current,regs,rt_sigsuspend)) {
++ if (SYSCALL_ERRNO(regs) == ERESTARTNOHAND) {
++ current->state = TASK_INTERRUPTIBLE;
++ schedule();
++ }
++ } else
++#endif
++ if (syscall_is(current,regs,rt_sigtimedwait)) {
++ if (SYSCALL_ERRNO(regs) == EAGAIN ||
++ SYSCALL_ERRNO(regs) == EINTR) {
++ SYSCALL_RESTART(regs);
++ }
++ } else if (syscall_is(current,regs,futex)) {
++ if (SYSCALL_ERRNO(regs) == EINTR &&
++ !signal_pending(current)) {
++ SYSCALL_RESTART(regs);
++ }
++ }
++
++ if (!signal_pending(current) &&
++ !current_thread_info()->status & TS_RESTORE_SIGMASK) {
++ if (SYSCALL_ERRNO(regs) == ERESTARTSYS ||
++ SYSCALL_ERRNO(regs) == ERESTARTNOINTR ||
++ SYSCALL_ERRNO(regs) == ERESTARTNOHAND) {
++ SYSCALL_RESTART(regs);
++ } else if (SYSCALL_ERRNO(regs) == ERESTART_RESTARTBLOCK) {
++ int new = __NR_restart_syscall;
++#ifdef CONFIG_X86_64
++ if (task_thread_info(current)->flags&_TIF_IA32)
++ new = __NR32_restart_syscall;
++#endif
++ SYSCALL_RESTART2(regs, new);
++ }
++ }
++}
++
++#ifdef CONFIG_X86_32
++
++static int restore_registers(struct task_struct *tsk, struct pt_regs *regs,
++ struct cpt_task_image *ti, struct cpt_x86_regs *b,
++ struct resume_info **rip, struct cpt_context *ctx)
++{
++ extern char i386_ret_from_resume;
++
++ if (b->cpt_object != CPT_OBJ_X86_REGS)
++ return -EINVAL;
++
++ tsk->thread.sp = (unsigned long) regs;
++ tsk->thread.sp0 = (unsigned long) (regs+1);
++ tsk->thread.ip = (unsigned long) &i386_ret_from_resume;
++
++ tsk->thread.gs = decode_segment(b->cpt_gs);
++ tsk->thread.debugreg0 = b->cpt_debugreg[0];
++ tsk->thread.debugreg1 = b->cpt_debugreg[1];
++ tsk->thread.debugreg2 = b->cpt_debugreg[2];
++ tsk->thread.debugreg3 = b->cpt_debugreg[3];
++ tsk->thread.debugreg6 = b->cpt_debugreg[6];
++ tsk->thread.debugreg7 = b->cpt_debugreg[7];
++
++ regs->bx = b->cpt_ebx;
++ regs->cx = b->cpt_ecx;
++ regs->dx = b->cpt_edx;
++ regs->si = b->cpt_esi;
++ regs->di = b->cpt_edi;
++ regs->bp = b->cpt_ebp;
++ regs->ax = b->cpt_eax;
++ regs->ds = b->cpt_xds;
++ regs->es = b->cpt_xes;
++ regs->orig_ax = b->cpt_orig_eax;
++ regs->ip = b->cpt_eip;
++ regs->cs = b->cpt_xcs;
++ regs->flags = b->cpt_eflags;
++ regs->sp = b->cpt_esp;
++ regs->ss = b->cpt_xss;
++
++ regs->cs = decode_segment(b->cpt_xcs);
++ regs->ss = decode_segment(b->cpt_xss);
++ regs->ds = decode_segment(b->cpt_xds);
++ regs->es = decode_segment(b->cpt_xes);
++ regs->fs = decode_segment(b->cpt_fs);
++
++ tsk->thread.sp -= HOOK_RESERVE;
++ memset((void*)tsk->thread.sp, 0, HOOK_RESERVE);
++ *rip = (void*)tsk->thread.sp;
++
++ return 0;
++}
++
++#elif defined(CONFIG_X86_64)
++
++static void xlate_ptregs_32_to_64(struct pt_regs *d, struct cpt_x86_regs *s)
++{
++ memset(d, 0, sizeof(struct pt_regs));
++ d->bp = s->cpt_ebp;
++ d->bx = s->cpt_ebx;
++ d->ax = (s32)s->cpt_eax;
++ d->cx = s->cpt_ecx;
++ d->dx = s->cpt_edx;
++ d->si = s->cpt_esi;
++ d->di = s->cpt_edi;
++ d->orig_ax = (s32)s->cpt_orig_eax;
++ d->ip = s->cpt_eip;
++ d->cs = s->cpt_xcs;
++ d->flags = s->cpt_eflags;
++ d->sp = s->cpt_esp;
++ d->ss = s->cpt_xss;
++}
++
++static int restore_registers(struct task_struct *tsk, struct pt_regs *regs,
++ struct cpt_task_image *ti, struct cpt_obj_bits *hdr,
++ struct resume_info **rip, struct cpt_context *ctx)
++{
++ if (hdr->cpt_object == CPT_OBJ_X86_64_REGS) {
++ struct cpt_x86_64_regs *b = (void*)hdr;
++
++ tsk->thread.sp = (unsigned long) regs;
++ tsk->thread.sp0 = (unsigned long) (regs+1);
++
++ tsk->thread.fs = b->cpt_fsbase;
++ tsk->thread.gs = b->cpt_gsbase;
++ tsk->thread.fsindex = decode_segment(b->cpt_fsindex);
++ tsk->thread.gsindex = decode_segment(b->cpt_gsindex);
++ tsk->thread.ds = decode_segment(b->cpt_ds);
++ tsk->thread.es = decode_segment(b->cpt_es);
++ tsk->thread.debugreg0 = b->cpt_debugreg[0];
++ tsk->thread.debugreg1 = b->cpt_debugreg[1];
++ tsk->thread.debugreg2 = b->cpt_debugreg[2];
++ tsk->thread.debugreg3 = b->cpt_debugreg[3];
++ tsk->thread.debugreg6 = b->cpt_debugreg[6];
++ tsk->thread.debugreg7 = b->cpt_debugreg[7];
++
++ memcpy(regs, &b->cpt_r15, sizeof(struct pt_regs));
++
++ tsk->thread.usersp = regs->sp;
++ regs->cs = decode_segment(b->cpt_cs);
++ regs->ss = decode_segment(b->cpt_ss);
++ } else if (hdr->cpt_object == CPT_OBJ_X86_REGS) {
++ struct cpt_x86_regs *b = (void*)hdr;
++
++ tsk->thread.sp = (unsigned long) regs;
++ tsk->thread.sp0 = (unsigned long) (regs+1);
++
++ tsk->thread.fs = 0;
++ tsk->thread.gs = 0;
++ tsk->thread.fsindex = decode_segment(b->cpt_fs);
++ tsk->thread.gsindex = decode_segment(b->cpt_gs);
++ tsk->thread.debugreg0 = b->cpt_debugreg[0];
++ tsk->thread.debugreg1 = b->cpt_debugreg[1];
++ tsk->thread.debugreg2 = b->cpt_debugreg[2];
++ tsk->thread.debugreg3 = b->cpt_debugreg[3];
++ tsk->thread.debugreg6 = b->cpt_debugreg[6];
++ tsk->thread.debugreg7 = b->cpt_debugreg[7];
++
++ xlate_ptregs_32_to_64(regs, b);
++
++ tsk->thread.usersp = regs->sp;
++ regs->cs = decode_segment(b->cpt_xcs);
++ regs->ss = decode_segment(b->cpt_xss);
++ tsk->thread.ds = decode_segment(b->cpt_xds);
++ tsk->thread.es = decode_segment(b->cpt_xes);
++ } else {
++ return -EINVAL;
++ }
++
++ tsk->thread.sp -= HOOK_RESERVE;
++ memset((void*)tsk->thread.sp, 0, HOOK_RESERVE);
++ *rip = (void*)tsk->thread.sp;
++ return 0;
++}
++
++#elif defined(CONFIG_IA64)
++
++#define MASK(nbits) ((1UL << (nbits)) - 1) /* mask with NBITS bits set */
++
++#define PUT_BITS(first, last, nat) \
++ ({ \
++ unsigned long bit = ia64_unat_pos(&pt->r##first); \
++ unsigned long nbits = (last - first + 1); \
++ unsigned long mask = MASK(nbits) << first; \
++ long dist; \
++ if (bit < first) \
++ dist = 64 + bit - first; \
++ else \
++ dist = bit - first; \
++ ia64_rotl(nat & mask, dist); \
++ })
++
++unsigned long
++ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat)
++{
++ unsigned long scratch_unat;
++
++ /*
++ * Registers that are stored consecutively in struct pt_regs
++ * can be handled in parallel. If the register order in
++ * struct_pt_regs changes, this code MUST be updated.
++ */
++ scratch_unat = PUT_BITS( 1, 1, nat);
++ scratch_unat |= PUT_BITS( 2, 3, nat);
++ scratch_unat |= PUT_BITS(12, 13, nat);
++ scratch_unat |= PUT_BITS(14, 14, nat);
++ scratch_unat |= PUT_BITS(15, 15, nat);
++ scratch_unat |= PUT_BITS( 8, 11, nat);
++ scratch_unat |= PUT_BITS(16, 31, nat);
++
++ return scratch_unat;
++
++}
++
++static unsigned long
++ia64_put_saved_nat_bits (struct switch_stack *pt, unsigned long nat)
++{
++ unsigned long scratch_unat;
++
++ scratch_unat = PUT_BITS( 4, 7, nat);
++
++ return scratch_unat;
++
++}
++
++#undef PUT_BITS
++
++
++static int restore_registers(struct task_struct *tsk, struct pt_regs *pt,
++ struct cpt_task_image *ti,
++ struct cpt_ia64_regs *r,
++ struct resume_info **rip,
++ struct cpt_context *ctx)
++{
++ extern char ia64_ret_from_resume;
++ struct switch_stack *sw;
++ struct resume_info *ri;
++ struct ia64_psr *psr = ia64_psr(pt);
++ void *krbs = (void *)tsk + IA64_RBS_OFFSET;
++ unsigned long reg;
++
++ if (r->cpt_object != CPT_OBJ_IA64_REGS)
++ return -EINVAL;
++
++ if (r->num_regs > 96) {
++ eprintk(CPT_FID " too much RSE regs %lu\n",
++ CPT_TID(tsk), r->num_regs);
++ return -EINVAL;
++ }
++
++ *rip = ri = ((void*)pt) - HOOK_RESERVE;
++ sw = ((struct switch_stack *) ri) - 1;
++
++ memmove(sw, (void*)tsk->thread.ksp + 16, sizeof(struct switch_stack));
++ memset(ri, 0, HOOK_RESERVE);
++
++ /* gr 1,2-3,8-11,12-13,14,15,16-31 are on pt_regs */
++ memcpy(&pt->r1, &r->gr[1], 8*(2-1));
++ memcpy(&pt->r2, &r->gr[2], 8*(4-2));
++ memcpy(&pt->r8, &r->gr[8], 8*(12-8));
++ memcpy(&pt->r12, &r->gr[12], 8*(14-12));
++ memcpy(&pt->r14, &r->gr[14], 8*(15-14));
++ memcpy(&pt->r15, &r->gr[15], 8*(16-15));
++ memcpy(&pt->r16, &r->gr[16], 8*(32-16));
++
++ pt->b0 = r->br[0];
++ pt->b6 = r->br[6];
++ pt->b7 = r->br[7];
++
++ pt->ar_bspstore = r->ar_bspstore;
++ pt->ar_unat = r->ar_unat;
++ pt->ar_pfs = r->ar_pfs;
++ pt->ar_ccv = r->ar_ccv;
++ pt->ar_fpsr = r->ar_fpsr;
++ pt->ar_csd = r->ar_csd;
++ pt->ar_ssd = r->ar_ssd;
++ pt->ar_rsc = r->ar_rsc;
++
++ pt->cr_iip = r->cr_iip;
++ pt->cr_ipsr = r->cr_ipsr;
++
++ pt->pr = r->pr;
++
++ pt->cr_ifs = r->cfm;
++
++ /* fpregs 6..9,10..11 are in pt_regs */
++ memcpy(&pt->f6, &r->fr[2*6], 16*(10-6));
++ memcpy(&pt->f10, &r->fr[2*10], 16*(12-10));
++ /* fpreg 12..15 are on switch stack */
++ memcpy(&sw->f12, &r->fr[2*12], 16*(16-12));
++ /* fpregs 32...127 */
++ tsk->thread.flags |= IA64_THREAD_FPH_VALID;
++ memcpy(tsk->thread.fph, &r->fr[32*2], 16*(128-32));
++ ia64_drop_fpu(tsk);
++ psr->dfh = 1;
++
++ memcpy(&sw->r4, &r->gr[4], 8*(8-4));
++ memcpy(&sw->b1, &r->br[1], 8*(6-1));
++ sw->ar_lc = r->ar_lc;
++
++ memcpy(&sw->f2, &r->fr[2*2], 16*(6-2));
++ memcpy(&sw->f16, &r->fr[2*16], 16*(32-16));
++
++ sw->caller_unat = 0;
++ sw->ar_fpsr = pt->ar_fpsr;
++ sw->ar_unat = 0;
++ if (r->nat[0] & 0xFFFFFF0FUL)
++ sw->caller_unat = ia64_put_scratch_nat_bits(pt, r->nat[0]);
++ if (r->nat[0] & 0xF0)
++ sw->ar_unat = ia64_put_saved_nat_bits(sw, r->nat[0]);
++
++ sw->ar_bspstore = (unsigned long)ia64_rse_skip_regs(krbs, r->num_regs);
++ memset(krbs, 0, (void*)sw->ar_bspstore - krbs);
++ sw->ar_rnat = 0;
++ sw->ar_pfs = 0;
++
++ /* This is tricky. When we are in syscall, we have frame
++ * of output register (sometimes, plus one input reg sometimes).
++ * It is not so easy to restore such frame, RSE optimizes
++ * and does not fetch those regs from backstore. So, we restore
++ * the whole frame as local registers, and then repartition it
++ * in ia64_ret_from_resume().
++ */
++ if ((long)pt->cr_ifs >= 0) {
++ unsigned long out = (r->cfm&0x7F) - ((r->cfm>>7)&0x7F);
++ sw->ar_pfs = out | (out<<7);
++ }
++ if (r->ar_ec)
++ sw->ar_pfs |= (r->ar_ec & 0x3F) << 52;
++
++ for (reg = 0; reg < r->num_regs; reg++) {
++ unsigned long *ptr = ia64_rse_skip_regs(krbs, reg);
++ unsigned long *rnatp;
++ unsigned long set_rnat = 0;
++
++ *ptr = r->gr[32+reg];
++
++ if (reg < 32)
++ set_rnat = (r->nat[0] & (1UL<<(reg+32)));
++ else
++ set_rnat = (r->nat[1] & (1UL<<(reg-32)));
++
++ if (set_rnat) {
++ rnatp = ia64_rse_rnat_addr(ptr);
++ if ((unsigned long)rnatp >= sw->ar_bspstore)
++ rnatp = &sw->ar_rnat;
++ *rnatp |= (1UL<<ia64_rse_slot_num(ptr));
++ }
++ }
++
++ sw->b0 = (unsigned long) &ia64_ret_from_resume;
++ tsk->thread.ksp = (unsigned long) sw - 16;
++
++#define PRED_LEAVE_SYSCALL 1 /* TRUE iff leave from syscall */
++#define PRED_KERNEL_STACK 2 /* returning to kernel-stacks? */
++#define PRED_USER_STACK 3 /* returning to user-stacks? */
++#define PRED_SYSCALL 4 /* inside a system call? */
++#define PRED_NON_SYSCALL 5 /* complement of PRED_SYSCALL */
++
++ pt->loadrs = r->loadrs;
++ sw->pr = 0;
++ sw->pr &= ~(1UL << PRED_LEAVE_SYSCALL);
++ sw->pr &= ~((1UL << PRED_SYSCALL) | (1UL << PRED_NON_SYSCALL));
++ sw->pr &= ~(1UL << PRED_KERNEL_STACK);
++ sw->pr |= (1UL << PRED_USER_STACK);
++ if ((long)pt->cr_ifs < 0) {
++ sw->pr |= (1UL << PRED_NON_SYSCALL);
++ } else {
++ sw->pr |= ((1UL << PRED_SYSCALL) | (1UL << PRED_LEAVE_SYSCALL));
++ }
++
++ return 0;
++}
++#endif
++
++asmlinkage void rst_resume_work(struct resume_info *ri)
++{
++ if (ri->hooks & (1<<HOOK_TID))
++ rst_child_tid(ri->tid_ptrs);
++ if (ri->hooks & (1<<HOOK_CONT))
++ rst_finish_stop();
++ if (ri->hooks & (1<<HOOK_LSI))
++ rst_last_siginfo();
++ if (ri->hooks & (1<<HOOK_RESTART))
++ rst_restart_sys();
++ module_put(THIS_MODULE);
++}
++
++static void rst_apply_mxcsr_mask(struct task_struct *tsk)
++{
++#ifdef CONFIG_X86_32
++ unsigned int flags;
++
++ flags = test_cpu_caps();
++
++ /* if cpu does not support sse2 mask 6 bit (DAZ flag) and 16-31 bits
++ in MXCSR to avoid general protection fault */
++ if (!(flags & (1 << CPT_CPU_X86_SSE2)))
++ tsk->thread.xstate->fxsave.mxcsr &= 0x0000ffbf;
++#endif
++}
++
++#ifdef CONFIG_X86
++#include <asm/i387.h>
++#endif
++
++int rst_restore_process(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ struct cpt_task_image *ti = obj->o_image;
++ struct pt_regs * regs;
++ struct cpt_object_hdr *b;
++ struct cpt_siginfo_image *lsi = NULL;
++ struct group_info *gids, *ogids;
++ struct resume_info *ri = NULL;
++ int i;
++ int err = 0;
++#ifdef CONFIG_BEANCOUNTERS
++ struct task_beancounter *tbc;
++ struct user_beancounter *new_bc, *old_bc;
++#endif
++
++ if (tsk == NULL) {
++ eprintk_ctx("oops, task %d/%s is missing\n", ti->cpt_pid, ti->cpt_comm);
++ return -EFAULT;
++ }
++
++ wait_task_inactive(tsk, 0);
++#ifdef CONFIG_BEANCOUNTERS
++ tbc = &tsk->task_bc;
++ new_bc = rst_lookup_ubc(ti->cpt_exec_ub, ctx);
++ err = virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_RSTTSK, new_bc);
++ if (err & NOTIFY_FAIL) {
++ put_beancounter(new_bc);
++ return -ECHRNG;
++ }
++ old_bc = tbc->exec_ub;
++ if ((err & VIRTNOTIFY_CHANGE) && old_bc != new_bc) {
++ dprintk(" *** replacing ub %p by %p for %p (%d %s)\n",
++ old_bc, new_bc, tsk,
++ tsk->pid, tsk->comm);
++ tbc->exec_ub = new_bc;
++ new_bc = old_bc;
++ }
++ put_beancounter(new_bc);
++#endif
++ regs = task_pt_regs(tsk);
++
++ if (!tsk->exit_state) {
++ tsk->lock_depth = -1;
++#ifdef CONFIG_PREEMPT
++ task_thread_info(tsk)->preempt_count--;
++#endif
++ }
++
++ if (tsk->static_prio != ti->cpt_static_prio)
++ set_user_nice(tsk, PRIO_TO_NICE((s32)ti->cpt_static_prio));
++
++ cpt_sigset_import(&tsk->blocked, ti->cpt_sigblocked);
++ cpt_sigset_import(&tsk->real_blocked, ti->cpt_sigrblocked);
++ cpt_sigset_import(&tsk->saved_sigmask, ti->cpt_sigsuspend_blocked);
++ cpt_sigset_import(&tsk->pending.signal, ti->cpt_sigpending);
++
++ tsk->uid = ti->cpt_uid;
++ tsk->euid = ti->cpt_euid;
++ tsk->suid = ti->cpt_suid;
++ tsk->fsuid = ti->cpt_fsuid;
++ tsk->gid = ti->cpt_gid;
++ tsk->egid = ti->cpt_egid;
++ tsk->sgid = ti->cpt_sgid;
++ tsk->fsgid = ti->cpt_fsgid;
++#ifdef CONFIG_IA64
++ SET_UNALIGN_CTL(tsk, ti->cpt_prctl_uac);
++ SET_FPEMU_CTL(tsk, ti->cpt_prctl_fpemu);
++#endif
++ memcpy(&tsk->cap_effective, &ti->cpt_ecap, sizeof(tsk->cap_effective));
++ memcpy(&tsk->cap_inheritable, &ti->cpt_icap, sizeof(tsk->cap_inheritable));
++ memcpy(&tsk->cap_permitted, &ti->cpt_pcap, sizeof(tsk->cap_permitted));
++ if (ctx->image_version < CPT_VERSION_26)
++ tsk->securebits = (ti->cpt_keepcap != 0) ?
++ issecure_mask(SECURE_KEEP_CAPS) : 0;
++ else
++ tsk->securebits = ti->cpt_keepcap;
++ tsk->did_exec = (ti->cpt_did_exec != 0);
++ gids = groups_alloc(ti->cpt_ngids);
++ ogids = tsk->group_info;
++ if (gids) {
++ int i;
++ for (i=0; i<32; i++)
++ gids->small_block[i] = ti->cpt_gids[i];
++ tsk->group_info = gids;
++ }
++ if (ogids)
++ put_group_info(ogids);
++ tsk->utime = ti->cpt_utime;
++ tsk->stime = ti->cpt_stime;
++ if (ctx->image_version == CPT_VERSION_8)
++ tsk->start_time = _ns_to_timespec(ti->cpt_starttime*TICK_NSEC);
++ else
++ cpt_timespec_import(&tsk->start_time, ti->cpt_starttime);
++ _set_normalized_timespec(&tsk->start_time,
++ tsk->start_time.tv_sec +
++ VE_TASK_INFO(tsk)->owner_env->start_timespec.tv_sec,
++ tsk->start_time.tv_nsec +
++ VE_TASK_INFO(tsk)->owner_env->start_timespec.tv_nsec);
++
++ tsk->nvcsw = ti->cpt_nvcsw;
++ tsk->nivcsw = ti->cpt_nivcsw;
++ tsk->min_flt = ti->cpt_min_flt;
++ tsk->maj_flt = ti->cpt_maj_flt;
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
++ tsk->cutime = ti->cpt_cutime;
++ tsk->cstime = ti->cpt_cstime;
++ tsk->cnvcsw = ti->cpt_cnvcsw;
++ tsk->cnivcsw = ti->cpt_cnivcsw;
++ tsk->cmin_flt = ti->cpt_cmin_flt;
++ tsk->cmaj_flt = ti->cpt_cmaj_flt;
++
++ BUILD_BUG_ON(RLIM_NLIMITS > CPT_RLIM_NLIMITS);
++
++ for (i=0; i<RLIM_NLIMITS; i++) {
++ tsk->rlim[i].rlim_cur = ti->cpt_rlim_cur[i];
++ tsk->rlim[i].rlim_max = ti->cpt_rlim_max[i];
++ }
++#else
++ if (thread_group_leader(tsk) && tsk->signal) {
++ tsk->signal->utime = ti->cpt_utime;
++ tsk->signal->stime = ti->cpt_stime;
++ tsk->signal->cutime = ti->cpt_cutime;
++ tsk->signal->cstime = ti->cpt_cstime;
++ tsk->signal->nvcsw = ti->cpt_nvcsw;
++ tsk->signal->nivcsw = ti->cpt_nivcsw;
++ tsk->signal->cnvcsw = ti->cpt_cnvcsw;
++ tsk->signal->cnivcsw = ti->cpt_cnivcsw;
++ tsk->signal->min_flt = ti->cpt_min_flt;
++ tsk->signal->maj_flt = ti->cpt_maj_flt;
++ tsk->signal->cmin_flt = ti->cpt_cmin_flt;
++ tsk->signal->cmaj_flt = ti->cpt_cmaj_flt;
++
++ for (i=0; i<RLIM_NLIMITS; i++) {
++ tsk->signal->rlim[i].rlim_cur = ti->cpt_rlim_cur[i];
++ tsk->signal->rlim[i].rlim_max = ti->cpt_rlim_max[i];
++ }
++ }
++#endif
++
++#ifdef CONFIG_X86
++ for (i=0; i<3; i++) {
++ if (i >= GDT_ENTRY_TLS_ENTRIES) {
++ eprintk_ctx("too many tls descs\n");
++ } else {
++ tsk->thread.tls_array[i].a = ti->cpt_tls[i]&0xFFFFFFFF;
++ tsk->thread.tls_array[i].b = ti->cpt_tls[i]>>32;
++ }
++ }
++#endif
++
++ clear_stopped_child_used_math(tsk);
++
++ b = (void *)(ti+1);
++ while ((void*)b < ((void*)ti) + ti->cpt_next) {
++ /* Siginfo objects are at the end of obj array */
++ if (b->cpt_object == CPT_OBJ_SIGINFO) {
++ struct ve_struct *env = set_exec_env(VE_TASK_INFO(tsk)->owner_env);
++ restore_sigqueue(tsk, &tsk->pending, (unsigned long)b, (unsigned long)ti + ti->cpt_next);
++ set_exec_env(env);
++ break;
++ }
++
++ switch (b->cpt_object) {
++#ifdef CONFIG_X86
++ case CPT_OBJ_BITS:
++ if (b->cpt_content == CPT_CONTENT_X86_FPUSTATE &&
++ cpu_has_fxsr) {
++ if (init_fpu(tsk))
++ return -ENOMEM;
++ memcpy(tsk->thread.xstate,
++ (void*)b + b->cpt_hdrlen,
++ sizeof(struct i387_fxsave_struct));
++ rst_apply_mxcsr_mask(tsk);
++ if (ti->cpt_used_math)
++ set_stopped_child_used_math(tsk);
++ }
++#ifndef CONFIG_X86_64
++ else if (b->cpt_content == CPT_CONTENT_X86_FPUSTATE_OLD &&
++ !cpu_has_fxsr) {
++ if (init_fpu(tsk))
++ return -ENOMEM;
++ memcpy(tsk->thread.xstate,
++ (void*)b + b->cpt_hdrlen,
++ sizeof(struct i387_fsave_struct));
++ if (ti->cpt_used_math)
++ set_stopped_child_used_math(tsk);
++ }
++#endif
++ break;
++#endif
++ case CPT_OBJ_LASTSIGINFO:
++ lsi = (void*)b;
++ break;
++ case CPT_OBJ_X86_REGS:
++ case CPT_OBJ_X86_64_REGS:
++ case CPT_OBJ_IA64_REGS:
++ if (restore_registers(tsk, regs, ti, (void*)b, &ri, ctx)) {
++ eprintk_ctx("cannot restore registers: image is corrupted\n");
++ return -EINVAL;
++ }
++ break;
++ case CPT_OBJ_SIGALTSTACK: {
++ struct cpt_sigaltstack_image *sas;
++ sas = (struct cpt_sigaltstack_image *)b;
++ tsk->sas_ss_sp = sas->cpt_stack;
++ tsk->sas_ss_size = sas->cpt_stacksize;
++ break;
++ }
++ case CPT_OBJ_TASK_AUX: {
++ struct cpt_task_aux_image *ai;
++ ai = (struct cpt_task_aux_image *)b;
++ tsk->robust_list = cpt_ptr_import(ai->cpt_robust_list);
++#ifdef CONFIG_X86_64
++#ifdef CONFIG_COMPAT
++ if (task_thread_info(tsk)->flags&_TIF_IA32) {
++ tsk->robust_list = (void __user *)NULL;
++ tsk->compat_robust_list = cpt_ptr_import(ai->cpt_robust_list);
++ }
++#endif
++#endif
++ break;
++ }
++ }
++ b = ((void*)b) + b->cpt_next;
++ }
++
++ if (ri == NULL && !(ti->cpt_state & (EXIT_ZOMBIE|EXIT_DEAD))) {
++ eprintk_ctx("missing register info\n");
++ return -EINVAL;
++ }
++
++ if (ti->cpt_ppid != ti->cpt_rppid) {
++ struct task_struct *parent;
++ struct ve_struct *env = set_exec_env(VE_TASK_INFO(tsk)->owner_env);
++ write_lock_irq(&tasklist_lock);
++ parent = find_task_by_vpid(ti->cpt_ppid);
++ if (parent && parent != tsk->parent) {
++ list_add(&tsk->ptrace_entry, &tsk->parent->ptraced);
++ /*
++ * Ptraced kids are no longer in the parent children
++ * remove_parent(tsk);
++ * tsk->parent = parent;
++ * add_parent(tsk);
++ */
++ }
++ write_unlock_irq(&tasklist_lock);
++ set_exec_env(env);
++ }
++
++ tsk->ptrace_message = ti->cpt_ptrace_message;
++ tsk->pn_state = ti->cpt_pn_state;
++ tsk->stopped_state = ti->cpt_stopped_state;
++ task_thread_info(tsk)->flags = ti->cpt_thrflags;
++
++ /* The image was created with kernel < 2.6.16, while
++ * task hanged in sigsuspend -> do_signal.
++ *
++ * FIXME! This needs more brain efforts...
++ */
++ if (ti->cpt_sigsuspend_state) {
++ set_restore_sigmask();
++ }
++
++#ifdef CONFIG_X86_64
++ task_thread_info(tsk)->flags |= _TIF_FORK | _TIF_RESUME;
++ if (!ti->cpt_64bit)
++ task_thread_info(tsk)->flags |= _TIF_IA32;
++#endif
++
++#ifdef CONFIG_X86_32
++ do {
++ if (regs->orig_ax == __NR__newselect && regs->di) {
++ struct timeval tv;
++ if (access_process_vm(tsk, regs->di, &tv,
++ sizeof(tv), 0) != sizeof(tv)) {
++ wprintk_ctx("task %d/%d(%s): Error 1 in access_process_vm: edi %ld\n",
++ task_pid_vnr(tsk), tsk->pid, tsk->comm,
++ regs->di);
++ break;
++ }
++ dprintk_ctx("task %d/%d(%s): Old timeval in newselect: %ld.%ld\n",
++ task_pid_vnr(tsk), tsk->pid, tsk->comm,
++ tv.tv_sec, tv.tv_usec);
++ tv.tv_sec -= ctx->delta_time.tv_sec;
++ if (tv.tv_usec < ctx->delta_time.tv_nsec / 1000) {
++ tv.tv_usec += 1000000 - ctx->delta_time.tv_nsec / 1000;
++ tv.tv_sec--;
++ } else {
++ tv.tv_usec -= ctx->delta_time.tv_nsec / 1000;
++ }
++ if (tv.tv_sec < 0) {
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++ }
++ dprintk_ctx("task %d/%d(%s): New timeval in newselect: %ld.%ld\n",
++ task_pid_vnr(tsk), tsk->pid, tsk->comm,
++ tv.tv_sec, tv.tv_usec);
++ if (access_process_vm(tsk, regs->di, &tv,
++ sizeof(tv), 1) != sizeof(tv)) {
++ wprintk_ctx("task %d/%d(%s): Error 1 in access_process_vm write: edi %ld\n",
++ task_pid_vnr(tsk), tsk->pid, tsk->comm, regs->di);
++ }
++
++ } else if (regs->orig_ax == __NR_select && regs->di) {
++ struct {
++ unsigned long n;
++ fd_set __user *inp, *outp, *exp;
++ struct timeval __user *tvp;
++ } a;
++ struct timeval tv;
++ if (access_process_vm(tsk, regs->bx, &a,
++ sizeof(a), 0) != sizeof(a)) {
++ wprintk_ctx("task %d: Error 2 in access_process_vm\n", tsk->pid);
++ break;
++ }
++ if (access_process_vm(tsk, (unsigned long)a.tvp,
++ &tv, sizeof(tv), 0) != sizeof(tv)) {
++ wprintk_ctx("task %d: Error 3 in access_process_vm\n", tsk->pid);
++ break;
++ }
++ dprintk_ctx("task %d: Old timeval in select: %ld.%ld\n",
++ tsk->pid, tv.tv_sec, tv.tv_usec);
++ tv.tv_sec -= ctx->delta_time.tv_sec;
++ if (tv.tv_usec < ctx->delta_time.tv_nsec / 1000) {
++ tv.tv_usec += 1000000 - ctx->delta_time.tv_nsec / 1000;
++ tv.tv_sec--;
++ } else {
++ tv.tv_usec -= ctx->delta_time.tv_nsec / 1000;
++ }
++ if (tv.tv_sec < 0) {
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++ }
++ dprintk_ctx("task %d: New timeval in select: %ld.%ld\n",
++ tsk->pid, tv.tv_sec, tv.tv_usec);
++ if (access_process_vm(tsk, (unsigned long)a.tvp,
++ &tv, sizeof(tv), 1) != sizeof(tv)) {
++ wprintk_ctx("task %d: Error 3 in access_process_vm write\n", tsk->pid);
++ }
++ }
++ } while (0);
++#endif
++
++ if (ri && IN_SYSCALL(regs) && IN_ERROR(regs)) {
++ switch (SYSCALL_ERRNO(regs)) {
++ case ERESTARTSYS:
++ case ERESTARTNOINTR:
++ case ERESTARTNOHAND:
++ case ERESTART_RESTARTBLOCK:
++ case EAGAIN:
++ case EINTR:
++ ri->hooks |= (1<<HOOK_RESTART);
++ }
++ }
++
++ if (ri && (lsi || tsk->pn_state)) {
++ /* ... -> ptrace_notify()
++ * or
++ * ... -> do_signal() -> get_signal_to_deliver() ->
++ * ptrace stop
++ */
++ tsk->last_siginfo = &ri->last_siginfo;
++ ri->hooks |= (1<<HOOK_LSI);
++ if (lsi)
++ decode_siginfo(tsk->last_siginfo, lsi);
++ }
++
++ tsk->ptrace = ti->cpt_ptrace;
++ tsk->flags = ti->cpt_flags & ~PF_FROZEN;
++ clear_tsk_thread_flag(tsk, TIF_FREEZE);
++ tsk->exit_signal = ti->cpt_exit_signal;
++
++ if (ri && tsk->stopped_state) {
++ dprintk_ctx("finish_stop\n");
++ if (ti->cpt_state != TASK_STOPPED)
++ eprintk_ctx("Hellooo, state is %u\n", (unsigned)ti->cpt_state);
++ ri->hooks |= (1<<HOOK_CONT);
++ }
++
++ if (ri && (ti->cpt_set_tid || ti->cpt_clear_tid)) {
++ ri->hooks |= (1<<HOOK_TID);
++ ri->tid_ptrs[0] = ti->cpt_clear_tid;
++ ri->tid_ptrs[1] = ti->cpt_set_tid;
++ dprintk_ctx("settids\n");
++ }
++
++ if (ri && ri->hooks &&
++ !(ti->cpt_state & (EXIT_ZOMBIE|EXIT_DEAD))) {
++ if (try_module_get(THIS_MODULE))
++ ri->hook = rst_resume_work;
++ }
++
++ if (ti->cpt_state == TASK_TRACED)
++ tsk->state = TASK_TRACED;
++ else if (ti->cpt_state & (EXIT_ZOMBIE|EXIT_DEAD)) {
++ tsk->signal->it_virt_expires = 0;
++ tsk->signal->it_prof_expires = 0;
++ if (tsk->state != TASK_DEAD)
++ eprintk_ctx("oops, schedule() did not make us dead\n");
++ }
++
++ if (thread_group_leader(tsk) &&
++ ti->cpt_it_real_value &&
++ !(ti->cpt_state & (EXIT_ZOMBIE|EXIT_DEAD))) {
++ ktime_t val;
++ s64 nsec;
++
++ nsec = ti->cpt_it_real_value;
++ val.tv64 = 0;
++
++ if (ctx->image_version < CPT_VERSION_9)
++ nsec *= TICK_NSEC;
++
++ val = ktime_add_ns(val, nsec - ctx->delta_nsec);
++ if (val.tv64 <= 0)
++ val.tv64 = NSEC_PER_USEC;
++ dprintk("rst itimer " CPT_FID " +%Ld %Lu\n", CPT_TID(tsk),
++ (long long)val.tv64,
++ (unsigned long long)ti->cpt_it_real_value);
++
++ spin_lock_irq(&tsk->sighand->siglock);
++ if (hrtimer_try_to_cancel(&tsk->signal->real_timer) >= 0) {
++ /* FIXME. Check!!!! */
++ hrtimer_start(&tsk->signal->real_timer, val, HRTIMER_MODE_REL);
++ } else {
++ wprintk_ctx("Timer clash. Impossible?\n");
++ }
++ spin_unlock_irq(&tsk->sighand->siglock);
++
++ dprintk_ctx("itimer " CPT_FID " +%Lu\n", CPT_TID(tsk),
++ (unsigned long long)val.tv64);
++ }
++
++ module_put(THIS_MODULE);
++ }
++ return 0;
++}
+diff --git a/kernel/cpt/rst_socket.c b/kernel/cpt/rst_socket.c
+new file mode 100644
+index 0000000..22e1d1b
+--- /dev/null
++++ b/kernel/cpt/rst_socket.c
+@@ -0,0 +1,918 @@
++/*
++ *
++ * kernel/cpt/rst_socket.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/socket.h>
++#include <linux/un.h>
++#include <net/tcp.h>
++#include <net/sock.h>
++#include <net/scm.h>
++#include <net/af_unix.h>
++
++#include <bc/kmem.h>
++#include <bc/sock_orphan.h>
++#include <bc/net.h>
++#include <bc/tcp.h>
++
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_files.h"
++#include "cpt_socket.h"
++#include "cpt_kernel.h"
++
++#include "cpt_syscalls.h"
++
++
++static int setup_sock_common(struct sock *sk, struct cpt_sock_image *si,
++ loff_t pos, struct cpt_context *ctx)
++{
++ struct timeval tmptv;
++
++ if (sk->sk_socket) {
++ sk->sk_socket->flags = si->cpt_ssflags;
++ sk->sk_socket->state = si->cpt_sstate;
++ }
++ sk->sk_reuse = si->cpt_reuse;
++ sk->sk_shutdown = si->cpt_shutdown;
++ sk->sk_userlocks = si->cpt_userlocks;
++ sk->sk_no_check = si->cpt_no_check;
++ sock_reset_flag(sk, SOCK_DBG);
++ if (si->cpt_debug)
++ sock_set_flag(sk, SOCK_DBG);
++ sock_reset_flag(sk, SOCK_RCVTSTAMP);
++ if (si->cpt_rcvtstamp)
++ sock_set_flag(sk, SOCK_RCVTSTAMP);
++ sock_reset_flag(sk, SOCK_LOCALROUTE);
++ if (si->cpt_localroute)
++ sock_set_flag(sk, SOCK_LOCALROUTE);
++ sk->sk_protocol = si->cpt_protocol;
++ sk->sk_err = si->cpt_err;
++ sk->sk_err_soft = si->cpt_err_soft;
++ sk->sk_priority = si->cpt_priority;
++ sk->sk_rcvlowat = si->cpt_rcvlowat;
++ sk->sk_rcvtimeo = si->cpt_rcvtimeo;
++ if (si->cpt_rcvtimeo == CPT_NULL)
++ sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
++ sk->sk_sndtimeo = si->cpt_sndtimeo;
++ if (si->cpt_sndtimeo == CPT_NULL)
++ sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
++ sk->sk_rcvbuf = si->cpt_rcvbuf;
++ sk->sk_sndbuf = si->cpt_sndbuf;
++ sk->sk_bound_dev_if = si->cpt_bound_dev_if;
++ sk->sk_flags = si->cpt_flags;
++ sk->sk_lingertime = si->cpt_lingertime;
++ if (si->cpt_lingertime == CPT_NULL)
++ sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
++ sk->sk_peercred.pid = si->cpt_peer_pid;
++ sk->sk_peercred.uid = si->cpt_peer_uid;
++ sk->sk_peercred.gid = si->cpt_peer_gid;
++ cpt_timeval_import(&tmptv, si->cpt_stamp);
++ sk->sk_stamp = timeval_to_ktime(tmptv);
++ return 0;
++}
++
++static struct file *sock_mapfile(struct socket *sock)
++{
++ int fd = sock_map_fd(sock, 0);
++
++ if (fd >= 0) {
++ struct file *file = sock->file;
++ get_file(file);
++ sc_close(fd);
++ return file;
++ }
++ return ERR_PTR(fd);
++}
++
++/* Assumption is that /tmp exists and writable.
++ * In previous versions we assumed that listen() will autobind
++ * the socket. It does not do this for AF_UNIX by evident reason:
++ * socket in abstract namespace is accessible, unlike socket bound
++ * to deleted FS object.
++ */
++
++static int
++select_deleted_name(char * name, cpt_context_t *ctx)
++{
++ int i;
++
++ for (i=0; i<100; i++) {
++ struct nameidata nd;
++ unsigned int rnd = net_random();
++
++ sprintf(name, "/tmp/SOCK.%08x", rnd);
++
++ if (path_lookup(name, 0, &nd) != 0)
++ return 0;
++
++ path_put(&nd.path);
++ }
++
++ eprintk_ctx("failed to allocate deleted socket inode\n");
++ return -ELOOP;
++}
++
++static int
++bind_unix_socket(struct socket *sock, struct cpt_sock_image *si,
++ cpt_context_t *ctx)
++{
++ int err;
++ char *name;
++ struct sockaddr* addr;
++ int addrlen;
++ struct sockaddr_un sun;
++ struct nameidata nd;
++
++ if ((addrlen = si->cpt_laddrlen) <= 2)
++ return 0;
++
++ nd.path.dentry = NULL;
++ name = ((char*)si->cpt_laddr) + 2;
++ addr = (struct sockaddr *)si->cpt_laddr;
++
++ if (name[0]) {
++ if (path_lookup(name, 0, &nd))
++ nd.path.dentry = NULL;
++
++ if (si->cpt_deleted) {
++ if (nd.path.dentry == NULL &&
++ sock->ops->bind(sock, addr, addrlen) == 0) {
++ sc_unlink(name);
++ return 0;
++ }
++
++ addr = (struct sockaddr*)&sun;
++ addr->sa_family = AF_UNIX;
++ name = ((char*)addr) + 2;
++ err = select_deleted_name(name, ctx);
++ if (err)
++ goto out;
++ addrlen = 2 + strlen(name);
++ } else if (nd.path.dentry) {
++ if (!S_ISSOCK(nd.path.dentry->d_inode->i_mode)) {
++ eprintk_ctx("bind_unix_socket: not a socket dentry\n");
++ err = -EINVAL;
++ goto out;
++ }
++ sc_unlink(name);
++ }
++ }
++
++ err = sock->ops->bind(sock, addr, addrlen);
++
++ if (!err && name[0]) {
++ if (nd.path.dentry) {
++ sc_chown(name, nd.path.dentry->d_inode->i_uid,
++ nd.path.dentry->d_inode->i_gid);
++ sc_chmod(name, nd.path.dentry->d_inode->i_mode);
++ }
++ if (si->cpt_deleted)
++ sc_unlink(name);
++ }
++
++out:
++ if (nd.path.dentry)
++ path_put(&nd.path);
++ return err;
++}
++
++static int fixup_unix_address(struct socket *sock, struct cpt_sock_image *si,
++ struct cpt_context *ctx)
++{
++ struct sock *sk = sock->sk;
++ cpt_object_t *obj;
++ struct sock *parent;
++
++ if (sk->sk_family != AF_UNIX || sk->sk_state == TCP_LISTEN)
++ return 0;
++
++ if (si->cpt_parent == -1)
++ return bind_unix_socket(sock, si, ctx);
++
++ obj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx);
++ if (!obj)
++ return 0;
++
++ parent = obj->o_obj;
++ if (unix_sk(parent)->addr) {
++ if (unix_sk(sk)->addr &&
++ atomic_dec_and_test(&unix_sk(sk)->addr->refcnt))
++ kfree(unix_sk(sk)->addr);
++ atomic_inc(&unix_sk(parent)->addr->refcnt);
++ unix_sk(sk)->addr = unix_sk(parent)->addr;
++ }
++ return 0;
++}
++
++static int generic_restore_queues(struct sock *sk, struct cpt_sock_image *si,
++ loff_t pos, struct cpt_context *ctx)
++{
++ loff_t endpos;
++
++ pos = pos + si->cpt_hdrlen;
++ endpos = pos + si->cpt_next;
++ while (pos < endpos) {
++ struct sk_buff *skb;
++ __u32 type;
++
++ skb = rst_skb(&pos, NULL, &type, ctx);
++ if (IS_ERR(skb)) {
++ if (PTR_ERR(skb) == -EINVAL) {
++ int err;
++
++ err = rst_sock_attr(&pos, sk, ctx);
++ if (err)
++ return err;
++ }
++ return PTR_ERR(skb);
++ }
++
++ if (type == CPT_SKB_RQ) {
++ skb_set_owner_r(skb, sk);
++ skb_queue_tail(&sk->sk_receive_queue, skb);
++ } else {
++ wprintk_ctx("strange socket queue type %u\n", type);
++ kfree_skb(skb);
++ }
++ }
++ return 0;
++}
++
++static int open_socket(cpt_object_t *obj, struct cpt_sock_image *si,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct socket *sock;
++ struct socket *sock2 = NULL;
++ struct file *file;
++ cpt_object_t *fobj;
++ cpt_object_t *pobj = NULL;
++
++ err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol,
++ &sock);
++ if (err)
++ return err;
++
++ if (si->cpt_socketpair) {
++ err = sock_create(si->cpt_family, si->cpt_type,
++ si->cpt_protocol, &sock2);
++ if (err)
++ goto err_out;
++
++ err = sock->ops->socketpair(sock, sock2);
++ if (err < 0)
++ goto err_out;
++
++ /* Socketpair with a peer outside our environment.
++ * So, we create real half-open pipe and do not worry
++ * about dead end anymore. */
++ if (si->cpt_peer == -1) {
++ sock_release(sock2);
++ sock2 = NULL;
++ }
++ }
++
++ cpt_obj_setobj(obj, sock->sk, ctx);
++
++ if (si->cpt_file != CPT_NULL) {
++ file = sock_mapfile(sock);
++ err = PTR_ERR(file);
++ if (IS_ERR(file))
++ goto err_out;
++
++ err = -ENOMEM;
++
++ obj->o_parent = file;
++
++ if ((fobj = cpt_object_add(CPT_OBJ_FILE, file, ctx)) == NULL)
++ goto err_out;
++ cpt_obj_setpos(fobj, si->cpt_file, ctx);
++ cpt_obj_setindex(fobj, si->cpt_index, ctx);
++ }
++
++ if (sock2) {
++ struct file *file2;
++
++ pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_peer, ctx);
++ if (!pobj) BUG();
++ if (pobj->o_obj) BUG();
++ cpt_obj_setobj(pobj, sock2->sk, ctx);
++
++ if (pobj->o_ppos != CPT_NULL) {
++ file2 = sock_mapfile(sock2);
++ err = PTR_ERR(file2);
++ if (IS_ERR(file2))
++ goto err_out;
++
++ err = -ENOMEM;
++ if ((fobj = cpt_object_add(CPT_OBJ_FILE, file2, ctx)) == NULL)
++ goto err_out;
++ cpt_obj_setpos(fobj, pobj->o_ppos, ctx);
++ cpt_obj_setindex(fobj, si->cpt_peer, ctx);
++
++ pobj->o_parent = file2;
++ }
++ }
++
++ setup_sock_common(sock->sk, si, obj->o_pos, ctx);
++ if (sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6) {
++ int saved_reuse = sock->sk->sk_reuse;
++
++ inet_sk(sock->sk)->freebind = 1;
++ sock->sk->sk_reuse = 2;
++ if (si->cpt_laddrlen) {
++ err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen);
++ if (err) {
++ dprintk_ctx("binding failed: %d, do not worry\n", err);
++ }
++ }
++ sock->sk->sk_reuse = saved_reuse;
++ rst_socket_in(si, obj->o_pos, sock->sk, ctx);
++ } else if (sock->sk->sk_family == AF_NETLINK) {
++ struct sockaddr_nl *nl = (struct sockaddr_nl *)&si->cpt_laddr;
++ if (nl->nl_pid) {
++ err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen);
++ if (err) {
++ eprintk_ctx("AF_NETLINK binding failed: %d\n", err);
++ }
++ }
++ if (si->cpt_raddrlen && nl->nl_pid) {
++ err = sock->ops->connect(sock, (struct sockaddr *)&si->cpt_raddr, si->cpt_raddrlen, O_NONBLOCK);
++ if (err) {
++ eprintk_ctx("oops, AF_NETLINK connect failed: %d\n", err);
++ }
++ }
++ generic_restore_queues(sock->sk, si, obj->o_pos, ctx);
++ } else if (sock->sk->sk_family == PF_PACKET) {
++ struct sockaddr_ll *ll = (struct sockaddr_ll *)&si->cpt_laddr;
++ if (ll->sll_protocol || ll->sll_ifindex) {
++ int alen = si->cpt_laddrlen;
++ if (alen < sizeof(struct sockaddr_ll))
++ alen = sizeof(struct sockaddr_ll);
++ err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, alen);
++ if (err) {
++ eprintk_ctx("AF_PACKET binding failed: %d\n", err);
++ }
++ }
++ generic_restore_queues(sock->sk, si, obj->o_pos, ctx);
++ }
++ fixup_unix_address(sock, si, ctx);
++
++ if (sock2) {
++ err = rst_get_object(CPT_OBJ_SOCKET, pobj->o_pos, si, ctx);
++ if (err)
++ return err;
++ setup_sock_common(sock2->sk, si, pobj->o_pos, ctx);
++ fixup_unix_address(sock2, si, ctx);
++ }
++
++ if ((sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6)
++ && (int)si->cpt_parent != -1) {
++ cpt_object_t *lobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx);
++ if (lobj && cpt_attach_accept(lobj->o_obj, sock->sk, ctx) == 0)
++ sock->sk = NULL;
++ }
++
++
++ if (si->cpt_file == CPT_NULL && sock->sk &&
++ sock->sk->sk_family == AF_INET) {
++ struct sock *sk = sock->sk;
++
++ if (sk) {
++ sock->sk = NULL;
++
++ local_bh_disable();
++ bh_lock_sock(sk);
++ if (sock_owned_by_user(sk))
++ eprintk_ctx("oops, sock is locked by user\n");
++
++ sock_hold(sk);
++ sock_orphan(sk);
++ ub_inc_orphan_count(sk);
++ bh_unlock_sock(sk);
++ local_bh_enable();
++ sock_put(sk);
++ dprintk_ctx("orphaning socket %p\n", sk);
++ }
++ }
++
++ if (si->cpt_file == CPT_NULL && sock->sk == NULL)
++ sock_release(sock);
++
++ return 0;
++
++err_out:
++ if (sock2)
++ sock_release(sock2);
++ sock_release(sock);
++ return err;
++}
++
++static int open_listening_socket(loff_t pos, struct cpt_sock_image *si,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct socket *sock;
++ struct file *file;
++ cpt_object_t *obj, *fobj;
++
++ err = sock_create(si->cpt_family, si->cpt_type, si->cpt_protocol,
++ &sock);
++ if (err) {
++ eprintk_ctx("open_listening_socket: sock_create: %d\n", err);
++ return err;
++ }
++
++ sock->sk->sk_reuse = 2;
++ sock->sk->sk_bound_dev_if = si->cpt_bound_dev_if;
++
++ if (sock->sk->sk_family == AF_UNIX) {
++ err = bind_unix_socket(sock, si, ctx);
++ } else if (si->cpt_laddrlen) {
++ if (sock->sk->sk_family == AF_INET || sock->sk->sk_family == AF_INET6)
++ inet_sk(sock->sk)->freebind = 1;
++
++ err = sock->ops->bind(sock, (struct sockaddr *)&si->cpt_laddr, si->cpt_laddrlen);
++
++ if (err) {
++ eprintk_ctx("open_listening_socket: bind: %d\n", err);
++ goto err_out;
++ }
++ }
++
++ err = sock->ops->listen(sock, si->cpt_max_ack_backlog);
++ if (err) {
++ eprintk_ctx("open_listening_socket: listen: %d, %Ld, %d\n", err, pos, si->cpt_deleted);
++ goto err_out;
++ }
++
++ /* Now we may access socket body directly and fixup all the things. */
++
++ file = sock_mapfile(sock);
++ err = PTR_ERR(file);
++ if (IS_ERR(file)) {
++ eprintk_ctx("open_listening_socket: map: %d\n", err);
++ goto err_out;
++ }
++
++ err = -ENOMEM;
++ if ((fobj = cpt_object_add(CPT_OBJ_FILE, file, ctx)) == NULL)
++ goto err_out;
++ if ((obj = cpt_object_add(CPT_OBJ_SOCKET, sock->sk, ctx)) == NULL)
++ goto err_out;
++ cpt_obj_setpos(obj, pos, ctx);
++ cpt_obj_setindex(obj, si->cpt_index, ctx);
++ obj->o_parent = file;
++ cpt_obj_setpos(fobj, si->cpt_file, ctx);
++ cpt_obj_setindex(fobj, si->cpt_index, ctx);
++
++ setup_sock_common(sock->sk, si, pos, ctx);
++
++ if (si->cpt_family == AF_INET || si->cpt_family == AF_INET6)
++ rst_restore_synwait_queue(sock->sk, si, pos, ctx);
++
++ return 0;
++
++err_out:
++ sock_release(sock);
++ return err;
++}
++
++static int
++rst_sock_attr_mcfilter(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx)
++{
++ int err;
++ loff_t pos = *pos_p;
++ struct cpt_sockmc_image v;
++
++ err = rst_get_object(CPT_OBJ_SOCK_MCADDR, pos, &v, ctx);
++ if (err)
++ return err;
++
++ *pos_p += v.cpt_next;
++
++ if (v.cpt_family == AF_INET)
++ return rst_sk_mcfilter_in(sk, &v, pos, ctx);
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ else if (v.cpt_family == AF_INET6)
++ return rst_sk_mcfilter_in6(sk, &v, pos, ctx);
++#endif
++ else
++ return -EAFNOSUPPORT;
++}
++
++
++static int
++rst_sock_attr_skfilter(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx)
++{
++ int err;
++ struct sk_filter *fp, *old_fp;
++ loff_t pos = *pos_p;
++ struct cpt_obj_bits v;
++
++ err = rst_get_object(CPT_OBJ_SKFILTER, pos, &v, ctx);
++ if (err)
++ return err;
++
++ *pos_p += v.cpt_next;
++
++ if (v.cpt_size % sizeof(struct sock_filter))
++ return -EINVAL;
++
++ fp = sock_kmalloc(sk, v.cpt_size+sizeof(*fp), GFP_KERNEL_UBC);
++ if (fp == NULL)
++ return -ENOMEM;
++ atomic_set(&fp->refcnt, 1);
++ fp->len = v.cpt_size/sizeof(struct sock_filter);
++
++ err = ctx->pread(fp->insns, v.cpt_size, ctx, pos+v.cpt_hdrlen);
++ if (err) {
++ sk_filter_uncharge(sk, fp);
++ return err;
++ }
++
++ old_fp = sk->sk_filter;
++ sk->sk_filter = fp;
++ if (old_fp)
++ sk_filter_uncharge(sk, old_fp);
++ return 0;
++}
++
++
++int rst_sock_attr(loff_t *pos_p, struct sock *sk, cpt_context_t *ctx)
++{
++ int err;
++ loff_t pos = *pos_p;
++
++ err = rst_sock_attr_skfilter(pos_p, sk, ctx);
++ if (err && pos == *pos_p)
++ err = rst_sock_attr_mcfilter(pos_p, sk, ctx);
++ return err;
++}
++
++struct sk_buff * rst_skb(loff_t *pos_p, __u32 *owner, __u32 *queue, struct cpt_context *ctx)
++{
++ int err;
++ struct sk_buff *skb;
++ struct cpt_skb_image v;
++ loff_t pos = *pos_p;
++ struct scm_fp_list *fpl = NULL;
++ struct timeval tmptv;
++
++ err = rst_get_object(CPT_OBJ_SKB, pos, &v, ctx);
++ if (err)
++ return ERR_PTR(err);
++ *pos_p = pos + v.cpt_next;
++
++ if (owner)
++ *owner = v.cpt_owner;
++ if (queue)
++ *queue = v.cpt_queue;
++
++ skb = alloc_skb(v.cpt_len + v.cpt_hspace + v.cpt_tspace, GFP_KERNEL);
++ if (skb == NULL)
++ return ERR_PTR(-ENOMEM);
++ skb_reserve(skb, v.cpt_hspace);
++ skb_put(skb, v.cpt_len);
++#ifdef NET_SKBUFF_DATA_USES_OFFSET
++ skb->transport_header = v.cpt_h;
++ skb->network_header = v.cpt_nh;
++ skb->mac_header = v.cpt_mac;
++#else
++ skb->transport_header = skb->head + v.cpt_h;
++ skb->network_header = skb->head + v.cpt_nh;
++ skb->mac_header = skb->head + v.cpt_mac;
++#endif
++ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(v.cpt_cb));
++ memcpy(skb->cb, v.cpt_cb, sizeof(v.cpt_cb));
++ skb->mac_len = v.cpt_mac_len;
++
++ skb->csum = v.cpt_csum;
++ skb->local_df = v.cpt_local_df;
++ skb->pkt_type = v.cpt_pkt_type;
++ skb->ip_summed = v.cpt_ip_summed;
++ skb->priority = v.cpt_priority;
++ skb->protocol = v.cpt_protocol;
++ cpt_timeval_import(&tmptv, v.cpt_stamp);
++ skb->tstamp = timeval_to_ktime(tmptv);
++
++ skb_shinfo(skb)->gso_segs = v.cpt_gso_segs;
++ skb_shinfo(skb)->gso_size = v.cpt_gso_size;
++ if (ctx->image_version == 0) {
++ skb_shinfo(skb)->gso_segs = 1;
++ skb_shinfo(skb)->gso_size = 0;
++ }
++
++ if (v.cpt_next > v.cpt_hdrlen) {
++ pos = pos + v.cpt_hdrlen;
++ while (pos < *pos_p) {
++ union {
++ struct cpt_obj_bits b;
++ struct cpt_fd_image f;
++ } u;
++
++ err = rst_get_object(-1, pos, &u, ctx);
++ if (err) {
++ kfree_skb(skb);
++ return ERR_PTR(err);
++ }
++ if (u.b.cpt_object == CPT_OBJ_BITS) {
++ if (u.b.cpt_size != v.cpt_hspace + skb->len) {
++ eprintk_ctx("invalid skb image %u != %u + %u\n", u.b.cpt_size, v.cpt_hspace, skb->len);
++ kfree_skb(skb);
++ return ERR_PTR(-EINVAL);
++ }
++
++ err = ctx->pread(skb->head, u.b.cpt_size, ctx, pos+u.b.cpt_hdrlen);
++ if (err) {
++ kfree_skb(skb);
++ return ERR_PTR(err);
++ }
++ } else if (u.f.cpt_object == CPT_OBJ_FILEDESC) {
++ if (!fpl) {
++ fpl = kmalloc(sizeof(struct scm_fp_list),
++ GFP_KERNEL_UBC);
++ if (!fpl) {
++ kfree_skb(skb);
++ return ERR_PTR(-ENOMEM);
++ }
++ fpl->count = 0;
++ UNIXCB(skb).fp = fpl;
++ }
++ fpl->fp[fpl->count] = rst_file(u.f.cpt_file, -1, ctx);
++ if (!IS_ERR(fpl->fp[fpl->count]))
++ fpl->count++;
++ }
++ pos += u.b.cpt_next;
++ }
++ }
++
++ return skb;
++}
++
++static int restore_unix_rqueue(struct sock *sk, struct cpt_sock_image *si,
++ loff_t pos, struct cpt_context *ctx)
++{
++ loff_t endpos;
++
++ pos = pos + si->cpt_hdrlen;
++ endpos = pos + si->cpt_next;
++ while (pos < endpos) {
++ struct sk_buff *skb;
++ struct sock *owner_sk;
++ __u32 owner;
++
++ skb = rst_skb(&pos, &owner, NULL, ctx);
++ if (IS_ERR(skb)) {
++ if (PTR_ERR(skb) == -EINVAL) {
++ int err;
++
++ err = rst_sock_attr(&pos, sk, ctx);
++ if (err)
++ return err;
++ }
++ return PTR_ERR(skb);
++ }
++
++ owner_sk = unix_peer(sk);
++ if (owner != -1) {
++ cpt_object_t *pobj;
++ pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, owner, ctx);
++ if (pobj == NULL) {
++ eprintk_ctx("orphan af_unix skb?\n");
++ kfree_skb(skb);
++ continue;
++ }
++ owner_sk = pobj->o_obj;
++ }
++ if (owner_sk == NULL) {
++ dprintk_ctx("orphan af_unix skb 2?\n");
++ kfree_skb(skb);
++ continue;
++ }
++ skb_set_owner_w(skb, owner_sk);
++ if (UNIXCB(skb).fp)
++ skb->destructor = unix_destruct_fds;
++ skb_queue_tail(&sk->sk_receive_queue, skb);
++ if (sk->sk_state == TCP_LISTEN) {
++ struct socket *sock = skb->sk->sk_socket;
++ if (sock == NULL) BUG();
++ if (sock->file) BUG();
++ skb->sk->sk_socket = NULL;
++ skb->sk->sk_sleep = NULL;
++ sock->sk = NULL;
++ sock_release(sock);
++ }
++ }
++ return 0;
++}
++
++
++/* All the sockets are created before we start to open files */
++
++int rst_sockets(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_SOCKET];
++ loff_t endsec;
++ cpt_object_t *obj;
++ struct cpt_section_hdr h;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err) {
++ eprintk_ctx("rst_sockets: ctx->pread: %d\n", err);
++ return err;
++ }
++ if (h.cpt_section != CPT_SECT_SOCKET || h.cpt_hdrlen < sizeof(h)) {
++ eprintk_ctx("rst_sockets: hdr err\n");
++ return -EINVAL;
++ }
++
++ /* The first pass: we create socket index and open listening sockets. */
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ struct cpt_sock_image *sbuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_SOCKET, sec, sbuf, ctx);
++ if (err) {
++ eprintk_ctx("rst_sockets: rst_get_object: %d\n", err);
++ cpt_release_buf(ctx);
++ return err;
++ }
++ if (sbuf->cpt_state == TCP_LISTEN) {
++ err = open_listening_socket(sec, sbuf, ctx);
++ cpt_release_buf(ctx);
++ if (err) {
++ eprintk_ctx("rst_sockets: open_listening_socket: %d\n", err);
++ return err;
++ }
++ } else {
++ cpt_release_buf(ctx);
++ obj = alloc_cpt_object(GFP_KERNEL, ctx);
++ if (obj == NULL)
++ return -ENOMEM;
++ cpt_obj_setindex(obj, sbuf->cpt_index, ctx);
++ cpt_obj_setpos(obj, sec, ctx);
++ obj->o_ppos = sbuf->cpt_file;
++ intern_cpt_object(CPT_OBJ_SOCKET, obj, ctx);
++ }
++ sec += sbuf->cpt_next;
++ }
++
++ /* Pass 2: really restore sockets */
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct cpt_sock_image *sbuf;
++ if (obj->o_obj != NULL)
++ continue;
++ sbuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
++ if (err) {
++ eprintk_ctx("rst_sockets: rst_get_object: %d\n", err);
++ cpt_release_buf(ctx);
++ return err;
++ }
++ if (sbuf->cpt_state == TCP_LISTEN) BUG();
++ err = open_socket(obj, sbuf, ctx);
++ cpt_release_buf(ctx);
++ if (err) {
++ eprintk_ctx("rst_sockets: open_socket: %d\n", err);
++ return err;
++ }
++ }
++
++ return 0;
++}
++
++int rst_orphans(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_ORPHANS];
++ loff_t endsec;
++ cpt_object_t *obj;
++ struct cpt_section_hdr h;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_ORPHANS || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ struct cpt_sock_image *sbuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_SOCKET, sec, sbuf, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++ obj = alloc_cpt_object(GFP_KERNEL, ctx);
++ if (obj == NULL) {
++ cpt_release_buf(ctx);
++ return -ENOMEM;
++ }
++ obj->o_pos = sec;
++ obj->o_ppos = sbuf->cpt_file;
++ err = open_socket(obj, sbuf, ctx);
++ dprintk_ctx("Restoring orphan: %d\n", err);
++ free_cpt_object(obj, ctx);
++ cpt_release_buf(ctx);
++ if (err)
++ return err;
++ sec += sbuf->cpt_next;
++ }
++
++ return 0;
++}
++
++
++/* Pass 3: I understand, this is not funny already :-),
++ * but we have to do another pass to establish links between
++ * not-paired AF_UNIX SOCK_DGRAM sockets and to restore AF_UNIX
++ * skb queues with proper skb->sk links.
++ *
++ * This could be made at the end of rst_sockets(), but we defer
++ * restoring af_unix queues up to the end of restoring files to
++ * make restoring passed FDs cleaner.
++ */
++
++int rst_sockets_complete(struct cpt_context *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct cpt_sock_image *sbuf;
++ struct sock *sk = obj->o_obj;
++ struct sock *peer;
++
++ if (!sk) BUG();
++
++ if (sk->sk_family != AF_UNIX)
++ continue;
++
++ sbuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++
++ if (sbuf->cpt_next > sbuf->cpt_hdrlen)
++ restore_unix_rqueue(sk, sbuf, obj->o_pos, ctx);
++
++ cpt_release_buf(ctx);
++
++ if (sk->sk_type == SOCK_DGRAM && unix_peer(sk) == NULL) {
++ cpt_object_t *pobj;
++
++ sbuf = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_SOCKET, obj->o_pos, sbuf, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++
++ if (sbuf->cpt_peer != -1) {
++ pobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, sbuf->cpt_peer, ctx);
++ if (pobj) {
++ peer = pobj->o_obj;
++ sock_hold(peer);
++ unix_peer(sk) = peer;
++ }
++ }
++ cpt_release_buf(ctx);
++ }
++ }
++
++ rst_orphans(ctx);
++
++ return 0;
++}
++
+diff --git a/kernel/cpt/rst_socket_in.c b/kernel/cpt/rst_socket_in.c
+new file mode 100644
+index 0000000..ddc2d5a
+--- /dev/null
++++ b/kernel/cpt/rst_socket_in.c
+@@ -0,0 +1,489 @@
++/*
++ *
++ * kernel/cpt/rst_socket_in.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/socket.h>
++#include <linux/tcp.h>
++#include <linux/jhash.h>
++#include <net/sock.h>
++#include <net/tcp.h>
++#include <linux/ipv6.h>
++#include <linux/igmp.h>
++#include <net/addrconf.h>
++#include <net/inet6_connection_sock.h>
++#include <linux/nsproxy.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_socket.h"
++#include "cpt_kernel.h"
++
++static inline unsigned long jiffies_import(__u32 tmo)
++{
++ __s32 delta = tmo;
++ return jiffies + (long)delta;
++}
++
++static inline __u32 tcp_jiffies_import(__u32 tmo)
++{
++ return ((__u32)jiffies) + tmo;
++}
++
++
++static int restore_queues(struct sock *sk, struct cpt_sock_image *si,
++ loff_t pos, struct cpt_context *ctx)
++{
++ loff_t endpos;
++
++ pos = pos + si->cpt_hdrlen;
++ endpos = pos + si->cpt_next;
++ while (pos < endpos) {
++ struct sk_buff *skb;
++ __u32 type;
++
++ skb = rst_skb(&pos, NULL, &type, ctx);
++ if (IS_ERR(skb)) {
++ if (PTR_ERR(skb) == -EINVAL) {
++ int err;
++
++ err = rst_sock_attr(&pos, sk, ctx);
++ if (err)
++ return err;
++ }
++ return PTR_ERR(skb);
++ }
++
++ if (sk->sk_type == SOCK_STREAM) {
++ if (type == CPT_SKB_RQ) {
++ skb_set_owner_r(skb, sk);
++ ub_tcprcvbuf_charge_forced(sk, skb);
++ skb_queue_tail(&sk->sk_receive_queue, skb);
++ } else if (type == CPT_SKB_OFOQ) {
++ struct tcp_sock *tp = tcp_sk(sk);
++ skb_set_owner_r(skb, sk);
++ ub_tcprcvbuf_charge_forced(sk, skb);
++ skb_queue_tail(&tp->out_of_order_queue, skb);
++ } else if (type == CPT_SKB_WQ) {
++ sk->sk_wmem_queued += skb->truesize;
++ sk->sk_forward_alloc -= skb->truesize;
++ ub_tcpsndbuf_charge_forced(sk, skb);
++ skb_queue_tail(&sk->sk_write_queue, skb);
++ } else {
++ wprintk_ctx("strange stream queue type %u\n", type);
++ kfree_skb(skb);
++ }
++ } else {
++ if (type == CPT_SKB_RQ) {
++ skb_set_owner_r(skb, sk);
++ skb_queue_tail(&sk->sk_receive_queue, skb);
++ } else if (type == CPT_SKB_WQ) {
++ struct inet_sock *inet = inet_sk(sk);
++ if (inet->cork.fragsize) {
++ skb_set_owner_w(skb, sk);
++ skb_queue_tail(&sk->sk_write_queue, skb);
++ } else {
++ eprintk_ctx("cork skb is dropped\n");
++ kfree_skb(skb);
++ }
++ } else {
++ wprintk_ctx("strange dgram queue type %u\n", type);
++ kfree_skb(skb);
++ }
++ }
++ }
++ return 0;
++}
++
++static struct sock *find_parent(__u16 sport, cpt_context_t *ctx)
++{
++ cpt_object_t *obj;
++ for_each_object(obj, CPT_OBJ_SOCKET) {
++ struct sock *sk = obj->o_obj;
++ if (sk &&
++ sk->sk_state == TCP_LISTEN &&
++ (sk->sk_family == AF_INET || sk->sk_family == AF_INET6) &&
++ inet_sk(sk)->sport == sport)
++ return sk;
++ }
++ return NULL;
++}
++
++static int rst_socket_tcp(struct cpt_sock_image *si, loff_t pos, struct sock *sk,
++ struct cpt_context *ctx)
++{
++ struct tcp_sock *tp = tcp_sk(sk);
++ struct sk_buff *skb;
++ tp->pred_flags = si->cpt_pred_flags;
++ tp->rcv_nxt = si->cpt_rcv_nxt;
++ tp->snd_nxt = si->cpt_snd_nxt;
++ tp->snd_una = si->cpt_snd_una;
++ tp->snd_sml = si->cpt_snd_sml;
++ tp->rcv_tstamp = tcp_jiffies_import(si->cpt_rcv_tstamp);
++ tp->lsndtime = tcp_jiffies_import(si->cpt_lsndtime);
++ tp->tcp_header_len = si->cpt_tcp_header_len;
++ inet_csk(sk)->icsk_ack.pending = si->cpt_ack_pending;
++ inet_csk(sk)->icsk_ack.quick = si->cpt_quick;
++ inet_csk(sk)->icsk_ack.pingpong = si->cpt_pingpong;
++ inet_csk(sk)->icsk_ack.blocked = si->cpt_blocked;
++ inet_csk(sk)->icsk_ack.ato = si->cpt_ato;
++ inet_csk(sk)->icsk_ack.timeout = jiffies_import(si->cpt_ack_timeout);
++ inet_csk(sk)->icsk_ack.lrcvtime = tcp_jiffies_import(si->cpt_lrcvtime);
++ inet_csk(sk)->icsk_ack.last_seg_size = si->cpt_last_seg_size;
++ inet_csk(sk)->icsk_ack.rcv_mss = si->cpt_rcv_mss;
++ tp->snd_wl1 = si->cpt_snd_wl1;
++ tp->snd_wnd = si->cpt_snd_wnd;
++ tp->max_window = si->cpt_max_window;
++ inet_csk(sk)->icsk_pmtu_cookie = si->cpt_pmtu_cookie;
++ tp->mss_cache = si->cpt_mss_cache;
++ tp->rx_opt.mss_clamp = si->cpt_mss_clamp;
++ inet_csk(sk)->icsk_ext_hdr_len = si->cpt_ext_header_len;
++ inet_csk(sk)->icsk_ca_state = si->cpt_ca_state;
++ inet_csk(sk)->icsk_retransmits = si->cpt_retransmits;
++ tp->reordering = si->cpt_reordering;
++ tp->frto_counter = si->cpt_frto_counter;
++ tp->frto_highmark = si->cpt_frto_highmark;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
++ // // tp->adv_cong = si->cpt_adv_cong;
++#endif
++ inet_csk(sk)->icsk_accept_queue.rskq_defer_accept = si->cpt_defer_accept;
++ inet_csk(sk)->icsk_backoff = si->cpt_backoff;
++ tp->srtt = si->cpt_srtt;
++ tp->mdev = si->cpt_mdev;
++ tp->mdev_max = si->cpt_mdev_max;
++ tp->rttvar = si->cpt_rttvar;
++ tp->rtt_seq = si->cpt_rtt_seq;
++ inet_csk(sk)->icsk_rto = si->cpt_rto;
++ tp->packets_out = si->cpt_packets_out;
++ tp->retrans_out = si->cpt_retrans_out;
++ tp->lost_out = si->cpt_lost_out;
++ tp->sacked_out = si->cpt_sacked_out;
++ tp->fackets_out = si->cpt_fackets_out;
++ tp->snd_ssthresh = si->cpt_snd_ssthresh;
++ tp->snd_cwnd = si->cpt_snd_cwnd;
++ tp->snd_cwnd_cnt = si->cpt_snd_cwnd_cnt;
++ tp->snd_cwnd_clamp = si->cpt_snd_cwnd_clamp;
++ tp->snd_cwnd_used = si->cpt_snd_cwnd_used;
++ tp->snd_cwnd_stamp = tcp_jiffies_import(si->cpt_snd_cwnd_stamp);
++ inet_csk(sk)->icsk_timeout = tcp_jiffies_import(si->cpt_timeout);
++ tp->rcv_wnd = si->cpt_rcv_wnd;
++ tp->rcv_wup = si->cpt_rcv_wup;
++ tp->write_seq = si->cpt_write_seq;
++ tp->pushed_seq = si->cpt_pushed_seq;
++ tp->copied_seq = si->cpt_copied_seq;
++ tp->rx_opt.tstamp_ok = si->cpt_tstamp_ok;
++ tp->rx_opt.wscale_ok = si->cpt_wscale_ok;
++ tp->rx_opt.sack_ok = si->cpt_sack_ok;
++ tp->rx_opt.saw_tstamp = si->cpt_saw_tstamp;
++ tp->rx_opt.snd_wscale = si->cpt_snd_wscale;
++ tp->rx_opt.rcv_wscale = si->cpt_rcv_wscale;
++ tp->nonagle = si->cpt_nonagle;
++ tp->keepalive_probes = si->cpt_keepalive_probes;
++ tp->rx_opt.rcv_tsval = si->cpt_rcv_tsval;
++ tp->rx_opt.rcv_tsecr = si->cpt_rcv_tsecr;
++ tp->rx_opt.ts_recent = si->cpt_ts_recent;
++ tp->rx_opt.ts_recent_stamp = si->cpt_ts_recent_stamp;
++ tp->rx_opt.user_mss = si->cpt_user_mss;
++ tp->rx_opt.dsack = si->cpt_dsack;
++ tp->rx_opt.eff_sacks = si->cpt_num_sacks;
++ tp->duplicate_sack[0].start_seq = si->cpt_sack_array[0];
++ tp->duplicate_sack[0].end_seq = si->cpt_sack_array[1];
++ tp->selective_acks[0].start_seq = si->cpt_sack_array[2];
++ tp->selective_acks[0].end_seq = si->cpt_sack_array[3];
++ tp->selective_acks[1].start_seq = si->cpt_sack_array[4];
++ tp->selective_acks[1].end_seq = si->cpt_sack_array[5];
++ tp->selective_acks[2].start_seq = si->cpt_sack_array[6];
++ tp->selective_acks[2].end_seq = si->cpt_sack_array[7];
++ tp->selective_acks[3].start_seq = si->cpt_sack_array[8];
++ tp->selective_acks[3].end_seq = si->cpt_sack_array[9];
++
++ tp->window_clamp = si->cpt_window_clamp;
++ tp->rcv_ssthresh = si->cpt_rcv_ssthresh;
++ inet_csk(sk)->icsk_probes_out = si->cpt_probes_out;
++ tp->rx_opt.num_sacks = si->cpt_num_sacks;
++ tp->advmss = si->cpt_advmss;
++ inet_csk(sk)->icsk_syn_retries = si->cpt_syn_retries;
++ tp->ecn_flags = si->cpt_ecn_flags;
++ tp->prior_ssthresh = si->cpt_prior_ssthresh;
++ tp->high_seq = si->cpt_high_seq;
++ tp->retrans_stamp = si->cpt_retrans_stamp;
++ tp->undo_marker = si->cpt_undo_marker;
++ tp->undo_retrans = si->cpt_undo_retrans;
++ tp->urg_seq = si->cpt_urg_seq;
++ tp->urg_data = si->cpt_urg_data;
++ inet_csk(sk)->icsk_pending = si->cpt_pending;
++ tp->urg_mode = si->cpt_urg_mode;
++ tp->snd_up = si->cpt_snd_up;
++ tp->keepalive_time = si->cpt_keepalive_time;
++ tp->keepalive_intvl = si->cpt_keepalive_intvl;
++ tp->linger2 = si->cpt_linger2;
++
++ sk->sk_send_head = NULL;
++ for (skb = skb_peek(&sk->sk_write_queue);
++ skb && skb != (struct sk_buff*)&sk->sk_write_queue;
++ skb = skb->next) {
++ if (!after(tp->snd_nxt, TCP_SKB_CB(skb)->seq)) {
++ sk->sk_send_head = skb;
++ break;
++ }
++ }
++
++ if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) {
++ struct inet_sock *inet = inet_sk(sk);
++ if (inet->num == 0) {
++ cpt_object_t *lobj = NULL;
++
++ if ((int)si->cpt_parent != -1)
++ lobj = lookup_cpt_obj_byindex(CPT_OBJ_SOCKET, si->cpt_parent, ctx);
++
++ if (lobj && lobj->o_obj) {
++ inet->num = ntohs(inet->sport);
++ local_bh_disable();
++ __inet_inherit_port(lobj->o_obj, sk);
++ local_bh_enable();
++ dprintk_ctx("port inherited from parent\n");
++ } else {
++ struct sock *lsk = find_parent(inet->sport, ctx);
++ if (lsk) {
++ inet->num = ntohs(inet->sport);
++ local_bh_disable();
++ __inet_inherit_port(lsk, sk);
++ local_bh_enable();
++ dprintk_ctx("port inherited\n");
++ } else {
++ eprintk_ctx("we are kinda lost...\n");
++ }
++ }
++ }
++
++ sk->sk_prot->hash(sk);
++
++ if (inet_csk(sk)->icsk_ack.pending&ICSK_ACK_TIMER)
++ sk_reset_timer(sk, &inet_csk(sk)->icsk_delack_timer, inet_csk(sk)->icsk_ack.timeout);
++ if (inet_csk(sk)->icsk_pending)
++ sk_reset_timer(sk, &inet_csk(sk)->icsk_retransmit_timer,
++ inet_csk(sk)->icsk_timeout);
++ if (sock_flag(sk, SOCK_KEEPOPEN)) {
++ unsigned long expires = jiffies_import(si->cpt_ka_timeout);
++ if (time_after(jiffies, expires))
++ expires = jiffies + HZ;
++ sk_reset_timer(sk, &sk->sk_timer, expires);
++ }
++ }
++
++ return 0;
++}
++
++
++int rst_socket_in(struct cpt_sock_image *si, loff_t pos, struct sock *sk,
++ struct cpt_context *ctx)
++{
++ struct inet_sock *inet = inet_sk(sk);
++ struct net *net = get_exec_env()->ve_ns->net_ns;
++
++ lock_sock(sk);
++
++ sk->sk_state = si->cpt_state;
++
++ inet->daddr = si->cpt_daddr;
++ inet->dport = si->cpt_dport;
++ inet->saddr = si->cpt_saddr;
++ inet->rcv_saddr = si->cpt_rcv_saddr;
++ inet->sport = si->cpt_sport;
++ inet->uc_ttl = si->cpt_uc_ttl;
++ inet->tos = si->cpt_tos;
++ inet->cmsg_flags = si->cpt_cmsg_flags;
++ inet->mc_index = si->cpt_mc_index;
++ inet->mc_addr = si->cpt_mc_addr;
++ inet->hdrincl = si->cpt_hdrincl;
++ inet->mc_ttl = si->cpt_mc_ttl;
++ inet->mc_loop = si->cpt_mc_loop;
++ inet->pmtudisc = si->cpt_pmtudisc;
++ inet->recverr = si->cpt_recverr;
++ inet->freebind = si->cpt_freebind;
++ inet->id = si->cpt_idcounter;
++
++ inet->cork.flags = si->cpt_cork_flags;
++ inet->cork.fragsize = si->cpt_cork_fragsize;
++ inet->cork.length = si->cpt_cork_length;
++ inet->cork.addr = si->cpt_cork_addr;
++ inet->cork.fl.fl4_src = si->cpt_cork_saddr;
++ inet->cork.fl.fl4_dst = si->cpt_cork_daddr;
++ inet->cork.fl.oif = si->cpt_cork_oif;
++ if (inet->cork.fragsize) {
++ if (ip_route_output_key(net, (struct rtable **)&inet->cork.dst, &inet->cork.fl)) {
++ eprintk_ctx("failed to restore cork route\n");
++ inet->cork.fragsize = 0;
++ }
++ }
++
++ if (sk->sk_type == SOCK_DGRAM && sk->sk_protocol == IPPROTO_UDP) {
++ struct udp_sock *up = udp_sk(sk);
++ up->pending = si->cpt_udp_pending;
++ up->corkflag = si->cpt_udp_corkflag;
++ up->encap_type = si->cpt_udp_encap;
++ up->len = si->cpt_udp_len;
++ }
++
++ if (sk->sk_family == AF_INET6) {
++ struct ipv6_pinfo *np = inet6_sk(sk);
++
++ memcpy(&np->saddr, si->cpt_saddr6, 16);
++ memcpy(&np->rcv_saddr, si->cpt_rcv_saddr6, 16);
++ memcpy(&np->daddr, si->cpt_daddr6, 16);
++ np->flow_label = si->cpt_flow_label6;
++ np->frag_size = si->cpt_frag_size6;
++ np->hop_limit = si->cpt_hop_limit6;
++ np->mcast_hops = si->cpt_mcast_hops6;
++ np->mcast_oif = si->cpt_mcast_oif6;
++ np->rxopt.all = si->cpt_rxopt6;
++ np->mc_loop = si->cpt_mc_loop6;
++ np->recverr = si->cpt_recverr6;
++ np->sndflow = si->cpt_sndflow6;
++ np->pmtudisc = si->cpt_pmtudisc6;
++ np->ipv6only = si->cpt_ipv6only6;
++
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ if (si->cpt_mapped) {
++ extern struct inet_connection_sock_af_ops ipv6_mapped;
++ if (sk->sk_type == SOCK_STREAM &&
++ sk->sk_protocol == IPPROTO_TCP) {
++ inet_csk(sk)->icsk_af_ops = &ipv6_mapped;
++ sk->sk_backlog_rcv = tcp_v4_do_rcv;
++ }
++ }
++#endif
++ }
++
++ restore_queues(sk, si, pos, ctx);
++
++ if (sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP)
++ rst_socket_tcp(si, pos, sk, ctx);
++
++ release_sock(sk);
++ return 0;
++}
++
++int cpt_attach_accept(struct sock *lsk, struct sock *sk, cpt_context_t *ctx)
++{
++ struct request_sock *req;
++
++ if (lsk->sk_state != TCP_LISTEN)
++ return -EINVAL;
++
++ req = reqsk_alloc(&tcp_request_sock_ops);
++ if (!req)
++ return -ENOMEM;
++
++ sk->sk_socket = NULL;
++ sk->sk_sleep = NULL;
++ inet_csk_reqsk_queue_add(lsk, req, sk);
++ return 0;
++}
++
++int rst_restore_synwait_queue(struct sock *sk, struct cpt_sock_image *si,
++ loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ loff_t end = si->cpt_next;
++
++ pos += si->cpt_hdrlen;
++ while (pos < end) {
++ struct cpt_openreq_image oi;
++
++ err = rst_get_object(CPT_OBJ_OPENREQ, pos, &oi, ctx);
++ if (err) {
++ err = rst_sock_attr(&pos, sk, ctx);
++ if (err)
++ return err;
++ continue;
++ }
++
++ if (oi.cpt_object == CPT_OBJ_OPENREQ) {
++ struct request_sock *req = reqsk_alloc(&tcp_request_sock_ops);
++ if (req == NULL)
++ return -ENOMEM;
++
++ memset(req, 0, sizeof(*req));
++ tcp_rsk(req)->rcv_isn = oi.cpt_rcv_isn;
++ tcp_rsk(req)->snt_isn = oi.cpt_snt_isn;
++ inet_rsk(req)->rmt_port = oi.cpt_rmt_port;
++ req->mss = oi.cpt_mss;
++ req->retrans = oi.cpt_retrans;
++ inet_rsk(req)->snd_wscale = oi.cpt_snd_wscale;
++ inet_rsk(req)->rcv_wscale = oi.cpt_rcv_wscale;
++ inet_rsk(req)->tstamp_ok = oi.cpt_tstamp_ok;
++ inet_rsk(req)->sack_ok = oi.cpt_sack_ok;
++ inet_rsk(req)->wscale_ok = oi.cpt_wscale_ok;
++ inet_rsk(req)->ecn_ok = oi.cpt_ecn_ok;
++ inet_rsk(req)->acked = oi.cpt_acked;
++ req->window_clamp = oi.cpt_window_clamp;
++ req->rcv_wnd = oi.cpt_rcv_wnd;
++ req->ts_recent = oi.cpt_ts_recent;
++ req->expires = jiffies_import(oi.cpt_expires);
++
++ if (oi.cpt_family == AF_INET) {
++ memcpy(&inet_rsk(req)->loc_addr, oi.cpt_loc_addr, 4);
++ memcpy(&inet_rsk(req)->rmt_addr, oi.cpt_rmt_addr, 4);
++ inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
++ } else {
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++ memcpy(&inet6_rsk(req)->loc_addr, oi.cpt_loc_addr, 16);
++ memcpy(&inet6_rsk(req)->rmt_addr, oi.cpt_rmt_addr, 16);
++ inet6_rsk(req)->iif = oi.cpt_iif;
++ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
++#endif
++ }
++ }
++ pos += oi.cpt_next;
++ }
++ return 0;
++}
++
++int rst_sk_mcfilter_in(struct sock *sk, struct cpt_sockmc_image *v,
++ loff_t pos, cpt_context_t *ctx)
++{
++ struct ip_mreqn imr;
++
++ if (v->cpt_mode || v->cpt_next != v->cpt_hdrlen) {
++ eprintk_ctx("IGMPv3 is still not supported\n");
++ return -EINVAL;
++ }
++
++ memset(&imr, 0, sizeof(imr));
++ imr.imr_ifindex = v->cpt_ifindex;
++ imr.imr_multiaddr.s_addr = v->cpt_mcaddr[0];
++ return ip_mc_join_group(sk, &imr);
++}
++
++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
++int rst_sk_mcfilter_in6(struct sock *sk, struct cpt_sockmc_image *v,
++ loff_t pos, cpt_context_t *ctx)
++{
++
++ if (v->cpt_mode || v->cpt_next != v->cpt_hdrlen) {
++ eprintk_ctx("IGMPv3 is still not supported\n");
++ return -EINVAL;
++ }
++
++ return ipv6_sock_mc_join(sk, v->cpt_ifindex,
++ (struct in6_addr*)v->cpt_mcaddr);
++}
++#endif
+diff --git a/kernel/cpt/rst_sysvipc.c b/kernel/cpt/rst_sysvipc.c
+new file mode 100644
+index 0000000..40127d7
+--- /dev/null
++++ b/kernel/cpt/rst_sysvipc.c
+@@ -0,0 +1,633 @@
++/*
++ *
++ * kernel/cpt/rst_sysvipc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/nsproxy.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/shm.h>
++#include <linux/msg.h>
++#include <asm/uaccess.h>
++#include <asm/unistd.h>
++#include <bc/kmem.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_kernel.h"
++
++struct _warg {
++ struct file *file;
++ struct cpt_sysvshm_image *v;
++};
++
++static int fixup_one_shm(struct shmid_kernel *shp, void *arg)
++{
++ struct _warg *warg = arg;
++
++ if (shp->shm_file != warg->file)
++ return 0;
++ if (shp->shm_nattch)
++ return -EEXIST;
++
++ shp->shm_perm.uid = warg->v->cpt_uid;
++ shp->shm_perm.gid = warg->v->cpt_gid;
++ shp->shm_perm.cuid = warg->v->cpt_cuid;
++ shp->shm_perm.cgid = warg->v->cpt_cgid;
++ shp->shm_perm.mode = warg->v->cpt_mode;
++
++ shp->shm_atim = warg->v->cpt_atime;
++ shp->shm_dtim = warg->v->cpt_dtime;
++ shp->shm_ctim = warg->v->cpt_ctime;
++ shp->shm_cprid = warg->v->cpt_creator;
++ shp->shm_lprid = warg->v->cpt_last;
++
++ /* TODO: fix shp->mlock_user? */
++ return 1;
++}
++
++static int fixup_shm(struct file *file, struct cpt_sysvshm_image *v)
++{
++ struct _warg warg;
++
++ warg.file = file;
++ warg.v = v;
++
++ return sysvipc_walk_shm(fixup_one_shm, &warg);
++}
++
++static int fixup_shm_data(struct file *file, loff_t pos, loff_t end,
++ struct cpt_context *ctx)
++{
++ struct cpt_page_block pgb;
++ ssize_t (*do_write)(struct file *, const char __user *, size_t, loff_t *ppos);
++
++ do_write = file->f_dentry->d_inode->i_fop->write;
++ if (do_write == NULL) {
++ eprintk_ctx("No TMPFS? Cannot restore content of SYSV SHM\n");
++ return -EINVAL;
++ }
++
++ while (pos < end) {
++ loff_t opos;
++ loff_t ipos;
++ int count;
++ int err;
++
++ err = rst_get_object(CPT_OBJ_PAGES, pos, &pgb, ctx);
++ if (err)
++ return err;
++ dprintk_ctx("restoring SHM block: %08x-%08x\n",
++ (__u32)pgb.cpt_start, (__u32)pgb.cpt_end);
++ ipos = pos + pgb.cpt_hdrlen;
++ opos = pgb.cpt_start;
++ count = pgb.cpt_end-pgb.cpt_start;
++ while (count > 0) {
++ mm_segment_t oldfs;
++ int copy = count;
++
++ if (copy > PAGE_SIZE)
++ copy = PAGE_SIZE;
++ (void)cpt_get_buf(ctx);
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ err = ctx->pread(ctx->tmpbuf, copy, ctx, ipos);
++ set_fs(oldfs);
++ if (err) {
++ __cpt_release_buf(ctx);
++ return err;
++ }
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ ipos += copy;
++ err = do_write(file, ctx->tmpbuf, copy, &opos);
++ set_fs(oldfs);
++ __cpt_release_buf(ctx);
++ if (err != copy) {
++ eprintk_ctx("write() failure\n");
++ if (err >= 0)
++ err = -EIO;
++ return err;
++ }
++ count -= copy;
++ }
++ pos += pgb.cpt_next;
++ }
++ return 0;
++}
++
++struct file * rst_sysv_shm_itself(loff_t pos, struct cpt_context *ctx)
++{
++ struct file *file;
++ int err;
++ loff_t dpos, epos;
++ union {
++ struct cpt_file_image fi;
++ struct cpt_sysvshm_image shmi;
++ struct cpt_inode_image ii;
++ } u;
++
++ err = rst_get_object(CPT_OBJ_FILE, pos, &u.fi, ctx);
++ if (err < 0)
++ goto err_out;
++ pos = u.fi.cpt_inode;
++ err = rst_get_object(CPT_OBJ_INODE, pos, &u.ii, ctx);
++ if (err < 0)
++ goto err_out;
++ dpos = pos + u.ii.cpt_hdrlen;
++ epos = pos + u.ii.cpt_next;
++ err = rst_get_object(CPT_OBJ_SYSV_SHM, pos + u.ii.cpt_hdrlen, &u.shmi, ctx);
++ if (err < 0)
++ goto err_out;
++ dpos += u.shmi.cpt_next;
++
++ file = sysvipc_setup_shm(u.shmi.cpt_key, u.shmi.cpt_id,
++ u.shmi.cpt_segsz, u.shmi.cpt_mode);
++ if (!IS_ERR(file)) {
++ err = fixup_shm(file, &u.shmi);
++ if (err != -EEXIST && dpos < epos)
++ err = fixup_shm_data(file, dpos, epos, ctx);
++ } else if (IS_ERR(file) && PTR_ERR(file) == -EEXIST) {
++ struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
++ struct shmid_kernel *shp;
++
++ shp = shm_lock(ipc_ns, u.shmi.cpt_id);
++ BUG_ON(IS_ERR(shp));
++ get_file(shp->shm_file);
++ file = shp->shm_file;
++ shm_unlock(shp);
++ }
++ return file;
++
++err_out:
++ return ERR_PTR(err);
++}
++
++struct file * rst_sysv_shm_vma(struct cpt_vma_image *vmai, struct cpt_context *ctx)
++{
++ struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
++ struct file *file;
++ union {
++ struct cpt_file_image fi;
++ struct cpt_inode_image ii;
++ struct cpt_sysvshm_image shmi;
++ } u;
++ struct shmid_kernel *shp;
++ struct shm_file_data *sfd;
++ struct path path;
++ mode_t f_mode;
++ loff_t pos;
++ int err;
++
++ pos = vmai->cpt_file;
++ file = rst_sysv_shm_itself(pos, ctx);
++ if (IS_ERR(file) && PTR_ERR(file) != -EEXIST)
++ return file;
++ fput(file);
++
++ err = rst_get_object(CPT_OBJ_FILE, pos, &u.fi, ctx);
++ if (err < 0)
++ goto err_out;
++ pos = u.fi.cpt_inode;
++ err = rst_get_object(CPT_OBJ_INODE, pos, &u.ii, ctx);
++ if (err < 0)
++ goto err_out;
++ err = rst_get_object(CPT_OBJ_SYSV_SHM, pos + u.ii.cpt_hdrlen, &u.shmi, ctx);
++ if (err < 0)
++ goto err_out;
++
++ shp = shm_lock(ipc_ns, u.shmi.cpt_id);
++ BUG_ON(IS_ERR(shp));
++ path.dentry = dget(shp->shm_file->f_path.dentry);
++ path.mnt = shp->shm_file->f_path.mnt;
++ shm_unlock(shp);
++
++ err = -ENOMEM;
++ sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);
++ if (!sfd)
++ goto out_put_dentry;
++
++ f_mode = 0;
++ if (vmai->cpt_flags & VM_READ)
++ f_mode |= FMODE_READ;
++ if (vmai->cpt_flags & VM_WRITE)
++ f_mode |= FMODE_WRITE;
++ if (vmai->cpt_flags & VM_EXEC)
++ f_mode |= FMODE_EXEC;
++
++ err = -ENOMEM;
++ file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
++ if (!file)
++ goto out_free;
++
++ file->private_data = sfd;
++ file->f_mapping = shp->shm_file->f_mapping;
++ sfd->id = shp->shm_perm.id;
++ sfd->ns = get_ipc_ns(ipc_ns);
++ sfd->file = shp->shm_file;
++ sfd->vm_ops = NULL;
++
++ return file;
++
++out_free:
++ kfree(sfd);
++out_put_dentry:
++ dput(path.dentry);
++err_out:
++ return ERR_PTR(err);
++}
++
++static int attach_one_undo(int semid, struct sem_array *sma, void *arg)
++{
++ struct sem_undo *su = arg;
++ struct sem_undo_list *undo_list = current->sysvsem.undo_list;
++
++ if (semid != su->semid)
++ return 0;
++
++ list_add(&su->list_proc, &undo_list->list_proc);
++ list_add(&su->list_id, &sma->list_id);
++
++ return 1;
++}
++
++static int attach_undo(struct sem_undo *su)
++{
++ return sysvipc_walk_sem(attach_one_undo, su);
++}
++
++static int do_rst_semundo(struct cpt_object_hdr *sui, loff_t pos, struct cpt_context *ctx)
++{
++ int err;
++ struct sem_undo_list *undo_list;
++
++ if (current->sysvsem.undo_list) {
++ eprintk_ctx("Funny undo_list\n");
++ return 0;
++ }
++
++ undo_list = kzalloc(sizeof(struct sem_undo_list), GFP_KERNEL_UBC);
++ if (undo_list == NULL)
++ return -ENOMEM;
++
++ atomic_set(&undo_list->refcnt, 1);
++ spin_lock_init(&undo_list->lock);
++ current->sysvsem.undo_list = undo_list;
++
++ if (sui->cpt_next > sui->cpt_hdrlen) {
++ loff_t offset = pos + sui->cpt_hdrlen;
++ do {
++ struct sem_undo *new;
++ struct cpt_sysvsem_undo_image spi;
++ err = rst_get_object(CPT_OBJ_SYSVSEM_UNDO_REC, offset, &spi, ctx);
++ if (err)
++ goto out;
++ new = kmalloc(sizeof(struct sem_undo) +
++ sizeof(short)*spi.cpt_nsem,
++ GFP_KERNEL_UBC);
++ if (!new) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ memset(new, 0, sizeof(struct sem_undo) + sizeof(short)*spi.cpt_nsem);
++ new->semadj = (short *) &new[1];
++ new->semid = spi.cpt_id;
++ err = ctx->pread(new->semadj, spi.cpt_nsem*sizeof(short), ctx, offset + spi.cpt_hdrlen);
++ if (err) {
++ kfree(new);
++ goto out;
++ }
++ err = attach_undo(new);
++ if (err <= 0) {
++ if (err == 0)
++ err = -ENOENT;
++ kfree(new);
++ goto out;
++ }
++ offset += spi.cpt_next;
++ } while (offset < pos + sui->cpt_next);
++ }
++ err = 0;
++
++out:
++ return err;
++}
++
++__u32 rst_semundo_flag(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ __u32 flag = 0;
++
++#if 0
++ if (ti->cpt_sysvsem_undo == CPT_NULL ||
++ lookup_cpt_obj_bypos(CPT_OBJ_SYSVSEM_UNDO, ti->cpt_sysvsem_undo))
++ flag |= CLONE_SYSVSEM;
++#endif
++ return flag;
++}
++
++int rst_semundo_complete(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ int err;
++ struct sem_undo_list *f = current->sysvsem.undo_list;
++ cpt_object_t *obj;
++ struct cpt_object_hdr sui;
++
++ if (ti->cpt_sysvsem_undo == CPT_NULL) {
++ exit_sem(current);
++ return 0;
++ }
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_SYSVSEM_UNDO, ti->cpt_sysvsem_undo, ctx);
++ if (obj) {
++ if (obj->o_obj != f) {
++ exit_sem(current);
++ f = obj->o_obj;
++ atomic_inc(&f->refcnt);
++ current->sysvsem.undo_list = f;
++ }
++ return 0;
++ }
++
++ if ((err = rst_get_object(CPT_OBJ_SYSVSEM_UNDO, ti->cpt_sysvsem_undo, &sui, ctx)) != 0)
++ goto out;
++
++ if ((err = do_rst_semundo(&sui, ti->cpt_sysvsem_undo, ctx)) != 0)
++ goto out;
++
++ err = -ENOMEM;
++ obj = cpt_object_add(CPT_OBJ_SYSVSEM_UNDO, f, ctx);
++ if (obj) {
++ err = 0;
++ cpt_obj_setpos(obj, ti->cpt_sysvsem_undo, ctx);
++ }
++
++ return 0;
++
++out:
++ return err;
++}
++
++struct _sarg {
++ int semid;
++ struct cpt_sysvsem_image *v;
++ __u32 *arr;
++};
++
++static int fixup_one_sem(int semid, struct sem_array *sma, void *arg)
++{
++ struct _sarg *warg = arg;
++
++ if (semid != warg->semid)
++ return 0;
++
++ sma->sem_perm.uid = warg->v->cpt_uid;
++ sma->sem_perm.gid = warg->v->cpt_gid;
++ sma->sem_perm.cuid = warg->v->cpt_cuid;
++ sma->sem_perm.cgid = warg->v->cpt_cgid;
++ sma->sem_perm.mode = warg->v->cpt_mode;
++ sma->sem_perm.seq = warg->v->cpt_seq;
++
++ sma->sem_ctime = warg->v->cpt_ctime;
++ sma->sem_otime = warg->v->cpt_otime;
++ memcpy(sma->sem_base, warg->arr, sma->sem_nsems*8);
++ return 1;
++}
++
++static int fixup_sem(int semid, struct cpt_sysvsem_image *v, __u32 *arr)
++{
++ struct _sarg warg;
++
++ warg.semid = semid;
++ warg.v = v;
++ warg.arr = arr;
++
++ return sysvipc_walk_sem(fixup_one_sem, &warg);
++}
++
++
++static int restore_sem(loff_t pos, struct cpt_sysvsem_image *si,
++ struct cpt_context *ctx)
++{
++ int err;
++ __u32 *arr;
++ int nsems = (si->cpt_next - si->cpt_hdrlen)/8;
++
++ arr = kmalloc(nsems*8, GFP_KERNEL);
++ if (!arr)
++ return -ENOMEM;
++
++ err = ctx->pread(arr, nsems*8, ctx, pos+si->cpt_hdrlen);
++ if (err)
++ goto out;
++ err = sysvipc_setup_sem(si->cpt_key, si->cpt_id, nsems, si->cpt_mode);
++ if (err < 0) {
++ eprintk_ctx("SEM 3\n");
++ goto out;
++ }
++ err = fixup_sem(si->cpt_id, si, arr);
++ if (err == 0)
++ err = -ESRCH;
++ if (err > 0)
++ err = 0;
++out:
++ kfree(arr);
++ return err;
++}
++
++static int rst_sysv_sem(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_SYSV_SEM];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_sysvsem_image sbuf;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_SYSV_SEM || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ int err;
++ err = rst_get_object(CPT_OBJ_SYSV_SEM, sec, &sbuf, ctx);
++ if (err)
++ return err;
++ err = restore_sem(sec, &sbuf, ctx);
++ if (err)
++ return err;
++ sec += sbuf.cpt_next;
++ }
++ return 0;
++}
++
++struct _marg {
++ int msqid;
++ struct cpt_sysvmsg_image *v;
++ struct msg_queue *m;
++};
++
++static int fixup_one_msg(int msqid, struct msg_queue *msq, void *arg)
++{
++ struct _marg *warg = arg;
++
++ if (msqid != warg->msqid)
++ return 0;
++
++ msq->q_perm.uid = warg->v->cpt_uid;
++ msq->q_perm.gid = warg->v->cpt_gid;
++ msq->q_perm.cuid = warg->v->cpt_cuid;
++ msq->q_perm.cgid = warg->v->cpt_cgid;
++ msq->q_perm.mode = warg->v->cpt_mode;
++ msq->q_perm.seq = warg->v->cpt_seq;
++
++ msq->q_stime = warg->v->cpt_stime;
++ msq->q_rtime = warg->v->cpt_rtime;
++ msq->q_ctime = warg->v->cpt_ctime;
++ msq->q_lspid = warg->v->cpt_last_sender;
++ msq->q_lrpid = warg->v->cpt_last_receiver;
++ msq->q_qbytes = warg->v->cpt_qbytes;
++
++ warg->m = msq;
++ return 1;
++}
++
++struct _larg
++{
++ cpt_context_t * ctx;
++ loff_t pos;
++};
++
++static int do_load_msg(void * dst, int len, int offset, void * data)
++{
++ struct _larg * arg = data;
++ return arg->ctx->pread(dst, len, arg->ctx, arg->pos + offset);
++}
++
++static int fixup_msg(int msqid, struct cpt_sysvmsg_image *v, loff_t pos,
++ cpt_context_t * ctx)
++{
++ int err;
++ struct _marg warg;
++ loff_t endpos = pos + v->cpt_next;
++ struct ipc_namespace *ns = current->nsproxy->ipc_ns;
++
++ pos += v->cpt_hdrlen;
++
++ warg.msqid = msqid;
++ warg.v = v;
++
++ err = sysvipc_walk_msg(fixup_one_msg, &warg);
++ if (err <= 0)
++ return err;
++
++ while (pos < endpos) {
++ struct cpt_sysvmsg_msg_image mi;
++ struct msg_msg *m;
++ struct _larg data = {
++ .ctx = ctx
++ };
++
++ err = rst_get_object(CPT_OBJ_SYSVMSG_MSG, pos, &mi, ctx);
++ if (err)
++ return err;
++ data.pos = pos + mi.cpt_hdrlen;
++ m = sysv_msg_load(do_load_msg, mi.cpt_size, &data);
++ if (IS_ERR(m))
++ return PTR_ERR(m);
++ m->m_type = mi.cpt_type;
++ m->m_ts = mi.cpt_size;
++ list_add_tail(&m->m_list, &warg.m->q_messages);
++ warg.m->q_cbytes += m->m_ts;
++ warg.m->q_qnum++;
++ atomic_add(m->m_ts, &ns->msg_bytes);
++ atomic_inc(&ns->msg_hdrs);
++
++ pos += mi.cpt_next;
++ }
++ return 1;
++}
++
++static int restore_msg(loff_t pos, struct cpt_sysvmsg_image *si,
++ struct cpt_context *ctx)
++{
++ int err;
++
++ err = sysvipc_setup_msg(si->cpt_key, si->cpt_id, si->cpt_mode);
++ if (err < 0) {
++ eprintk_ctx("MSG 3\n");
++ goto out;
++ }
++ err = fixup_msg(si->cpt_id, si, pos, ctx);
++ if (err == 0)
++ err = -ESRCH;
++ if (err > 0)
++ err = 0;
++out:
++ return err;
++}
++
++static int rst_sysv_msg(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_SYSV_MSG];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_sysvmsg_image sbuf;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_SYSV_MSG || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ int err;
++ err = rst_get_object(CPT_OBJ_SYSVMSG, sec, &sbuf, ctx);
++ if (err)
++ return err;
++ err = restore_msg(sec, &sbuf, ctx);
++ if (err)
++ return err;
++ sec += sbuf.cpt_next;
++ }
++ return 0;
++}
++
++
++int rst_sysv_ipc(struct cpt_context *ctx)
++{
++ int err;
++
++ err = rst_sysv_sem(ctx);
++ if (!err)
++ err = rst_sysv_msg(ctx);
++
++ return err;
++}
+diff --git a/kernel/cpt/rst_tty.c b/kernel/cpt/rst_tty.c
+new file mode 100644
+index 0000000..48bc4ce
+--- /dev/null
++++ b/kernel/cpt/rst_tty.c
+@@ -0,0 +1,384 @@
++/*
++ *
++ * kernel/cpt/rst_tty.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/mman.h>
++#include <linux/mount.h>
++#include <linux/tty.h>
++#include <linux/vmalloc.h>
++#include <linux/nsproxy.h>
++#include <asm/unistd.h>
++#include <asm/uaccess.h>
++#include <linux/cpt_image.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_mm.h"
++#include "cpt_process.h"
++#include "cpt_files.h"
++#include "cpt_kernel.h"
++
++static int pty_setup(struct tty_struct *stty, loff_t pos,
++ struct cpt_tty_image *pi, struct cpt_context *ctx)
++{
++ unsigned long flags;
++
++ stty->pgrp = NULL;
++ stty->session = NULL;
++ stty->packet = pi->cpt_packet;
++ stty->stopped = pi->cpt_stopped;
++ stty->hw_stopped = pi->cpt_hw_stopped;
++ stty->flow_stopped = pi->cpt_flow_stopped;
++#define DONOT_CHANGE ((1<<TTY_CHARGED)|(1<<TTY_CLOSING)|(1<<TTY_LDISC))
++ flags = stty->flags & DONOT_CHANGE;
++ stty->flags = flags | (pi->cpt_flags & ~DONOT_CHANGE);
++ stty->ctrl_status = pi->cpt_ctrl_status;
++ stty->winsize.ws_row = pi->cpt_ws_row;
++ stty->winsize.ws_col = pi->cpt_ws_col;
++ stty->winsize.ws_ypixel = pi->cpt_ws_prow;
++ stty->winsize.ws_xpixel = pi->cpt_ws_pcol;
++ stty->canon_column = pi->cpt_canon_column;
++ stty->column = pi->cpt_column;
++ stty->raw = pi->cpt_raw;
++ stty->real_raw = pi->cpt_real_raw;
++ stty->erasing = pi->cpt_erasing;
++ stty->lnext = pi->cpt_lnext;
++ stty->icanon = pi->cpt_icanon;
++ stty->closing = pi->cpt_closing;
++ stty->minimum_to_wake = pi->cpt_minimum_to_wake;
++
++ stty->termios->c_iflag = pi->cpt_c_iflag;
++ stty->termios->c_oflag = pi->cpt_c_oflag;
++ stty->termios->c_lflag = pi->cpt_c_lflag;
++ stty->termios->c_cflag = pi->cpt_c_cflag;
++ memcpy(&stty->termios->c_cc, &pi->cpt_c_cc, NCCS);
++ memcpy(stty->read_flags, pi->cpt_read_flags, sizeof(stty->read_flags));
++
++ if (pi->cpt_next > pi->cpt_hdrlen) {
++ int err;
++ struct cpt_obj_bits b;
++ err = rst_get_object(CPT_OBJ_BITS, pos + pi->cpt_hdrlen, &b, ctx);
++ if (err)
++ return err;
++ if (b.cpt_size == 0)
++ return 0;
++ err = ctx->pread(stty->read_buf, b.cpt_size, ctx, pos + pi->cpt_hdrlen + b.cpt_hdrlen);
++ if (err)
++ return err;
++
++ spin_lock_irq(&stty->read_lock);
++ stty->read_tail = 0;
++ stty->read_cnt = b.cpt_size;
++ stty->read_head = b.cpt_size;
++ stty->canon_head = stty->read_tail + pi->cpt_canon_head;
++ stty->canon_data = pi->cpt_canon_data;
++ spin_unlock_irq(&stty->read_lock);
++ }
++
++ return 0;
++}
++
++/* Find slave/master tty in image, when we already know master/slave.
++ * It might be optimized, of course. */
++static loff_t find_pty_pair(struct tty_struct *stty, loff_t pos, struct cpt_tty_image *pi, struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_TTY];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_tty_image *pibuf;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return CPT_NULL;
++ if (h.cpt_section != CPT_SECT_TTY || h.cpt_hdrlen < sizeof(h))
++ return CPT_NULL;
++ pibuf = kmalloc(sizeof(*pibuf), GFP_KERNEL);
++ if (pibuf == NULL) {
++ eprintk_ctx("cannot allocate buffer\n");
++ return CPT_NULL;
++ }
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ if (rst_get_object(CPT_OBJ_TTY, sec, pibuf, ctx))
++ return CPT_NULL;
++ if (pibuf->cpt_index == pi->cpt_index &&
++ !((pi->cpt_drv_flags^pibuf->cpt_drv_flags)&TTY_DRIVER_DEVPTS_MEM) &&
++ pos != sec) {
++ pty_setup(stty, sec, pibuf, ctx);
++ return sec;
++ }
++ sec += pibuf->cpt_next;
++ }
++ kfree(pibuf);
++ return CPT_NULL;
++}
++
++static int fixup_tty_attrs(struct cpt_inode_image *ii, struct file *master,
++ struct cpt_context *ctx)
++{
++ int err;
++ struct iattr newattrs;
++ struct dentry *d = master->f_dentry;
++
++ newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE;
++ newattrs.ia_uid = ii->cpt_uid;
++ newattrs.ia_gid = ii->cpt_gid;
++ newattrs.ia_mode = ii->cpt_mode;
++
++ mutex_lock(&d->d_inode->i_mutex);
++ err = notify_change(d, &newattrs);
++ mutex_unlock(&d->d_inode->i_mutex);
++
++ return err;
++}
++
++/* NOTE: "portable", but ugly thing. To allocate /dev/pts/N, we open
++ * /dev/ptmx until we get pty with desired index.
++ */
++
++struct file *ptmx_open(int index, unsigned int flags)
++{
++ struct file *file;
++ struct file **stack = NULL;
++ int depth = 0;
++
++ for (;;) {
++ struct tty_struct *tty;
++
++ file = filp_open("/dev/ptmx", flags|O_NONBLOCK|O_NOCTTY|O_RDWR, 0);
++ if (IS_ERR(file))
++ break;
++ tty = file->private_data;
++ if (tty->index == index)
++ break;
++
++ if (depth == PAGE_SIZE/sizeof(struct file *)) {
++ fput(file);
++ file = ERR_PTR(-EBUSY);
++ break;
++ }
++ if (stack == NULL) {
++ stack = (struct file **)__get_free_page(GFP_KERNEL);
++ if (!stack) {
++ fput(file);
++ file = ERR_PTR(-ENOMEM);
++ break;
++ }
++ }
++ stack[depth] = file;
++ depth++;
++ }
++ while (depth > 0) {
++ depth--;
++ fput(stack[depth]);
++ }
++ if (stack)
++ free_page((unsigned long)stack);
++ return file;
++}
++
++
++struct file * rst_open_tty(struct cpt_file_image *fi, struct cpt_inode_image *ii,
++ unsigned flags, struct cpt_context *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++ struct file *master, *slave;
++ struct tty_struct *stty;
++ struct cpt_tty_image *pi;
++ static char *a = "pqrstuvwxyzabcde";
++ static char *b = "0123456789abcdef";
++ char pairname[16];
++ unsigned master_flags, slave_flags;
++
++ if (fi->cpt_priv == CPT_NULL)
++ return ERR_PTR(-EINVAL);
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_TTY, fi->cpt_priv, ctx);
++ if (obj && obj->o_parent) {
++ dprintk_ctx("obtained pty as pair to existing\n");
++ master = obj->o_parent;
++ stty = master->private_data;
++
++ if (stty->driver->subtype == PTY_TYPE_MASTER &&
++ (stty->driver->flags&TTY_DRIVER_DEVPTS_MEM)) {
++ wprintk_ctx("cloning ptmx\n");
++ get_file(master);
++ return master;
++ }
++
++ master = dentry_open(dget(master->f_dentry),
++ mntget(master->f_vfsmnt), flags);
++ if (!IS_ERR(master)) {
++ stty = master->private_data;
++ if (stty->driver->subtype != PTY_TYPE_MASTER)
++ fixup_tty_attrs(ii, master, ctx);
++ }
++ return master;
++ }
++
++ pi = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_TTY, fi->cpt_priv, pi, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return ERR_PTR(err);
++ }
++
++ master_flags = slave_flags = 0;
++ if (pi->cpt_drv_subtype == PTY_TYPE_MASTER)
++ master_flags = flags;
++ else
++ slave_flags = flags;
++
++ /*
++ * Open pair master/slave.
++ */
++ if (pi->cpt_drv_flags&TTY_DRIVER_DEVPTS_MEM) {
++ master = ptmx_open(pi->cpt_index, master_flags);
++ } else {
++ sprintf(pairname, "/dev/pty%c%c", a[pi->cpt_index/16], b[pi->cpt_index%16]);
++ master = filp_open(pairname, master_flags|O_NONBLOCK|O_NOCTTY|O_RDWR, 0);
++ }
++ if (IS_ERR(master)) {
++ eprintk_ctx("filp_open master: %Ld %ld\n", (long long)fi->cpt_priv, PTR_ERR(master));
++ cpt_release_buf(ctx);
++ return master;
++ }
++ stty = master->private_data;
++ clear_bit(TTY_PTY_LOCK, &stty->flags);
++ if (pi->cpt_drv_flags&TTY_DRIVER_DEVPTS_MEM)
++ sprintf(pairname, "/dev/pts/%d", stty->index);
++ else
++ sprintf(pairname, "/dev/tty%c%c", a[stty->index/16], b[stty->index%16]);
++ slave = filp_open(pairname, slave_flags|O_NONBLOCK|O_NOCTTY|O_RDWR, 0);
++ if (IS_ERR(slave)) {
++ eprintk_ctx("filp_open slave %s: %ld\n", pairname, PTR_ERR(slave));
++ fput(master);
++ cpt_release_buf(ctx);
++ return slave;
++ }
++
++ if (pi->cpt_drv_subtype != PTY_TYPE_MASTER)
++ fixup_tty_attrs(ii, slave, ctx);
++
++ cpt_object_add(CPT_OBJ_TTY, master->private_data, ctx);
++ cpt_object_add(CPT_OBJ_TTY, slave->private_data, ctx);
++ cpt_object_add(CPT_OBJ_FILE, master, ctx);
++ cpt_object_add(CPT_OBJ_FILE, slave, ctx);
++
++ if (pi->cpt_drv_subtype == PTY_TYPE_MASTER) {
++ loff_t pos;
++ obj = lookup_cpt_object(CPT_OBJ_TTY, master->private_data, ctx);
++ obj->o_parent = master;
++ cpt_obj_setpos(obj, fi->cpt_priv, ctx);
++ pty_setup(stty, fi->cpt_priv, pi, ctx);
++
++ obj = lookup_cpt_object(CPT_OBJ_TTY, slave->private_data, ctx);
++ obj->o_parent = slave;
++ pos = find_pty_pair(stty->link, fi->cpt_priv, pi, ctx);
++ cpt_obj_setpos(obj, pos, ctx);
++
++ obj = lookup_cpt_object(CPT_OBJ_FILE, slave, ctx);
++ cpt_obj_setpos(obj, CPT_NULL, ctx);
++ get_file(master);
++ cpt_release_buf(ctx);
++ return master;
++ } else {
++ loff_t pos;
++ obj = lookup_cpt_object(CPT_OBJ_TTY, slave->private_data, ctx);
++ obj->o_parent = slave;
++ cpt_obj_setpos(obj, fi->cpt_priv, ctx);
++ pty_setup(stty->link, fi->cpt_priv, pi, ctx);
++
++ obj = lookup_cpt_object(CPT_OBJ_TTY, master->private_data, ctx);
++ obj->o_parent = master;
++ pos = find_pty_pair(stty, fi->cpt_priv, pi, ctx);
++ cpt_obj_setpos(obj, pos, ctx);
++
++ obj = lookup_cpt_object(CPT_OBJ_FILE, master, ctx);
++ cpt_obj_setpos(obj, CPT_NULL, ctx);
++ get_file(slave);
++ cpt_release_buf(ctx);
++ return slave;
++ }
++}
++
++int rst_tty_jobcontrol(struct cpt_context *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_TTY];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_TTY || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ cpt_object_t *obj;
++ struct cpt_tty_image *pibuf = cpt_get_buf(ctx);
++
++ if (rst_get_object(CPT_OBJ_TTY, sec, pibuf, ctx)) {
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_TTY, sec, ctx);
++ if (obj) {
++ struct tty_struct *stty = obj->o_obj;
++ if ((int)pibuf->cpt_pgrp > 0) {
++ rcu_read_lock();
++ stty->pgrp = get_pid(alloc_vpid_safe(pibuf->cpt_pgrp));
++ rcu_read_unlock();
++ if (!stty->pgrp)
++ dprintk_ctx("unknown tty pgrp %d\n", pibuf->cpt_pgrp);
++ } else if (pibuf->cpt_pgrp) {
++ stty->pgrp = alloc_pid(current->nsproxy->pid_ns,
++ 0);
++ if (!stty->pgrp) {
++ eprintk_ctx("cannot allocate stray tty->pgrp");
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++ }
++ if ((int)pibuf->cpt_session > 0) {
++ struct pid *sess;
++
++ rcu_read_lock();
++ sess = get_pid(alloc_vpid_safe(pibuf->cpt_session));
++ rcu_read_unlock();
++ if (!sess) {
++ dprintk_ctx("unknown tty session %d\n", pibuf->cpt_session);
++ } else if (!stty->session) {
++ stty->session = sess;
++ }
++ }
++ }
++ sec += pibuf->cpt_next;
++ cpt_release_buf(ctx);
++ }
++ return 0;
++}
+diff --git a/kernel/cpt/rst_ubc.c b/kernel/cpt/rst_ubc.c
+new file mode 100644
+index 0000000..a39ae28
+--- /dev/null
++++ b/kernel/cpt/rst_ubc.c
+@@ -0,0 +1,131 @@
++/*
++ *
++ * kernel/cpt/rst_ubc.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/types.h>
++#include <bc/beancounter.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++
++struct user_beancounter *rst_lookup_ubc(__u64 pos, struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ obj = lookup_cpt_obj_bypos(CPT_OBJ_UBC, pos, ctx);
++ if (obj == NULL) {
++ eprintk("RST: unknown ub @%Ld\n", (long long)pos);
++ return get_beancounter(get_exec_ub());
++ }
++ return get_beancounter(obj->o_obj);
++}
++
++void copy_one_ubparm(struct ubparm *from, struct ubparm *to, int bc_parm_id)
++{
++ to[bc_parm_id].barrier = from[bc_parm_id].barrier;
++ to[bc_parm_id].limit = from[bc_parm_id].limit;
++}
++
++void set_one_ubparm_to_max(struct ubparm *ubprm, int bc_parm_id)
++{
++ ubprm[bc_parm_id].barrier = UB_MAXVALUE;
++ ubprm[bc_parm_id].limit = UB_MAXVALUE;
++}
++
++static void restore_one_bc_parm(struct cpt_ubparm *dmp, struct ubparm *prm,
++ int held)
++{
++ prm->barrier = (dmp->barrier == CPT_NULL ? UB_MAXVALUE : dmp->barrier);
++ prm->limit = (dmp->limit == CPT_NULL ? UB_MAXVALUE : dmp->limit);
++ if (held)
++ prm->held = dmp->held;
++ prm->maxheld = dmp->maxheld;
++ prm->minheld = dmp->minheld;
++ prm->failcnt = dmp->failcnt;
++}
++
++static int restore_one_bc(struct cpt_beancounter_image *v,
++ cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct user_beancounter *bc;
++ cpt_object_t *pobj;
++ int i;
++
++ if (v->cpt_parent != CPT_NULL) {
++ pobj = lookup_cpt_obj_bypos(CPT_OBJ_UBC, v->cpt_parent, ctx);
++ if (pobj == NULL)
++ return -ESRCH;
++ bc = get_subbeancounter_byid(pobj->o_obj, v->cpt_id, 1);
++ } else {
++ bc = get_exec_ub();
++ while (bc->parent)
++ bc = bc->parent;
++ get_beancounter(bc);
++ }
++ if (bc == NULL)
++ return -ENOMEM;
++ obj->o_obj = bc;
++
++ if (ctx->image_version < CPT_VERSION_18 &&
++ CPT_VERSION_MINOR(ctx->image_version) < 1)
++ goto out;
++
++ for (i = 0; i < UB_RESOURCES; i++) {
++ restore_one_bc_parm(v->cpt_parms + i * 2, bc->ub_parms + i, 0);
++ restore_one_bc_parm(v->cpt_parms + i * 2 + 1,
++ bc->ub_store + i, 1);
++ }
++
++out:
++ if (!bc->parent)
++ for (i = 0; i < UB_RESOURCES; i++)
++ copy_one_ubparm(bc->ub_parms, ctx->saved_ubc, i);
++
++ return 0;
++}
++
++int rst_undump_ubc(struct cpt_context *ctx)
++{
++ loff_t start, end;
++ struct cpt_beancounter_image *v;
++ cpt_object_t *obj;
++ int err;
++
++ err = rst_get_section(CPT_SECT_UBC, ctx, &start, &end);
++ if (err)
++ return err;
++
++ while (start < end) {
++ v = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_UBC, start, v, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++
++ obj = alloc_cpt_object(GFP_KERNEL, ctx);
++ cpt_obj_setpos(obj, start, ctx);
++ intern_cpt_object(CPT_OBJ_UBC, obj, ctx);
++
++ restore_one_bc(v, obj, ctx);
++
++ cpt_release_buf(ctx);
++ start += v->cpt_next;
++ }
++ return 0;
++}
++
++void rst_finish_ubc(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++
++ for_each_object(obj, CPT_OBJ_UBC)
++ put_beancounter(obj->o_obj);
++}
+diff --git a/kernel/cpt/rst_undump.c b/kernel/cpt/rst_undump.c
+new file mode 100644
+index 0000000..5c8977a
+--- /dev/null
++++ b/kernel/cpt/rst_undump.c
+@@ -0,0 +1,1007 @@
++/*
++ *
++ * kernel/cpt/rst_undump.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/errno.h>
++#include <linux/pagemap.h>
++#include <linux/poll.h>
++#include <linux/mnt_namespace.h>
++#include <linux/personality.h>
++#include <linux/binfmts.h>
++#include <linux/smp_lock.h>
++#include <linux/ve_proto.h>
++#include <linux/virtinfo.h>
++#include <linux/virtinfoscp.h>
++#include <linux/compat.h>
++#include <linux/vzcalluser.h>
++#include <bc/beancounter.h>
++#ifdef CONFIG_X86
++#include <asm/desc.h>
++#endif
++#include <asm/unistd.h>
++#include <linux/nsproxy.h>
++#include <linux/pid_namespace.h>
++#include <linux/utsname.h>
++#include <linux/futex.h>
++#include <linux/shm.h>
++
++#include "cpt_obj.h"
++#include "cpt_context.h"
++#include "cpt_files.h"
++#include "cpt_mm.h"
++#include "cpt_process.h"
++#include "cpt_socket.h"
++#include "cpt_net.h"
++#include "cpt_ubc.h"
++#include "cpt_kernel.h"
++
++static int rst_utsname(cpt_context_t *ctx);
++
++
++struct thr_context {
++ struct completion init_complete;
++ struct completion task_done;
++ int error;
++ struct cpt_context *ctx;
++ cpt_object_t *tobj;
++};
++
++static int rst_clone_children(cpt_object_t *obj, struct cpt_context *ctx);
++
++static int vps_rst_veinfo(struct cpt_context *ctx)
++{
++ int err;
++ struct cpt_veinfo_image *i;
++ struct ve_struct *ve;
++ struct timespec delta;
++ loff_t start, end;
++ struct ipc_namespace *ns;
++
++ err = rst_get_section(CPT_SECT_VEINFO, ctx, &start, &end);
++ if (err)
++ goto out;
++
++ i = cpt_get_buf(ctx);
++ memset(i, 0, sizeof(*i));
++ err = rst_get_object(CPT_OBJ_VEINFO, start, i, ctx);
++ if (err)
++ goto out_rel;
++
++ ve = get_exec_env();
++ ns = ve->ve_ns->ipc_ns;
++
++ /* Damn. Fatal mistake, these two values are size_t! */
++ ns->shm_ctlall = i->shm_ctl_all ? : 0xFFFFFFFFU;
++ ns->shm_ctlmax = i->shm_ctl_max ? : 0xFFFFFFFFU;
++ ns->shm_ctlmni = i->shm_ctl_mni;
++
++ ns->msg_ctlmax = i->msg_ctl_max;
++ ns->msg_ctlmni = i->msg_ctl_mni;
++ ns->msg_ctlmnb = i->msg_ctl_mnb;
++
++ BUILD_BUG_ON(sizeof(ns->sem_ctls) != sizeof(i->sem_ctl_arr));
++ ns->sem_ctls[0] = i->sem_ctl_arr[0];
++ ns->sem_ctls[1] = i->sem_ctl_arr[1];
++ ns->sem_ctls[2] = i->sem_ctl_arr[2];
++ ns->sem_ctls[3] = i->sem_ctl_arr[3];
++
++ cpt_timespec_import(&delta, i->start_timespec_delta);
++ _set_normalized_timespec(&ve->start_timespec,
++ ve->start_timespec.tv_sec - delta.tv_sec,
++ ve->start_timespec.tv_nsec - delta.tv_nsec);
++ ve->start_jiffies -= i->start_jiffies_delta;
++ // // FIXME: what???
++ // // ve->start_cycles -= (s64)i->start_jiffies_delta * cycles_per_jiffy;
++
++ ctx->last_vpid = i->last_pid;
++
++ err = 0;
++out_rel:
++ cpt_release_buf(ctx);
++out:
++ return err;
++}
++
++static int vps_rst_reparent_root(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ int err;
++ struct env_create_param3 param;
++
++ do_posix_clock_monotonic_gettime(&ctx->cpt_monotonic_time);
++ do_gettimespec(&ctx->delta_time);
++
++ _set_normalized_timespec(&ctx->delta_time,
++ ctx->delta_time.tv_sec - ctx->start_time.tv_sec,
++ ctx->delta_time.tv_nsec - ctx->start_time.tv_nsec);
++ ctx->delta_nsec = (s64)ctx->delta_time.tv_sec*NSEC_PER_SEC + ctx->delta_time.tv_nsec;
++ if (ctx->delta_nsec < 0) {
++ wprintk_ctx("Wall time is behind source by %Ld ns, "
++ "time sensitive applications can misbehave\n", (long long)-ctx->delta_nsec);
++ }
++
++ _set_normalized_timespec(&ctx->cpt_monotonic_time,
++ ctx->cpt_monotonic_time.tv_sec - ctx->delta_time.tv_sec,
++ ctx->cpt_monotonic_time.tv_nsec - ctx->delta_time.tv_nsec);
++
++ memset(&param, 0, sizeof(param));
++ param.iptables_mask = ctx->iptables_mask;
++ param.feature_mask = ctx->features;
++
++ /* feature_mask is set as required - pretend we know everything */
++ param.known_features = (ctx->image_version < CPT_VERSION_18) ?
++ VE_FEATURES_OLD : ~(__u64)0;
++
++ err = real_env_create(ctx->ve_id, VE_CREATE|VE_LOCK, 2,
++ &param, sizeof(param));
++ if (err < 0)
++ eprintk_ctx("real_env_create: %d\n", err);
++
++ get_exec_env()->jiffies_fixup =
++ (ctx->delta_time.tv_sec < 0 ?
++ 0 : timespec_to_jiffies(&ctx->delta_time)) -
++ (unsigned long)(get_jiffies_64() - ctx->virt_jiffies64);
++ dprintk_ctx("JFixup %ld %Ld\n", get_exec_env()->jiffies_fixup,
++ (long long)ctx->delta_nsec);
++ return err < 0 ? err : 0;
++}
++
++static int hook(void *arg)
++{
++ struct thr_context *thr_ctx = arg;
++ struct cpt_context *ctx;
++ cpt_object_t *tobj;
++ struct cpt_task_image *ti;
++ int err = 0;
++ int exiting = 0;
++
++ current->state = TASK_UNINTERRUPTIBLE;
++ complete(&thr_ctx->init_complete);
++ schedule();
++
++ ctx = thr_ctx->ctx;
++ tobj = thr_ctx->tobj;
++ ti = tobj->o_image;
++
++ current->fs->umask = 0;
++
++ if (ti->cpt_pid == 1) {
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *bc;
++#endif
++
++ err = vps_rst_reparent_root(tobj, ctx);
++
++ if (err) {
++ rst_report_error(err, ctx);
++ goto out;
++ }
++
++ memcpy(&get_exec_env()->ve_cap_bset, &ti->cpt_ecap, sizeof(kernel_cap_t));
++
++ if (ctx->statusfile) {
++ fput(ctx->statusfile);
++ ctx->statusfile = NULL;
++ }
++
++ if (ctx->lockfile) {
++ char b;
++ mm_segment_t oldfs;
++ err = -EINVAL;
++
++ oldfs = get_fs(); set_fs(KERNEL_DS);
++ if (ctx->lockfile->f_op && ctx->lockfile->f_op->read)
++ err = ctx->lockfile->f_op->read(ctx->lockfile, &b, 1, &ctx->lockfile->f_pos);
++ set_fs(oldfs);
++ fput(ctx->lockfile);
++ ctx->lockfile = NULL;
++ }
++
++ if (err) {
++ eprintk_ctx("CPT: lock fd is closed incorrectly: %d\n", err);
++ goto out;
++ }
++ err = vps_rst_veinfo(ctx);
++ if (err) {
++ eprintk_ctx("rst_veinfo: %d\n", err);
++ goto out;
++ }
++
++ err = rst_utsname(ctx);
++ if (err) {
++ eprintk_ctx("rst_utsname: %d\n", err);
++ goto out;
++ }
++
++ err = rst_root_namespace(ctx);
++ if (err) {
++ eprintk_ctx("rst_namespace: %d\n", err);
++ goto out;
++ }
++
++ if ((err = rst_restore_net(ctx)) != 0) {
++ eprintk_ctx("rst_restore_net: %d\n", err);
++ goto out;
++ }
++
++ err = rst_sockets(ctx);
++ if (err) {
++ eprintk_ctx("rst_sockets: %d\n", err);
++ goto out;
++ }
++ err = rst_sysv_ipc(ctx);
++ if (err) {
++ eprintk_ctx("rst_sysv_ipc: %d\n", err);
++ goto out;
++ }
++#ifdef CONFIG_BEANCOUNTERS
++ bc = get_exec_ub();
++ set_one_ubparm_to_max(bc->ub_parms, UB_KMEMSIZE);
++ set_one_ubparm_to_max(bc->ub_parms, UB_NUMPROC);
++ set_one_ubparm_to_max(bc->ub_parms, UB_NUMFILE);
++ set_one_ubparm_to_max(bc->ub_parms, UB_DCACHESIZE);
++#endif
++ }
++
++ do {
++ if (current->user->uid != ti->cpt_user) {
++ struct user_struct *u;
++
++ u = alloc_uid(get_exec_env()->ve_ns->user_ns, ti->cpt_user);
++ if (!u) {
++ eprintk_ctx("alloc_user\n");
++ } else {
++ switch_uid(u);
++ }
++ }
++ } while (0);
++
++ if ((err = rst_mm_complete(ti, ctx)) != 0) {
++ eprintk_ctx("rst_mm: %d\n", err);
++ goto out;
++ }
++
++ if ((err = rst_files_complete(ti, ctx)) != 0) {
++ eprintk_ctx("rst_files: %d\n", err);
++ goto out;
++ }
++
++ if ((err = rst_fs_complete(ti, ctx)) != 0) {
++ eprintk_ctx("rst_fs: %d\n", err);
++ goto out;
++ }
++
++ if ((err = rst_semundo_complete(ti, ctx)) != 0) {
++ eprintk_ctx("rst_semundo: %d\n", err);
++ goto out;
++ }
++
++ if ((err = rst_signal_complete(ti, &exiting, ctx)) != 0) {
++ eprintk_ctx("rst_signal: %d\n", err);
++ goto out;
++ }
++
++ if (ti->cpt_personality != 0)
++ __set_personality(ti->cpt_personality);
++
++#ifdef CONFIG_X86_64
++ /* 32bit app from 32bit OS, won't have PER_LINUX32 set... :/ */
++ if (!ti->cpt_64bit)
++ __set_personality(PER_LINUX32);
++#endif
++
++ current->set_child_tid = NULL;
++ current->clear_child_tid = NULL;
++ current->flags &= ~(PF_FORKNOEXEC|PF_SUPERPRIV);
++ current->flags |= ti->cpt_flags&(PF_FORKNOEXEC|PF_SUPERPRIV);
++ current->exit_code = ti->cpt_exit_code;
++ current->pdeath_signal = ti->cpt_pdeath_signal;
++
++ if (ti->cpt_restart.fn != CPT_RBL_0) {
++ if (ti->cpt_restart.fn == CPT_RBL_NANOSLEEP
++#ifdef CONFIG_COMPAT
++ || ti->cpt_restart.fn == CPT_RBL_COMPAT_NANOSLEEP
++#endif
++ ) {
++ struct restart_block *rb;
++ ktime_t e;
++
++ e.tv64 = 0;
++
++ if (ctx->image_version >= CPT_VERSION_20)
++ e = ktime_add_ns(e, ti->cpt_restart.arg2);
++ else if (ctx->image_version >= CPT_VERSION_9)
++ e = ktime_add_ns(e, ti->cpt_restart.arg0);
++ else
++ e = ktime_add_ns(e, ti->cpt_restart.arg0*TICK_NSEC);
++ if (e.tv64 < 0)
++ e.tv64 = TICK_NSEC;
++ e = ktime_add(e, timespec_to_ktime(ctx->cpt_monotonic_time));
++
++ rb = &task_thread_info(current)->restart_block;
++ if (ti->cpt_restart.fn == CPT_RBL_NANOSLEEP)
++ rb->fn = hrtimer_nanosleep_restart;
++#ifdef CONFIG_COMPAT
++ else
++ rb->fn = compat_nanosleep_restart;
++#endif
++ if (ctx->image_version >= CPT_VERSION_20) {
++ rb->arg0 = ti->cpt_restart.arg0;
++ rb->arg1 = ti->cpt_restart.arg1;
++ rb->arg2 = e.tv64 & 0xFFFFFFFF;
++ rb->arg3 = e.tv64 >> 32;
++ } else if (ctx->image_version >= CPT_VERSION_9) {
++ rb->arg0 = ti->cpt_restart.arg2;
++ rb->arg1 = ti->cpt_restart.arg3;
++ rb->arg2 = e.tv64 & 0xFFFFFFFF;
++ rb->arg3 = e.tv64 >> 32;
++ } else {
++ rb->arg0 = ti->cpt_restart.arg1;
++ rb->arg1 = CLOCK_MONOTONIC;
++ rb->arg2 = e.tv64 & 0xFFFFFFFF;
++ rb->arg3 = e.tv64 >> 32;
++ }
++ } else if (ti->cpt_restart.fn == CPT_RBL_POLL) {
++ struct restart_block *rb;
++ ktime_t e;
++ struct timespec ts;
++ unsigned long timeout_jiffies;
++
++ e.tv64 = 0;
++ e = ktime_add_ns(e, ti->cpt_restart.arg2);
++ e = ktime_sub(e, timespec_to_ktime(ctx->delta_time));
++ ts = ns_to_timespec(ktime_to_ns(e));
++ timeout_jiffies = timespec_to_jiffies(&ts);
++
++ rb = &task_thread_info(current)->restart_block;
++ rb->fn = do_restart_poll;
++ rb->arg0 = ti->cpt_restart.arg0;
++ rb->arg1 = ti->cpt_restart.arg1;
++ rb->arg2 = timeout_jiffies & 0xFFFFFFFF;
++ rb->arg3 = (u64)timeout_jiffies >> 32;
++ } else if (ti->cpt_restart.fn == CPT_RBL_FUTEX_WAIT) {
++ struct restart_block *rb;
++ ktime_t e;
++
++ e.tv64 = 0;
++ e = ktime_add_ns(e, ti->cpt_restart.arg2);
++ e = ktime_add(e, timespec_to_ktime(ctx->cpt_monotonic_time));
++
++ rb = &task_thread_info(current)->restart_block;
++ rb->fn = futex_wait_restart;
++ rb->futex.uaddr = (void *)(unsigned long)ti->cpt_restart.arg0;
++ rb->futex.val = ti->cpt_restart.arg1;
++ rb->futex.time = e.tv64;
++ rb->futex.flags = ti->cpt_restart.arg3;
++ } else
++ eprintk_ctx("unknown restart block\n");
++ }
++
++ if (thread_group_leader(current)) {
++ current->signal->it_real_incr.tv64 = 0;
++ if (ctx->image_version >= CPT_VERSION_9) {
++ current->signal->it_real_incr =
++ ktime_add_ns(current->signal->it_real_incr, ti->cpt_it_real_incr);
++ } else {
++ current->signal->it_real_incr =
++ ktime_add_ns(current->signal->it_real_incr, ti->cpt_it_real_incr*TICK_NSEC);
++ }
++ current->signal->it_prof_incr = ti->cpt_it_prof_incr;
++ current->signal->it_virt_incr = ti->cpt_it_virt_incr;
++ current->signal->it_prof_expires = ti->cpt_it_prof_value;
++ current->signal->it_virt_expires = ti->cpt_it_virt_value;
++ }
++
++ err = rst_clone_children(tobj, ctx);
++ if (err) {
++ eprintk_ctx("rst_clone_children\n");
++ goto out;
++ }
++
++ if (exiting)
++ current->signal->flags |= SIGNAL_GROUP_EXIT;
++
++ if (ti->cpt_pid == 1) {
++ if ((err = rst_process_linkage(ctx)) != 0) {
++ eprintk_ctx("rst_process_linkage: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_do_filejobs(ctx)) != 0) {
++ eprintk_ctx("rst_do_filejobs: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_eventpoll(ctx)) != 0) {
++ eprintk_ctx("rst_eventpoll: %d\n", err);
++ goto out;
++ }
++#ifdef CONFIG_INOTIFY_USER
++ if ((err = rst_inotify(ctx)) != 0) {
++ eprintk_ctx("rst_inotify: %d\n", err);
++ goto out;
++ }
++#endif
++ if ((err = rst_sockets_complete(ctx)) != 0) {
++ eprintk_ctx("rst_sockets_complete: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_stray_files(ctx)) != 0) {
++ eprintk_ctx("rst_stray_files: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_posix_locks(ctx)) != 0) {
++ eprintk_ctx("rst_posix_locks: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_tty_jobcontrol(ctx)) != 0) {
++ eprintk_ctx("rst_tty_jobcontrol: %d\n", err);
++ goto out;
++ }
++ if ((err = rst_restore_fs(ctx)) != 0) {
++ eprintk_ctx("rst_restore_fs: %d\n", err);
++ goto out;
++ }
++ if (virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_RESTORE, ctx) & NOTIFY_FAIL) {
++ err = -ECHRNG;
++ eprintk_ctx("scp_restore failed\n");
++ goto out;
++ }
++ if (ctx->last_vpid)
++ get_exec_env()->ve_ns->pid_ns->last_pid =
++ ctx->last_vpid;
++ }
++
++out:
++ thr_ctx->error = err;
++ complete(&thr_ctx->task_done);
++
++ if (!err && (ti->cpt_state & (EXIT_ZOMBIE|EXIT_DEAD))) {
++ current->flags |= PF_EXIT_RESTART;
++ do_exit(ti->cpt_exit_code);
++ } else {
++ __set_current_state(TASK_UNINTERRUPTIBLE);
++ }
++
++ schedule();
++
++ dprintk_ctx("leaked through %d/%d %p\n", task_pid_nr(current), task_pid_vnr(current), current->mm);
++
++ module_put(THIS_MODULE);
++ complete_and_exit(NULL, 0);
++ return 0;
++}
++
++#if 0
++static void set_task_ubs(struct cpt_task_image *ti, struct cpt_context *ctx)
++{
++ struct task_beancounter *tbc;
++
++ tbc = task_bc(current);
++
++ put_beancounter(tbc->fork_sub);
++ tbc->fork_sub = rst_lookup_ubc(ti->cpt_task_ub, ctx);
++ if (ti->cpt_mm_ub != CPT_NULL) {
++ put_beancounter(tbc->exec_ub);
++ tbc->exec_ub = rst_lookup_ubc(ti->cpt_mm_ub, ctx);
++ }
++}
++#endif
++
++static int create_root_task(cpt_object_t *obj, struct cpt_context *ctx,
++ struct thr_context *thr_ctx)
++{
++ struct task_struct *tsk;
++ int pid;
++
++ thr_ctx->ctx = ctx;
++ thr_ctx->error = 0;
++ init_completion(&thr_ctx->init_complete);
++ init_completion(&thr_ctx->task_done);
++#if 0
++ set_task_ubs(obj->o_image, ctx);
++#endif
++
++ pid = local_kernel_thread(hook, thr_ctx, 0, 0);
++ if (pid < 0)
++ return pid;
++ read_lock(&tasklist_lock);
++ tsk = find_task_by_vpid(pid);
++ if (tsk)
++ get_task_struct(tsk);
++ read_unlock(&tasklist_lock);
++ if (tsk == NULL)
++ return -ESRCH;
++ cpt_obj_setobj(obj, tsk, ctx);
++ thr_ctx->tobj = obj;
++ return 0;
++}
++
++static int rst_basic_init_task(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ struct task_struct *tsk = obj->o_obj;
++ struct cpt_task_image *ti = obj->o_image;
++
++ memcpy(tsk->comm, ti->cpt_comm, sizeof(tsk->comm));
++ rst_mm_basic(obj, ti, ctx);
++ return 0;
++}
++
++static int make_baby(cpt_object_t *cobj,
++ struct cpt_task_image *pi,
++ struct cpt_context *ctx)
++{
++ unsigned long flags;
++ struct cpt_task_image *ci = cobj->o_image;
++ struct thr_context thr_ctx;
++ struct task_struct *tsk;
++ pid_t pid;
++ struct fs_struct *tfs = NULL;
++
++ flags = rst_mm_flag(ci, ctx) | rst_files_flag(ci, ctx)
++ | rst_signal_flag(ci, ctx) | rst_semundo_flag(ci, ctx);
++ if (ci->cpt_rppid != pi->cpt_pid) {
++ flags |= CLONE_THREAD|CLONE_PARENT;
++ if (ci->cpt_signal != pi->cpt_signal ||
++ !(flags&CLONE_SIGHAND) ||
++ (!(flags&CLONE_VM) && pi->cpt_mm != CPT_NULL)) {
++ eprintk_ctx("something is wrong with threads: %d %d %d %Ld %Ld %08lx\n",
++ (int)ci->cpt_pid, (int)ci->cpt_rppid, (int)pi->cpt_pid,
++ (long long)ci->cpt_signal, (long long)pi->cpt_signal, flags
++ );
++ return -EINVAL;
++ }
++ }
++
++ thr_ctx.ctx = ctx;
++ thr_ctx.error = 0;
++ init_completion(&thr_ctx.init_complete);
++ init_completion(&thr_ctx.task_done);
++ thr_ctx.tobj = cobj;
++
++#if 0
++ set_task_ubs(ci, ctx);
++#endif
++
++ if (current->fs == NULL) {
++ tfs = get_exec_env()->ve_ns->pid_ns->child_reaper->fs;
++ if (tfs == NULL)
++ return -EINVAL;
++ atomic_inc(&tfs->count);
++ current->fs = tfs;
++ }
++ pid = local_kernel_thread(hook, &thr_ctx, flags, ci->cpt_pid);
++ if (tfs) {
++ current->fs = NULL;
++ atomic_dec(&tfs->count);
++ }
++ if (pid < 0)
++ return pid;
++
++ read_lock(&tasklist_lock);
++ tsk = find_task_by_vpid(pid);
++ if (tsk)
++ get_task_struct(tsk);
++ read_unlock(&tasklist_lock);
++ if (tsk == NULL)
++ return -ESRCH;
++ cpt_obj_setobj(cobj, tsk, ctx);
++ thr_ctx.tobj = cobj;
++ wait_for_completion(&thr_ctx.init_complete);
++ wait_task_inactive(cobj->o_obj, 0);
++ rst_basic_init_task(cobj, ctx);
++
++ /* clone() increases group_stop_count if it was not zero and
++ * CLONE_THREAD was asked. Undo.
++ */
++ if (current->signal->group_stop_count && (flags & CLONE_THREAD)) {
++ if (tsk->signal != current->signal) BUG();
++ current->signal->group_stop_count--;
++ }
++
++ wake_up_process(tsk);
++ wait_for_completion(&thr_ctx.task_done);
++ wait_task_inactive(tsk, 0);
++
++ return thr_ctx.error;
++}
++
++static int rst_clone_children(cpt_object_t *obj, struct cpt_context *ctx)
++{
++ int err = 0;
++ struct cpt_task_image *ti = obj->o_image;
++ cpt_object_t *cobj;
++
++ for_each_object(cobj, CPT_OBJ_TASK) {
++ struct cpt_task_image *ci = cobj->o_image;
++ if (cobj == obj)
++ continue;
++ if ((ci->cpt_rppid == ti->cpt_pid && ci->cpt_tgid == ci->cpt_pid) ||
++ (ci->cpt_leader == ti->cpt_pid &&
++ ci->cpt_tgid != ci->cpt_pid && ci->cpt_pid != 1)) {
++ err = make_baby(cobj, ti, ctx);
++ if (err) {
++ eprintk_ctx("make_baby: %d\n", err);
++ return err;
++ }
++ }
++ }
++ return 0;
++}
++
++static int read_task_images(struct cpt_context *ctx)
++{
++ int err;
++ loff_t start, end;
++
++ err = rst_get_section(CPT_SECT_TASKS, ctx, &start, &end);
++ if (err)
++ return err;
++
++ while (start < end) {
++ cpt_object_t *obj;
++ struct cpt_task_image *ti = cpt_get_buf(ctx);
++
++ err = rst_get_object(CPT_OBJ_TASK, start, ti, ctx);
++ if (err) {
++ cpt_release_buf(ctx);
++ return err;
++ }
++#if 0
++ if (ti->cpt_pid != 1 && !__is_virtual_pid(ti->cpt_pid)) {
++ eprintk_ctx("BUG: pid %d is not virtual\n", ti->cpt_pid);
++ cpt_release_buf(ctx);
++ return -EINVAL;
++ }
++#endif
++ obj = alloc_cpt_object(GFP_KERNEL, ctx);
++ cpt_obj_setpos(obj, start, ctx);
++ intern_cpt_object(CPT_OBJ_TASK, obj, ctx);
++ obj->o_image = kmalloc(ti->cpt_next, GFP_KERNEL);
++ if (obj->o_image == NULL) {
++ cpt_release_buf(ctx);
++ return -ENOMEM;
++ }
++ memcpy(obj->o_image, ti, sizeof(*ti));
++ err = ctx->pread(obj->o_image + sizeof(*ti),
++ ti->cpt_next - sizeof(*ti), ctx, start + sizeof(*ti));
++ cpt_release_buf(ctx);
++ if (err)
++ return err;
++ start += ti->cpt_next;
++ }
++ return 0;
++}
++
++
++static int vps_rst_restore_tree(struct cpt_context *ctx)
++{
++ int err;
++ cpt_object_t *obj;
++ struct thr_context thr_ctx_root;
++
++ err = read_task_images(ctx);
++ if (err)
++ return err;
++
++ err = rst_undump_ubc(ctx);
++ if (err)
++ return err;
++
++ if (virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_RSTCHECK, ctx) & NOTIFY_FAIL)
++ return -ECHRNG;
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ err = rst_setup_pagein(ctx);
++ if (err)
++ return err;
++#endif
++ for_each_object(obj, CPT_OBJ_TASK) {
++ err = create_root_task(obj, ctx, &thr_ctx_root);
++ if (err)
++ return err;
++
++ wait_for_completion(&thr_ctx_root.init_complete);
++ wait_task_inactive(obj->o_obj, 0);
++ rst_basic_init_task(obj, ctx);
++
++ wake_up_process(obj->o_obj);
++ wait_for_completion(&thr_ctx_root.task_done);
++ wait_task_inactive(obj->o_obj, 0);
++ err = thr_ctx_root.error;
++ if (err)
++ return err;
++ break;
++ }
++
++ return err;
++}
++
++#ifndef CONFIG_IA64
++int rst_read_vdso(struct cpt_context *ctx)
++{
++ int err;
++ loff_t start, end;
++ struct cpt_page_block *pgb;
++
++ ctx->vdso = NULL;
++ err = rst_get_section(CPT_SECT_VSYSCALL, ctx, &start, &end);
++ if (err)
++ return err;
++ if (start == CPT_NULL)
++ return 0;
++ if (end < start + sizeof(*pgb) + PAGE_SIZE)
++ return -EINVAL;
++
++ pgb = cpt_get_buf(ctx);
++ err = rst_get_object(CPT_OBJ_VSYSCALL, start, pgb, ctx);
++ if (err) {
++ goto err_buf;
++ }
++ ctx->vdso = (char*)__get_free_page(GFP_KERNEL);
++ if (ctx->vdso == NULL) {
++ err = -ENOMEM;
++ goto err_buf;
++ }
++ err = ctx->pread(ctx->vdso, PAGE_SIZE, ctx, start + sizeof(*pgb));
++ if (err)
++ goto err_page;
++ if (!memcmp(ctx->vdso, vsyscall_addr, PAGE_SIZE)) {
++ free_page((unsigned long)ctx->vdso);
++ ctx->vdso = NULL;
++ }
++
++ cpt_release_buf(ctx);
++ return 0;
++err_page:
++ free_page((unsigned long)ctx->vdso);
++ ctx->vdso = NULL;
++err_buf:
++ cpt_release_buf(ctx);
++ return err;
++}
++#endif
++
++int vps_rst_undump(struct cpt_context *ctx)
++{
++ int err;
++ unsigned long umask;
++
++ err = rst_open_dumpfile(ctx);
++ if (err)
++ return err;
++
++ if (ctx->tasks64) {
++#if defined(CONFIG_IA64)
++ if (ctx->image_arch != CPT_OS_ARCH_IA64)
++#elif defined(CONFIG_X86_64)
++ if (ctx->image_arch != CPT_OS_ARCH_EMT64)
++#else
++ if (1)
++#endif
++ {
++ eprintk_ctx("Cannot restore 64 bit container on this architecture\n");
++ return -EINVAL;
++ }
++ }
++
++ umask = current->fs->umask;
++ current->fs->umask = 0;
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ err = rst_setup_pagein(ctx);
++#endif
++#ifndef CONFIG_IA64
++ if (err == 0)
++ err = rst_read_vdso(ctx);
++#endif
++ if (err == 0)
++ err = vps_rst_restore_tree(ctx);
++
++ if (err == 0)
++ err = rst_restore_process(ctx);
++
++ if (err)
++ virtinfo_notifier_call(VITYPE_SCP,
++ VIRTINFO_SCP_RSTFAIL, ctx);
++
++ current->fs->umask = umask;
++
++ return err;
++}
++
++static int rst_unlock_ve(struct cpt_context *ctx)
++{
++ struct ve_struct *env;
++
++ env = get_ve_by_id(ctx->ve_id);
++ if (!env)
++ return -ESRCH;
++ down_write(&env->op_sem);
++ env->is_locked = 0;
++ up_write(&env->op_sem);
++ put_ve(env);
++ return 0;
++}
++
++int recalc_sigpending_tsk(struct task_struct *t);
++
++int rst_resume(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++ int err = 0;
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *bc;
++#endif
++
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++
++ fput(file);
++ }
++
++#ifdef CONFIG_BEANCOUNTERS
++ bc = get_beancounter_byuid(ctx->ve_id, 0);
++ BUG_ON(!bc);
++ copy_one_ubparm(ctx->saved_ubc, bc->ub_parms, UB_KMEMSIZE);
++ copy_one_ubparm(ctx->saved_ubc, bc->ub_parms, UB_NUMPROC);
++ copy_one_ubparm(ctx->saved_ubc, bc->ub_parms, UB_NUMFILE);
++ copy_one_ubparm(ctx->saved_ubc, bc->ub_parms, UB_DCACHESIZE);
++ put_beancounter(bc);
++#endif
++
++ rst_resume_network(ctx);
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++ struct cpt_task_image *ti = obj->o_image;
++
++ if (!tsk)
++ continue;
++
++ if (ti->cpt_state == TASK_UNINTERRUPTIBLE) {
++ dprintk_ctx("task %d/%d(%s) is started\n", task_pid_vnr(tsk), tsk->pid, tsk->comm);
++
++ /* Weird... If a signal is sent to stopped task,
++ * nobody makes recalc_sigpending(). We have to do
++ * this by hands after wake_up_process().
++ * if we did this before a signal could arrive before
++ * wake_up_process() and stall.
++ */
++ spin_lock_irq(&tsk->sighand->siglock);
++ if (!signal_pending(tsk))
++ recalc_sigpending_tsk(tsk);
++ spin_unlock_irq(&tsk->sighand->siglock);
++
++ wake_up_process(tsk);
++ } else {
++ if (ti->cpt_state == TASK_STOPPED ||
++ ti->cpt_state == TASK_TRACED) {
++ set_task_state(tsk, ti->cpt_state);
++ }
++ }
++ put_task_struct(tsk);
++ }
++
++ rst_unlock_ve(ctx);
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ rst_complete_pagein(ctx, 0);
++#endif
++
++ rst_finish_ubc(ctx);
++ cpt_object_destroy(ctx);
++
++ return err;
++}
++
++int rst_kill(struct cpt_context *ctx)
++{
++ cpt_object_t *obj;
++ int err = 0;
++
++ for_each_object(obj, CPT_OBJ_FILE) {
++ struct file *file = obj->o_obj;
++
++ fput(file);
++ }
++
++ for_each_object(obj, CPT_OBJ_TASK) {
++ struct task_struct *tsk = obj->o_obj;
++
++ if (tsk == NULL)
++ continue;
++
++ if (tsk->exit_state == 0) {
++ send_sig(SIGKILL, tsk, 1);
++
++ spin_lock_irq(&tsk->sighand->siglock);
++ sigfillset(&tsk->blocked);
++ sigdelsetmask(&tsk->blocked, sigmask(SIGKILL));
++ set_tsk_thread_flag(tsk, TIF_SIGPENDING);
++ clear_tsk_thread_flag(tsk, TIF_FREEZE);
++ if (tsk->flags & PF_FROZEN)
++ tsk->flags &= ~PF_FROZEN;
++ spin_unlock_irq(&tsk->sighand->siglock);
++
++ wake_up_process(tsk);
++ }
++
++ put_task_struct(tsk);
++ }
++
++#ifdef CONFIG_VZ_CHECKPOINT_LAZY
++ rst_complete_pagein(ctx, 1);
++#endif
++
++ rst_finish_ubc(ctx);
++ cpt_object_destroy(ctx);
++
++ return err;
++}
++
++static int rst_utsname(cpt_context_t *ctx)
++{
++ int err;
++ loff_t sec = ctx->sections[CPT_SECT_UTSNAME];
++ loff_t endsec;
++ struct cpt_section_hdr h;
++ struct cpt_object_hdr o;
++ struct ve_struct *ve;
++ struct uts_namespace *ns;
++ int i;
++
++ if (sec == CPT_NULL)
++ return 0;
++
++ err = ctx->pread(&h, sizeof(h), ctx, sec);
++ if (err)
++ return err;
++ if (h.cpt_section != CPT_SECT_UTSNAME || h.cpt_hdrlen < sizeof(h))
++ return -EINVAL;
++
++ ve = get_exec_env();
++ ns = ve->ve_ns->uts_ns;
++
++ i = 0;
++ endsec = sec + h.cpt_next;
++ sec += h.cpt_hdrlen;
++ while (sec < endsec) {
++ int len;
++ char *ptr;
++ err = rst_get_object(CPT_OBJ_NAME, sec, &o, ctx);
++ if (err)
++ return err;
++ len = o.cpt_next - o.cpt_hdrlen;
++ if (len > __NEW_UTS_LEN + 1)
++ return -ENAMETOOLONG;
++ switch (i) {
++ case 0:
++ ptr = ns->name.nodename; break;
++ case 1:
++ ptr = ns->name.domainname; break;
++ default:
++ return -EINVAL;
++ }
++ err = ctx->pread(ptr, len, ctx, sec+o.cpt_hdrlen);
++ if (err)
++ return err;
++ i++;
++ sec += o.cpt_next;
++ }
++
++ return 0;
++}
+diff --git a/kernel/cpu.c b/kernel/cpu.c
+index f17e985..f489d1c 100644
+--- a/kernel/cpu.c
++++ b/kernel/cpu.c
+@@ -176,7 +176,7 @@ static inline void check_for_tasks(int cpu)
+ struct task_struct *p;
+
+ write_lock_irq(&tasklist_lock);
+- for_each_process(p) {
++ for_each_process_all(p) {
+ if (task_cpu(p) == cpu &&
+ (!cputime_eq(p->utime, cputime_zero) ||
+ !cputime_eq(p->stime, cputime_zero)))
+diff --git a/kernel/exit.c b/kernel/exit.c
+index 85a83c8..0760834 100644
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -23,6 +23,9 @@
+ #include <linux/fdtable.h>
+ #include <linux/binfmts.h>
+ #include <linux/nsproxy.h>
++#include <linux/virtinfo.h>
++#include <linux/ve.h>
++#include <linux/fairsched.h>
+ #include <linux/pid_namespace.h>
+ #include <linux/ptrace.h>
+ #include <linux/profile.h>
+@@ -48,12 +51,15 @@
+ #include <linux/task_io_accounting_ops.h>
+ #include <linux/tracehook.h>
+
++#include <bc/misc.h>
++#include <bc/oom_kill.h>
++
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+ #include <asm/pgtable.h>
+ #include <asm/mmu_context.h>
+
+-static void exit_mm(struct task_struct * tsk);
++void exit_mm(struct task_struct * tsk);
+
+ static inline int task_detached(struct task_struct *p)
+ {
+@@ -69,6 +75,9 @@ static void __unhash_process(struct task_struct *p)
+ detach_pid(p, PIDTYPE_SID);
+
+ list_del_rcu(&p->tasks);
++#ifdef CONFIG_VE
++ list_del_rcu(&p->ve_task_info.vetask_list);
++#endif
+ __get_cpu_var(process_counts)--;
+ }
+ list_del_rcu(&p->thread_group);
+@@ -164,6 +173,8 @@ repeat:
+ write_lock_irq(&tasklist_lock);
+ tracehook_finish_release_task(p);
+ __exit_signal(p);
++ nr_zombie--;
++ atomic_inc(&nr_dead);
+
+ /*
+ * If we are the last non-leader member of the thread
+@@ -192,9 +203,12 @@ repeat:
+ if (zap_leader)
+ leader->exit_state = EXIT_DEAD;
+ }
++ put_task_fairsched_node(p);
+
+ write_unlock_irq(&tasklist_lock);
+ release_thread(p);
++ ub_task_uncharge(p);
++ pput_ve(p->ve_task_info.owner_env);
+ call_rcu(&p->rcu, delayed_put_task_struct);
+
+ p = leader;
+@@ -523,6 +537,7 @@ void put_files_struct(struct files_struct *files)
+ free_fdtable(fdt);
+ }
+ }
++EXPORT_SYMBOL_GPL(put_files_struct);
+
+ void reset_files_struct(struct files_struct *files)
+ {
+@@ -666,7 +681,7 @@ assign_new_owner:
+ * Turn us into a lazy TLB process if we
+ * aren't already..
+ */
+-static void exit_mm(struct task_struct * tsk)
++void exit_mm(struct task_struct * tsk)
+ {
+ struct mm_struct *mm = tsk->mm;
+ struct core_state *core_state;
+@@ -674,6 +689,10 @@ static void exit_mm(struct task_struct * tsk)
+ mm_release(tsk, mm);
+ if (!mm)
+ return;
++
++ if (test_tsk_thread_flag(tsk, TIF_MEMDIE))
++ mm->oom_killed = 1;
++
+ /*
+ * Serialize with any possible pending coredump.
+ * We must hold mmap_sem around checking core_state
+@@ -718,6 +737,7 @@ static void exit_mm(struct task_struct * tsk)
+ mm_update_next_owner(mm);
+ mmput(mm);
+ }
++EXPORT_SYMBOL_GPL(exit_mm);
+
+ /*
+ * Return nonzero if @parent's children should reap themselves.
+@@ -845,7 +865,7 @@ static struct task_struct *find_new_reaper(struct task_struct *father)
+ struct task_struct *thread;
+
+ thread = father;
+- while_each_thread(father, thread) {
++ while_each_thread_ve(father, thread) {
+ if (thread->flags & PF_EXITING)
+ continue;
+ if (unlikely(pid_ns->child_reaper == father))
+@@ -942,11 +962,16 @@ static void exit_notify(struct task_struct *tsk, int group_dead)
+ !capable(CAP_KILL))
+ tsk->exit_signal = SIGCHLD;
+
++ if (tsk->exit_signal != -1 && tsk == init_pid_ns.child_reaper)
++ /* We dont want people slaying init. */
++ tsk->exit_signal = SIGCHLD;
++
+ signal = tracehook_notify_death(tsk, &cookie, group_dead);
+ if (signal >= 0)
+ signal = do_notify_parent(tsk, signal);
+
+ tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE;
++ nr_zombie++;
+
+ /* mt-exec, de_thread() is waiting for us */
+ if (thread_group_leader(tsk) &&
+@@ -1006,6 +1031,7 @@ NORET_TYPE void do_exit(long code)
+ panic("Attempted to kill the idle task!");
+
+ tracehook_report_exit(&code);
++ (void)virtinfo_gencall(VIRTINFO_DOEXIT, NULL);
+
+ /*
+ * We're taking recursive faults here in do_exit. Safest is to just
+@@ -1055,12 +1081,14 @@ NORET_TYPE void do_exit(long code)
+ }
+ acct_collect(code, group_dead);
+ #ifdef CONFIG_FUTEX
+- if (unlikely(tsk->robust_list))
+- exit_robust_list(tsk);
++ if (!(tsk->flags & PF_EXIT_RESTART)) {
++ if (unlikely(tsk->robust_list))
++ exit_robust_list(tsk);
+ #ifdef CONFIG_COMPAT
+- if (unlikely(tsk->compat_robust_list))
+- compat_exit_robust_list(tsk);
++ if (unlikely(tsk->compat_robust_list))
++ compat_exit_robust_list(tsk);
+ #endif
++ }
+ #endif
+ if (group_dead)
+ tty_audit_exit();
+@@ -1089,8 +1117,16 @@ NORET_TYPE void do_exit(long code)
+ if (tsk->binfmt)
+ module_put(tsk->binfmt->module);
+
+- proc_exit_connector(tsk);
+- exit_notify(tsk, group_dead);
++ if (!(tsk->flags & PF_EXIT_RESTART)) {
++ proc_exit_connector(tsk);
++ exit_notify(tsk, group_dead);
++ } else {
++ write_lock_irq(&tasklist_lock);
++ tsk->exit_state = EXIT_ZOMBIE;
++ nr_zombie++;
++ write_unlock_irq(&tasklist_lock);
++ exit_task_namespaces(tsk);
++ }
+ #ifdef CONFIG_NUMA
+ mpol_put(tsk->mempolicy);
+ tsk->mempolicy = NULL;
+@@ -1821,6 +1857,7 @@ asmlinkage long sys_wait4(pid_t upid, int __user *stat_addr,
+ asmlinkage_protect(4, ret, upid, stat_addr, options, ru);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(sys_wait4);
+
+ #ifdef __ARCH_WANT_SYS_WAITPID
+
+diff --git a/kernel/fairsched.c b/kernel/fairsched.c
+new file mode 100644
+index 0000000..bfa5c33
+--- /dev/null
++++ b/kernel/fairsched.c
+@@ -0,0 +1,633 @@
++/*
++ * Fair Scheduler
++ *
++ * Copyright (C) 2000-2008 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/fairsched.h>
++#include <linux/err.h>
++#include <linux/module.h>
++
++struct fairsched_node fairsched_init_node = {
++ .id = FAIRSCHED_INIT_NODE_ID,
++ .tg = &init_task_group,
++#ifdef CONFIG_VE
++ .owner_env = get_ve0(),
++#endif
++ .weight = 1,
++};
++
++static DEFINE_MUTEX(fairsched_mutex);
++
++/* list protected with fairsched_mutex */
++static LIST_HEAD(fairsched_node_head);
++static int fairsched_nr_nodes;
++
++void __init fairsched_init_early(void)
++{
++ list_add(&fairsched_init_node.nodelist, &fairsched_node_head);
++ fairsched_nr_nodes++;
++}
++
++#define FSCHWEIGHT_BASE 512000
++
++/******************************************************************************
++ * cfs group shares = FSCHWEIGHT_BASE / fairsched weight
++ *
++ * vzctl cpuunits default 1000
++ * cfs shares default value is 1024 (see init_task_group_load in sched.c)
++ * cpuunits = 1000 --> weight = 500000 / cpuunits = 500 --> shares = 1024
++ * ^--- from vzctl
++ * weight in 1..65535 --> shares in 7..512000
++ * shares should be >1 (see comment in sched_group_set_shares function)
++ *****************************************************************************/
++
++static struct fairsched_node *fairsched_find(unsigned int id)
++{
++ struct fairsched_node *p;
++ list_for_each_entry(p, &fairsched_node_head, nodelist) {
++ if (p->id == id)
++ return p;
++ }
++ return NULL;
++}
++
++/******************************************************************************
++ * System calls
++ *
++ * All do_xxx functions are called under fairsched mutex and after
++ * capability check.
++ *
++ * The binary interfaces follow some other Fair Scheduler implementations
++ * (although some system call arguments are not needed for our implementation).
++ *****************************************************************************/
++
++static int do_fairsched_mknod(unsigned int parent, unsigned int weight,
++ unsigned int newid)
++{
++ struct fairsched_node *node;
++ int retval;
++
++ retval = -EINVAL;
++ if (weight < 1 || weight > FSCHWEIGHT_MAX)
++ goto out;
++ if (newid < 0 || newid > INT_MAX)
++ goto out;
++
++ retval = -EBUSY;
++ if (fairsched_find(newid) != NULL)
++ goto out;
++
++ retval = -ENOMEM;
++ node = kzalloc(sizeof(*node), GFP_KERNEL);
++ if (node == NULL)
++ goto out;
++
++ node->tg = sched_create_group(&init_task_group);
++ if (IS_ERR(node->tg))
++ goto out_free;
++
++ node->id = newid;
++ node->weight = weight;
++ sched_group_set_shares(node->tg, FSCHWEIGHT_BASE / weight);
++#ifdef CONFIG_VE
++ node->owner_env = get_exec_env();
++#endif
++ list_add(&node->nodelist, &fairsched_node_head);
++ fairsched_nr_nodes++;
++
++ retval = newid;
++out:
++ return retval;
++
++out_free:
++ kfree(node);
++ return retval;
++}
++
++asmlinkage int sys_fairsched_mknod(unsigned int parent, unsigned int weight,
++ unsigned int newid)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_mknod(parent, weight, newid);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++EXPORT_SYMBOL(sys_fairsched_mknod);
++
++static int do_fairsched_rmnod(unsigned int id)
++{
++ struct fairsched_node *node;
++ int retval;
++
++ retval = -EINVAL;
++ node = fairsched_find(id);
++ if (node == NULL)
++ goto out;
++ if (node == &fairsched_init_node)
++ goto out;
++
++ retval = -EBUSY;
++ if (node->refcnt)
++ goto out;
++
++ list_del(&node->nodelist);
++ fairsched_nr_nodes--;
++
++ sched_destroy_group(node->tg);
++ kfree(node);
++ retval = 0;
++out:
++ return retval;
++}
++
++asmlinkage int sys_fairsched_rmnod(unsigned int id)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_rmnod(id);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++EXPORT_SYMBOL(sys_fairsched_rmnod);
++
++static int do_fairsched_chwt(unsigned int id, unsigned weight)
++{
++ struct fairsched_node *node;
++
++ if (id == 0)
++ return -EINVAL;
++ if (weight < 1 || weight > FSCHWEIGHT_MAX)
++ return -EINVAL;
++
++ node = fairsched_find(id);
++ if (node == NULL)
++ return -ENOENT;
++
++ node->weight = weight;
++ sched_group_set_shares(node->tg, FSCHWEIGHT_BASE / weight);
++
++ return 0;
++}
++
++asmlinkage int sys_fairsched_chwt(unsigned int id, unsigned weight)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_chwt(id, weight);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++
++static int do_fairsched_vcpus(unsigned int id, unsigned int vcpus)
++{
++ struct fairsched_node *node;
++
++ if (id == 0)
++ return -EINVAL;
++
++ node = fairsched_find(id);
++ if (node == NULL)
++ return -ENOENT;
++
++ return 0;
++}
++
++asmlinkage int sys_fairsched_vcpus(unsigned int id, unsigned int vcpus)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_vcpus(id, vcpus);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++EXPORT_SYMBOL(sys_fairsched_vcpus);
++
++static int do_fairsched_rate(unsigned int id, int op, unsigned rate)
++{
++ struct fairsched_node *node;
++ int retval;
++
++ if (id == 0)
++ return -EINVAL;
++ if (op == FAIRSCHED_SET_RATE && (rate < 1 || rate >= (1UL << 31)))
++ return -EINVAL;
++
++ node = fairsched_find(id);
++ if (node == NULL)
++ return -ENOENT;
++
++ retval = -EINVAL;
++ switch (op) {
++ case FAIRSCHED_SET_RATE:
++ node->rate = rate;
++ node->rate_limited = 1;
++ retval = rate;
++ break;
++ case FAIRSCHED_DROP_RATE:
++ node->rate = 0;
++ node->rate_limited = 0;
++ retval = 0;
++ break;
++ case FAIRSCHED_GET_RATE:
++ if (node->rate_limited)
++ retval = node->rate;
++ else
++ retval = -ENODATA;
++ break;
++ }
++ return retval;
++}
++
++asmlinkage int sys_fairsched_rate(unsigned int id, int op, unsigned rate)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_rate(id, op, rate);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++
++static int do_fairsched_mvpr(pid_t pid, unsigned int nodeid)
++{
++ struct task_struct *p;
++ struct fairsched_node *node;
++ int retval;
++
++ retval = -ENOENT;
++ node = fairsched_find(nodeid);
++ if (node == NULL)
++ goto out;
++
++ write_lock_irq(&tasklist_lock);
++ retval = -ESRCH;
++ p = find_task_by_vpid(pid);
++ if (p == NULL)
++ goto out_unlock;
++
++ get_task_struct(p);
++ put_task_fairsched_node(p);
++ p->fsched_node = node;
++ get_task_fairsched_node(p);
++ write_unlock_irq(&tasklist_lock);
++
++ smp_wmb();
++ sched_move_task(p);
++ put_task_struct(p);
++ return 0;
++
++out_unlock:
++ write_unlock_irq(&tasklist_lock);
++out:
++ return retval;
++}
++
++asmlinkage int sys_fairsched_mvpr(pid_t pid, unsigned int nodeid)
++{
++ int retval;
++
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++
++ mutex_lock(&fairsched_mutex);
++ retval = do_fairsched_mvpr(pid, nodeid);
++ mutex_unlock(&fairsched_mutex);
++
++ return retval;
++}
++EXPORT_SYMBOL(sys_fairsched_mvpr);
++
++#ifdef CONFIG_PROC_FS
++
++/*********************************************************************/
++/*
++ * proc interface
++ */
++/*********************************************************************/
++
++#include <linux/proc_fs.h>
++#include <linux/seq_file.h>
++#include <linux/vmalloc.h>
++
++struct fairsched_node_dump {
++ int id;
++ unsigned weight;
++ unsigned rate;
++ int rate_limited;
++ int nr_pcpu;
++ int nr_tasks, nr_runtasks;
++};
++
++struct fairsched_dump {
++ int len;
++ struct fairsched_node_dump nodes[0];
++};
++
++static struct fairsched_dump *fairsched_do_dump(int compat)
++{
++ int nr_nodes;
++ int len;
++ struct fairsched_dump *dump;
++ struct fairsched_node *node;
++ struct fairsched_node_dump *p;
++
++ mutex_lock(&fairsched_mutex);
++ nr_nodes = (ve_is_super(get_exec_env()) ? fairsched_nr_nodes + 16 : 1);
++ len = sizeof(*dump) + nr_nodes * sizeof(dump->nodes[0]);
++ dump = ub_vmalloc(len);
++ if (dump == NULL)
++ goto out;
++
++ p = dump->nodes;
++ list_for_each_entry_reverse(node, &fairsched_node_head, nodelist) {
++ if ((char *)p - (char *)dump >= len)
++ break;
++ p->nr_tasks = 0;
++ p->nr_runtasks = 0;
++#ifdef CONFIG_VE
++ if (!ve_accessible(node->owner_env, get_exec_env()))
++ continue;
++ p->nr_tasks = atomic_read(&node->owner_env->pcounter);
++ p->nr_runtasks = nr_running_ve(node->owner_env);
++#endif
++ p->id = node->id;
++ p->weight = node->weight;
++ p->rate = node->rate;
++ p->rate_limited = node->rate_limited;
++ p->nr_pcpu = num_online_cpus();
++ p++;
++ }
++ dump->len = p - dump->nodes;
++out:
++ mutex_unlock(&fairsched_mutex);
++ return dump;
++}
++
++#define FAIRSCHED_PROC_HEADLINES 2
++
++#define FAIRSHED_DEBUG " debug"
++
++#ifdef CONFIG_VE
++/*
++ * File format is dictated by compatibility reasons.
++ */
++static int fairsched_seq_show(struct seq_file *m, void *v)
++{
++ struct fairsched_dump *dump;
++ struct fairsched_node_dump *p;
++ unsigned vid, nid, pid, r;
++
++ dump = m->private;
++ p = (struct fairsched_node_dump *)((unsigned long)v & ~3UL);
++ if (p - dump->nodes < FAIRSCHED_PROC_HEADLINES) {
++ if (p == dump->nodes)
++ seq_printf(m, "Version: 2.6 debug\n");
++ else if (p == dump->nodes + 1)
++ seq_printf(m,
++ " veid "
++ " id "
++ " parent "
++ "weight "
++ " rate "
++ "tasks "
++ " run "
++ "cpus"
++ " "
++ "flg "
++ "ready "
++ " start_tag "
++ " value "
++ " delay"
++ "\n");
++ } else {
++ p -= FAIRSCHED_PROC_HEADLINES;
++ vid = nid = pid = 0;
++ r = (unsigned long)v & 3;
++ if (p == dump->nodes) {
++ if (r == 2)
++ nid = p->id;
++ } else {
++ if (!r)
++ nid = p->id;
++ else if (r == 1)
++ vid = pid = p->id;
++ else
++ vid = p->id, nid = 1;
++ }
++ seq_printf(m,
++ "%10u "
++ "%10u %10u %6u %5u %5u %5u %4u"
++ " "
++ " %c%c %5u %20Lu %20Lu %20Lu"
++ "\n",
++ vid,
++ nid,
++ pid,
++ p->weight,
++ p->rate,
++ p->nr_tasks,
++ p->nr_runtasks,
++ p->nr_pcpu,
++ p->rate_limited ? 'L' : '.',
++ '.',
++ p->nr_runtasks,
++ 0ll, 0ll, 0ll);
++ }
++
++ return 0;
++}
++
++static void *fairsched_seq_start(struct seq_file *m, loff_t *pos)
++{
++ struct fairsched_dump *dump;
++ unsigned long l;
++
++ dump = m->private;
++ if (*pos >= dump->len * 3 - 1 + FAIRSCHED_PROC_HEADLINES)
++ return NULL;
++ if (*pos < FAIRSCHED_PROC_HEADLINES)
++ return dump->nodes + *pos;
++ /* guess why... */
++ l = (unsigned long)(dump->nodes +
++ ((unsigned long)*pos + FAIRSCHED_PROC_HEADLINES * 2 + 1) / 3);
++ l |= ((unsigned long)*pos + FAIRSCHED_PROC_HEADLINES * 2 + 1) % 3;
++ return (void *)l;
++}
++static void *fairsched_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ ++*pos;
++ return fairsched_seq_start(m, pos);
++}
++#endif /* CONFIG_VE */
++
++static int fairsched2_seq_show(struct seq_file *m, void *v)
++{
++ struct fairsched_dump *dump;
++ struct fairsched_node_dump *p;
++
++ dump = m->private;
++ p = v;
++ if (p - dump->nodes < FAIRSCHED_PROC_HEADLINES) {
++ if (p == dump->nodes)
++ seq_printf(m, "Version: 2.7" FAIRSHED_DEBUG "\n");
++ else if (p == dump->nodes + 1)
++ seq_printf(m,
++ " id "
++ "weight "
++ " rate "
++ " run "
++ "cpus"
++#ifdef FAIRSHED_DEBUG
++ " "
++ "flg "
++ "ready "
++ " start_tag "
++ " value "
++ " delay"
++#endif
++ "\n");
++ } else {
++ p -= FAIRSCHED_PROC_HEADLINES;
++ seq_printf(m,
++ "%10u %6u %5u %5u %4u"
++#ifdef FAIRSHED_DEBUG
++ " "
++ " %c%c %5u %20Lu %20Lu %20Lu"
++#endif
++ "\n",
++ p->id,
++ p->weight,
++ p->rate,
++ p->nr_runtasks,
++ p->nr_pcpu
++#ifdef FAIRSHED_DEBUG
++ ,
++ p->rate_limited ? 'L' : '.',
++ '.',
++ p->nr_runtasks,
++ 0ll, 0ll, 0ll
++#endif
++ );
++ }
++
++ return 0;
++}
++
++static void *fairsched2_seq_start(struct seq_file *m, loff_t *pos)
++{
++ struct fairsched_dump *dump;
++
++ dump = m->private;
++ if (*pos >= dump->len + FAIRSCHED_PROC_HEADLINES)
++ return NULL;
++ return dump->nodes + *pos;
++}
++static void *fairsched2_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ ++*pos;
++ return fairsched2_seq_start(m, pos);
++}
++static void fairsched2_seq_stop(struct seq_file *m, void *v)
++{
++}
++
++#ifdef CONFIG_VE
++static struct seq_operations fairsched_seq_op = {
++ .start = fairsched_seq_start,
++ .next = fairsched_seq_next,
++ .stop = fairsched2_seq_stop,
++ .show = fairsched_seq_show
++};
++#endif
++static struct seq_operations fairsched2_seq_op = {
++ .start = fairsched2_seq_start,
++ .next = fairsched2_seq_next,
++ .stop = fairsched2_seq_stop,
++ .show = fairsched2_seq_show
++};
++static int fairsched_seq_open(struct inode *inode, struct file *file)
++{
++ int ret;
++ struct seq_file *m;
++ int compat;
++
++#ifdef CONFIG_VE
++ compat = (file->f_dentry->d_name.len == sizeof("fairsched") - 1);
++ ret = seq_open(file, compat ? &fairsched_seq_op : &fairsched2_seq_op);
++#else
++ compat = 0;
++ ret = seq_open(file, &fairsched2_seq_op);
++#endif
++ if (ret)
++ return ret;
++ m = file->private_data;
++ m->private = fairsched_do_dump(compat);
++ if (m->private == NULL) {
++ seq_release(inode, file);
++ ret = -ENOMEM;
++ }
++ return ret;
++}
++static int fairsched_seq_release(struct inode *inode, struct file *file)
++{
++ struct seq_file *m;
++ struct fairsched_dump *dump;
++
++ m = file->private_data;
++ dump = m->private;
++ m->private = NULL;
++ vfree(dump);
++ seq_release(inode, file);
++ return 0;
++}
++static struct file_operations proc_fairsched_operations = {
++ .open = fairsched_seq_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = fairsched_seq_release
++};
++
++void __init fairsched_init_late(void)
++{
++ proc_create("fairsched", S_IRUGO, &glob_proc_root,
++ &proc_fairsched_operations);
++ proc_create("fairsched2", S_IRUGO, &glob_proc_root,
++ &proc_fairsched_operations);
++}
++
++#else
++
++void __init fairsched_init_late(void) { }
++
++#endif /* CONFIG_PROC_FS */
+diff --git a/kernel/fork.c b/kernel/fork.c
+index 7ce2ebe..fefefd3 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -58,6 +58,8 @@
+ #include <linux/tty.h>
+ #include <linux/proc_fs.h>
+ #include <linux/blkdev.h>
++#include <linux/virtinfo.h>
++#include <linux/ve.h>
+
+ #include <asm/pgtable.h>
+ #include <asm/pgalloc.h>
+@@ -66,17 +68,23 @@
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#include <bc/vmpages.h>
++#include <bc/misc.h>
++#include <bc/oom_kill.h>
++
+ /*
+ * Protected counters by write_lock_irq(&tasklist_lock)
+ */
+ unsigned long total_forks; /* Handle normal Linux uptimes. */
+ int nr_threads; /* The idle threads do not count.. */
++EXPORT_SYMBOL_GPL(nr_threads);
+
+ int max_threads; /* tunable limit on nr_threads */
+
+ DEFINE_PER_CPU(unsigned long, process_counts) = 0;
+
+ __cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */
++EXPORT_SYMBOL(tasklist_lock);
+
+ int nr_processes(void)
+ {
+@@ -145,14 +153,20 @@ void __put_task_struct(struct task_struct *tsk)
+ WARN_ON(atomic_read(&tsk->usage));
+ WARN_ON(tsk == current);
+
++ ub_task_put(tsk);
+ security_task_free(tsk);
+ free_uid(tsk->user);
+ put_group_info(tsk->group_info);
+ delayacct_tsk_free(tsk);
+
++#ifdef CONFIG_VE
++ put_ve(VE_TASK_INFO(tsk)->owner_env);
++ atomic_dec(&nr_dead);
++#endif
+ if (!profile_handoff_task(tsk))
+ free_task(tsk);
+ }
++EXPORT_SYMBOL_GPL(__put_task_struct);
+
+ /*
+ * macro override instead of weak attribute alias, to workaround
+@@ -171,7 +185,7 @@ void __init fork_init(unsigned long mempages)
+ /* create a slab on which task_structs can be allocated */
+ task_struct_cachep =
+ kmem_cache_create("task_struct", sizeof(struct task_struct),
+- ARCH_MIN_TASKALIGN, SLAB_PANIC, NULL);
++ ARCH_MIN_TASKALIGN, SLAB_PANIC|SLAB_UBC, NULL);
+ #endif
+
+ /* do the arch specific task caches init */
+@@ -291,6 +305,10 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+ continue;
+ }
+ charge = 0;
++ if (ub_memory_charge(mm, mpnt->vm_end - mpnt->vm_start,
++ mpnt->vm_flags & ~VM_LOCKED,
++ mpnt->vm_file, UB_HARD))
++ goto fail_noch;
+ if (mpnt->vm_flags & VM_ACCOUNT) {
+ unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+ if (security_vm_enough_memory(len))
+@@ -345,7 +363,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+ rb_parent = &tmp->vm_rb;
+
+ mm->map_count++;
+- retval = copy_page_range(mm, oldmm, mpnt);
++ retval = copy_page_range(mm, oldmm, tmp, mpnt);
+
+ if (tmp->vm_ops && tmp->vm_ops->open)
+ tmp->vm_ops->open(tmp);
+@@ -364,6 +382,9 @@ out:
+ fail_nomem_policy:
+ kmem_cache_free(vm_area_cachep, tmp);
+ fail_nomem:
++ ub_memory_uncharge(mm, mpnt->vm_end - mpnt->vm_start,
++ mpnt->vm_flags & ~VM_LOCKED, mpnt->vm_file);
++fail_noch:
+ retval = -ENOMEM;
+ vm_unacct_memory(charge);
+ goto out;
+@@ -412,6 +433,15 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
+ mm->free_area_cache = TASK_UNMAPPED_BASE;
+ mm->cached_hole_size = ~0UL;
+ mm_init_owner(mm, p);
++ /*
++ * This looks ugly, buy when we came from
++ * sys_execve -> mm_alloc -> here
++ * we need to get exec_ub, not task_ub. But when
++ * we're here like this
++ * sys_fork() -> dup_mm -> here
++ * we need task_ub, not the exec one... xemul
++ */
++ set_mm_ub(mm, p);
+
+ if (likely(!mm_alloc_pgd(mm))) {
+ mm->def_flags = 0;
+@@ -419,6 +449,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
+ return mm;
+ }
+
++ put_mm_ub(mm);
+ free_mm(mm);
+ return NULL;
+ }
+@@ -437,6 +468,7 @@ struct mm_struct * mm_alloc(void)
+ }
+ return mm;
+ }
++EXPORT_SYMBOL_GPL(mm_alloc);
+
+ /*
+ * Called when the last reference to the mm
+@@ -449,6 +481,7 @@ void __mmdrop(struct mm_struct *mm)
+ mm_free_pgd(mm);
+ destroy_context(mm);
+ mmu_notifier_mm_destroy(mm);
++ put_mm_ub(mm);
+ free_mm(mm);
+ }
+ EXPORT_SYMBOL_GPL(__mmdrop);
+@@ -470,6 +503,9 @@ void mmput(struct mm_struct *mm)
+ spin_unlock(&mmlist_lock);
+ }
+ put_swap_token(mm);
++ (void) virtinfo_gencall(VIRTINFO_EXITMMAP, mm);
++ if (mm->oom_killed)
++ ub_oom_task_dead(current);
+ mmdrop(mm);
+ }
+ }
+@@ -599,6 +635,7 @@ fail_nocontext:
+ * because it calls destroy_context()
+ */
+ mm_free_pgd(mm);
++ put_mm_ub(mm);
+ free_mm(mm);
+ return NULL;
+ }
+@@ -899,14 +936,20 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+ unsigned long stack_size,
+ int __user *child_tidptr,
+ struct pid *pid,
++ pid_t vpid,
+ int trace)
+ {
+ int retval;
+ struct task_struct *p;
+ int cgroup_callbacks_done = 0;
+
++#ifdef CONFIG_VE
++ if (clone_flags & CLONE_NAMESPACES_MASK)
++ return ERR_PTR(-EINVAL);
++#else
+ if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
+ return ERR_PTR(-EINVAL);
++#endif
+
+ /*
+ * Thread groups must share signals as well, and detached threads
+@@ -934,6 +977,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+
+ rt_mutex_init_task(p);
+
++ if (ub_task_charge(current, p))
++ goto bad_fork_charge;
++
+ #ifdef CONFIG_PROVE_LOCKING
+ DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
+ DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
+@@ -1083,7 +1129,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+
+ if (pid != &init_struct_pid) {
+ retval = -ENOMEM;
+- pid = alloc_pid(task_active_pid_ns(p));
++ pid = alloc_pid(task_active_pid_ns(p), vpid);
+ if (!pid)
+ goto bad_fork_cleanup_io;
+
+@@ -1091,6 +1137,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+ retval = pid_ns_prepare_proc(task_active_pid_ns(p));
+ if (retval < 0)
+ goto bad_fork_free_pid;
++ if (task_active_pid_ns(current)->flags & PID_NS_HIDE_CHILD)
++ task_active_pid_ns(p)->flags |= PID_NS_HIDDEN;
+ }
+ }
+
+@@ -1191,7 +1239,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+ * thread can't slip out of an OOM kill (or normal SIGKILL).
+ */
+ recalc_sigpending();
+- if (signal_pending(current)) {
++ if (signal_pending(current) && !vpid) {
+ spin_unlock(&current->sighand->siglock);
+ write_unlock_irq(&tasklist_lock);
+ retval = -ERESTARTNOINTR;
+@@ -1233,14 +1281,24 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+ attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
+ attach_pid(p, PIDTYPE_SID, task_session(current));
+ list_add_tail_rcu(&p->tasks, &init_task.tasks);
++#ifdef CONFIG_VE
++ list_add_tail_rcu(&p->ve_task_info.vetask_list,
++ &p->ve_task_info.owner_env->vetask_lh);
++#endif
+ __get_cpu_var(process_counts)++;
+ }
+ attach_pid(p, PIDTYPE_PID, pid);
+ nr_threads++;
+ }
++ (void)get_ve(p->ve_task_info.owner_env);
++ pget_ve(p->ve_task_info.owner_env);
+
++#ifdef CONFIG_VE
++ seqcount_init(&p->ve_task_info.wakeup_lock);
++#endif
+ total_forks++;
+ spin_unlock(&current->sighand->siglock);
++ get_task_fairsched_node(p);
+ write_unlock_irq(&tasklist_lock);
+ proc_fork_connector(p);
+ cgroup_post_fork(p);
+@@ -1288,6 +1346,9 @@ bad_fork_cleanup_count:
+ atomic_dec(&p->user->processes);
+ free_uid(p->user);
+ bad_fork_free:
++ ub_task_uncharge(p);
++ ub_task_put(p);
++bad_fork_charge:
+ free_task(p);
+ fork_out:
+ return ERR_PTR(retval);
+@@ -1305,7 +1366,7 @@ struct task_struct * __cpuinit fork_idle(int cpu)
+ struct pt_regs regs;
+
+ task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL,
+- &init_struct_pid, 0);
++ &init_struct_pid, 0, 0);
+ if (!IS_ERR(task))
+ init_idle(task, cpu);
+
+@@ -1318,12 +1379,13 @@ struct task_struct * __cpuinit fork_idle(int cpu)
+ * It copies the process, and if successful kick-starts
+ * it and waits for it to finish using the VM if required.
+ */
+-long do_fork(unsigned long clone_flags,
++long do_fork_pid(unsigned long clone_flags,
+ unsigned long stack_start,
+ struct pt_regs *regs,
+ unsigned long stack_size,
+ int __user *parent_tidptr,
+- int __user *child_tidptr)
++ int __user *child_tidptr,
++ long vpid)
+ {
+ struct task_struct *p;
+ int trace = 0;
+@@ -1346,6 +1408,10 @@ long do_fork(unsigned long clone_flags,
+ }
+ }
+
++ nr = virtinfo_gencall(VIRTINFO_DOFORK, (void *)clone_flags);
++ if (nr)
++ return nr;
++
+ /*
+ * When called from kernel_thread, don't do user tracing stuff.
+ */
+@@ -1353,7 +1419,7 @@ long do_fork(unsigned long clone_flags,
+ trace = tracehook_prepare_clone(clone_flags);
+
+ p = copy_process(clone_flags, stack_start, regs, stack_size,
+- child_tidptr, NULL, trace);
++ child_tidptr, NULL, vpid, trace);
+ /*
+ * Do this prior waking up the new thread - the thread pointer
+ * might get invalid after that point, if the thread exits quickly.
+@@ -1381,6 +1447,8 @@ long do_fork(unsigned long clone_flags,
+ */
+ p->flags &= ~PF_STARTING;
+
++ (void)virtinfo_gencall(VIRTINFO_DOFORKRET, p);
++
+ if (unlikely(clone_flags & CLONE_STOPPED)) {
+ /*
+ * We'll start up with an immediate SIGSTOP.
+@@ -1404,6 +1472,8 @@ long do_fork(unsigned long clone_flags,
+ } else {
+ nr = PTR_ERR(p);
+ }
++
++ (void)virtinfo_gencall(VIRTINFO_DOFORKPOST, (void *)(long)nr);
+ return nr;
+ }
+
+@@ -1419,27 +1489,40 @@ static void sighand_ctor(void *data)
+ init_waitqueue_head(&sighand->signalfd_wqh);
+ }
+
++EXPORT_SYMBOL(do_fork_pid);
++
++long do_fork(unsigned long clone_flags,
++ unsigned long stack_start,
++ struct pt_regs *regs,
++ unsigned long stack_size,
++ int __user *parent_tidptr,
++ int __user *child_tidptr)
++{
++ return do_fork_pid(clone_flags, stack_start, regs, stack_size,
++ parent_tidptr, child_tidptr, 0);
++}
++
+ void __init proc_caches_init(void)
+ {
+ sighand_cachep = kmem_cache_create("sighand_cache",
+ sizeof(struct sighand_struct), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU,
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU|SLAB_UBC,
+ sighand_ctor);
+ signal_cachep = kmem_cache_create("signal_cache",
+ sizeof(struct signal_struct), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+ files_cachep = kmem_cache_create("files_cache",
+ sizeof(struct files_struct), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+ fs_cachep = kmem_cache_create("fs_cache",
+ sizeof(struct fs_struct), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+ vm_area_cachep = kmem_cache_create("vm_area_struct",
+ sizeof(struct vm_area_struct), 0,
+- SLAB_PANIC, NULL);
++ SLAB_PANIC|SLAB_UBC, NULL);
+ mm_cachep = kmem_cache_create("mm_struct",
+ sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+ }
+
+ /*
+@@ -1577,6 +1660,10 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
+ CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
+ CLONE_NEWNET))
+ goto bad_unshare_out;
++#ifdef CONFIG_VE
++ if (unshare_flags & CLONE_NAMESPACES_MASK)
++ goto bad_unshare_out;
++#endif
+
+ /*
+ * CLONE_NEWIPC must also detach from the undolist: after switching
+@@ -1595,9 +1682,11 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
+ goto bad_unshare_cleanup_sigh;
+ if ((err = unshare_fd(unshare_flags, &new_fd)))
+ goto bad_unshare_cleanup_vm;
++#ifndef CONFIG_VE
+ if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy,
+ new_fs)))
+ goto bad_unshare_cleanup_fd;
++#endif
+
+ if (new_fs || new_mm || new_fd || do_sysvsem || new_nsproxy) {
+ if (do_sysvsem) {
+@@ -1641,7 +1730,9 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
+ if (new_nsproxy)
+ put_nsproxy(new_nsproxy);
+
++#ifndef CONFIG_VE
+ bad_unshare_cleanup_fd:
++#endif
+ if (new_fd)
+ put_files_struct(new_fd);
+
+diff --git a/kernel/futex.c b/kernel/futex.c
+index 7d1136e..a02be16 100644
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -1198,8 +1198,6 @@ handle_fault:
+ */
+ #define FLAGS_SHARED 1
+
+-static long futex_wait_restart(struct restart_block *restart);
+-
+ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
+ u32 val, ktime_t *abs_time, u32 bitset)
+ {
+@@ -1365,7 +1363,7 @@ static int futex_wait(u32 __user *uaddr, struct rw_semaphore *fshared,
+ }
+
+
+-static long futex_wait_restart(struct restart_block *restart)
++long futex_wait_restart(struct restart_block *restart)
+ {
+ u32 __user *uaddr = (u32 __user *)restart->futex.uaddr;
+ struct rw_semaphore *fshared = NULL;
+@@ -1378,6 +1376,7 @@ static long futex_wait_restart(struct restart_block *restart)
+ return (long)futex_wait(uaddr, fshared, restart->futex.val, &t,
+ restart->futex.bitset);
+ }
++EXPORT_SYMBOL_GPL(futex_wait_restart);
+
+
+ /*
+diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
+index cdec83e..b1de384 100644
+--- a/kernel/hrtimer.c
++++ b/kernel/hrtimer.c
+@@ -1523,6 +1523,7 @@ out:
+ destroy_hrtimer_on_stack(&t.timer);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(hrtimer_nanosleep_restart);
+
+ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
+ const enum hrtimer_mode mode, const clockid_t clockid)
+diff --git a/kernel/kmod.c b/kernel/kmod.c
+index 2456d1a..7f97ec1 100644
+--- a/kernel/kmod.c
++++ b/kernel/kmod.c
+@@ -78,6 +78,10 @@ int request_module(const char *fmt, ...)
+ #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
+ static int kmod_loop_msg;
+
++ /* Don't allow request_module() inside VE. */
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
++
+ va_start(args, fmt);
+ ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
+ va_end(args);
+@@ -452,6 +456,9 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
+ DECLARE_COMPLETION_ONSTACK(done);
+ int retval = 0;
+
++ if (!ve_is_super(get_exec_env()))
++ return -EPERM;
++
+ helper_lock();
+ if (sub_info->path[0] == '\0')
+ goto out;
+diff --git a/kernel/kprobes.c b/kernel/kprobes.c
+index 75bc2cd..d4839ae 100644
+--- a/kernel/kprobes.c
++++ b/kernel/kprobes.c
+@@ -126,14 +126,14 @@ static int __kprobes check_safety(void)
+ ret = freeze_processes();
+ if (ret == 0) {
+ struct task_struct *p, *q;
+- do_each_thread(p, q) {
++ do_each_thread_all(p, q) {
+ if (p != current && p->state == TASK_RUNNING &&
+ p->pid != 0) {
+ printk("Check failed: %s is running\n",p->comm);
+ ret = -1;
+ goto loop_end;
+ }
+- } while_each_thread(p, q);
++ } while_each_thread_all(p, q);
+ }
+ loop_end:
+ thaw_processes();
+diff --git a/kernel/lockdep.c b/kernel/lockdep.c
+index dbda475..055464e 100644
+--- a/kernel/lockdep.c
++++ b/kernel/lockdep.c
+@@ -3421,7 +3421,7 @@ retry:
+ if (count != 10)
+ printk(" locked it.\n");
+
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ /*
+ * It's not reliable to print a task's held locks
+ * if it's not sleeping (or if it's not the current
+@@ -3434,7 +3434,7 @@ retry:
+ if (!unlock)
+ if (read_trylock(&tasklist_lock))
+ unlock = 1;
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+
+ printk("\n");
+ printk("=============================================\n\n");
+diff --git a/kernel/module.c b/kernel/module.c
+index 9db1191..c3188b8 100644
+--- a/kernel/module.c
++++ b/kernel/module.c
+@@ -2531,6 +2531,8 @@ unsigned long module_kallsyms_lookup_name(const char *name)
+ static void *m_start(struct seq_file *m, loff_t *pos)
+ {
+ mutex_lock(&module_mutex);
++ if (!ve_is_super(get_exec_env()))
++ return NULL;
+ return seq_list_start(&modules, *pos);
+ }
+
+diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
+index 1d3ef29..ef348c5 100644
+--- a/kernel/nsproxy.c
++++ b/kernel/nsproxy.c
+@@ -26,6 +26,14 @@ static struct kmem_cache *nsproxy_cachep;
+
+ struct nsproxy init_nsproxy = INIT_NSPROXY(init_nsproxy);
+
++void get_task_namespaces(struct task_struct *tsk)
++{
++ struct nsproxy *ns = tsk->nsproxy;
++ if (ns) {
++ get_nsproxy(ns);
++ }
++}
++
+ /*
+ * creates a copy of "orig" with refcount 1.
+ */
+@@ -133,10 +141,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
+ CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
+ return 0;
+
++#ifndef CONFIG_VE
+ if (!capable(CAP_SYS_ADMIN)) {
+ err = -EPERM;
+ goto out;
+ }
++#endif
+
+ /*
+ * CLONE_NEWIPC must detach from the undolist: after switching
+@@ -162,6 +172,7 @@ out:
+ put_nsproxy(old_ns);
+ return err;
+ }
++EXPORT_SYMBOL(copy_namespaces);
+
+ void free_nsproxy(struct nsproxy *ns)
+ {
+@@ -178,6 +189,22 @@ void free_nsproxy(struct nsproxy *ns)
+ put_net(ns->net_ns);
+ kmem_cache_free(nsproxy_cachep, ns);
+ }
++EXPORT_SYMBOL(free_nsproxy);
++
++struct mnt_namespace * get_task_mnt_ns(struct task_struct *tsk)
++{
++ struct mnt_namespace *mnt_ns = NULL;
++
++ task_lock(tsk);
++ if (tsk->nsproxy)
++ mnt_ns = tsk->nsproxy->mnt_ns;
++ if (mnt_ns)
++ get_mnt_ns(mnt_ns);
++ task_unlock(tsk);
++
++ return mnt_ns;
++}
++EXPORT_SYMBOL(get_task_mnt_ns);
+
+ /*
+ * Called from unshare. Unshare all the namespaces part of nsproxy.
+diff --git a/kernel/pid.c b/kernel/pid.c
+index 064e76a..6051d2f 100644
+--- a/kernel/pid.c
++++ b/kernel/pid.c
+@@ -33,6 +33,7 @@
+ #include <linux/rculist.h>
+ #include <linux/bootmem.h>
+ #include <linux/hash.h>
++#include <bc/kmem.h>
+ #include <linux/pid_namespace.h>
+ #include <linux/init_task.h>
+ #include <linux/syscalls.h>
+@@ -110,7 +111,7 @@ EXPORT_SYMBOL(is_container_init);
+ * For now it is easier to be safe than to prove it can't happen.
+ */
+
+-static __cacheline_aligned_in_smp DEFINE_SPINLOCK(pidmap_lock);
++__cacheline_aligned_in_smp DEFINE_SPINLOCK(pidmap_lock);
+
+ static void free_pidmap(struct upid *upid)
+ {
+@@ -121,8 +122,9 @@ static void free_pidmap(struct upid *upid)
+ clear_bit(offset, map->page);
+ atomic_inc(&map->nr_free);
+ }
++EXPORT_SYMBOL_GPL(free_pidmap);
+
+-static int alloc_pidmap(struct pid_namespace *pid_ns)
++int alloc_pidmap(struct pid_namespace *pid_ns)
+ {
+ int i, offset, max_scan, pid, last = pid_ns->last_pid;
+ struct pidmap *map;
+@@ -182,6 +184,36 @@ static int alloc_pidmap(struct pid_namespace *pid_ns)
+ return -1;
+ }
+
++int set_pidmap(struct pid_namespace *pid_ns, pid_t pid)
++{
++ int offset;
++ struct pidmap *map;
++
++ offset = pid & BITS_PER_PAGE_MASK;
++ map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
++ if (unlikely(!map->page)) {
++ void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
++ /*
++ * Free the page if someone raced with us
++ * installing it:
++ */
++ spin_lock_irq(&pidmap_lock);
++ if (map->page)
++ kfree(page);
++ else
++ map->page = page;
++ spin_unlock_irq(&pidmap_lock);
++ if (unlikely(!map->page))
++ return -ENOMEM;
++ }
++
++ if (test_and_set_bit(offset, map->page))
++ return -EBUSY;
++
++ atomic_dec(&map->nr_free);
++ return pid;
++}
++
+ int next_pidmap(struct pid_namespace *pid_ns, int last)
+ {
+ int offset;
+@@ -227,25 +259,33 @@ void free_pid(struct pid *pid)
+ /* We can be called with write_lock_irq(&tasklist_lock) held */
+ int i;
+ unsigned long flags;
++ struct upid *upid;
+
+ spin_lock_irqsave(&pidmap_lock, flags);
+- for (i = 0; i <= pid->level; i++)
+- hlist_del_rcu(&pid->numbers[i].pid_chain);
+- spin_unlock_irqrestore(&pidmap_lock, flags);
++ for (i = 0; i <= pid->level; i++) {
++ upid = &pid->numbers[i];
++ if (!hlist_unhashed(&upid->pid_chain))
++ hlist_del_rcu(&upid->pid_chain);
++ }
++ spin_unlock(&pidmap_lock);
++ ub_kmemsize_uncharge(pid->ub, pid->numbers[pid->level].ns->pid_cachep->objuse);
++ local_irq_restore(flags);
+
+ for (i = 0; i <= pid->level; i++)
+ free_pidmap(pid->numbers + i);
+-
++ put_beancounter(pid->ub);
+ call_rcu(&pid->rcu, delayed_put_pid);
+ }
++EXPORT_SYMBOL_GPL(free_pid);
+
+-struct pid *alloc_pid(struct pid_namespace *ns)
++struct pid *alloc_pid(struct pid_namespace *ns, pid_t vpid)
+ {
+ struct pid *pid;
+ enum pid_type type;
+ int i, nr;
+ struct pid_namespace *tmp;
+ struct upid *upid;
++ struct user_beancounter *ub;
+
+ pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
+ if (!pid)
+@@ -253,7 +293,10 @@ struct pid *alloc_pid(struct pid_namespace *ns)
+
+ tmp = ns;
+ for (i = ns->level; i >= 0; i--) {
+- nr = alloc_pidmap(tmp);
++ if (vpid != 0 && i == ns->level)
++ nr = set_pidmap(tmp, vpid);
++ else
++ nr = alloc_pidmap(tmp);
+ if (nr < 0)
+ goto out_free;
+
+@@ -268,17 +311,32 @@ struct pid *alloc_pid(struct pid_namespace *ns)
+ for (type = 0; type < PIDTYPE_MAX; ++type)
+ INIT_HLIST_HEAD(&pid->tasks[type]);
+
++#ifdef CONFIG_BEANCOUNTERS
++ ub = get_exec_ub();
++ local_irq_disable();
++ if (ub_kmemsize_charge(ub, ns->pid_cachep->objuse, UB_HARD))
++ goto out_enable;
++ pid->ub = get_beancounter(ub);
++ spin_lock(&pidmap_lock);
++#else
+ spin_lock_irq(&pidmap_lock);
++#endif
+ for (i = ns->level; i >= 0; i--) {
+ upid = &pid->numbers[i];
+ hlist_add_head_rcu(&upid->pid_chain,
+ &pid_hash[pid_hashfn(upid->nr, upid->ns)]);
++ if (upid->ns->flags & PID_NS_HIDDEN)
++ while (i--)
++ INIT_HLIST_NODE(&pid->numbers[i].pid_chain);
+ }
+ spin_unlock_irq(&pidmap_lock);
+
+ out:
+ return pid;
+
++out_enable:
++ local_irq_enable();
++ put_pid_ns(ns);
+ out_free:
+ while (++i <= ns->level)
+ free_pidmap(pid->numbers + i);
+@@ -287,6 +345,7 @@ out_free:
+ pid = NULL;
+ goto out;
+ }
++EXPORT_SYMBOL_GPL(alloc_pid);
+
+ struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
+ {
+@@ -309,6 +368,45 @@ struct pid *find_vpid(int nr)
+ }
+ EXPORT_SYMBOL_GPL(find_vpid);
+
++void reattach_pid(struct task_struct *tsk, enum pid_type type,
++ struct pid *pid)
++{
++ int i;
++ struct pid *old_pid;
++ struct pid_link *link;
++ struct upid *upid;
++
++ link = &tsk->pids[type];
++ old_pid = link->pid;
++
++ hlist_del_rcu(&link->node);
++ link->pid = pid;
++ hlist_add_head_rcu(&link->node, &pid->tasks[type]);
++
++ if (type != PIDTYPE_PID) {
++ for (i = PIDTYPE_MAX; --i >= 0; )
++ if (!hlist_empty(&old_pid->tasks[i]))
++ return;
++
++ for (i = 0; i < pid->level; i++)
++ hlist_del_rcu(&old_pid->numbers[i].pid_chain);
++ } else {
++ for (i = PIDTYPE_MAX; --i >= 0; )
++ if (!hlist_empty(&old_pid->tasks[i]))
++ BUG();
++
++ for (i = 0; i < pid->level; i++)
++ hlist_replace_rcu(&old_pid->numbers[i].pid_chain,
++ &pid->numbers[i].pid_chain);
++
++ upid = &pid->numbers[pid->level];
++ hlist_add_head_rcu(&upid->pid_chain,
++ &pid_hash[pid_hashfn(upid->nr, upid->ns)]);
++ }
++
++ call_rcu(&old_pid->rcu, delayed_put_pid);
++}
++
+ /*
+ * attach_pid() must be called with the tasklist_lock write-held.
+ */
+@@ -321,6 +419,7 @@ void attach_pid(struct task_struct *task, enum pid_type type,
+ link->pid = pid;
+ hlist_add_head_rcu(&link->node, &pid->tasks[type]);
+ }
++EXPORT_SYMBOL_GPL(attach_pid);
+
+ static void __change_pid(struct task_struct *task, enum pid_type type,
+ struct pid *new)
+@@ -341,6 +440,7 @@ static void __change_pid(struct task_struct *task, enum pid_type type,
+
+ free_pid(pid);
+ }
++EXPORT_SYMBOL_GPL(detach_pid);
+
+ void detach_pid(struct task_struct *task, enum pid_type type)
+ {
+@@ -431,6 +531,17 @@ struct pid *find_get_pid(pid_t nr)
+ }
+ EXPORT_SYMBOL_GPL(find_get_pid);
+
++pid_t pid_to_vpid(pid_t nr)
++{
++ struct pid *pid;
++
++ pid = find_pid_ns(nr, &init_pid_ns);
++ if (pid)
++ return pid->numbers[pid->level].nr;
++ return -1;
++}
++EXPORT_SYMBOL_GPL(pid_to_vpid);
++
+ pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
+ {
+ struct upid *upid;
+diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
+index fab8ea8..910d183 100644
+--- a/kernel/pid_namespace.c
++++ b/kernel/pid_namespace.c
+@@ -14,6 +14,8 @@
+ #include <linux/err.h>
+ #include <linux/acct.h>
+
++#include <bc/kmem.h>
++
+ #define BITS_PER_PAGE (PAGE_SIZE*8)
+
+ struct pid_cache {
+@@ -148,6 +150,160 @@ void free_pid_ns(struct kref *kref)
+ put_pid_ns(parent);
+ }
+
++/*
++ * this is a dirty ugly hack.
++ */
++
++static int __pid_ns_attach_task(struct pid_namespace *ns,
++ struct task_struct *tsk, pid_t nr)
++{
++ struct pid *pid;
++ enum pid_type type;
++ unsigned long old_size, new_size;
++
++ pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
++ if (!pid)
++ goto out;
++
++ if (nr == 0)
++ nr = alloc_pidmap(ns);
++ else
++ nr = set_pidmap(ns, nr);
++
++ if (nr < 0)
++ goto out_free;
++
++ memcpy(pid, task_pid(tsk),
++ sizeof(struct pid) + (ns->level - 1) * sizeof(struct upid));
++ get_pid_ns(ns);
++ pid->level++;
++ BUG_ON(pid->level != ns->level);
++ pid->numbers[pid->level].nr = nr;
++ pid->numbers[pid->level].ns = ns;
++ atomic_set(&pid->count, 1);
++ for (type = 0; type < PIDTYPE_MAX; ++type)
++ INIT_HLIST_HEAD(&pid->tasks[type]);
++
++ old_size = pid->numbers[pid->level - 1].ns->pid_cachep->objuse;
++ new_size = pid->numbers[pid->level].ns->pid_cachep->objuse;
++ local_irq_disable();
++ /*
++ * Depending on sizeof(struct foo), cache flags (redzoning, etc)
++ * and actual CPU (cacheline_size() jump from 64 to 128 bytes after
++ * CPU detection) new size can very well be smaller than old size.
++ */
++ if (new_size > old_size) {
++ if (ub_kmemsize_charge(pid->ub, new_size - old_size, UB_HARD) < 0)
++ goto out_enable;
++ } else
++ ub_kmemsize_uncharge(pid->ub, old_size - new_size);
++
++ write_lock(&tasklist_lock);
++
++ spin_lock(&pidmap_lock);
++ reattach_pid(tsk, PIDTYPE_SID, pid);
++ set_task_session(tsk, pid_nr(pid));
++ reattach_pid(tsk, PIDTYPE_PGID, pid);
++ tsk->signal->__pgrp = pid_nr(pid);
++ current->signal->tty_old_pgrp = NULL;
++
++ reattach_pid(tsk, PIDTYPE_PID, pid);
++ spin_unlock(&pidmap_lock);
++
++ write_unlock_irq(&tasklist_lock);
++
++ return 0;
++
++out_enable:
++ local_irq_enable();
++ put_pid_ns(ns);
++out_free:
++ kmem_cache_free(ns->pid_cachep, pid);
++out:
++ return -ENOMEM;
++}
++
++int pid_ns_attach_task(struct pid_namespace *ns, struct task_struct *tsk)
++{
++ return __pid_ns_attach_task(ns, tsk, 0);
++}
++EXPORT_SYMBOL_GPL(pid_ns_attach_task);
++
++int pid_ns_attach_init(struct pid_namespace *ns, struct task_struct *tsk)
++{
++ int err;
++
++ err = __pid_ns_attach_task(ns, tsk, 1);
++ if (err < 0)
++ return err;
++
++ ns->child_reaper = tsk;
++ return 0;
++}
++EXPORT_SYMBOL_GPL(pid_ns_attach_init);
++
++#ifdef CONFIG_VE
++static noinline void show_lost_task(struct task_struct *p)
++{
++ char buf[512] = "N/A";
++#ifdef CONFIG_PROC_FS
++ extern char * task_sig(struct task_struct *p, char *buffer);
++
++ task_sig(p, buf);
++#endif
++ printk("Lost task: %d/%s/%p\nSignals:%s\n", p->pid, p->comm, p, buf);
++}
++
++static void zap_ve_processes(struct ve_struct *env)
++{
++ /*
++ * Here the VE changes its state into "not running".
++ * op_sem taken for write is a barrier to all VE manipulations from
++ * ioctl: it waits for operations currently in progress and blocks all
++ * subsequent operations until is_running is set to 0 and op_sem is
++ * released.
++ */
++ down_write(&env->op_sem);
++ env->is_running = 0;
++ up_write(&env->op_sem);
++
++ /* wait for all init childs exit */
++ while (atomic_read(&env->pcounter) > 1) {
++ struct task_struct *g, *p;
++ long delay = 1;
++
++ if (sys_wait4(-1, NULL, __WALL | WNOHANG, NULL) > 0)
++ continue;
++ /* it was ENOCHLD or no more children somehow */
++ if (atomic_read(&env->pcounter) == 1)
++ break;
++
++ /* clear all signals to avoid wakeups */
++ if (signal_pending(current))
++ flush_signals(current);
++ /* we have child without signal sent */
++ __set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(delay);
++ delay = (delay < HZ) ? (delay << 1) : HZ;
++ read_lock(&tasklist_lock);
++ do_each_thread_ve(g, p) {
++ if (p != current) {
++ /*
++ * by that time no processes other then entered
++ * may exist in the VE. if some were missed by
++ * zap_pid_ns_processes() this was a BUG
++ */
++ if (!p->did_ve_enter)
++ show_lost_task(p);
++
++ force_sig_specific(SIGKILL, p);
++ }
++ } while_each_thread_ve(g, p);
++ read_unlock(&tasklist_lock);
++ }
++}
++#endif
++
+ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
+ {
+ int nr;
+@@ -180,6 +336,10 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
+ } while (rc != -ECHILD);
+
+ acct_exit_ns(pid_ns);
++
++#ifdef CONFIG_VE
++ zap_ve_processes(get_exec_env());
++#endif
+ return;
+ }
+
+diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
+index 5131e54..73fbe29 100644
+--- a/kernel/posix-timers.c
++++ b/kernel/posix-timers.c
+@@ -31,6 +31,8 @@
+ * POSIX clocks & timers
+ */
+ #include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/smp_lock.h>
+ #include <linux/interrupt.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+@@ -46,6 +48,9 @@
+ #include <linux/wait.h>
+ #include <linux/workqueue.h>
+ #include <linux/module.h>
++#include <linux/pid_namespace.h>
++
++#include <bc/beancounter.h>
+
+ /*
+ * Management arrays for POSIX timers. Timers are kept in slab memory
+@@ -240,8 +245,8 @@ static __init int init_posix_timers(void)
+ register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
+
+ posix_timers_cache = kmem_cache_create("posix_timers_cache",
+- sizeof (struct k_itimer), 0, SLAB_PANIC,
+- NULL);
++ sizeof (struct k_itimer), 0,
++ SLAB_PANIC|SLAB_UBC, NULL);
+ idr_init(&posix_timers_id);
+ return 0;
+ }
+@@ -298,6 +303,13 @@ void do_schedule_next_timer(struct siginfo *info)
+
+ int posix_timer_event(struct k_itimer *timr, int si_private)
+ {
++ int ret;
++ struct ve_struct *ve;
++ struct user_beancounter *ub;
++
++ ve = set_exec_env(timr->it_process->ve_task_info.owner_env);
++ ub = set_exec_ub(timr->it_process->task_bc.task_ub);
++
+ /*
+ * FIXME: if ->sigq is queued we can race with
+ * dequeue_signal()->do_schedule_next_timer().
+@@ -318,10 +330,10 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
+
+ if (timr->it_sigev_notify & SIGEV_THREAD_ID) {
+ struct task_struct *leader;
+- int ret = send_sigqueue(timr->sigq, timr->it_process, 0);
++ ret = send_sigqueue(timr->sigq, timr->it_process, 0);
+
+ if (likely(ret >= 0))
+- return ret;
++ goto out;
+
+ timr->it_sigev_notify = SIGEV_SIGNAL;
+ leader = timr->it_process->group_leader;
+@@ -329,7 +341,11 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
+ timr->it_process = leader;
+ }
+
+- return send_sigqueue(timr->sigq, timr->it_process, 1);
++ ret = send_sigqueue(timr->sigq, timr->it_process, 1);
++out:
++ (void)set_exec_ub(ub);
++ (void)set_exec_env(ve);
++ return ret;
+ }
+ EXPORT_SYMBOL_GPL(posix_timer_event);
+
+diff --git a/kernel/power/process.c b/kernel/power/process.c
+index 278946a..e60acd5 100644
+--- a/kernel/power/process.c
++++ b/kernel/power/process.c
+@@ -14,6 +14,8 @@
+ #include <linux/syscalls.h>
+ #include <linux/freezer.h>
+
++static atomic_t global_suspend = ATOMIC_INIT(0);
++
+ /*
+ * Timeout for stopping processes
+ */
+@@ -23,7 +25,9 @@ static inline int freezeable(struct task_struct * p)
+ {
+ if ((p == current) ||
+ (p->flags & PF_NOFREEZE) ||
+- (p->exit_state != 0))
++ (p->exit_state != 0) ||
++ (p->state == TASK_STOPPED) ||
++ (p->state == TASK_TRACED))
+ return 0;
+ return 1;
+ }
+@@ -47,6 +51,28 @@ void refrigerator(void)
+ processes around? */
+ long save;
+
++#if defined(CONFIG_VZ_CHECKPOINT) || defined(CONFIG_VZ_CHECKPOINT_MODULE)
++ save = current->state;
++ current->state = TASK_UNINTERRUPTIBLE;
++
++ spin_lock_irq(&current->sighand->siglock);
++ if (test_and_clear_thread_flag(TIF_FREEZE)) {
++ recalc_sigpending(); /* We sent fake signal, clean it up */
++ if (atomic_read(&global_suspend) ||
++ atomic_read(&get_exec_env()->suspend))
++ current->flags |= PF_FROZEN;
++ else
++ current->state = save;
++ } else {
++ /* Freeze request could be canceled before we entered
++ * refrigerator(). In this case we do nothing. */
++ current->state = save;
++ }
++ spin_unlock_irq(&current->sighand->siglock);
++
++ while (current->flags & PF_FROZEN)
++ schedule();
++#else
+ task_lock(current);
+ if (freezing(current)) {
+ frozen_process();
+@@ -68,6 +94,7 @@ void refrigerator(void)
+ break;
+ schedule();
+ }
++#endif
+ pr_debug("%s left refrigerator\n", current->comm);
+ __set_current_state(save);
+ }
+@@ -158,7 +185,7 @@ static int try_to_freeze_tasks(bool sig_only)
+ do {
+ todo = 0;
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ if (frozen(p) || !freezeable(p))
+ continue;
+
+@@ -174,7 +201,7 @@ static int try_to_freeze_tasks(bool sig_only)
+ if (!task_is_stopped_or_traced(p) &&
+ !freezer_should_skip(p))
+ todo++;
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ read_unlock(&tasklist_lock);
+ yield(); /* Yield is okay here */
+ if (time_after(jiffies, end_time))
+@@ -198,13 +225,13 @@ static int try_to_freeze_tasks(bool sig_only)
+ elapsed_csecs / 100, elapsed_csecs % 100, todo);
+ show_state();
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ task_lock(p);
+ if (freezing(p) && !freezer_should_skip(p))
+ printk(KERN_ERR " %s\n", p->comm);
+ cancel_freezing(p);
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ read_unlock(&tasklist_lock);
+ } else {
+ printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
+@@ -221,6 +248,7 @@ int freeze_processes(void)
+ {
+ int error;
+
++ atomic_inc(&global_suspend);
+ printk("Freezing user space processes ... ");
+ error = try_to_freeze_tasks(true);
+ if (error)
+@@ -235,6 +263,7 @@ int freeze_processes(void)
+ Exit:
+ BUG_ON(in_atomic());
+ printk("\n");
++ atomic_dec(&global_suspend);
+ return error;
+ }
+
+@@ -243,15 +272,17 @@ static void thaw_tasks(bool nosig_only)
+ struct task_struct *g, *p;
+
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ if (!freezeable(p))
+ continue;
+
+ if (nosig_only && should_send_signal(p))
+ continue;
+
+- thaw_process(p);
+- } while_each_thread(g, p);
++ if (!thaw_process(p))
++ printk(KERN_WARNING " Strange, %s not stopped\n",
++ p->comm );
++ } while_each_thread_all(g, p);
+ read_unlock(&tasklist_lock);
+ }
+
+diff --git a/kernel/printk.c b/kernel/printk.c
+index b51b156..d95b686 100644
+--- a/kernel/printk.c
++++ b/kernel/printk.c
+@@ -31,7 +31,9 @@
+ #include <linux/smp.h>
+ #include <linux/security.h>
+ #include <linux/bootmem.h>
++#include <linux/vzratelimit.h>
+ #include <linux/syscalls.h>
++#include <linux/veprintk.h>
+
+ #include <asm/uaccess.h>
+
+@@ -92,7 +94,7 @@ static int console_locked, console_suspended;
+ * It is also used in interesting ways to provide interlocking in
+ * release_console_sem().
+ */
+-static DEFINE_SPINLOCK(logbuf_lock);
++DEFINE_SPINLOCK(logbuf_lock);
+
+ #define LOG_BUF_MASK (log_buf_len-1)
+ #define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
+@@ -128,6 +130,7 @@ EXPORT_SYMBOL(console_set_on_cmdline);
+
+ /* Flag: console code may call schedule() */
+ static int console_may_schedule;
++int console_silence_loglevel;
+
+ #ifdef CONFIG_PRINTK
+
+@@ -136,6 +139,19 @@ static char *log_buf = __log_buf;
+ static int log_buf_len = __LOG_BUF_LEN;
+ static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+
++static int __init setup_console_silencelevel(char *str)
++{
++ int level;
++
++ if (get_option(&str, &level) != 1)
++ return 0;
++
++ console_silence_loglevel = level;
++ return 1;
++}
++
++__setup("silencelevel=", setup_console_silencelevel);
++
+ static int __init log_buf_len_setup(char *str)
+ {
+ unsigned size = memparse(str, &str);
+@@ -293,6 +309,9 @@ int do_syslog(int type, char __user *buf, int len)
+ char c;
+ int error = 0;
+
++ if (!ve_is_super(get_exec_env()) && (type == 6 || type == 7))
++ goto out;
++
+ error = security_syslog(type);
+ if (error)
+ return error;
+@@ -313,15 +332,15 @@ int do_syslog(int type, char __user *buf, int len)
+ error = -EFAULT;
+ goto out;
+ }
+- error = wait_event_interruptible(log_wait,
+- (log_start - log_end));
++ error = wait_event_interruptible(ve_log_wait,
++ (ve_log_start - ve_log_end));
+ if (error)
+ goto out;
+ i = 0;
+ spin_lock_irq(&logbuf_lock);
+- while (!error && (log_start != log_end) && i < len) {
+- c = LOG_BUF(log_start);
+- log_start++;
++ while (!error && (ve_log_start != ve_log_end) && i < len) {
++ c = VE_LOG_BUF(ve_log_start);
++ ve_log_start++;
+ spin_unlock_irq(&logbuf_lock);
+ error = __put_user(c,buf);
+ buf++;
+@@ -347,15 +366,17 @@ int do_syslog(int type, char __user *buf, int len)
+ error = -EFAULT;
+ goto out;
+ }
++ if (ve_log_buf == NULL)
++ goto out;
+ count = len;
+- if (count > log_buf_len)
+- count = log_buf_len;
++ if (count > ve_log_buf_len)
++ count = ve_log_buf_len;
+ spin_lock_irq(&logbuf_lock);
+- if (count > logged_chars)
+- count = logged_chars;
++ if (count > ve_logged_chars)
++ count = ve_logged_chars;
+ if (do_clear)
+- logged_chars = 0;
+- limit = log_end;
++ ve_logged_chars = 0;
++ limit = ve_log_end;
+ /*
+ * __put_user() could sleep, and while we sleep
+ * printk() could overwrite the messages
+@@ -364,9 +385,9 @@ int do_syslog(int type, char __user *buf, int len)
+ */
+ for (i = 0; i < count && !error; i++) {
+ j = limit-1-i;
+- if (j + log_buf_len < log_end)
++ if (j + ve_log_buf_len < ve_log_end)
+ break;
+- c = LOG_BUF(j);
++ c = VE_LOG_BUF(j);
+ spin_unlock_irq(&logbuf_lock);
+ error = __put_user(c,&buf[count-1-i]);
+ cond_resched();
+@@ -390,7 +411,7 @@ int do_syslog(int type, char __user *buf, int len)
+ }
+ break;
+ case 5: /* Clear ring buffer */
+- logged_chars = 0;
++ ve_logged_chars = 0;
+ break;
+ case 6: /* Disable logging to console */
+ console_loglevel = minimum_console_loglevel;
+@@ -402,16 +423,19 @@ int do_syslog(int type, char __user *buf, int len)
+ error = -EINVAL;
+ if (len < 1 || len > 8)
+ goto out;
++ error = 0;
++ /* VE has no console, so return success */
++ if (!ve_is_super(get_exec_env()))
++ goto out;
+ if (len < minimum_console_loglevel)
+ len = minimum_console_loglevel;
+ console_loglevel = len;
+- error = 0;
+ break;
+ case 9: /* Number of chars in the log buffer */
+- error = log_end - log_start;
++ error = ve_log_end - ve_log_start;
+ break;
+ case 10: /* Size of the log buffer */
+- error = log_buf_len;
++ error = ve_log_buf_len;
+ break;
+ default:
+ error = -EINVAL;
+@@ -522,14 +546,14 @@ static void call_console_drivers(unsigned start, unsigned end)
+
+ static void emit_log_char(char c)
+ {
+- LOG_BUF(log_end) = c;
+- log_end++;
+- if (log_end - log_start > log_buf_len)
+- log_start = log_end - log_buf_len;
+- if (log_end - con_start > log_buf_len)
+- con_start = log_end - log_buf_len;
+- if (logged_chars < log_buf_len)
+- logged_chars++;
++ VE_LOG_BUF(ve_log_end) = c;
++ ve_log_end++;
++ if (ve_log_end - ve_log_start > ve_log_buf_len)
++ ve_log_start = ve_log_end - ve_log_buf_len;
++ if (ve_is_super(get_exec_env()) && ve_log_end - con_start > ve_log_buf_len)
++ con_start = ve_log_end - ve_log_buf_len;
++ if (ve_logged_chars < ve_log_buf_len)
++ ve_logged_chars++;
+ }
+
+ /*
+@@ -595,6 +619,30 @@ static int have_callable_console(void)
+ * printf(3)
+ */
+
++static inline int ve_log_init(void)
++{
++#ifdef CONFIG_VE
++ if (ve_log_buf != NULL)
++ return 0;
++
++ if (ve_is_super(get_exec_env())) {
++ ve0._log_wait = &log_wait;
++ ve0._log_start = &log_start;
++ ve0._log_end = &log_end;
++ ve0._logged_chars = &logged_chars;
++ ve0.log_buf = log_buf;
++ return 0;
++ }
++
++ ve_log_buf = kmalloc(ve_log_buf_len, GFP_ATOMIC);
++ if (!ve_log_buf)
++ return -ENOMEM;
++
++ memset(ve_log_buf, 0, ve_log_buf_len);
++#endif
++ return 0;
++}
++
+ asmlinkage int printk(const char *fmt, ...)
+ {
+ va_list args;
+@@ -662,13 +710,14 @@ static int recursion_bug;
+ static int new_text_line = 1;
+ static char printk_buf[1024];
+
+-asmlinkage int vprintk(const char *fmt, va_list args)
++asmlinkage int __vprintk(const char *fmt, va_list args)
+ {
+ int printed_len = 0;
+ int current_log_level = default_message_loglevel;
+ unsigned long flags;
+ int this_cpu;
+ char *p;
++ int err, need_wake;
+
+ boot_delay_msec();
+
+@@ -699,6 +748,12 @@ asmlinkage int vprintk(const char *fmt, va_list args)
+ spin_lock(&logbuf_lock);
+ printk_cpu = this_cpu;
+
++ err = ve_log_init();
++ if (err) {
++ spin_unlock_irqrestore(&logbuf_lock, flags);
++ return err;
++ }
++
+ if (recursion_bug) {
+ recursion_bug = 0;
+ strcpy(printk_buf, recursion_bug_msg);
+@@ -767,7 +822,12 @@ asmlinkage int vprintk(const char *fmt, va_list args)
+ * will release 'logbuf_lock' regardless of whether it
+ * actually gets the semaphore or not.
+ */
+- if (acquire_console_semaphore_for_printk(this_cpu))
++ if (!ve_is_super(get_exec_env())) {
++ need_wake = (ve_log_start != ve_log_end);
++ spin_unlock_irqrestore(&logbuf_lock, flags);
++ if (!oops_in_progress && need_wake)
++ wake_up_interruptible(&ve_log_wait);
++ } else if (acquire_console_semaphore_for_printk(this_cpu))
+ release_console_sem();
+
+ lockdep_on();
+@@ -780,6 +840,41 @@ out_restore_irqs:
+ EXPORT_SYMBOL(printk);
+ EXPORT_SYMBOL(vprintk);
+
++asmlinkage int vprintk(const char *fmt, va_list args)
++{
++ int i;
++ struct ve_struct *env;
++
++ env = set_exec_env(get_ve0());
++ i = __vprintk(fmt, args);
++ (void)set_exec_env(env);
++ return i;
++}
++
++asmlinkage int ve_vprintk(int dst, const char *fmt, va_list args)
++{
++ int printed_len;
++
++ printed_len = 0;
++ if (ve_is_super(get_exec_env()) || (dst & VE0_LOG))
++ printed_len = vprintk(fmt, args);
++ if (!ve_is_super(get_exec_env()) && (dst & VE_LOG))
++ printed_len = __vprintk(fmt, args);
++ return printed_len;
++}
++
++asmlinkage int ve_printk(int dst, const char *fmt, ...)
++{
++ va_list args;
++ int printed_len;
++
++ va_start(args, fmt);
++ printed_len = ve_vprintk(dst, fmt, args);
++ va_end(args);
++ return printed_len;
++}
++EXPORT_SYMBOL(ve_printk);
++
+ #else
+
+ asmlinkage long sys_syslog(int type, char __user *buf, int len)
+@@ -1323,6 +1418,36 @@ int printk_ratelimit(void)
+ }
+ EXPORT_SYMBOL(printk_ratelimit);
+
++/*
++ * Rate limiting stuff.
++ */
++int vz_ratelimit(struct vz_rate_info *p)
++{
++ unsigned long cjif, djif;
++ unsigned long flags;
++ static spinlock_t ratelimit_lock = SPIN_LOCK_UNLOCKED;
++ long new_bucket;
++
++ spin_lock_irqsave(&ratelimit_lock, flags);
++ cjif = jiffies;
++ djif = cjif - p->last;
++ if (djif < p->interval) {
++ if (p->bucket >= p->burst) {
++ spin_unlock_irqrestore(&ratelimit_lock, flags);
++ return 0;
++ }
++ p->bucket++;
++ } else {
++ new_bucket = p->bucket - (djif / (unsigned)p->interval);
++ if (new_bucket < 0)
++ new_bucket = 0;
++ p->bucket = new_bucket + 1;
++ }
++ p->last = cjif;
++ spin_unlock_irqrestore(&ratelimit_lock, flags);
++ return 1;
++}
++
+ /**
+ * printk_timed_ratelimit - caller-controlled printk ratelimiting
+ * @caller_jiffies: pointer to caller's state
+diff --git a/kernel/ptrace.c b/kernel/ptrace.c
+index 356699a..05cbe69 100644
+--- a/kernel/ptrace.c
++++ b/kernel/ptrace.c
+@@ -124,6 +124,8 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
+ * or halting the specified task is impossible.
+ */
+ int dumpable = 0;
++ int vps_dumpable = 0;
++
+ /* Don't let security modules deny introspection */
+ if (task == current)
+ return 0;
+@@ -135,11 +137,17 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
+ (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
+ return -EPERM;
+ smp_rmb();
+- if (task->mm)
++ if (task->mm) {
+ dumpable = get_dumpable(task->mm);
++ vps_dumpable = (task->mm->vps_dumpable == 1);
++ }
++
+ if (!dumpable && !capable(CAP_SYS_PTRACE))
+ return -EPERM;
+-
++ if (!vps_dumpable && !ve_is_super(get_exec_env()))
++ return -EPERM;
++ if (!ve_accessible(VE_TASK_INFO(task)->owner_env, get_exec_env()))
++ return -EPERM;
+ return security_ptrace_may_access(task, mode);
+ }
+
+@@ -190,6 +198,8 @@ repeat:
+ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
+ if (retval)
+ goto bad;
++ if (task->mm->vps_dumpable == 2)
++ goto bad;
+
+ /* Go */
+ task->ptrace |= PT_PTRACED;
+@@ -283,6 +293,7 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
+ }
+ return copied;
+ }
++EXPORT_SYMBOL_GPL(access_process_vm);
+
+ static int ptrace_setoptions(struct task_struct *child, long data)
+ {
+diff --git a/kernel/sched.c b/kernel/sched.c
+index ad1962d..6f173a5 100644
+--- a/kernel/sched.c
++++ b/kernel/sched.c
+@@ -71,6 +71,7 @@
+ #include <linux/debugfs.h>
+ #include <linux/ctype.h>
+ #include <linux/ftrace.h>
++#include <linux/fairsched.h>
+
+ #include <asm/tlb.h>
+ #include <asm/irq_regs.h>
+@@ -343,6 +344,8 @@ static inline struct task_group *task_group(struct task_struct *p)
+ #elif defined(CONFIG_CGROUP_SCHED)
+ tg = container_of(task_subsys_state(p, cpu_cgroup_subsys_id),
+ struct task_group, css);
++#elif defined(CONFIG_VZ_FAIRSCHED)
++ tg = p->fsched_node->tg;
+ #else
+ tg = &init_task_group;
+ #endif
+@@ -545,6 +548,9 @@ struct rq {
+ */
+ unsigned long nr_uninterruptible;
+
++ unsigned long nr_sleeping;
++ unsigned long nr_stopped;
++
+ struct task_struct *curr, *idle;
+ unsigned long next_balance;
+ struct mm_struct *prev_mm;
+@@ -618,6 +624,11 @@ static inline int cpu_of(struct rq *rq)
+ #endif
+ }
+
++struct kernel_stat_glob kstat_glob;
++DEFINE_SPINLOCK(kstat_glb_lock);
++EXPORT_SYMBOL(kstat_glob);
++EXPORT_SYMBOL(kstat_glb_lock);
++
+ /*
+ * The domain tree (rq->sd) is protected by RCU's quiescent state transition.
+ * See detach_destroy_domains: synchronize_sched for details.
+@@ -969,6 +980,217 @@ static inline void task_rq_unlock(struct rq *rq, unsigned long *flags)
+ spin_unlock_irqrestore(&rq->lock, *flags);
+ }
+
++#ifdef CONFIG_VE
++static inline void ve_nr_iowait_inc(struct ve_struct *ve, int cpu)
++{
++ VE_CPU_STATS(ve, cpu)->nr_iowait++;
++}
++
++static inline void ve_nr_iowait_dec(struct ve_struct *ve, int cpu)
++{
++ VE_CPU_STATS(ve, cpu)->nr_iowait--;
++}
++
++static inline void ve_nr_unint_inc(struct ve_struct *ve, int cpu)
++{
++ VE_CPU_STATS(ve, cpu)->nr_unint++;
++}
++
++static inline void ve_nr_unint_dec(struct ve_struct *ve, int cpu)
++{
++ VE_CPU_STATS(ve, cpu)->nr_unint--;
++}
++
++#define cycles_after(a, b) ((long long)(b) - (long long)(a) < 0)
++
++cycles_t ve_sched_get_idle_time(struct ve_struct *ve, int cpu)
++{
++ struct ve_cpu_stats *ve_stat;
++ unsigned v;
++ cycles_t strt, ret, cycles;
++
++ ve_stat = VE_CPU_STATS(ve, cpu);
++ do {
++ v = read_seqcount_begin(&ve_stat->stat_lock);
++ ret = ve_stat->idle_time;
++ strt = ve_stat->strt_idle_time;
++ if (strt && nr_uninterruptible_ve(ve) == 0) {
++ cycles = get_cycles();
++ if (cycles_after(cycles, strt))
++ ret += cycles - strt;
++ }
++ } while (read_seqcount_retry(&ve_stat->stat_lock, v));
++ return ret;
++}
++EXPORT_SYMBOL(ve_sched_get_idle_time);
++
++cycles_t ve_sched_get_iowait_time(struct ve_struct *ve, int cpu)
++{
++ struct ve_cpu_stats *ve_stat;
++ unsigned v;
++ cycles_t strt, ret, cycles;
++
++ ve_stat = VE_CPU_STATS(ve, cpu);
++ do {
++ v = read_seqcount_begin(&ve_stat->stat_lock);
++ ret = ve_stat->iowait_time;
++ strt = ve_stat->strt_idle_time;
++ if (strt && nr_iowait_ve(ve) > 0) {
++ cycles = get_cycles();
++ if (cycles_after(cycles, strt))
++ ret += cycles - strt;
++ }
++ } while (read_seqcount_retry(&ve_stat->stat_lock, v));
++ return ret;
++}
++EXPORT_SYMBOL(ve_sched_get_iowait_time);
++
++static void ve_stop_idle(struct ve_struct *ve, unsigned int cpu, cycles_t cycles)
++{
++ struct ve_cpu_stats *ve_stat;
++
++ ve_stat = VE_CPU_STATS(ve, cpu);
++
++ write_seqcount_begin(&ve_stat->stat_lock);
++ if (ve_stat->strt_idle_time) {
++ if (cycles_after(cycles, ve_stat->strt_idle_time)) {
++ if (nr_iowait_ve(ve) == 0)
++ ve_stat->idle_time +=
++ cycles - ve_stat->strt_idle_time;
++ else
++ ve_stat->iowait_time +=
++ cycles - ve_stat->strt_idle_time;
++ }
++ ve_stat->strt_idle_time = 0;
++ }
++ write_seqcount_end(&ve_stat->stat_lock);
++}
++
++static void ve_strt_idle(struct ve_struct *ve, unsigned int cpu, cycles_t cycles)
++{
++ struct ve_cpu_stats *ve_stat;
++
++ ve_stat = VE_CPU_STATS(ve, cpu);
++
++ write_seqcount_begin(&ve_stat->stat_lock);
++ ve_stat->strt_idle_time = cycles;
++ write_seqcount_end(&ve_stat->stat_lock);
++}
++
++static inline void ve_nr_running_inc(struct ve_struct *ve, int cpu, cycles_t cycles)
++{
++ if (++VE_CPU_STATS(ve, cpu)->nr_running == 1)
++ ve_stop_idle(ve, cpu, cycles);
++}
++
++static inline void ve_nr_running_dec(struct ve_struct *ve, int cpu, cycles_t cycles)
++{
++ if (--VE_CPU_STATS(ve, cpu)->nr_running == 0)
++ ve_strt_idle(ve, cpu, cycles);
++}
++
++void ve_sched_attach(struct ve_struct *target_ve)
++{
++ struct task_struct *tsk;
++ unsigned int cpu;
++ cycles_t cycles;
++
++ tsk = current;
++ preempt_disable();
++ cycles = get_cycles();
++ cpu = task_cpu(tsk);
++ ve_nr_running_dec(VE_TASK_INFO(tsk)->owner_env, cpu, cycles);
++ ve_nr_running_inc(target_ve, cpu, cycles);
++ preempt_enable();
++}
++EXPORT_SYMBOL(ve_sched_attach);
++
++static inline void write_wakeup_stamp(struct task_struct *p, cycles_t cyc)
++{
++ struct ve_task_info *ti;
++
++ ti = VE_TASK_INFO(p);
++ write_seqcount_begin(&ti->wakeup_lock);
++ ti->wakeup_stamp = cyc;
++ write_seqcount_end(&ti->wakeup_lock);
++}
++
++static inline void update_sched_lat(struct task_struct *t, cycles_t cycles)
++{
++ int cpu;
++ cycles_t ve_wstamp;
++
++ /* safe due to runqueue lock */
++ cpu = smp_processor_id();
++ ve_wstamp = t->ve_task_info.wakeup_stamp;
++
++ if (ve_wstamp && cycles > ve_wstamp) {
++ KSTAT_LAT_PCPU_ADD(&kstat_glob.sched_lat,
++ cpu, cycles - ve_wstamp);
++ KSTAT_LAT_PCPU_ADD(&t->ve_task_info.exec_env->sched_lat_ve,
++ cpu, cycles - ve_wstamp);
++ }
++}
++
++static inline void update_ve_task_info(struct task_struct *prev, cycles_t cycles)
++{
++#ifdef CONFIG_FAIRSCHED
++ if (prev != this_pcpu()->idle) {
++#else
++ if (prev != this_rq()->idle) {
++#endif
++ VE_CPU_STATS(prev->ve_task_info.owner_env,
++ smp_processor_id())->used_time +=
++ cycles - prev->ve_task_info.sched_time;
++
++ prev->ve_task_info.sched_time = cycles;
++ }
++}
++#else
++static inline void ve_nr_running_inc(struct ve_struct, int cpu, cycles_t cycles)
++{
++}
++
++static inline void ve_nr_running_dec(struct ve_struct, int cpu, cycles_t cycles)
++{
++}
++
++static inline void ve_nr_iowait_inc(struct ve_struct *ve, int cpu)
++{
++}
++
++static inline void ve_nr_iowait_dec(struct ve_struct *ve, int cpu)
++{
++}
++
++static inline void ve_nr_unint_inc(struct ve_struct *ve, int cpu)
++{
++}
++
++static inline void ve_nr_unint_dec(struct ve_struct *ve, int cpu)
++{
++}
++
++static inline void update_ve_task_info(struct task_struct *prev, cycles_t cycles)
++{
++}
++#endif
++
++struct task_nrs_struct {
++ long nr_running;
++ long nr_unint;
++ long nr_stopped;
++ long nr_sleeping;
++ long nr_iowait;
++ long long nr_switches;
++} ____cacheline_aligned_in_smp;
++
++unsigned long nr_zombie = 0; /* protected by tasklist_lock */
++EXPORT_SYMBOL(nr_zombie);
++
++atomic_t nr_dead = ATOMIC_INIT(0);
++EXPORT_SYMBOL(nr_dead);
++
+ /*
+ * this_rq_lock - lock this runqueue and disable interrupts.
+ */
+@@ -1709,11 +1931,21 @@ static int effective_prio(struct task_struct *p)
+ */
+ static void activate_task(struct rq *rq, struct task_struct *p, int wakeup)
+ {
+- if (task_contributes_to_load(p))
++ cycles_t cycles;
++
++#ifdef CONFIG_VE
++ cycles = get_cycles();
++ write_wakeup_stamp(p, cycles);
++ p->ve_task_info.sleep_time += cycles;
++#endif
++ if (task_contributes_to_load(p)) {
+ rq->nr_uninterruptible--;
++ ve_nr_unint_dec(VE_TASK_INFO(p)->owner_env, task_cpu(p));
++ }
+
+ enqueue_task(rq, p, wakeup);
+ inc_nr_running(rq);
++ ve_nr_running_inc(VE_TASK_INFO(p)->owner_env, task_cpu(p), cycles);
+ }
+
+ /*
+@@ -1721,6 +1953,30 @@ static void activate_task(struct rq *rq, struct task_struct *p, int wakeup)
+ */
+ static void deactivate_task(struct rq *rq, struct task_struct *p, int sleep)
+ {
++ cycles_t cycles;
++#ifdef CONFIG_VE
++ unsigned int cpu, pcpu;
++ struct ve_struct *ve;
++
++ cycles = get_cycles();
++ cpu = task_cpu(p);
++ pcpu = smp_processor_id();
++ ve = p->ve_task_info.owner_env;
++
++ p->ve_task_info.sleep_time -= cycles;
++#endif
++ if (p->state == TASK_UNINTERRUPTIBLE) {
++ ve_nr_unint_inc(ve, cpu);
++ }
++ if (p->state == TASK_INTERRUPTIBLE) {
++ rq->nr_sleeping++;
++ }
++ if (p->state == TASK_STOPPED) {
++ rq->nr_stopped++;
++ }
++
++ ve_nr_running_dec(VE_TASK_INFO(p)->owner_env, cpu, cycles);
++
+ if (task_contributes_to_load(p))
+ rq->nr_uninterruptible++;
+
+@@ -1969,6 +2225,7 @@ unsigned long wait_task_inactive(struct task_struct *p, long match_state)
+
+ return ncsw;
+ }
++EXPORT_SYMBOL_GPL(wait_task_inactive);
+
+ /***
+ * kick_process - kick a running thread to enter/exit the kernel
+@@ -2386,6 +2643,10 @@ void sched_fork(struct task_struct *p, int clone_flags)
+ /* Want to start with kernel preemption disabled. */
+ task_thread_info(p)->preempt_count = 1;
+ #endif
++#ifdef CONFIG_VE
++ /* cosmetic: sleep till wakeup below */
++ p->ve_task_info.sleep_time -= get_cycles();
++#endif
+ put_cpu();
+ }
+
+@@ -2416,6 +2677,8 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
+ */
+ p->sched_class->task_new(rq, p);
+ inc_nr_running(rq);
++ ve_nr_running_inc(VE_TASK_INFO(p)->owner_env, task_cpu(p),
++ get_cycles());
+ }
+ trace_mark(kernel_sched_wakeup_new,
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+@@ -2580,6 +2843,7 @@ asmlinkage void schedule_tail(struct task_struct *prev)
+ if (current->set_child_tid)
+ put_user(task_pid_vnr(current), current->set_child_tid);
+ }
++EXPORT_SYMBOL_GPL(schedule_tail);
+
+ /*
+ * context_switch - switch to the new MM and the new
+@@ -2655,6 +2919,7 @@ unsigned long nr_running(void)
+
+ return sum;
+ }
++EXPORT_SYMBOL_GPL(nr_running);
+
+ unsigned long nr_uninterruptible(void)
+ {
+@@ -2672,6 +2937,7 @@ unsigned long nr_uninterruptible(void)
+
+ return sum;
+ }
++EXPORT_SYMBOL_GPL(nr_uninterruptible);
+
+ unsigned long long nr_context_switches(void)
+ {
+@@ -2709,6 +2975,72 @@ unsigned long nr_active(void)
+ return running + uninterruptible;
+ }
+
++unsigned long nr_stopped(void)
++{
++ unsigned long i, sum = 0;
++
++ for_each_online_cpu(i)
++ sum += cpu_rq(i)->nr_stopped;
++ if (unlikely((long)sum < 0))
++ sum = 0;
++ return sum;
++}
++EXPORT_SYMBOL(nr_stopped);
++
++unsigned long nr_sleeping(void)
++{
++ unsigned long i, sum = 0;
++
++ for_each_online_cpu(i)
++ sum += cpu_rq(i)->nr_sleeping;
++ if (unlikely((long)sum < 0))
++ sum = 0;
++ return sum;
++}
++EXPORT_SYMBOL(nr_sleeping);
++
++#ifdef CONFIG_VE
++unsigned long nr_running_ve(struct ve_struct *ve)
++{
++ int i;
++ long sum = 0;
++ cpumask_t ve_cpus;
++
++ ve_cpu_online_map(ve, &ve_cpus);
++ for_each_cpu_mask(i, ve_cpus)
++ sum += VE_CPU_STATS(ve, i)->nr_running;
++ return (unsigned long)(sum < 0 ? 0 : sum);
++}
++EXPORT_SYMBOL(nr_running_ve);
++
++unsigned long nr_uninterruptible_ve(struct ve_struct *ve)
++{
++ int i;
++ long sum = 0;
++ cpumask_t ve_cpus;
++
++ sum = 0;
++ ve_cpu_online_map(ve, &ve_cpus);
++ for_each_cpu_mask(i, ve_cpus)
++ sum += VE_CPU_STATS(ve, i)->nr_unint;
++ return (unsigned long)(sum < 0 ? 0 : sum);
++}
++EXPORT_SYMBOL(nr_uninterruptible_ve);
++
++unsigned long nr_iowait_ve(struct ve_struct *ve)
++{
++ int i;
++ long sum = 0;
++ cpumask_t ve_cpus;
++
++ ve_cpu_online_map(ve, &ve_cpus);
++ for_each_cpu_mask(i, ve_cpus)
++ sum += VE_CPU_STATS(ve, i)->nr_iowait;
++ return (unsigned long)(sum < 0 ? 0 : sum);
++}
++EXPORT_SYMBOL(nr_iowait_ve);
++#endif
++
+ /*
+ * Update rq->cpu_load[] statistics. This function is usually called every
+ * scheduler tick (TICK_NSEC).
+@@ -2739,6 +3071,16 @@ static void update_cpu_load(struct rq *this_rq)
+ }
+ }
+
++#ifdef CONFIG_VE
++#define update_ve_cpu_time(p, time, tick) \
++ do { \
++ VE_CPU_STATS((p)->ve_task_info.owner_env, \
++ task_cpu(p))->time += tick; \
++ } while (0)
++#else
++#define update_ve_cpu_time(p, time, tick) do { } while (0)
++#endif
++
+ #ifdef CONFIG_SMP
+
+ /*
+@@ -2873,8 +3215,15 @@ void sched_exec(void)
+ static void pull_task(struct rq *src_rq, struct task_struct *p,
+ struct rq *this_rq, int this_cpu)
+ {
++ struct ve_struct *ve;
++ cycles_t cycles = get_cycles();
++
++ ve = VE_TASK_INFO(p)->owner_env;
++
+ deactivate_task(src_rq, p, 0);
++ ve_nr_running_dec(ve, task_cpu(p), cycles);
+ set_task_cpu(p, this_cpu);
++ ve_nr_running_inc(ve, task_cpu(p), cycles);
+ activate_task(this_rq, p, 0);
+ /*
+ * Note that idle threads have a prio of MAX_PRIO, for this test
+@@ -4073,10 +4422,13 @@ void account_user_time(struct task_struct *p, cputime_t cputime)
+
+ /* Add user time to cpustat. */
+ tmp = cputime_to_cputime64(cputime);
+- if (TASK_NICE(p) > 0)
++ if (TASK_NICE(p) > 0) {
+ cpustat->nice = cputime64_add(cpustat->nice, tmp);
+- else
++ update_ve_cpu_time(p, nice, tmp);
++ } else {
+ cpustat->user = cputime64_add(cpustat->user, tmp);
++ update_ve_cpu_time(p, user, tmp);
++ }
+ /* Account for user time used */
+ acct_update_integrals(p);
+ }
+@@ -4132,6 +4484,7 @@ void account_system_time(struct task_struct *p, int hardirq_offset,
+
+ /* Add system time to cpustat. */
+ tmp = cputime_to_cputime64(cputime);
++ update_ve_cpu_time(p, system, tmp);
+ if (hardirq_count() - hardirq_offset)
+ cpustat->irq = cputime64_add(cpustat->irq, tmp);
+ else if (softirq_count())
+@@ -4454,12 +4807,30 @@ need_resched_nonpreemptible:
+ next = pick_next_task(rq, prev);
+
+ if (likely(prev != next)) {
++ cycles_t cycles = get_cycles();
++
+ sched_info_switch(prev, next);
+
+ rq->nr_switches++;
+ rq->curr = next;
+ ++*switch_count;
+
++#ifdef CONFIG_VE
++ prev->ve_task_info.sleep_stamp = cycles;
++ if (prev->state == TASK_RUNNING && prev != this_rq()->idle)
++ write_wakeup_stamp(prev, cycles);
++ update_sched_lat(next, cycles);
++
++ /* because next & prev are protected with
++ * runqueue lock we may not worry about
++ * wakeup_stamp and sched_time protection
++ * (same thing in 'else' branch below)
++ */
++ update_ve_task_info(prev, cycles);
++ next->ve_task_info.sched_time = cycles;
++ write_wakeup_stamp(next, 0);
++#endif
++
+ context_switch(rq, prev, next); /* unlocks the rq */
+ /*
+ * the context switch might have flipped the stack from under
+@@ -4467,8 +4838,10 @@ need_resched_nonpreemptible:
+ */
+ cpu = smp_processor_id();
+ rq = cpu_rq(cpu);
+- } else
++ } else {
++ update_ve_task_info(prev, get_cycles());
+ spin_unlock_irq(&rq->lock);
++ }
+
+ if (unlikely(reacquire_kernel_lock(current) < 0))
+ goto need_resched_nonpreemptible;
+@@ -5084,7 +5457,7 @@ recheck:
+ /*
+ * Allow unprivileged RT tasks to decrease priority:
+ */
+- if (user && !capable(CAP_SYS_NICE)) {
++ if (user && !capable(CAP_SYS_ADMIN)) {
+ if (rt_policy(policy)) {
+ unsigned long rlim_rtprio;
+
+@@ -5572,10 +5945,15 @@ EXPORT_SYMBOL(yield);
+ void __sched io_schedule(void)
+ {
+ struct rq *rq = &__raw_get_cpu_var(runqueues);
++#ifdef CONFIG_VE
++ struct ve_struct *ve = current->ve_task_info.owner_env;
++#endif
+
+ delayacct_blkio_start();
+ atomic_inc(&rq->nr_iowait);
++ ve_nr_iowait_inc(ve, task_cpu(current));
+ schedule();
++ ve_nr_iowait_dec(ve, task_cpu(current));
+ atomic_dec(&rq->nr_iowait);
+ delayacct_blkio_end();
+ }
+@@ -5585,10 +5963,15 @@ long __sched io_schedule_timeout(long timeout)
+ {
+ struct rq *rq = &__raw_get_cpu_var(runqueues);
+ long ret;
++#ifdef CONFIG_VE
++ struct ve_struct *ve = current->ve_task_info.owner_env;
++#endif
+
+ delayacct_blkio_start();
+ atomic_inc(&rq->nr_iowait);
++ ve_nr_iowait_inc(ve, task_cpu(current));
+ ret = schedule_timeout(timeout);
++ ve_nr_iowait_dec(ve, task_cpu(current));
+ atomic_dec(&rq->nr_iowait);
+ delayacct_blkio_end();
+ return ret;
+@@ -5709,17 +6092,7 @@ void sched_show_task(struct task_struct *p)
+ state = p->state ? __ffs(p->state) + 1 : 0;
+ printk(KERN_INFO "%-13.13s %c", p->comm,
+ state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?');
+-#if BITS_PER_LONG == 32
+- if (state == TASK_RUNNING)
+- printk(KERN_CONT " running ");
+- else
+- printk(KERN_CONT " %08lx ", thread_saved_pc(p));
+-#else
+- if (state == TASK_RUNNING)
+- printk(KERN_CONT " running task ");
+- else
+- printk(KERN_CONT " %016lx ", thread_saved_pc(p));
+-#endif
++ printk(KERN_CONT " %p ", p);
+ #ifdef CONFIG_DEBUG_STACK_USAGE
+ {
+ unsigned long *n = end_of_stack(p);
+@@ -5740,13 +6113,13 @@ void show_state_filter(unsigned long state_filter)
+
+ #if BITS_PER_LONG == 32
+ printk(KERN_INFO
+- " task PC stack pid father\n");
++ " task taskaddr stack pid father\n");
+ #else
+ printk(KERN_INFO
+- " task PC stack pid father\n");
++ " task taskaddr stack pid father\n");
+ #endif
+ read_lock(&tasklist_lock);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ /*
+ * reset the NMI-timeout, listing all files on a slow
+ * console might take alot of time:
+@@ -5754,7 +6127,7 @@ void show_state_filter(unsigned long state_filter)
+ touch_nmi_watchdog();
+ if (!state_filter || (p->state & state_filter))
+ sched_show_task(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+
+ touch_all_softlockup_watchdogs();
+
+@@ -6118,13 +6491,13 @@ static void migrate_live_tasks(int src_cpu)
+
+ read_lock(&tasklist_lock);
+
+- do_each_thread(t, p) {
++ do_each_thread_all(t, p) {
+ if (p == current)
+ continue;
+
+ if (task_cpu(p) == src_cpu)
+ move_task_off_dead_cpu(src_cpu, p);
+- } while_each_thread(t, p);
++ } while_each_thread_all(t, p);
+
+ read_unlock(&tasklist_lock);
+ }
+@@ -8126,7 +8499,7 @@ void __init sched_init(void)
+ #ifdef CONFIG_FAIR_GROUP_SCHED
+ init_task_group.shares = init_task_group_load;
+ INIT_LIST_HEAD(&rq->leaf_cfs_rq_list);
+-#ifdef CONFIG_CGROUP_SCHED
++#if defined(CONFIG_CGROUP_SCHED) || defined(CONFIG_VZ_FAIRSCHED)
+ /*
+ * How much cpu bandwidth does init_task_group get?
+ *
+@@ -8172,7 +8545,7 @@ void __init sched_init(void)
+ rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
+ #ifdef CONFIG_RT_GROUP_SCHED
+ INIT_LIST_HEAD(&rq->leaf_rt_rq_list);
+-#ifdef CONFIG_CGROUP_SCHED
++#if defined(CONFIG_CGROUP_SCHED) || defined(CONFIG_VZ_FAIRSCHED)
+ init_tg_rt_entry(&init_task_group, &rq->rt, NULL, i, 1, NULL);
+ #elif defined CONFIG_USER_SCHED
+ init_tg_rt_entry(&root_task_group, &rq->rt, NULL, i, 0, NULL);
+@@ -8232,6 +8605,7 @@ void __init sched_init(void)
+ * During early bootup we pretend to be a normal task:
+ */
+ current->sched_class = &fair_sched_class;
++ fairsched_init_early();
+
+ scheduler_running = 1;
+ }
+@@ -8284,7 +8658,7 @@ void normalize_rt_tasks(void)
+ struct rq *rq;
+
+ read_lock_irqsave(&tasklist_lock, flags);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ /*
+ * Only normalize user tasks:
+ */
+@@ -8315,7 +8689,7 @@ void normalize_rt_tasks(void)
+
+ __task_rq_unlock(rq);
+ spin_unlock(&p->pi_lock);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+
+ read_unlock_irqrestore(&tasklist_lock, flags);
+ }
+@@ -8758,7 +9132,7 @@ static unsigned long to_ratio(u64 period, u64 runtime)
+ return div64_u64(runtime << 16, period);
+ }
+
+-#ifdef CONFIG_CGROUP_SCHED
++#if defined(CONFIG_CGROUP_SCHED) || defined(CONFIG_VZ_FAIRSCHED)
+ static int __rt_schedulable(struct task_group *tg, u64 period, u64 runtime)
+ {
+ struct task_group *tgi, *parent = tg->parent;
+@@ -8815,10 +9189,10 @@ static int __rt_schedulable(struct task_group *tg, u64 period, u64 runtime)
+ static inline int tg_has_rt_tasks(struct task_group *tg)
+ {
+ struct task_struct *g, *p;
+- do_each_thread(g, p) {
++ do_each_thread_ve(g, p) {
+ if (rt_task(p) && rt_rq_of_se(&p->rt)->tg == tg)
+ return 1;
+- } while_each_thread(g, p);
++ } while_each_thread_ve(g, p);
+ return 0;
+ }
+
+diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c
+index bbe6b31..5997d36 100644
+--- a/kernel/sched_debug.c
++++ b/kernel/sched_debug.c
+@@ -101,12 +101,12 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu)
+
+ read_lock_irqsave(&tasklist_lock, flags);
+
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ if (!p->se.on_rq || task_cpu(p) != rq_cpu)
+ continue;
+
+ print_task(m, rq, p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+
+ read_unlock_irqrestore(&tasklist_lock, flags);
+ }
+diff --git a/kernel/signal.c b/kernel/signal.c
+index e661b01..c1f2e30 100644
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -32,13 +32,32 @@
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+ #include <asm/siginfo.h>
++#include <bc/misc.h>
+ #include "audit.h" /* audit_signal_info() */
+
+ /*
+ * SLAB caches for signal bits.
+ */
+
+-static struct kmem_cache *sigqueue_cachep;
++struct kmem_cache *sigqueue_cachep;
++EXPORT_SYMBOL(sigqueue_cachep);
++
++static int sig_ve_ignored(int sig, struct siginfo *info, struct task_struct *t)
++{
++ struct ve_struct *ve;
++
++ /* always allow signals from the kernel */
++ if (info == SEND_SIG_FORCED ||
++ (!is_si_special(info) && SI_FROMKERNEL(info)))
++ return 0;
++
++ ve = current->ve_task_info.owner_env;
++ if (ve->ve_ns->pid_ns->child_reaper != t)
++ return 0;
++ if (ve_is_super(get_exec_env()))
++ return 0;
++ return !sig_user_defined(t, sig) || sig_kernel_only(sig);
++}
+
+ static void __user *sig_handler(struct task_struct *t, int sig)
+ {
+@@ -106,7 +125,7 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
+
+ #define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
+
+-static int recalc_sigpending_tsk(struct task_struct *t)
++int recalc_sigpending_tsk(struct task_struct *t)
+ {
+ if (t->signal->group_stop_count > 0 ||
+ PENDING(&t->pending, &t->blocked) ||
+@@ -131,6 +150,7 @@ void recalc_sigpending_and_wake(struct task_struct *t)
+ if (recalc_sigpending_tsk(t))
+ signal_wake_up(t, 0);
+ }
++EXPORT_SYMBOL_GPL(recalc_sigpending_tsk);
+
+ void recalc_sigpending(void)
+ {
+@@ -191,8 +211,13 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
+ atomic_inc(&user->sigpending);
+ if (override_rlimit ||
+ atomic_read(&user->sigpending) <=
+- t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur)
++ t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) {
+ q = kmem_cache_alloc(sigqueue_cachep, flags);
++ if (q && ub_siginfo_charge(q, get_task_ub(t))) {
++ kmem_cache_free(sigqueue_cachep, q);
++ q = NULL;
++ }
++ }
+ if (unlikely(q == NULL)) {
+ atomic_dec(&user->sigpending);
+ } else {
+@@ -209,6 +234,7 @@ static void __sigqueue_free(struct sigqueue *q)
+ return;
+ atomic_dec(&q->user->sigpending);
+ free_uid(q->user);
++ ub_siginfo_uncharge(q);
+ kmem_cache_free(sigqueue_cachep, q);
+ }
+
+@@ -384,7 +410,18 @@ still_pending:
+ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
+ siginfo_t *info)
+ {
+- int sig = next_signal(pending, mask);
++ int sig = 0;
++
++ /* SIGKILL must have priority, otherwise it is quite easy
++ * to create an unkillable process, sending sig < SIGKILL
++ * to self */
++ if (unlikely(sigismember(&pending->signal, SIGKILL))) {
++ if (!sigismember(mask, SIGKILL))
++ sig = SIGKILL;
++ }
++
++ if (likely(!sig))
++ sig = next_signal(pending, mask);
+
+ if (sig) {
+ if (current->notifier) {
+@@ -507,6 +544,7 @@ void signal_wake_up(struct task_struct *t, int resume)
+ if (!wake_up_state(t, mask))
+ kick_process(t);
+ }
++EXPORT_SYMBOL_GPL(signal_wake_up);
+
+ /*
+ * Remove signals in mask from the pending set and queue.
+@@ -625,7 +663,7 @@ static int prepare_signal(int sig, struct task_struct *p)
+ t = p;
+ do {
+ rm_from_queue(sigmask(SIGCONT), &t->pending);
+- } while_each_thread(p, t);
++ } while_each_thread_all(p, t);
+ } else if (sig == SIGCONT) {
+ unsigned int why;
+ /*
+@@ -657,7 +695,7 @@ static int prepare_signal(int sig, struct task_struct *p)
+ state |= TASK_INTERRUPTIBLE;
+ }
+ wake_up_state(t, state);
+- } while_each_thread(p, t);
++ } while_each_thread_all(p, t);
+
+ /*
+ * Notify the parent with CLD_CONTINUED if we were stopped.
+@@ -779,7 +817,7 @@ static void complete_signal(int sig, struct task_struct *p, int group)
+ do {
+ sigaddset(&t->pending.signal, SIGKILL);
+ signal_wake_up(t, 1);
+- } while_each_thread(p, t);
++ } while_each_thread_all(p, t);
+ return;
+ }
+ }
+@@ -1015,7 +1053,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
+ if (!ret && sig) {
+ ret = -ESRCH;
+ if (lock_task_sighand(p, &flags)) {
+- ret = __group_send_sig_info(sig, info, p);
++ ret = sig_ve_ignored(sig, info, p) ? 0 :
++ __group_send_sig_info(sig, info, p);
+ unlock_task_sighand(p, &flags);
+ }
+ }
+@@ -1140,7 +1179,7 @@ static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
+ int retval = 0, count = 0;
+ struct task_struct * p;
+
+- for_each_process(p) {
++ for_each_process_ve(p) {
+ if (p->pid > 1 && !same_thread_group(p, current)) {
+ int err = group_send_sig_info(sig, info, p);
+ ++count;
+@@ -1348,6 +1387,14 @@ int do_notify_parent(struct task_struct *tsk, int sig)
+ BUG_ON(!tsk->ptrace &&
+ (tsk->group_leader != tsk || !thread_group_empty(tsk)));
+
++#ifdef CONFIG_VE
++ /* Allow to send only SIGCHLD from VE */
++ if (sig != SIGCHLD &&
++ tsk->ve_task_info.owner_env !=
++ tsk->parent->ve_task_info.owner_env)
++ sig = SIGCHLD;
++#endif
++
+ info.si_signo = sig;
+ info.si_errno = 0;
+ /*
+@@ -1617,7 +1664,9 @@ finish_stop(int stop_count)
+ }
+
+ do {
++ set_stop_state(current);
+ schedule();
++ clear_stop_state(current);
+ } while (try_to_freeze());
+ /*
+ * Now we don't run again until continued.
+@@ -1669,6 +1718,7 @@ static int do_signal_stop(int signr)
+ sig->group_stop_count = stop_count;
+ }
+
++ clear_pn_state(current);
+ if (stop_count == 0)
+ sig->flags = SIGNAL_STOP_STOPPED;
+ current->exit_code = sig->group_exit_code;
+@@ -1732,8 +1782,6 @@ relock:
+ * Now that we woke up, it's crucial if we're supposed to be
+ * frozen that we freeze now before running anything substantial.
+ */
+- try_to_freeze();
+-
+ spin_lock_irq(&sighand->siglock);
+ /*
+ * Every stopped thread goes here after wakeup. Check to see if
+@@ -2239,7 +2287,8 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
+ * signal is private anyway.
+ */
+ if (!error && sig && lock_task_sighand(p, &flags)) {
+- error = specific_send_sig_info(sig, &info, p);
++ if (!sig_ve_ignored(sig, &info, p))
++ error = specific_send_sig_info(sig, &info, p);
+ unlock_task_sighand(p, &flags);
+ }
+ }
+@@ -2595,5 +2644,5 @@ __attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)
+
+ void __init signals_init(void)
+ {
+- sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);
++ sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC|SLAB_UBC);
+ }
+diff --git a/kernel/softirq.c b/kernel/softirq.c
+index c506f26..03cb8a5 100644
+--- a/kernel/softirq.c
++++ b/kernel/softirq.c
+@@ -22,6 +22,8 @@
+ #include <linux/smp.h>
+ #include <linux/tick.h>
+
++#include <bc/beancounter.h>
++
+ #include <asm/irq.h>
+ /*
+ - No shared variables, all the data are CPU local.
+@@ -183,10 +185,14 @@ EXPORT_SYMBOL(local_bh_enable_ip);
+
+ asmlinkage void __do_softirq(void)
+ {
++ struct user_beancounter *ub;
+ struct softirq_action *h;
+ __u32 pending;
+ int max_restart = MAX_SOFTIRQ_RESTART;
+ int cpu;
++ struct ve_struct *envid;
++
++ envid = set_exec_env(get_ve0());
+
+ pending = local_softirq_pending();
+ account_system_vtime(current);
+@@ -203,6 +209,7 @@ restart:
+
+ h = softirq_vec;
+
++ ub = set_exec_ub(get_ub0());
+ do {
+ if (pending & 1) {
+ h->action(h);
+@@ -211,6 +218,7 @@ restart:
+ h++;
+ pending >>= 1;
+ } while (pending);
++ (void)set_exec_ub(ub);
+
+ local_irq_disable();
+
+@@ -224,6 +232,7 @@ restart:
+ trace_softirq_exit();
+
+ account_system_vtime(current);
++ (void)set_exec_env(envid);
+ _local_bh_enable();
+ }
+
+@@ -279,6 +288,7 @@ void irq_exit(void)
+ {
+ account_system_vtime(current);
+ trace_hardirq_exit();
++ restore_context();
+ sub_preempt_count(IRQ_EXIT_OFFSET);
+ if (!in_interrupt() && local_softirq_pending())
+ invoke_softirq();
+diff --git a/kernel/softlockup.c b/kernel/softlockup.c
+index cb838ee..51a33fd 100644
+--- a/kernel/softlockup.c
++++ b/kernel/softlockup.c
+@@ -230,13 +230,13 @@ static void check_hung_uninterruptible_tasks(int this_cpu)
+ return;
+
+ read_lock(&tasklist_lock);
+- do_each_thread(g, t) {
++ do_each_thread_all(g, t) {
+ if (!--max_count)
+ goto unlock;
+ /* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
+ if (t->state == TASK_UNINTERRUPTIBLE)
+ check_hung_task(t, now);
+- } while_each_thread(g, t);
++ } while_each_thread_all(g, t);
+ unlock:
+ read_unlock(&tasklist_lock);
+ }
+diff --git a/kernel/sys.c b/kernel/sys.c
+index 038a7bc..bd617ce 100644
+--- a/kernel/sys.c
++++ b/kernel/sys.c
+@@ -10,6 +10,7 @@
+ #include <linux/mman.h>
+ #include <linux/smp_lock.h>
+ #include <linux/notifier.h>
++#include <linux/virtinfo.h>
+ #include <linux/reboot.h>
+ #include <linux/prctl.h>
+ #include <linux/highuid.h>
+@@ -33,6 +34,7 @@
+ #include <linux/task_io_accounting_ops.h>
+ #include <linux/seccomp.h>
+ #include <linux/cpu.h>
++#include <linux/pid_namespace.h>
+
+ #include <linux/compat.h>
+ #include <linux/syscalls.h>
+@@ -112,6 +114,102 @@ EXPORT_SYMBOL(cad_pid);
+
+ void (*pm_power_off_prepare)(void);
+
++DECLARE_MUTEX(virtinfo_sem);
++EXPORT_SYMBOL(virtinfo_sem);
++static struct vnotifier_block *virtinfo_chain[VIRT_TYPES];
++
++void __virtinfo_notifier_register(int type, struct vnotifier_block *nb)
++{
++ struct vnotifier_block **p;
++
++ for (p = &virtinfo_chain[type];
++ *p != NULL && nb->priority < (*p)->priority;
++ p = &(*p)->next);
++ nb->next = *p;
++ smp_wmb();
++ *p = nb;
++}
++
++EXPORT_SYMBOL(__virtinfo_notifier_register);
++
++void virtinfo_notifier_register(int type, struct vnotifier_block *nb)
++{
++ down(&virtinfo_sem);
++ __virtinfo_notifier_register(type, nb);
++ up(&virtinfo_sem);
++}
++
++EXPORT_SYMBOL(virtinfo_notifier_register);
++
++struct virtinfo_cnt_struct {
++ volatile unsigned long exit[NR_CPUS];
++ volatile unsigned long entry;
++};
++static DEFINE_PER_CPU(struct virtinfo_cnt_struct, virtcnt);
++
++void virtinfo_notifier_unregister(int type, struct vnotifier_block *nb)
++{
++ struct vnotifier_block **p;
++ int entry_cpu, exit_cpu;
++ unsigned long cnt, ent;
++
++ down(&virtinfo_sem);
++ for (p = &virtinfo_chain[type]; *p != nb; p = &(*p)->next);
++ *p = nb->next;
++ smp_mb();
++
++ for_each_cpu_mask(entry_cpu, cpu_possible_map) {
++ while (1) {
++ cnt = 0;
++ for_each_cpu_mask(exit_cpu, cpu_possible_map)
++ cnt +=
++ per_cpu(virtcnt, entry_cpu).exit[exit_cpu];
++ smp_rmb();
++ ent = per_cpu(virtcnt, entry_cpu).entry;
++ if (cnt == ent)
++ break;
++ __set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(HZ / 100);
++ }
++ }
++ up(&virtinfo_sem);
++}
++
++EXPORT_SYMBOL(virtinfo_notifier_unregister);
++
++int virtinfo_notifier_call(int type, unsigned long n, void *data)
++{
++ int ret;
++ int entry_cpu, exit_cpu;
++ struct vnotifier_block *nb;
++
++ entry_cpu = get_cpu();
++ per_cpu(virtcnt, entry_cpu).entry++;
++ smp_wmb();
++ put_cpu();
++
++ nb = virtinfo_chain[type];
++ ret = NOTIFY_DONE;
++ while (nb)
++ {
++ ret = nb->notifier_call(nb, n, data, ret);
++ if(ret & NOTIFY_STOP_MASK) {
++ ret &= ~NOTIFY_STOP_MASK;
++ break;
++ }
++ nb = nb->next;
++ }
++
++ exit_cpu = get_cpu();
++ smp_wmb();
++ per_cpu(virtcnt, entry_cpu).exit[exit_cpu]++;
++ put_cpu();
++
++ return ret;
++}
++
++EXPORT_SYMBOL(virtinfo_notifier_call);
++
+ static int set_one_prio(struct task_struct *p, int niceval, int error)
+ {
+ int no_nice;
+@@ -181,10 +279,10 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
+ if ((who != current->uid) && !(user = find_user(who)))
+ goto out_unlock; /* No processes for this user */
+
+- do_each_thread(g, p)
++ do_each_thread_ve(g, p) {
+ if (p->uid == who)
+ error = set_one_prio(p, niceval, error);
+- while_each_thread(g, p);
++ } while_each_thread_ve(g, p);
+ if (who != current->uid)
+ free_uid(user); /* For find_user() */
+ break;
+@@ -243,13 +341,13 @@ asmlinkage long sys_getpriority(int which, int who)
+ if ((who != current->uid) && !(user = find_user(who)))
+ goto out_unlock; /* No processes for this user */
+
+- do_each_thread(g, p)
++ do_each_thread_ve(g, p)
+ if (p->uid == who) {
+ niceval = 20 - task_nice(p);
+ if (niceval > retval)
+ retval = niceval;
+ }
+- while_each_thread(g, p);
++ while_each_thread_ve(g, p);
+ if (who != current->uid)
+ free_uid(user); /* for find_user() */
+ break;
+@@ -363,6 +461,25 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
+ magic2 != LINUX_REBOOT_MAGIC2C))
+ return -EINVAL;
+
++#ifdef CONFIG_VE
++ if (!ve_is_super(get_exec_env()))
++ switch (cmd) {
++ case LINUX_REBOOT_CMD_RESTART:
++ case LINUX_REBOOT_CMD_HALT:
++ case LINUX_REBOOT_CMD_POWER_OFF:
++ case LINUX_REBOOT_CMD_RESTART2:
++ force_sig(SIGKILL,
++ get_exec_env()->ve_ns->pid_ns->child_reaper);
++
++ case LINUX_REBOOT_CMD_CAD_ON:
++ case LINUX_REBOOT_CMD_CAD_OFF:
++ return 0;
++
++ default:
++ return -EINVAL;
++ }
++#endif
++
+ /* Instead of trying to make the power_off code look like
+ * halt when pm_power_off is not set do it the easy way.
+ */
+@@ -549,7 +666,7 @@ asmlinkage long sys_setgid(gid_t gid)
+ return 0;
+ }
+
+-static int set_user(uid_t new_ruid, int dumpclear)
++int set_user(uid_t new_ruid, int dumpclear)
+ {
+ struct user_struct *new_user;
+
+@@ -853,8 +970,27 @@ asmlinkage long sys_setfsgid(gid_t gid)
+ return old_fsgid;
+ }
+
++#ifdef CONFIG_VE
++unsigned long long ve_relative_clock(struct timespec * ts)
++{
++ unsigned long long offset = 0;
++
++ if (ts->tv_sec > get_exec_env()->start_timespec.tv_sec ||
++ (ts->tv_sec == get_exec_env()->start_timespec.tv_sec &&
++ ts->tv_nsec >= get_exec_env()->start_timespec.tv_nsec))
++ offset = (unsigned long long)(ts->tv_sec -
++ get_exec_env()->start_timespec.tv_sec) * NSEC_PER_SEC
++ + ts->tv_nsec - get_exec_env()->start_timespec.tv_nsec;
++ return nsec_to_clock_t(offset);
++}
++#endif
++
+ asmlinkage long sys_times(struct tms __user * tbuf)
+ {
++#ifdef CONFIG_VE
++ struct timespec now;
++#endif
++
+ /*
+ * In the SMP world we might just be unlucky and have one of
+ * the times increment as we use it. Since the value is an
+@@ -888,7 +1024,13 @@ asmlinkage long sys_times(struct tms __user * tbuf)
+ if (copy_to_user(tbuf, &tmp, sizeof(struct tms)))
+ return -EFAULT;
+ }
++#ifndef CONFIG_VE
+ return (long) jiffies_64_to_clock_t(get_jiffies_64());
++#else
++ /* Compare to calculation in fs/proc/array.c */
++ do_posix_clock_monotonic_gettime(&now);
++ return ve_relative_clock(&now);
++#endif
+ }
+
+ /*
+@@ -1062,6 +1204,7 @@ asmlinkage long sys_setsid(void)
+
+ spin_lock(&group_leader->sighand->siglock);
+ group_leader->signal->tty = NULL;
++ group_leader->signal->tty_old_pgrp = 0;
+ spin_unlock(&group_leader->sighand->siglock);
+
+ err = session;
+@@ -1344,7 +1487,7 @@ asmlinkage long sys_sethostname(char __user *name, int len)
+ int errno;
+ char tmp[__NEW_UTS_LEN];
+
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ if (len < 0 || len > __NEW_UTS_LEN)
+ return -EINVAL;
+@@ -1389,7 +1532,7 @@ asmlinkage long sys_setdomainname(char __user *name, int len)
+ int errno;
+ char tmp[__NEW_UTS_LEN];
+
+- if (!capable(CAP_SYS_ADMIN))
++ if (!capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ if (len < 0 || len > __NEW_UTS_LEN)
+ return -EINVAL;
+diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
+index 08d6e1b..ca1bb3e 100644
+--- a/kernel/sys_ni.c
++++ b/kernel/sys_ni.c
+@@ -168,3 +168,15 @@ cond_syscall(compat_sys_timerfd_settime);
+ cond_syscall(compat_sys_timerfd_gettime);
+ cond_syscall(sys_eventfd);
+ cond_syscall(sys_eventfd2);
++cond_syscall(sys_getluid);
++cond_syscall(sys_setluid);
++cond_syscall(sys_setublimit);
++cond_syscall(sys_ubstat);
++
++/* fairsched compat */
++cond_syscall(sys_fairsched_mknod);
++cond_syscall(sys_fairsched_rmnod);
++cond_syscall(sys_fairsched_mvpr);
++cond_syscall(sys_fairsched_vcpus);
++cond_syscall(sys_fairsched_chwt);
++cond_syscall(sys_fairsched_rate);
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index 50ec088..60b39bf 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -81,6 +81,7 @@ extern int sysctl_drop_caches;
+ extern int percpu_pagelist_fraction;
+ extern int compat_log;
+ extern int maps_protect;
++extern int ve_area_access_check; /* fs/namei.c */
+ extern int latencytop_enabled;
+ extern int sysctl_nr_open_min, sysctl_nr_open_max;
+ #ifdef CONFIG_RCU_TORTURE_TEST
+@@ -111,6 +112,13 @@ static int min_percpu_pagelist_fract = 8;
+
+ static int ngroups_max = NGROUPS_MAX;
+
++int ve_allow_kthreads = 1;
++EXPORT_SYMBOL(ve_allow_kthreads);
++
++#ifdef CONFIG_MAGIC_SYSRQ
++extern int sysrq_key_scancode;
++#endif
++
+ #ifdef CONFIG_MODULES
+ extern char modprobe_path[];
+ #endif
+@@ -124,6 +132,8 @@ extern int stop_a_enabled;
+ extern int scons_pwroff;
+ #endif
+
++extern int alloc_fail_warn;
++
+ #ifdef __hppa__
+ extern int pwrsw_enabled;
+ extern int unaligned_enabled;
+@@ -136,6 +146,7 @@ extern int sysctl_ieee_emulation_warnings;
+ extern int sysctl_userprocess_debug;
+ extern int spin_retry;
+ #endif
++int decode_call_traces = 1;
+
+ #ifdef CONFIG_BSD_PROCESS_ACCT
+ extern int acct_parm[];
+@@ -144,6 +155,10 @@ extern int acct_parm[];
+ #ifdef CONFIG_IA64
+ extern int no_unaligned_warning;
+ #endif
++#ifdef CONFIG_VE
++int glob_ve_meminfo = 0;
++EXPORT_SYMBOL(glob_ve_meminfo);
++#endif
+
+ #ifdef CONFIG_RT_MUTEXES
+ extern int max_lock_depth;
+@@ -165,9 +180,31 @@ static struct ctl_table_header root_table_header = {
+ .root = &sysctl_table_root,
+ .set = &sysctl_table_root.default_set,
+ };
+-static struct ctl_table_root sysctl_table_root = {
++
++#ifdef CONFIG_VE
++static int sysctl_root_perms(struct ctl_table_root *root,
++ struct nsproxy *namespaces, struct ctl_table *table)
++{
++ if (ve_is_super(get_exec_env()))
++ return table->mode;
++ else
++ return table->mode & ~0222;
++}
++
++static struct ctl_table_root sysctl_table_groot = {
+ .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list),
++ .default_set.list = LIST_HEAD_INIT(sysctl_table_groot.default_set.list),
++ .default_set.parent = &sysctl_table_root.default_set,
++};
++#else
++#define sysctl_root_perms NULL
++#define sysctl_table_groot sysctl_table_root
++#endif
++
++static struct ctl_table_root sysctl_table_root = {
++ .root_list = LIST_HEAD_INIT(sysctl_table_groot.root_list),
+ .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry),
++ .permissions = sysctl_root_perms,
+ };
+
+ static struct ctl_table kern_table[];
+@@ -442,6 +479,20 @@ static struct ctl_table kern_table[] = {
+ .proc_handler = &proc_dointvec,
+ },
+ #endif
++ {
++ .procname = "silence-level",
++ .data = &console_silence_loglevel,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {
++ .procname = "alloc_fail_warn",
++ .data = &alloc_fail_warn,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
+ #ifdef __hppa__
+ {
+ .ctl_name = KERN_HPPA_PWRSW,
+@@ -606,6 +657,24 @@ static struct ctl_table kern_table[] = {
+ .extra1 = &pid_max_min,
+ .extra2 = &pid_max_max,
+ },
++#ifdef CONFIG_VE
++ {
++ .procname = "ve_meminfo",
++ .data = &glob_ve_meminfo,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++#endif
++#ifdef CONFIG_MAGIC_SYSRQ
++ {
++ .procname = "sysrq-key",
++ .data = &sysrq_key_scancode,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++#endif
+ {
+ .ctl_name = KERN_PANIC_ON_OOPS,
+ .procname = "panic_on_oops",
+@@ -1176,6 +1245,21 @@ static struct ctl_table vm_table[] = {
+ .extra2 = &one,
+ },
+ #endif
++ {
++ .procname = "vsyscall",
++ .data = &sysctl_at_vsyscall,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ {
++ .ctl_name = CTL_UNNUMBERED,
++ .procname = "odirect_enable",
++ .data = &odirect_enable,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
+ /*
+ * NOTE: do not add new entries to this table unless you have read
+ * Documentation/sysctl/ctl_unnumbered.txt
+@@ -1338,6 +1422,13 @@ static struct ctl_table fs_table[] = {
+ };
+
+ static struct ctl_table debug_table[] = {
++ {
++ .procname = "decode_call_traces",
++ .data = &decode_call_traces,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
+ #if defined(CONFIG_X86) || defined(CONFIG_PPC)
+ {
+ .ctl_name = CTL_UNNUMBERED,
+@@ -1890,10 +1981,27 @@ struct ctl_table_header *__register_sysctl_paths(
+ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
+ struct ctl_table *table)
+ {
++ if (!ve_is_super(get_exec_env())) {
++ WARN_ON(1);
++ return NULL;
++ }
++
+ return __register_sysctl_paths(&sysctl_table_root, current->nsproxy,
+ path, table);
+ }
+
++struct ctl_table_header *register_sysctl_glob_paths(const struct ctl_path *path,
++ struct ctl_table *table, int virtual_handler)
++{
++ if (!ve_is_super(get_exec_env())) {
++ WARN_ON(1);
++ return NULL;
++ }
++
++ return __register_sysctl_paths(&sysctl_table_groot, current->nsproxy,
++ path, table);
++}
++
+ /**
+ * register_sysctl_table - register a sysctl table hierarchy
+ * @table: the top-level table structure
+@@ -1910,6 +2018,14 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table *table)
+ return register_sysctl_paths(null_path, table);
+ }
+
++struct ctl_table_header *register_sysctl_glob_table(struct ctl_table *table,
++ int virtual_handler)
++{
++ static const struct ctl_path null_path[] = { {} };
++
++ return register_sysctl_glob_paths(null_path, table, virtual_handler);
++}
++
+ /**
+ * unregister_sysctl_table - unregister a sysctl table hierarchy
+ * @header: the header returned from register_sysctl_table
+@@ -1971,6 +2087,18 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
+ return NULL;
+ }
+
++struct ctl_table_header *register_sysctl_glob_table(struct ctl_table *table,
++ int vh)
++{
++ return NULL;
++}
++
++struct ctl_table_header *register_sysctl_glob_paths(const struct ctl_path *path,
++ struct ctl_table *table, int vh)
++{
++ return NULL;
++}
++
+ void unregister_sysctl_table(struct ctl_table_header * table)
+ {
+ }
+@@ -3000,6 +3128,57 @@ static int deprecated_sysctl_warning(struct __sysctl_args *args)
+ return 0;
+ }
+
++#ifdef CONFIG_PID_NS
++#include <linux/pid_namespace.h>
++
++static int proc_pid_ns_hide_child(struct ctl_table *table, int write,
++ struct file *filp, void __user *buffer,
++ size_t *lenp, loff_t *ppos)
++{
++ int tmp, res;
++
++ tmp = (current->nsproxy->pid_ns->flags & PID_NS_HIDE_CHILD) ? 1 : 0;
++
++ res = __do_proc_dointvec(&tmp, table, write, filp, buffer,
++ lenp, ppos, NULL, NULL);
++ if (res || !write)
++ return res;
++
++ if (tmp)
++ current->nsproxy->pid_ns->flags |= PID_NS_HIDE_CHILD;
++ else
++ current->nsproxy->pid_ns->flags &= ~PID_NS_HIDE_CHILD;
++ return 0;
++}
++
++static struct ctl_table pid_ns_kern_table[] = {
++ {
++ .procname = "pid_ns_hide_child",
++ .maxlen = sizeof(int),
++ .mode = 0600,
++ .proc_handler = proc_pid_ns_hide_child,
++ },
++ {}
++};
++
++static struct ctl_table pid_ns_root_table[] = {
++ {
++ .ctl_name = CTL_KERN,
++ .procname = "kernel",
++ .mode = 0555,
++ .child = pid_ns_kern_table,
++ },
++ {}
++};
++
++static __init int pid_ns_sysctl_init(void)
++{
++ register_sysctl_table(pid_ns_root_table);
++ return 0;
++}
++postcore_initcall(pid_ns_sysctl_init);
++#endif /* CONFIG_PID_NS */
++
+ /*
+ * No sense putting this after each symbol definition, twice,
+ * exception granted :-)
+@@ -3013,7 +3192,9 @@ EXPORT_SYMBOL(proc_dostring);
+ EXPORT_SYMBOL(proc_doulongvec_minmax);
+ EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
+ EXPORT_SYMBOL(register_sysctl_table);
++EXPORT_SYMBOL(register_sysctl_glob_table);
+ EXPORT_SYMBOL(register_sysctl_paths);
++EXPORT_SYMBOL(register_sysctl_glob_paths);
+ EXPORT_SYMBOL(sysctl_intvec);
+ EXPORT_SYMBOL(sysctl_jiffies);
+ EXPORT_SYMBOL(sysctl_ms_jiffies);
+diff --git a/kernel/taskstats.c b/kernel/taskstats.c
+index bd6be76..e115826 100644
+--- a/kernel/taskstats.c
++++ b/kernel/taskstats.c
+@@ -254,7 +254,7 @@ static int fill_tgid(pid_t tgid, struct task_struct *first,
+
+ stats->nvcsw += tsk->nvcsw;
+ stats->nivcsw += tsk->nivcsw;
+- } while_each_thread(first, tsk);
++ } while_each_thread_all(first, tsk);
+
+ unlock_task_sighand(first, &flags);
+ rc = 0;
+diff --git a/kernel/time.c b/kernel/time.c
+index 6a08660..c986346 100644
+--- a/kernel/time.c
++++ b/kernel/time.c
+@@ -601,10 +601,12 @@ EXPORT_SYMBOL(jiffies_to_clock_t);
+ unsigned long clock_t_to_jiffies(unsigned long x)
+ {
+ #if (HZ % USER_HZ)==0
++ WARN_ON((long)x < 0);
+ if (x >= ~0UL / (HZ / USER_HZ))
+ return ~0UL;
+ return x * (HZ / USER_HZ);
+ #else
++ WARN_ON((long)x < 0);
+ /* Don't worry about loss of precision here .. */
+ if (x >= ~0UL / HZ * USER_HZ)
+ return ~0UL;
+@@ -617,6 +619,7 @@ EXPORT_SYMBOL(clock_t_to_jiffies);
+
+ u64 jiffies_64_to_clock_t(u64 x)
+ {
++ WARN_ON((s64)x < 0);
+ #if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
+ # if HZ < USER_HZ
+ x = div_u64(x * USER_HZ, HZ);
+@@ -639,6 +642,7 @@ EXPORT_SYMBOL(jiffies_64_to_clock_t);
+
+ u64 nsec_to_clock_t(u64 x)
+ {
++ WARN_ON((s64)x < 0);
+ #if (NSEC_PER_SEC % USER_HZ) == 0
+ return div_u64(x, NSEC_PER_SEC / USER_HZ);
+ #elif (USER_HZ % 512) == 0
+diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
+index e91c29f..3db0c59 100644
+--- a/kernel/time/timekeeping.c
++++ b/kernel/time/timekeeping.c
+@@ -43,6 +43,7 @@ __cacheline_aligned_in_smp DEFINE_SEQLOCK(xtime_lock);
+ * used instead.
+ */
+ struct timespec xtime __attribute__ ((aligned (16)));
++EXPORT_SYMBOL_GPL(xtime);
+ struct timespec wall_to_monotonic __attribute__ ((aligned (16)));
+ static unsigned long total_sleep_time; /* seconds */
+
+diff --git a/kernel/timer.c b/kernel/timer.c
+index 03bc7f1..05b6a6d 100644
+--- a/kernel/timer.c
++++ b/kernel/timer.c
+@@ -37,6 +37,8 @@
+ #include <linux/delay.h>
+ #include <linux/tick.h>
+ #include <linux/kallsyms.h>
++#include <linux/virtinfo.h>
++#include <linux/ve_proto.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+@@ -795,7 +797,11 @@ static inline void __run_timers(struct tvec_base *base)
+ spin_unlock_irq(&base->lock);
+ {
+ int preempt_count = preempt_count();
++ struct ve_struct *ve;
++
++ ve = set_exec_env(get_ve0());
+ fn(data);
++ (void)set_exec_env(ve);
+ if (preempt_count != preempt_count()) {
+ printk(KERN_ERR "huh, entered %p "
+ "with preempt_count %08x, exited"
+@@ -1006,6 +1012,37 @@ EXPORT_SYMBOL(avenrun);
+ * calc_load - given tick count, update the avenrun load estimates.
+ * This is called while holding a write_lock on xtime_lock.
+ */
++
++
++#ifdef CONFIG_VE
++static void calc_load_ve(void)
++{
++ unsigned long flags, nr_unint, nr_active;
++ struct ve_struct *ve;
++
++ read_lock(&ve_list_lock);
++ for_each_ve(ve) {
++ nr_active = nr_running_ve(ve) + nr_uninterruptible_ve(ve);
++ nr_active *= FIXED_1;
++
++ CALC_LOAD(ve->avenrun[0], EXP_1, nr_active);
++ CALC_LOAD(ve->avenrun[1], EXP_5, nr_active);
++ CALC_LOAD(ve->avenrun[2], EXP_15, nr_active);
++ }
++ read_unlock(&ve_list_lock);
++
++ nr_unint = nr_uninterruptible() * FIXED_1;
++ spin_lock_irqsave(&kstat_glb_lock, flags);
++ CALC_LOAD(kstat_glob.nr_unint_avg[0], EXP_1, nr_unint);
++ CALC_LOAD(kstat_glob.nr_unint_avg[1], EXP_5, nr_unint);
++ CALC_LOAD(kstat_glob.nr_unint_avg[2], EXP_15, nr_unint);
++ spin_unlock_irqrestore(&kstat_glb_lock, flags);
++
++}
++#else
++#define calc_load_ve() do { } while (0)
++#endif
++
+ static inline void calc_load(unsigned long ticks)
+ {
+ unsigned long active_tasks; /* fixed-point */
+@@ -1018,6 +1055,7 @@ static inline void calc_load(unsigned long ticks)
+ CALC_LOAD(avenrun[0], EXP_1, active_tasks);
+ CALC_LOAD(avenrun[1], EXP_5, active_tasks);
+ CALC_LOAD(avenrun[2], EXP_15, active_tasks);
++ calc_load_ve();
+ count += LOAD_FREQ;
+ } while (count < 0);
+ }
+@@ -1267,11 +1305,12 @@ int do_sysinfo(struct sysinfo *info)
+ unsigned long mem_total, sav_total;
+ unsigned int mem_unit, bitcount;
+ unsigned long seq;
++ unsigned long *__avenrun;
++ struct timespec tp;
+
+ memset(info, 0, sizeof(struct sysinfo));
+
+ do {
+- struct timespec tp;
+ seq = read_seqbegin(&xtime_lock);
+
+ /*
+@@ -1289,18 +1328,34 @@ int do_sysinfo(struct sysinfo *info)
+ tp.tv_nsec = tp.tv_nsec - NSEC_PER_SEC;
+ tp.tv_sec++;
+ }
+- info->uptime = tp.tv_sec + (tp.tv_nsec ? 1 : 0);
+-
+- info->loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT);
+- info->loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT);
+- info->loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
++ } while (read_seqretry(&xtime_lock, seq));
+
++ if (ve_is_super(get_exec_env())) {
++ info->uptime = tp.tv_sec + (tp.tv_nsec ? 1 : 0);
++ __avenrun = &avenrun[0];
+ info->procs = nr_threads;
+- } while (read_seqretry(&xtime_lock, seq));
++ }
++#ifdef CONFIG_VE
++ else {
++ struct ve_struct *ve;
++ ve = get_exec_env();
++ __avenrun = &ve->avenrun[0];
++ info->procs = atomic_read(&ve->pcounter);
++ info->uptime = tp.tv_sec - ve->start_timespec.tv_sec;
++ }
++#endif
++ info->loads[0] = __avenrun[0] << (SI_LOAD_SHIFT - FSHIFT);
++ info->loads[1] = __avenrun[1] << (SI_LOAD_SHIFT - FSHIFT);
++ info->loads[2] = __avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
+
+ si_meminfo(info);
+ si_swapinfo(info);
+
++#ifdef CONFIG_BEANCOUNTERS
++ if (virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_SYSINFO, info)
++ & NOTIFY_FAIL)
++ return -ENOMSG;
++#endif
+ /*
+ * If the sum of all the available memory (i.e. ram + swap)
+ * is less than can be stored in a 32 bit unsigned long then
+diff --git a/kernel/user.c b/kernel/user.c
+index 865ecf5..b1139b3 100644
+--- a/kernel/user.c
++++ b/kernel/user.c
+@@ -314,6 +314,7 @@ static void remove_user_sysfs_dir(struct work_struct *w)
+ done:
+ uids_mutex_unlock();
+ }
++EXPORT_SYMBOL_GPL(free_uid);
+
+ /* IRQs are disabled and uidhash_lock is held upon function entry.
+ * IRQ state (as stored in flags) is restored and uidhash_lock released
+@@ -383,6 +384,7 @@ void free_uid(struct user_struct *up)
+ else
+ local_irq_restore(flags);
+ }
++EXPORT_SYMBOL_GPL(free_uid);
+
+ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
+ {
+@@ -447,6 +449,7 @@ out_unlock:
+ uids_mutex_unlock();
+ return NULL;
+ }
++EXPORT_SYMBOL_GPL(alloc_uid);
+
+ void switch_uid(struct user_struct *new_user)
+ {
+@@ -477,6 +480,7 @@ void switch_uid(struct user_struct *new_user)
+ free_uid(old_user);
+ suid_keys(current);
+ }
++EXPORT_SYMBOL_GPL(switch_uid);
+
+ #ifdef CONFIG_USER_NS
+ void release_uids(struct user_namespace *ns)
+@@ -510,7 +514,7 @@ static int __init uid_cache_init(void)
+ int n;
+
+ uid_cachep = kmem_cache_create("uid_cache", sizeof(struct user_struct),
+- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+
+ for(n = 0; n < UIDHASH_SZ; ++n)
+ INIT_HLIST_HEAD(init_user_ns.uidhash_table + n);
+diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c
+index 4ab9659..5e4b983 100644
+--- a/kernel/utsname_sysctl.c
++++ b/kernel/utsname_sysctl.c
+@@ -26,6 +26,10 @@ static void *get_uts(ctl_table *table, int write)
+ down_read(&uts_sem);
+ else
+ down_write(&uts_sem);
++
++ if (strcmp(table->procname, "virt_osrelease") == 0)
++ return virt_utsname.release;
++
+ return which;
+ }
+
+@@ -127,19 +131,27 @@ static struct ctl_table uts_kern_table[] = {
+ {}
+ };
+
+-static struct ctl_table uts_root_table[] = {
++static struct ctl_table uts_virt_osrelease_table[] = {
+ {
+- .ctl_name = CTL_KERN,
+- .procname = "kernel",
+- .mode = 0555,
+- .child = uts_kern_table,
++ .procname = "virt_osrelease",
++ .data = virt_utsname.release,
++ .maxlen = sizeof(virt_utsname.release),
++ .mode = 0644,
++ .proc_handler = &proc_do_uts_string,
++ .strategy = sysctl_uts_string,
+ },
+ {}
+ };
+
++static struct ctl_path uts_path[] = {
++ { .ctl_name = CTL_KERN, .procname = "kernel", },
++ { }
++};
++
+ static int __init utsname_sysctl_init(void)
+ {
+- register_sysctl_table(uts_root_table);
++ register_sysctl_glob_paths(uts_path, uts_kern_table, 1);
++ register_sysctl_paths(uts_path, uts_virt_osrelease_table);
+ return 0;
+ }
+
+diff --git a/kernel/ve/Makefile b/kernel/ve/Makefile
+new file mode 100644
+index 0000000..9d60161
+--- /dev/null
++++ b/kernel/ve/Makefile
+@@ -0,0 +1,16 @@
++#
++#
++# kernel/ve/Makefile
++#
++# Copyright (C) 2000-2005 SWsoft
++# All rights reserved.
++#
++# Licensing governed by "linux/COPYING.SWsoft" file.
++
++obj-$(CONFIG_VE) = ve.o veowner.o hooks.o
++obj-$(CONFIG_VZ_WDOG) += vzwdog.o
++obj-$(CONFIG_VE_CALLS) += vzmon.o
++
++vzmon-objs = vecalls.o
++
++obj-$(CONFIG_VZ_DEV) += vzdev.o
+diff --git a/kernel/ve/hooks.c b/kernel/ve/hooks.c
+new file mode 100644
+index 0000000..1b82c35
+--- /dev/null
++++ b/kernel/ve/hooks.c
+@@ -0,0 +1,114 @@
++/*
++ * linux/kernel/ve/hooks.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/ve.h>
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/ve_proto.h>
++#include <linux/module.h>
++
++static struct list_head ve_hooks[VE_MAX_CHAINS];
++static DECLARE_RWSEM(ve_hook_sem);
++
++void ve_hook_register(int chain, struct ve_hook *vh)
++{
++ struct list_head *lh;
++ struct ve_hook *tmp;
++
++ BUG_ON(chain > VE_MAX_CHAINS);
++
++ down_write(&ve_hook_sem);
++ list_for_each(lh, &ve_hooks[chain]) {
++ tmp = list_entry(lh, struct ve_hook, list);
++ if (vh->priority < tmp->priority)
++ break;
++ }
++
++ list_add_tail(&vh->list, lh);
++ up_write(&ve_hook_sem);
++}
++
++EXPORT_SYMBOL(ve_hook_register);
++
++void ve_hook_unregister(struct ve_hook *vh)
++{
++ down_write(&ve_hook_sem);
++ list_del(&vh->list);
++ up_write(&ve_hook_sem);
++}
++
++EXPORT_SYMBOL(ve_hook_unregister);
++
++static inline int ve_hook_init(struct ve_hook *vh, struct ve_struct *ve)
++{
++ int err;
++
++ err = 0;
++ if (try_module_get(vh->owner)) {
++ err = vh->init(ve);
++ module_put(vh->owner);
++ }
++ return err;
++}
++
++static inline void ve_hook_fini(struct ve_hook *vh, struct ve_struct *ve)
++{
++ if (vh->fini != NULL && try_module_get(vh->owner)) {
++ vh->fini(ve);
++ module_put(vh->owner);
++ }
++}
++
++int ve_hook_iterate_init(int chain, void *ve)
++{
++ struct ve_hook *vh;
++ int err;
++
++ err = 0;
++
++ down_read(&ve_hook_sem);
++ list_for_each_entry(vh, &ve_hooks[chain], list)
++ if ((err = ve_hook_init(vh, ve)) < 0)
++ break;
++
++ if (err)
++ list_for_each_entry_continue_reverse(vh, &ve_hooks[chain], list)
++ ve_hook_fini(vh, ve);
++
++ up_read(&ve_hook_sem);
++ return err;
++}
++
++EXPORT_SYMBOL(ve_hook_iterate_init);
++
++void ve_hook_iterate_fini(int chain, void *ve)
++{
++ struct ve_hook *vh;
++
++ down_read(&ve_hook_sem);
++ list_for_each_entry_reverse(vh, &ve_hooks[chain], list)
++ ve_hook_fini(vh, ve);
++ up_read(&ve_hook_sem);
++}
++
++EXPORT_SYMBOL(ve_hook_iterate_fini);
++
++static int __init ve_hooks_init(void)
++{
++ int i;
++
++ for (i = 0; i < VE_MAX_CHAINS; i++)
++ INIT_LIST_HEAD(&ve_hooks[i]);
++ return 0;
++}
++
++core_initcall(ve_hooks_init);
++
+diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
+new file mode 100644
+index 0000000..0248f38
+--- /dev/null
++++ b/kernel/ve/ve.c
+@@ -0,0 +1,147 @@
++/*
++ * linux/kernel/ve/ve.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++/*
++ * 've.c' helper file performing VE sub-system initialization
++ */
++
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/capability.h>
++#include <linux/ve.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++
++#include <linux/errno.h>
++#include <linux/unistd.h>
++#include <linux/slab.h>
++#include <linux/sys.h>
++#include <linux/kdev_t.h>
++#include <linux/termios.h>
++#include <linux/tty_driver.h>
++#include <linux/netdevice.h>
++#include <linux/utsname.h>
++#include <linux/proc_fs.h>
++#include <linux/kernel_stat.h>
++#include <linux/module.h>
++#include <linux/rcupdate.h>
++#include <linux/ve_proto.h>
++#include <linux/devpts_fs.h>
++
++#include <linux/nfcalls.h>
++#include <linux/vzcalluser.h>
++
++unsigned long vz_rstamp = 0x37e0f59d;
++
++#ifdef CONFIG_MODULES
++struct module no_module = { .state = MODULE_STATE_GOING };
++EXPORT_SYMBOL(no_module);
++#endif
++
++INIT_KSYM_MODULE(ip_tables);
++INIT_KSYM_MODULE(ip6_tables);
++INIT_KSYM_MODULE(iptable_filter);
++INIT_KSYM_MODULE(ip6table_filter);
++INIT_KSYM_MODULE(iptable_mangle);
++INIT_KSYM_MODULE(ip6table_mangle);
++INIT_KSYM_MODULE(ip_conntrack);
++INIT_KSYM_MODULE(nf_conntrack);
++INIT_KSYM_MODULE(nf_conntrack_ipv4);
++INIT_KSYM_MODULE(nf_conntrack_ipv6);
++INIT_KSYM_MODULE(ip_nat);
++INIT_KSYM_MODULE(nf_nat);
++INIT_KSYM_MODULE(iptable_nat);
++
++INIT_KSYM_CALL(int, init_iptable_conntrack, (void));
++INIT_KSYM_CALL(int, nf_conntrack_init_ve, (void));
++INIT_KSYM_CALL(int, init_nf_ct_l3proto_ipv4, (void));
++INIT_KSYM_CALL(int, init_nf_ct_l3proto_ipv6, (void));
++INIT_KSYM_CALL(int, nf_nat_init, (void));
++INIT_KSYM_CALL(int, init_iptable_nat, (void));
++INIT_KSYM_CALL(void, fini_iptable_nat, (void));
++INIT_KSYM_CALL(int, init_nftable_nat, (void));
++INIT_KSYM_CALL(void, fini_nftable_nat, (void));
++INIT_KSYM_CALL(void, nf_nat_cleanup, (void));
++INIT_KSYM_CALL(void, fini_iptable_conntrack, (void));
++INIT_KSYM_CALL(void, nf_conntrack_cleanup_ve, (void));
++INIT_KSYM_CALL(void, fini_nf_ct_l3proto_ipv4, (void));
++INIT_KSYM_CALL(void, fini_nf_ct_l3proto_ipv6, (void));
++
++#if defined(CONFIG_VE_CALLS_MODULE) || defined(CONFIG_VE_CALLS)
++INIT_KSYM_MODULE(vzmon);
++INIT_KSYM_CALL(void, real_do_env_free, (struct ve_struct *env));
++
++void do_env_free(struct ve_struct *env)
++{
++ KSYMSAFECALL_VOID(vzmon, real_do_env_free, (env));
++}
++EXPORT_SYMBOL(do_env_free);
++#endif
++
++#if defined(CONFIG_VE_ETHDEV) || defined(CONFIG_VE_ETHDEV_MODULE)
++INIT_KSYM_MODULE(vzethdev);
++INIT_KSYM_CALL(int, veth_open, (struct net_device *dev));
++#endif
++
++struct ve_struct ve0 = {
++ .counter = ATOMIC_INIT(1),
++ .pcounter = ATOMIC_INIT(1),
++ .ve_list = LIST_HEAD_INIT(ve0.ve_list),
++ .vetask_lh = LIST_HEAD_INIT(ve0.vetask_lh),
++ .start_jiffies = INITIAL_JIFFIES,
++#ifdef CONFIG_UNIX98_PTYS
++ .devpts_config = &devpts_config,
++#endif
++ .ve_ns = &init_nsproxy,
++ .ve_netns = &init_net,
++ .is_running = 1,
++ .op_sem = __RWSEM_INITIALIZER(ve0.op_sem),
++#ifdef CONFIG_VE_IPTABLES
++ .ipt_mask = ~0ULL,
++#endif
++ .features = VE_FEATURE_SIT | VE_FEATURE_IPIP,
++};
++
++EXPORT_SYMBOL(ve0);
++
++DEFINE_PER_CPU_STATIC(struct ve_cpu_stats, ve0_cpu_stats);
++
++LIST_HEAD(ve_list_head);
++rwlock_t ve_list_lock = RW_LOCK_UNLOCKED;
++
++LIST_HEAD(ve_cleanup_list);
++DEFINE_SPINLOCK(ve_cleanup_lock);
++struct task_struct *ve_cleanup_thread;
++
++EXPORT_SYMBOL(ve_list_lock);
++EXPORT_SYMBOL(ve_list_head);
++EXPORT_SYMBOL(ve_cleanup_lock);
++EXPORT_SYMBOL(ve_cleanup_list);
++EXPORT_SYMBOL(ve_cleanup_thread);
++
++void init_ve0(void)
++{
++ struct ve_struct *ve;
++
++ ve = get_ve0();
++ ve->cpu_stats = percpu_static_init(ve0_cpu_stats);
++ list_add(&ve->ve_list, &ve_list_head);
++}
++
++void ve_cleanup_schedule(struct ve_struct *ve)
++{
++ BUG_ON(ve_cleanup_thread == NULL);
++
++ spin_lock(&ve_cleanup_lock);
++ list_add_tail(&ve->cleanup_list, &ve_cleanup_list);
++ spin_unlock(&ve_cleanup_lock);
++
++ wake_up_process(ve_cleanup_thread);
++}
+diff --git a/kernel/ve/vecalls.c b/kernel/ve/vecalls.c
+new file mode 100644
+index 0000000..86dd8ef
+--- /dev/null
++++ b/kernel/ve/vecalls.c
+@@ -0,0 +1,2417 @@
++/*
++ * linux/kernel/ve/vecalls.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ */
++
++/*
++ * 'vecalls.c' is file with basic VE support. It provides basic primities
++ * along with initialization script
++ */
++
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/capability.h>
++#include <linux/ve.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/errno.h>
++#include <linux/unistd.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/sys.h>
++#include <linux/fs.h>
++#include <linux/mnt_namespace.h>
++#include <linux/termios.h>
++#include <linux/tty_driver.h>
++#include <linux/netdevice.h>
++#include <linux/wait.h>
++#include <linux/inetdevice.h>
++#include <net/addrconf.h>
++#include <linux/utsname.h>
++#include <linux/sysctl.h>
++#include <linux/proc_fs.h>
++#include <linux/devpts_fs.h>
++#include <linux/shmem_fs.h>
++#include <linux/sysfs.h>
++#include <linux/seq_file.h>
++#include <linux/kernel_stat.h>
++#include <linux/module.h>
++#include <linux/suspend.h>
++#include <linux/rcupdate.h>
++#include <linux/in.h>
++#include <linux/idr.h>
++#include <linux/inetdevice.h>
++#include <linux/pid.h>
++#include <net/pkt_sched.h>
++#include <bc/beancounter.h>
++#include <linux/nsproxy.h>
++#include <linux/kobject.h>
++#include <linux/freezer.h>
++#include <linux/pid_namespace.h>
++#include <linux/tty.h>
++
++#include <net/route.h>
++#include <net/ip_fib.h>
++#include <net/ip6_route.h>
++#include <net/arp.h>
++#include <net/ipv6.h>
++
++#include <linux/ve_proto.h>
++#include <linux/venet.h>
++#include <linux/vzctl.h>
++#include <linux/vzcalluser.h>
++#ifdef CONFIG_VZ_FAIRSCHED
++#include <linux/fairsched.h>
++#endif
++
++#include <linux/nfcalls.h>
++#include <linux/virtinfo.h>
++#include <linux/utsrelease.h>
++#include <linux/major.h>
++
++int nr_ve = 1; /* One VE always exists. Compatibility with vestat */
++EXPORT_SYMBOL(nr_ve);
++
++static int do_env_enter(struct ve_struct *ve, unsigned int flags);
++static int alloc_ve_tty_drivers(struct ve_struct* ve);
++static void free_ve_tty_drivers(struct ve_struct* ve);
++static int register_ve_tty_drivers(struct ve_struct* ve);
++static void unregister_ve_tty_drivers(struct ve_struct* ve);
++static int init_ve_tty_drivers(struct ve_struct *);
++static void fini_ve_tty_drivers(struct ve_struct *);
++static void clear_termios(struct tty_driver* driver );
++
++static void vecalls_exit(void);
++
++struct ve_struct *__find_ve_by_id(envid_t veid)
++{
++ struct ve_struct *ve;
++
++ for_each_ve(ve) {
++ if (ve->veid == veid)
++ return ve;
++ }
++ return NULL;
++}
++EXPORT_SYMBOL(__find_ve_by_id);
++
++struct ve_struct *get_ve_by_id(envid_t veid)
++{
++ struct ve_struct *ve;
++ read_lock(&ve_list_lock);
++ ve = __find_ve_by_id(veid);
++ get_ve(ve);
++ read_unlock(&ve_list_lock);
++ return ve;
++}
++EXPORT_SYMBOL(get_ve_by_id);
++
++/*
++ * real_put_ve() MUST be used instead of put_ve() inside vecalls.
++ */
++void real_do_env_free(struct ve_struct *ve);
++static inline void real_put_ve(struct ve_struct *ve)
++{
++ if (ve && atomic_dec_and_test(&ve->counter)) {
++ BUG_ON(atomic_read(&ve->pcounter) > 0);
++ BUG_ON(ve->is_running);
++ real_do_env_free(ve);
++ }
++}
++
++static int ve_get_cpu_stat(envid_t veid, struct vz_cpu_stat __user *buf)
++{
++ struct ve_struct *ve;
++ struct vz_cpu_stat *vstat;
++ int retval;
++ int i, cpu;
++ unsigned long tmp;
++
++ if (!ve_is_super(get_exec_env()) && (veid != get_exec_env()->veid))
++ return -EPERM;
++ if (veid == 0)
++ return -ESRCH;
++
++ vstat = kzalloc(sizeof(*vstat), GFP_KERNEL);
++ if (!vstat)
++ return -ENOMEM;
++
++ retval = -ESRCH;
++ read_lock(&ve_list_lock);
++ ve = __find_ve_by_id(veid);
++ if (ve == NULL)
++ goto out_unlock;
++ for_each_online_cpu(cpu) {
++ struct ve_cpu_stats *st;
++
++ st = VE_CPU_STATS(ve, cpu);
++ vstat->user_jif += (unsigned long)cputime64_to_clock_t(st->user);
++ vstat->nice_jif += (unsigned long)cputime64_to_clock_t(st->nice);
++ vstat->system_jif += (unsigned long)cputime64_to_clock_t(st->system);
++ vstat->idle_clk += ve_sched_get_idle_time(ve, cpu);
++ }
++ vstat->uptime_clk = get_cycles() - ve->start_cycles;
++ vstat->uptime_jif = (unsigned long)cputime64_to_clock_t(
++ get_jiffies_64() - ve->start_jiffies);
++ for (i = 0; i < 3; i++) {
++ tmp = ve->avenrun[i] + (FIXED_1/200);
++ vstat->avenrun[i].val_int = LOAD_INT(tmp);
++ vstat->avenrun[i].val_frac = LOAD_FRAC(tmp);
++ }
++ read_unlock(&ve_list_lock);
++
++ retval = 0;
++ if (copy_to_user(buf, vstat, sizeof(*vstat)))
++ retval = -EFAULT;
++out_free:
++ kfree(vstat);
++ return retval;
++
++out_unlock:
++ read_unlock(&ve_list_lock);
++ goto out_free;
++}
++
++static int real_setdevperms(envid_t veid, unsigned type,
++ dev_t dev, unsigned mask)
++{
++ struct ve_struct *ve;
++ int err;
++
++ if (!capable(CAP_SETVEID) || veid == 0)
++ return -EPERM;
++
++ if ((ve = get_ve_by_id(veid)) == NULL)
++ return -ESRCH;
++
++ down_read(&ve->op_sem);
++ err = -ESRCH;
++ if (ve->is_running)
++ err = set_device_perms_ve(ve, type, dev, mask);
++ up_read(&ve->op_sem);
++ real_put_ve(ve);
++ return err;
++}
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * VE start: subsystems
++ *
++ **********************************************************************
++ **********************************************************************/
++
++#ifdef CONFIG_INET
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <net/udp.h>
++#include <net/icmp.h>
++
++static int init_fini_ve_mibs(struct ve_struct *ve, int fini)
++{
++ if (fini)
++ goto fini;
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ if (init_ipv6_mibs())
++ goto err_ipv6;
++#endif
++ return 0;
++
++fini:
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ cleanup_ipv6_mibs();
++err_ipv6:
++#endif
++ return -ENOMEM;
++}
++
++static inline int init_ve_mibs(struct ve_struct *ve)
++{
++ return init_fini_ve_mibs(ve, 0);
++}
++
++static inline void fini_ve_mibs(struct ve_struct *ve)
++{
++ (void)init_fini_ve_mibs(ve, 1);
++}
++#else
++#define init_ve_mibs(ve) (0)
++#define fini_ve_mibs(ve) do { } while (0)
++#endif
++
++static int prepare_proc_root(struct ve_struct *ve)
++{
++ struct proc_dir_entry *de;
++
++ de = kzalloc(sizeof(struct proc_dir_entry) + 6, GFP_KERNEL);
++ if (de == NULL)
++ return -ENOMEM;
++
++ memcpy(de + 1, "/proc", 6);
++ de->name = (char *)(de + 1);
++ de->namelen = 5;
++ de->mode = S_IFDIR | S_IRUGO | S_IXUGO;
++ de->nlink = 2;
++ atomic_set(&de->count, 1);
++
++ ve->proc_root = de;
++ return 0;
++}
++
++#ifdef CONFIG_PROC_FS
++static int init_ve_proc(struct ve_struct *ve)
++{
++ int err;
++
++ err = prepare_proc_root(ve);
++ if (err)
++ goto out_root;
++
++ err = register_ve_fs_type(ve, &proc_fs_type,
++ &ve->proc_fstype, &ve->proc_mnt);
++ if (err)
++ goto out_reg;
++
++#ifdef CONFIG_PRINTK
++ proc_create("kmsg", S_IRUSR, ve->proc_root, &proc_kmsg_operations);
++#endif
++ proc_mkdir("vz", ve->proc_root);
++
++ ve->ve_ns->pid_ns->proc_mnt = mntget(ve->proc_mnt);
++ return 0;
++
++out_reg:
++ /* proc_fstype and proc_root are freed in real_put_ve -> free_ve_proc */
++ ;
++out_root:
++ return err;
++}
++
++static void fini_ve_proc(struct ve_struct *ve)
++{
++ remove_proc_entry("vz", ve->proc_root);
++ remove_proc_entry("kmsg", ve->proc_root);
++ unregister_ve_fs_type(ve->proc_fstype, ve->proc_mnt);
++ ve->proc_mnt = NULL;
++}
++
++static void free_ve_proc(struct ve_struct *ve)
++{
++ /* proc filesystem frees proc_dir_entries on remove_proc_entry() only,
++ so we check that everything was removed and not lost */
++ if (ve->proc_root && ve->proc_root->subdir) {
++ struct proc_dir_entry *p = ve->proc_root;
++ printk(KERN_WARNING "CT: %d: proc entry /proc", ve->veid);
++ while ((p = p->subdir) != NULL)
++ printk("/%s", p->name);
++ printk(" is not removed!\n");
++ }
++
++ kfree(ve->proc_root);
++ kfree(ve->proc_fstype);
++
++ ve->proc_fstype = NULL;
++ ve->proc_root = NULL;
++}
++#else
++#define init_ve_proc(ve) (0)
++#define fini_ve_proc(ve) do { } while (0)
++#define free_ve_proc(ve) do { } while (0)
++#endif
++
++#ifdef CONFIG_UNIX98_PTYS
++#include <linux/devpts_fs.h>
++
++/*
++ * DEVPTS needs a virtualization: each environment should see each own list of
++ * pseudo-terminals.
++ * To implement it we need to have separate devpts superblocks for each
++ * VE, and each VE should mount its own one.
++ * Thus, separate vfsmount structures are required.
++ * To minimize intrusion into vfsmount lookup code, separate file_system_type
++ * structures are created.
++ *
++ * In addition to this, patch fo character device itself is required, as file
++ * system itself is used only for MINOR/MAJOR lookup.
++ */
++
++static int init_ve_devpts(struct ve_struct *ve)
++{
++ int err;
++
++ err = -ENOMEM;
++ ve->devpts_config = kzalloc(sizeof(struct devpts_config), GFP_KERNEL);
++ if (ve->devpts_config == NULL)
++ goto out;
++
++ ve->devpts_config->mode = 0600;
++ err = register_ve_fs_type(ve, &devpts_fs_type,
++ &ve->devpts_fstype, &ve->devpts_mnt);
++ if (err) {
++ kfree(ve->devpts_config);
++ ve->devpts_config = NULL;
++ }
++out:
++ return err;
++}
++
++static void fini_ve_devpts(struct ve_struct *ve)
++{
++ unregister_ve_fs_type(ve->devpts_fstype, ve->devpts_mnt);
++ /* devpts_fstype is freed in real_put_ve -> free_ve_filesystems */
++ ve->devpts_mnt = NULL;
++ kfree(ve->devpts_config);
++ ve->devpts_config = NULL;
++}
++#else
++#define init_ve_devpts(ve) (0)
++#define fini_ve_devpts(ve) do { } while (0)
++#endif
++
++static int init_ve_shmem(struct ve_struct *ve)
++{
++ return register_ve_fs_type(ve,
++ &tmpfs_fs_type,
++ &ve->shmem_fstype,
++ &ve->shmem_mnt);
++}
++
++static void fini_ve_shmem(struct ve_struct *ve)
++{
++ unregister_ve_fs_type(ve->shmem_fstype, ve->shmem_mnt);
++ /* shmem_fstype is freed in real_put_ve -> free_ve_filesystems */
++ ve->shmem_mnt = NULL;
++}
++
++#ifdef CONFIG_SYSFS
++static int init_ve_sysfs_root(struct ve_struct *ve)
++{
++ struct sysfs_dirent *sysfs_root;
++
++ sysfs_root = kzalloc(sizeof(struct sysfs_dirent), GFP_KERNEL);
++ if (sysfs_root == NULL)
++ return -ENOMEM;
++ sysfs_root->s_name = "";
++ atomic_set(&sysfs_root->s_count, 1);
++ sysfs_root->s_flags = SYSFS_DIR;
++ sysfs_root->s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
++ sysfs_root->s_ino = 1;
++
++ ve->_sysfs_root = sysfs_root;
++ return 0;
++}
++#endif
++
++#if defined(CONFIG_NET) && defined(CONFIG_SYSFS)
++extern struct device_attribute ve_net_class_attributes[];
++static inline int init_ve_netclass(void)
++{
++ struct class *nc;
++ int err;
++
++ nc = kzalloc(sizeof(*nc), GFP_KERNEL);
++ if (!nc)
++ return -ENOMEM;
++
++ nc->name = net_class.name;
++ nc->dev_release = net_class.dev_release;
++ nc->dev_uevent = net_class.dev_uevent;
++ nc->dev_attrs = ve_net_class_attributes;
++
++ err = class_register(nc);
++ if (!err) {
++ get_exec_env()->net_class = nc;
++ return 0;
++ }
++ kfree(nc);
++ return err;
++}
++
++static inline void fini_ve_netclass(void)
++{
++ struct ve_struct *ve = get_exec_env();
++
++ class_unregister(ve->net_class);
++ kfree(ve->net_class);
++ ve->net_class = NULL;
++}
++#else
++static inline int init_ve_netclass(void) { return 0; }
++static inline void fini_ve_netclass(void) { ; }
++#endif
++
++extern struct kset devices_subsys;
++
++static const struct {
++ unsigned minor;
++ char *name;
++} mem_class_devices [] = {
++ {3, "null"},
++ {5, "zero"},
++ {7, "full"},
++ {8, "random"},
++ {9, "urandom"},
++ {0, NULL},
++};
++
++static int init_ve_mem_class(void)
++{
++ int i;
++ struct class *ve_mem_class;
++
++ ve_mem_class = class_create(THIS_MODULE, "mem");
++ if (IS_ERR(ve_mem_class))
++ return -ENOMEM;
++
++ for (i = 0; mem_class_devices[i].name; i++)
++ device_create(ve_mem_class, NULL,
++ MKDEV(MEM_MAJOR, mem_class_devices[i].minor),
++ NULL, mem_class_devices[i].name);
++
++ get_exec_env()->mem_class = ve_mem_class;
++ return 0;
++}
++
++
++void fini_ve_mem_class(void)
++{
++ int i;
++ struct class *ve_mem_class = get_exec_env()->mem_class;
++
++ for (i = 0; mem_class_devices[i].name; i++)
++ device_destroy(ve_mem_class,
++ MKDEV(MEM_MAJOR, mem_class_devices[i].minor));
++ class_destroy(ve_mem_class);
++}
++
++static int init_ve_sysfs(struct ve_struct *ve)
++{
++ int err;
++
++#ifdef CONFIG_SYSFS
++ err = 0;
++ if (ve->features & VE_FEATURE_SYSFS) {
++ err = init_ve_sysfs_root(ve);
++ if (err != 0)
++ goto out;
++ err = register_ve_fs_type(ve,
++ &sysfs_fs_type,
++ &ve->sysfs_fstype,
++ &ve->sysfs_mnt);
++ if (err != 0)
++ goto out_fs_type;
++ }
++#endif
++
++ err = classes_init();
++ if (err != 0)
++ goto err_classes;
++
++ err = devices_init();
++ if (err != 0)
++ goto err_devices;
++
++ err = init_ve_netclass();
++ if (err != 0)
++ goto err_net;
++
++ err = init_ve_tty_class();
++ if (err != 0)
++ goto err_tty;
++
++ err = init_ve_mem_class();
++ if (err != 0)
++ goto err_mem;
++
++ return 0;
++
++err_mem:
++ fini_ve_tty_class();
++err_tty:
++ fini_ve_netclass();
++err_net:
++ devices_fini();
++err_devices:
++ classes_fini();
++err_classes:
++#ifdef CONFIG_SYSFS
++ unregister_ve_fs_type(ve->sysfs_fstype, ve->sysfs_mnt);
++ /* sysfs_fstype is freed in real_put_ve -> free_ve_filesystems */
++out_fs_type:
++ kfree(ve->_sysfs_root);
++ ve->_sysfs_root = NULL;
++out:
++#endif
++ return err;
++}
++
++static void fini_ve_sysfs(struct ve_struct *ve)
++{
++ fini_ve_mem_class();
++ fini_ve_tty_class();
++ fini_ve_netclass();
++ devices_fini();
++ classes_fini();
++#ifdef CONFIG_SYSFS
++ unregister_ve_fs_type(ve->sysfs_fstype, ve->sysfs_mnt);
++ ve->sysfs_mnt = NULL;
++ kfree(ve->_sysfs_root);
++ ve->_sysfs_root = NULL;
++ /* sysfs_fstype is freed in real_put_ve -> free_ve_filesystems */
++#endif
++}
++
++static void free_ve_filesystems(struct ve_struct *ve)
++{
++#ifdef CONFIG_SYSFS
++ kfree(ve->sysfs_fstype);
++ ve->sysfs_fstype = NULL;
++#endif
++ kfree(ve->shmem_fstype);
++ ve->shmem_fstype = NULL;
++
++ kfree(ve->devpts_fstype);
++ ve->devpts_fstype = NULL;
++
++ free_ve_proc(ve);
++}
++
++static int init_printk(struct ve_struct *ve)
++{
++ struct ve_prep_printk {
++ wait_queue_head_t log_wait;
++ unsigned log_start;
++ unsigned log_end;
++ unsigned logged_chars;
++ } *tmp;
++
++ tmp = kzalloc(sizeof(struct ve_prep_printk), GFP_KERNEL);
++ if (!tmp)
++ return -ENOMEM;
++
++ init_waitqueue_head(&tmp->log_wait);
++ ve->_log_wait = &tmp->log_wait;
++ ve->_log_start = &tmp->log_start;
++ ve->_log_end = &tmp->log_end;
++ ve->_logged_chars = &tmp->logged_chars;
++ /* ve->log_buf will be initialized later by ve_log_init() */
++ return 0;
++}
++
++static void fini_printk(struct ve_struct *ve)
++{
++ /*
++ * there is no spinlock protection here because nobody can use
++ * log_buf at the moments when this code is called.
++ */
++ kfree(ve->log_buf);
++ kfree(ve->_log_wait);
++}
++
++static void fini_venet(struct ve_struct *ve)
++{
++#ifdef CONFIG_INET
++ tcp_v4_kill_ve_sockets(ve);
++ synchronize_net();
++#endif
++}
++
++static int init_ve_sched(struct ve_struct *ve)
++{
++#ifdef CONFIG_VZ_FAIRSCHED
++ int err;
++
++ /*
++ * We refuse to switch to an already existing node since nodes
++ * keep a pointer to their ve_struct...
++ */
++ err = sys_fairsched_mknod(0, 1, ve->veid);
++ if (err < 0) {
++ printk(KERN_WARNING "Can't create fairsched node %d\n",
++ ve->veid);
++ return err;
++ }
++ err = sys_fairsched_mvpr(current->pid, ve->veid);
++ if (err) {
++ printk(KERN_WARNING "Can't switch to fairsched node %d\n",
++ ve->veid);
++ if (sys_fairsched_rmnod(ve->veid))
++ printk(KERN_ERR "Can't clean fairsched node %d\n",
++ ve->veid);
++ return err;
++ }
++#endif
++ ve_sched_attach(ve);
++ return 0;
++}
++
++static void fini_ve_sched(struct ve_struct *ve)
++{
++#ifdef CONFIG_VZ_FAIRSCHED
++ if (task_fairsched_node_id(current) == ve->veid)
++ if (sys_fairsched_mvpr(current->pid, FAIRSCHED_INIT_NODE_ID))
++ printk(KERN_WARNING "Can't leave fairsched node %d\n",
++ ve->veid);
++ if (sys_fairsched_rmnod(ve->veid))
++ printk(KERN_ERR "Can't remove fairsched node %d\n",
++ ve->veid);
++#endif
++}
++
++/*
++ * Namespaces
++ */
++
++static inline int init_ve_namespaces(struct ve_struct *ve,
++ struct nsproxy **old)
++{
++ int err;
++ struct task_struct *tsk;
++ struct nsproxy *cur;
++
++ tsk = current;
++ cur = tsk->nsproxy;
++
++ err = copy_namespaces(CLONE_NAMESPACES_MASK & ~CLONE_NEWNET, tsk);
++ if (err < 0)
++ return err;
++
++ ve->ve_ns = get_nsproxy(tsk->nsproxy);
++ memcpy(ve->ve_ns->uts_ns->name.release, virt_utsname.release,
++ sizeof(virt_utsname.release));
++
++ if (cur->pid_ns->flags & PID_NS_HIDE_CHILD)
++ ve->ve_ns->pid_ns->flags |= PID_NS_HIDDEN;
++
++ *old = cur;
++ return 0;
++}
++
++static inline void fini_ve_namespaces(struct ve_struct *ve,
++ struct nsproxy *old)
++{
++ struct task_struct *tsk = current;
++ struct nsproxy *tmp;
++
++ if (old) {
++ tmp = tsk->nsproxy;
++ tsk->nsproxy = get_nsproxy(old);
++ put_nsproxy(tmp);
++ tmp = ve->ve_ns;
++ ve->ve_ns = get_nsproxy(old);
++ put_nsproxy(tmp);
++ } else {
++ put_nsproxy(ve->ve_ns);
++ ve->ve_ns = NULL;
++ }
++}
++
++static int init_ve_netns(struct ve_struct *ve, struct nsproxy **old)
++{
++ int err;
++ struct task_struct *tsk;
++ struct nsproxy *cur;
++
++ tsk = current;
++ cur = tsk->nsproxy;
++
++ err = copy_namespaces(CLONE_NEWNET, tsk);
++ if (err < 0)
++ return err;
++
++ put_nsproxy(ve->ve_ns);
++ ve->ve_ns = get_nsproxy(tsk->nsproxy);
++ ve->ve_netns = get_net(ve->ve_ns->net_ns);
++ *old = cur;
++ return 0;
++}
++
++static inline void switch_ve_namespaces(struct ve_struct *ve,
++ struct task_struct *tsk)
++{
++ struct nsproxy *old_ns;
++ struct nsproxy *new_ns;
++
++ BUG_ON(tsk != current);
++ old_ns = tsk->nsproxy;
++ new_ns = ve->ve_ns;
++
++ if (old_ns != new_ns) {
++ tsk->nsproxy = get_nsproxy(new_ns);
++ put_nsproxy(old_ns);
++ }
++}
++
++static __u64 get_ve_features(env_create_param_t *data, int datalen)
++{
++ __u64 known_features;
++
++ if (datalen < sizeof(struct env_create_param3))
++ /* this version of vzctl is aware of VE_FEATURES_OLD only */
++ known_features = VE_FEATURES_OLD;
++ else
++ known_features = data->known_features;
++
++ /*
++ * known features are set as required
++ * yet unknown features are set as in VE_FEATURES_DEF
++ */
++ return (data->feature_mask & known_features) |
++ (VE_FEATURES_DEF & ~known_features);
++}
++
++static int init_ve_struct(struct ve_struct *ve, envid_t veid,
++ u32 class_id, env_create_param_t *data, int datalen)
++{
++ (void)get_ve(ve);
++ ve->veid = veid;
++ ve->class_id = class_id;
++ ve->features = get_ve_features(data, datalen);
++ INIT_LIST_HEAD(&ve->vetask_lh);
++ init_rwsem(&ve->op_sem);
++
++ ve->start_timespec = current->start_time;
++ /* The value is wrong, but it is never compared to process
++ * start times */
++ ve->start_jiffies = get_jiffies_64();
++ ve->start_cycles = get_cycles();
++
++ return 0;
++}
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * /proc/meminfo virtualization
++ *
++ **********************************************************************
++ **********************************************************************/
++static int ve_set_meminfo(envid_t veid, unsigned long val)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ struct ve_struct *ve;
++
++ ve = get_ve_by_id(veid);
++ if (!ve)
++ return -EINVAL;
++
++ ve->meminfo_val = val;
++ real_put_ve(ve);
++ return 0;
++#else
++ return -ENOTTY;
++#endif
++}
++
++static int init_ve_meminfo(struct ve_struct *ve)
++{
++ ve->meminfo_val = 0;
++ return 0;
++}
++
++static inline void fini_ve_meminfo(struct ve_struct *ve)
++{
++}
++
++static void set_ve_root(struct ve_struct *ve, struct task_struct *tsk)
++{
++ read_lock(&tsk->fs->lock);
++ ve->root_path = tsk->fs->root;
++ read_unlock(&tsk->fs->lock);
++ mark_tree_virtual(&ve->root_path);
++}
++
++static void set_ve_caps(struct ve_struct *ve, struct task_struct *tsk)
++{
++ /* required for real_setdevperms from register_ve_<fs> above */
++ memcpy(&ve->ve_cap_bset, &tsk->cap_effective, sizeof(kernel_cap_t));
++ cap_lower(ve->ve_cap_bset, CAP_SETVEID);
++}
++
++static int ve_list_add(struct ve_struct *ve)
++{
++ write_lock_irq(&ve_list_lock);
++ if (__find_ve_by_id(ve->veid) != NULL)
++ goto err_exists;
++
++ list_add(&ve->ve_list, &ve_list_head);
++ nr_ve++;
++ write_unlock_irq(&ve_list_lock);
++ return 0;
++
++err_exists:
++ write_unlock_irq(&ve_list_lock);
++ return -EEXIST;
++}
++
++static void ve_list_del(struct ve_struct *ve)
++{
++ write_lock_irq(&ve_list_lock);
++ list_del(&ve->ve_list);
++ nr_ve--;
++ write_unlock_irq(&ve_list_lock);
++}
++
++static void set_task_ve_caps(struct task_struct *tsk, struct ve_struct *ve)
++{
++ kernel_cap_t bset;
++
++ spin_lock(&task_capability_lock);
++ bset = ve->ve_cap_bset;
++ tsk->cap_effective = cap_intersect(tsk->cap_effective, bset);
++ tsk->cap_inheritable = cap_intersect(tsk->cap_inheritable, bset);
++ tsk->cap_permitted = cap_intersect(tsk->cap_permitted, bset);
++ spin_unlock(&task_capability_lock);
++}
++
++void ve_move_task(struct task_struct *tsk, struct ve_struct *new)
++{
++ struct ve_struct *old;
++
++ might_sleep();
++ BUG_ON(tsk != current);
++ BUG_ON(!(thread_group_leader(tsk) && thread_group_empty(tsk)));
++
++ /* this probihibts ptracing of task entered to VE from host system */
++ tsk->mm->vps_dumpable = 0;
++ /* setup capabilities before enter */
++ set_task_ve_caps(tsk, new);
++
++ old = tsk->ve_task_info.owner_env;
++ tsk->ve_task_info.owner_env = new;
++ tsk->ve_task_info.exec_env = new;
++
++ write_lock_irq(&tasklist_lock);
++ list_del_rcu(&tsk->ve_task_info.vetask_list);
++ write_unlock_irq(&tasklist_lock);
++
++ synchronize_rcu();
++
++ write_lock_irq(&tasklist_lock);
++ list_add_tail_rcu(&tsk->ve_task_info.vetask_list,
++ &new->vetask_lh);
++ write_unlock_irq(&tasklist_lock);
++
++ atomic_dec(&old->pcounter);
++ real_put_ve(old);
++
++ atomic_inc(&new->pcounter);
++ get_ve(new);
++
++ tsk->cgroups = new->ve_css_set;
++}
++
++EXPORT_SYMBOL(ve_move_task);
++
++#ifdef CONFIG_VE_IPTABLES
++
++#define KSYMIPTINIT(mask, ve, full_mask, mod, name, args) \
++({ \
++ int ret = 0; \
++ if (VE_IPT_CMP(mask, full_mask) && \
++ VE_IPT_CMP((ve)->_iptables_modules, \
++ full_mask & ~(full_mask##_MOD))) { \
++ ret = KSYMERRCALL(1, mod, name, args); \
++ if (ret == 0) \
++ (ve)->_iptables_modules |= \
++ full_mask##_MOD; \
++ if (ret == 1) \
++ ret = 0; \
++ } \
++ ret; \
++})
++
++#define KSYMIPTFINI(mask, full_mask, mod, name, args) \
++({ \
++ if (VE_IPT_CMP(mask, full_mask##_MOD)) \
++ KSYMSAFECALL_VOID(mod, name, args); \
++})
++
++
++static int do_ve_iptables(struct ve_struct *ve, __u64 init_mask,
++ int init_or_cleanup)
++{
++ int err;
++
++ /* Remove when userspace will start supplying IPv6-related bits. */
++ init_mask &= ~VE_IP_IPTABLES6;
++ init_mask &= ~VE_IP_FILTER6;
++ init_mask &= ~VE_IP_MANGLE6;
++ init_mask &= ~VE_IP_IPTABLE_NAT_MOD;
++ init_mask &= ~VE_NF_CONNTRACK_MOD;
++ if ((init_mask & VE_IP_IPTABLES) == VE_IP_IPTABLES)
++ init_mask |= VE_IP_IPTABLES6;
++ if ((init_mask & VE_IP_FILTER) == VE_IP_FILTER)
++ init_mask |= VE_IP_FILTER6;
++ if ((init_mask & VE_IP_MANGLE) == VE_IP_MANGLE)
++ init_mask |= VE_IP_MANGLE6;
++ if ((init_mask & VE_IP_NAT) == VE_IP_NAT)
++ init_mask |= VE_IP_IPTABLE_NAT;
++
++ if ((init_mask & VE_IP_CONNTRACK) == VE_IP_CONNTRACK)
++ init_mask |= VE_NF_CONNTRACK;
++
++ err = 0;
++ if (!init_or_cleanup)
++ goto cleanup;
++
++ /* init part */
++#if defined(CONFIG_NF_CONNTRACK_IPV4) || \
++ defined(CONFIG_NF_CONNTRACK_IPV4_MODULE)
++ err = KSYMIPTINIT(init_mask, ve, VE_NF_CONNTRACK,
++ nf_conntrack, nf_conntrack_init_ve, ());
++ if (err < 0)
++ goto err_nf_conntrack;
++
++ err = KSYMIPTINIT(init_mask, ve, VE_IP_CONNTRACK,
++ nf_conntrack_ipv4, init_nf_ct_l3proto_ipv4, ());
++ if (err < 0)
++ goto err_nf_conntrack_ipv4;
++#endif
++#if defined(CONFIG_NF_NAT) || \
++ defined(CONFIG_NF_NAT_MODULE)
++ err = KSYMIPTINIT(init_mask, ve, VE_IP_NAT,
++ nf_nat, nf_nat_init, ());
++ if (err < 0)
++ goto err_nftable_nat;
++ err = KSYMIPTINIT(init_mask, ve, VE_IP_IPTABLE_NAT,
++ iptable_nat, init_nftable_nat, ());
++ if (err < 0)
++ goto err_nftable_nat2;
++#endif
++ return 0;
++
++/* ------------------------------------------------------------------------- */
++
++cleanup:
++#if defined(CONFIG_NF_NAT) || \
++ defined(CONFIG_NF_NAT_MODULE)
++ KSYMIPTFINI(ve->_iptables_modules, VE_IP_IPTABLE_NAT,
++ iptable_nat, fini_nftable_nat, ());
++err_nftable_nat2:
++ KSYMIPTFINI(ve->_iptables_modules, VE_IP_NAT,
++ nf_nat, nf_nat_cleanup, ());
++err_nftable_nat:
++#endif
++#if defined(CONFIG_NF_CONNTRACK_IPV4) || \
++ defined(CONFIG_NF_CONNTRACK_IPV4_MODULE)
++ KSYMIPTFINI(ve->_iptables_modules, VE_IP_CONNTRACK,
++ nf_conntrack_ipv4, fini_nf_ct_l3proto_ipv4, ());
++err_nf_conntrack_ipv4:
++ KSYMIPTFINI(ve->_iptables_modules, VE_NF_CONNTRACK,
++ nf_conntrack, nf_conntrack_cleanup_ve, ());
++err_nf_conntrack:
++#endif
++ /* Do not reset _iptables_modules as
++ * net hooks used one
++ */
++ return err;
++}
++
++static inline int init_ve_iptables(struct ve_struct *ve, __u64 init_mask)
++{
++ return do_ve_iptables(ve, init_mask, 1);
++}
++
++static inline void fini_ve_iptables(struct ve_struct *ve, __u64 init_mask)
++{
++ (void)do_ve_iptables(ve, init_mask, 0);
++}
++
++#else
++#define init_ve_iptables(x, y) (0)
++#define fini_ve_iptables(x, y) do { } while (0)
++#endif
++
++static inline int init_ve_cpustats(struct ve_struct *ve)
++{
++ ve->cpu_stats = alloc_percpu(struct ve_cpu_stats);
++ return ve->cpu_stats == NULL ? -ENOMEM : 0;
++}
++
++static inline void free_ve_cpustats(struct ve_struct *ve)
++{
++ free_percpu(ve->cpu_stats);
++ ve->cpu_stats = NULL;
++}
++
++static int alone_in_pgrp(struct task_struct *tsk)
++{
++ struct task_struct *p;
++ int alone = 0;
++
++ read_lock(&tasklist_lock);
++ do_each_pid_task(task_pid(tsk), PIDTYPE_PGID, p) {
++ if (p != tsk)
++ goto out;
++ } while_each_pid_task(task_pid(tsk), PIDTYPE_PGID, p);
++ do_each_pid_task(task_pid(tsk), PIDTYPE_SID, p) {
++ if (p != tsk)
++ goto out;
++ } while_each_pid_task(task_pid(tsk), PIDTYPE_SID, p);
++ alone = 1;
++out:
++ read_unlock(&tasklist_lock);
++ return alone;
++}
++
++static int do_env_create(envid_t veid, unsigned int flags, u32 class_id,
++ env_create_param_t *data, int datalen)
++{
++ struct task_struct *tsk;
++ struct ve_struct *old;
++ struct ve_struct *old_exec;
++ struct ve_struct *ve;
++ __u64 init_mask;
++ int err;
++ struct nsproxy *old_ns, *old_ns_net;
++ DECLARE_COMPLETION_ONSTACK(sysfs_completion);
++
++ tsk = current;
++ old = VE_TASK_INFO(tsk)->owner_env;
++
++ if (!thread_group_leader(tsk) || !thread_group_empty(tsk))
++ return -EINVAL;
++
++ if (tsk->signal->tty) {
++ printk("ERR: CT init has controlling terminal\n");
++ return -EINVAL;
++ }
++ if (task_pgrp(tsk) != task_pid(tsk) ||
++ task_session(tsk) != task_pid(tsk)) {
++ int may_setsid;
++
++ read_lock(&tasklist_lock);
++ may_setsid = !tsk->signal->leader &&
++ !find_task_by_pid_type_ns(PIDTYPE_PGID, task_pid_nr(tsk), &init_pid_ns);
++ read_unlock(&tasklist_lock);
++
++ if (!may_setsid) {
++ printk("ERR: CT init is process group leader\n");
++ return -EINVAL;
++ }
++ }
++ /* Check that the process is not a leader of non-empty group/session.
++ * If it is, we cannot virtualize its PID and must fail. */
++ if (!alone_in_pgrp(tsk)) {
++ printk("ERR: CT init is not alone in process group\n");
++ return -EINVAL;
++ }
++
++
++ VZTRACE("%s: veid=%d classid=%d pid=%d\n",
++ __FUNCTION__, veid, class_id, current->pid);
++
++ err = -ENOMEM;
++ ve = kzalloc(sizeof(struct ve_struct), GFP_KERNEL);
++ if (ve == NULL)
++ goto err_struct;
++
++ init_ve_struct(ve, veid, class_id, data, datalen);
++ __module_get(THIS_MODULE);
++ down_write(&ve->op_sem);
++ if (flags & VE_LOCK)
++ ve->is_locked = 1;
++
++ /*
++ * this should be done before adding to list
++ * because if calc_load_ve finds this ve in
++ * list it will be very surprised
++ */
++ if ((err = init_ve_cpustats(ve)) < 0)
++ goto err_cpu_stats;
++
++ if ((err = ve_list_add(ve)) < 0)
++ goto err_exist;
++
++ /* this should be done before context switching */
++ if ((err = init_printk(ve)) < 0)
++ goto err_log_wait;
++
++ old_exec = set_exec_env(ve);
++
++ if ((err = init_ve_sched(ve)) < 0)
++ goto err_sched;
++
++ set_ve_root(ve, tsk);
++
++ if ((err = init_ve_sysfs(ve)))
++ goto err_sysfs;
++
++ if ((err = init_ve_mibs(ve)))
++ goto err_mibs;
++
++ if ((err = init_ve_namespaces(ve, &old_ns)))
++ goto err_ns;
++
++ if ((err = init_ve_proc(ve)))
++ goto err_proc;
++
++
++ init_mask = data ? data->iptables_mask : VE_IP_DEFAULT;
++
++#ifdef CONFIG_VE_IPTABLES
++ /* Set up ipt_mask as it will be used during
++ * net namespace initialization
++ */
++ ve->ipt_mask = init_mask;
++#endif
++
++ if ((err = init_ve_netns(ve, &old_ns_net)))
++ goto err_netns;
++
++ if ((err = init_ve_cgroups(ve)))
++ goto err_cgroup;
++
++ if ((err = init_ve_tty_drivers(ve)) < 0)
++ goto err_tty;
++
++ if ((err = init_ve_shmem(ve)))
++ goto err_shmem;
++
++ if ((err = init_ve_devpts(ve)))
++ goto err_devpts;
++
++ if((err = init_ve_meminfo(ve)))
++ goto err_meminf;
++
++ set_ve_caps(ve, tsk);
++
++ /* It is safe to initialize netfilter here as routing initialization and
++ interface setup will be done below. This means that NO skb can be
++ passed inside. Den */
++ /* iptables ve initialization for non ve0;
++ ve0 init is in module_init */
++
++ if ((err = init_ve_iptables(ve, init_mask)) < 0)
++ goto err_iptables;
++
++ if ((err = pid_ns_attach_init(ve->ve_ns->pid_ns, tsk)) < 0)
++ goto err_vpid;
++
++ if ((err = ve_hook_iterate_init(VE_SS_CHAIN, ve)) < 0)
++ goto err_ve_hook;
++
++ put_nsproxy(old_ns);
++ put_nsproxy(old_ns_net);
++
++ /* finally: set vpids and move inside */
++ ve_move_task(tsk, ve);
++
++ ve->is_running = 1;
++ up_write(&ve->op_sem);
++
++ printk(KERN_INFO "CT: %d: started\n", veid);
++ return veid;
++
++err_ve_hook:
++ mntget(ve->proc_mnt);
++err_vpid:
++ fini_venet(ve);
++ fini_ve_iptables(ve, init_mask);
++err_iptables:
++ fini_ve_meminfo(ve);
++err_meminf:
++ fini_ve_devpts(ve);
++err_devpts:
++ fini_ve_shmem(ve);
++err_shmem:
++ fini_ve_tty_drivers(ve);
++err_tty:
++ fini_ve_cgroups(ve);
++err_cgroup:
++ fini_ve_namespaces(ve, old_ns_net);
++ put_nsproxy(old_ns_net);
++ ve->ve_netns->sysfs_completion = &sysfs_completion;
++ put_net(ve->ve_netns);
++ wait_for_completion(&sysfs_completion);
++err_netns:
++ /*
++ * If process hasn't become VE's init, proc_mnt won't be put during
++ * pidns death, so this mntput by hand is needed. If it has, we
++ * compensate with mntget above.
++ */
++ mntput(ve->proc_mnt);
++ fini_ve_proc(ve);
++err_proc:
++ /* free_ve_utsname() is called inside real_put_ve() */
++ fini_ve_namespaces(ve, old_ns);
++ put_nsproxy(old_ns);
++ /*
++ * We need to compensate, because fini_ve_namespaces() assumes
++ * ve->ve_ns will continue to be used after, but VE will be freed soon
++ * (in kfree() sense).
++ */
++ put_nsproxy(ve->ve_ns);
++err_ns:
++ fini_ve_mibs(ve);
++err_mibs:
++ fini_ve_sysfs(ve);
++err_sysfs:
++ /* It is safe to restore current->envid here because
++ * ve_fairsched_detach does not use current->envid. */
++ /* Really fairsched code uses current->envid in sys_fairsched_mknod
++ * only. It is correct if sys_fairsched_mknod is called from
++ * userspace. If sys_fairsched_mknod is called from
++ * ve_fairsched_attach, then node->envid and node->parent_node->envid
++ * are explicitly set to valid value after the call. */
++ /* FIXME */
++ VE_TASK_INFO(tsk)->owner_env = old;
++ VE_TASK_INFO(tsk)->exec_env = old_exec;
++
++ fini_ve_sched(ve);
++err_sched:
++ (void)set_exec_env(old_exec);
++
++ /* we can jump here having incorrect envid */
++ VE_TASK_INFO(tsk)->owner_env = old;
++ fini_printk(ve);
++err_log_wait:
++ /* cpustats will be freed in do_env_free */
++ ve_list_del(ve);
++ up_write(&ve->op_sem);
++
++ real_put_ve(ve);
++err_struct:
++ printk(KERN_INFO "CT: %d: failed to start with err=%d\n", veid, err);
++ return err;
++
++err_exist:
++ free_ve_cpustats(ve);
++err_cpu_stats:
++ kfree(ve);
++ goto err_struct;
++}
++
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * VE start/stop callbacks
++ *
++ **********************************************************************
++ **********************************************************************/
++
++int real_env_create(envid_t veid, unsigned flags, u32 class_id,
++ env_create_param_t *data, int datalen)
++{
++ int status;
++ struct ve_struct *ve;
++
++ if (!flags) {
++ status = get_exec_env()->veid;
++ goto out;
++ }
++
++ status = -EPERM;
++ if (!capable(CAP_SETVEID))
++ goto out;
++
++ status = -EINVAL;
++ if ((flags & VE_TEST) && (flags & (VE_ENTER|VE_CREATE)))
++ goto out;
++
++ status = -EINVAL;
++ ve = get_ve_by_id(veid);
++ if (ve) {
++ if (flags & VE_TEST) {
++ status = 0;
++ goto out_put;
++ }
++ if (flags & VE_EXCLUSIVE) {
++ status = -EACCES;
++ goto out_put;
++ }
++ if (flags & VE_CREATE) {
++ flags &= ~VE_CREATE;
++ flags |= VE_ENTER;
++ }
++ } else {
++ if (flags & (VE_TEST|VE_ENTER)) {
++ status = -ESRCH;
++ goto out;
++ }
++ }
++
++ if (flags & VE_CREATE) {
++ status = do_env_create(veid, flags, class_id, data, datalen);
++ goto out;
++ } else if (flags & VE_ENTER)
++ status = do_env_enter(ve, flags);
++
++ /* else: returning EINVAL */
++
++out_put:
++ real_put_ve(ve);
++out:
++ return status;
++}
++EXPORT_SYMBOL(real_env_create);
++
++static int do_env_enter(struct ve_struct *ve, unsigned int flags)
++{
++ struct task_struct *tsk = current;
++ int err;
++
++ VZTRACE("%s: veid=%d\n", __FUNCTION__, ve->veid);
++
++ err = -EBUSY;
++ down_read(&ve->op_sem);
++ if (!ve->is_running)
++ goto out_up;
++ if (ve->is_locked && !(flags & VE_SKIPLOCK))
++ goto out_up;
++ err = -EINVAL;
++ if (!thread_group_leader(tsk) || !thread_group_empty(tsk))
++ goto out_up;
++
++#ifdef CONFIG_VZ_FAIRSCHED
++ err = sys_fairsched_mvpr(current->pid, ve->veid);
++ if (err)
++ goto out_up;
++#endif
++ ve_sched_attach(ve);
++ switch_ve_namespaces(ve, tsk);
++ ve_move_task(current, ve);
++
++ /* Check that the process is not a leader of non-empty group/session.
++ * If it is, we cannot virtualize its PID. Do not fail, just leave
++ * it non-virtual.
++ */
++ if (alone_in_pgrp(tsk) && !(flags & VE_SKIPLOCK))
++ pid_ns_attach_task(ve->ve_ns->pid_ns, tsk);
++
++ /* Unlike VE_CREATE, we do not setsid() in VE_ENTER.
++ * Process is allowed to be in an external group/session.
++ * If user space callers wants, it will do setsid() after
++ * VE_ENTER.
++ */
++ err = VE_TASK_INFO(tsk)->owner_env->veid;
++ tsk->did_ve_enter = 1;
++
++out_up:
++ up_read(&ve->op_sem);
++ return err;
++}
++
++static void env_cleanup(struct ve_struct *ve)
++{
++ struct ve_struct *old_ve;
++ DECLARE_COMPLETION_ONSTACK(sysfs_completion);
++
++ VZTRACE("real_do_env_cleanup\n");
++
++ down_read(&ve->op_sem);
++ old_ve = set_exec_env(ve);
++
++ ve_hook_iterate_fini(VE_SS_CHAIN, ve);
++
++ fini_venet(ve);
++
++ /* no new packets in flight beyond this point */
++
++ /* kill iptables */
++ /* No skb belonging to VE can exist at this point as unregister_netdev
++ is an operation awaiting until ALL skb's gone */
++ fini_ve_iptables(ve, ve->_iptables_modules);
++
++ fini_ve_sched(ve);
++
++ fini_ve_devpts(ve);
++ fini_ve_shmem(ve);
++ unregister_ve_tty_drivers(ve);
++ fini_ve_meminfo(ve);
++
++ fini_ve_cgroups(ve);
++
++ fini_ve_namespaces(ve, NULL);
++ ve->ve_netns->sysfs_completion = &sysfs_completion;
++ put_net(ve->ve_netns);
++ wait_for_completion(&sysfs_completion);
++ fini_ve_mibs(ve);
++ fini_ve_proc(ve);
++ fini_ve_sysfs(ve);
++
++ (void)set_exec_env(old_ve);
++ fini_printk(ve); /* no printk can happen in ve context anymore */
++
++ ve_list_del(ve);
++ up_read(&ve->op_sem);
++
++ real_put_ve(ve);
++}
++
++static DECLARE_COMPLETION(vzmond_complete);
++static volatile int stop_vzmond;
++
++static int vzmond_helper(void *arg)
++{
++ char name[18];
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)arg;
++ snprintf(name, sizeof(name), "vzmond/%d", ve->veid);
++ daemonize(name);
++ env_cleanup(ve);
++ module_put_and_exit(0);
++}
++
++static void do_pending_env_cleanups(void)
++{
++ int err;
++ struct ve_struct *ve;
++
++ spin_lock(&ve_cleanup_lock);
++ while (1) {
++ if (list_empty(&ve_cleanup_list) || need_resched())
++ break;
++
++ ve = list_first_entry(&ve_cleanup_list,
++ struct ve_struct, cleanup_list);
++ list_del(&ve->cleanup_list);
++ spin_unlock(&ve_cleanup_lock);
++
++ __module_get(THIS_MODULE);
++ err = kernel_thread(vzmond_helper, (void *)ve, 0);
++ if (err < 0) {
++ env_cleanup(ve);
++ module_put(THIS_MODULE);
++ }
++
++ spin_lock(&ve_cleanup_lock);
++ }
++ spin_unlock(&ve_cleanup_lock);
++}
++
++static inline int have_pending_cleanups(void)
++{
++ return !list_empty(&ve_cleanup_list);
++}
++
++static int vzmond(void *arg)
++{
++ daemonize("vzmond");
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ while (!stop_vzmond || have_pending_cleanups()) {
++ schedule();
++ try_to_freeze();
++ if (signal_pending(current))
++ flush_signals(current);
++
++ do_pending_env_cleanups();
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (have_pending_cleanups())
++ __set_current_state(TASK_RUNNING);
++ }
++
++ __set_task_state(current, TASK_RUNNING);
++ complete_and_exit(&vzmond_complete, 0);
++}
++
++static int __init init_vzmond(void)
++{
++ int pid;
++ struct task_struct *tsk;
++
++ pid = kernel_thread(vzmond, NULL, 0);
++ if (pid > 0) {
++ tsk = find_task_by_vpid(pid);
++ BUG_ON(tsk == NULL);
++ ve_cleanup_thread = tsk;
++ }
++ return pid;
++}
++
++static void fini_vzmond(void)
++{
++ stop_vzmond = 1;
++ wake_up_process(ve_cleanup_thread);
++ wait_for_completion(&vzmond_complete);
++ ve_cleanup_thread = NULL;
++ WARN_ON(!list_empty(&ve_cleanup_list));
++}
++
++void real_do_env_free(struct ve_struct *ve)
++{
++ VZTRACE("real_do_env_free\n");
++
++ free_ve_tty_drivers(ve);
++ free_ve_filesystems(ve);
++ free_ve_cpustats(ve);
++ printk(KERN_INFO "CT: %d: stopped\n", VEID(ve));
++ kfree(ve);
++
++ module_put(THIS_MODULE);
++}
++EXPORT_SYMBOL(real_do_env_free);
++
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * VE TTY handling
++ *
++ **********************************************************************
++ **********************************************************************/
++
++static struct tty_driver *alloc_ve_tty_driver(struct tty_driver *base,
++ struct ve_struct *ve)
++{
++ size_t size;
++ struct tty_driver *driver;
++
++ /* FIXME: make it a normal way (or wait till ms version) */
++
++ driver = kmalloc(sizeof(struct tty_driver), GFP_KERNEL_UBC);
++ if (!driver)
++ goto out;
++
++ memcpy(driver, base, sizeof(struct tty_driver));
++
++ driver->driver_state = NULL;
++
++ size = base->num * 3 * sizeof(void *);
++ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
++ void **p;
++ p = kzalloc(size, GFP_KERNEL_UBC);
++ if (!p)
++ goto out_free;
++
++ driver->ttys = (struct tty_struct **)p;
++ driver->termios = (struct ktermios **)(p + driver->num);
++ driver->termios_locked = (struct ktermios **)
++ (p + driver->num * 2);
++ } else {
++ driver->ttys = NULL;
++ driver->termios = NULL;
++ driver->termios_locked = NULL;
++ }
++
++ driver->owner_env = ve;
++ driver->flags |= TTY_DRIVER_INSTALLED;
++ driver->refcount = 0;
++
++ return driver;
++
++out_free:
++ kfree(driver);
++out:
++ return NULL;
++}
++
++static void free_ve_tty_driver(struct tty_driver *driver)
++{
++ if (!driver)
++ return;
++
++ clear_termios(driver);
++ kfree(driver->ttys);
++ kfree(driver);
++}
++
++static int alloc_ve_tty_drivers(struct ve_struct* ve)
++{
++#ifdef CONFIG_LEGACY_PTYS
++ /* Traditional BSD devices */
++ ve->pty_driver = alloc_ve_tty_driver(pty_driver, ve);
++ if (!ve->pty_driver)
++ goto out_mem;
++
++ ve->pty_slave_driver = alloc_ve_tty_driver(pty_slave_driver, ve);
++ if (!ve->pty_slave_driver)
++ goto out_mem;
++
++ ve->pty_driver->other = ve->pty_slave_driver;
++ ve->pty_slave_driver->other = ve->pty_driver;
++#endif
++
++#ifdef CONFIG_UNIX98_PTYS
++ ve->ptm_driver = alloc_ve_tty_driver(ptm_driver, ve);
++ if (!ve->ptm_driver)
++ goto out_mem;
++
++ ve->pts_driver = alloc_ve_tty_driver(pts_driver, ve);
++ if (!ve->pts_driver)
++ goto out_mem;
++
++ ve->ptm_driver->other = ve->pts_driver;
++ ve->pts_driver->other = ve->ptm_driver;
++
++ ve->allocated_ptys = kmalloc(sizeof(*ve->allocated_ptys),
++ GFP_KERNEL_UBC);
++ if (!ve->allocated_ptys)
++ goto out_mem;
++ ida_init(ve->allocated_ptys);
++#endif
++ return 0;
++
++out_mem:
++ free_ve_tty_drivers(ve);
++ return -ENOMEM;
++}
++
++static void free_ve_tty_drivers(struct ve_struct* ve)
++{
++#ifdef CONFIG_LEGACY_PTYS
++ free_ve_tty_driver(ve->pty_driver);
++ free_ve_tty_driver(ve->pty_slave_driver);
++ ve->pty_driver = ve->pty_slave_driver = NULL;
++#endif
++#ifdef CONFIG_UNIX98_PTYS
++ free_ve_tty_driver(ve->ptm_driver);
++ free_ve_tty_driver(ve->pts_driver);
++ kfree(ve->allocated_ptys);
++ ve->ptm_driver = ve->pts_driver = NULL;
++ ve->allocated_ptys = NULL;
++#endif
++}
++
++static inline void __register_tty_driver(struct tty_driver *driver)
++{
++ list_add(&driver->tty_drivers, &tty_drivers);
++}
++
++static inline void __unregister_tty_driver(struct tty_driver *driver)
++{
++ if (!driver)
++ return;
++ list_del(&driver->tty_drivers);
++}
++
++static int register_ve_tty_drivers(struct ve_struct* ve)
++{
++ mutex_lock(&tty_mutex);
++#ifdef CONFIG_UNIX98_PTYS
++ __register_tty_driver(ve->ptm_driver);
++ __register_tty_driver(ve->pts_driver);
++#endif
++#ifdef CONFIG_LEGACY_PTYS
++ __register_tty_driver(ve->pty_driver);
++ __register_tty_driver(ve->pty_slave_driver);
++#endif
++ mutex_unlock(&tty_mutex);
++
++ return 0;
++}
++
++static void unregister_ve_tty_drivers(struct ve_struct* ve)
++{
++ VZTRACE("unregister_ve_tty_drivers\n");
++
++ mutex_lock(&tty_mutex);
++#ifdef CONFIG_LEGACY_PTYS
++ __unregister_tty_driver(ve->pty_driver);
++ __unregister_tty_driver(ve->pty_slave_driver);
++#endif
++#ifdef CONFIG_UNIX98_PTYS
++ __unregister_tty_driver(ve->ptm_driver);
++ __unregister_tty_driver(ve->pts_driver);
++#endif
++ mutex_unlock(&tty_mutex);
++}
++
++static int init_ve_tty_drivers(struct ve_struct *ve)
++{
++ int err;
++
++ if ((err = alloc_ve_tty_drivers(ve)))
++ goto err_ttyalloc;
++ if ((err = register_ve_tty_drivers(ve)))
++ goto err_ttyreg;
++ return 0;
++
++err_ttyreg:
++ free_ve_tty_drivers(ve);
++err_ttyalloc:
++ return err;
++}
++
++static void fini_ve_tty_drivers(struct ve_struct *ve)
++{
++ unregister_ve_tty_drivers(ve);
++ free_ve_tty_drivers(ve);
++}
++
++/*
++ * Free the termios and termios_locked structures because
++ * we don't want to get memory leaks when modular tty
++ * drivers are removed from the kernel.
++ */
++static void clear_termios(struct tty_driver *driver)
++{
++ int i;
++ struct ktermios *tp;
++
++ if (driver->termios == NULL)
++ return;
++ for (i = 0; i < driver->num; i++) {
++ tp = driver->termios[i];
++ if (tp) {
++ driver->termios[i] = NULL;
++ kfree(tp);
++ }
++ tp = driver->termios_locked[i];
++ if (tp) {
++ driver->termios_locked[i] = NULL;
++ kfree(tp);
++ }
++ }
++}
++
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * Pieces of VE network
++ *
++ **********************************************************************
++ **********************************************************************/
++
++#ifdef CONFIG_NET
++#include <asm/uaccess.h>
++#include <net/sock.h>
++#include <linux/netlink.h>
++#include <linux/rtnetlink.h>
++#include <net/route.h>
++#include <net/ip_fib.h>
++#endif
++
++static int ve_dev_add(envid_t veid, char *dev_name)
++{
++ struct net_device *dev;
++ struct ve_struct *dst_ve;
++ struct net *dst_net;
++ int err = -ESRCH;
++
++ dst_ve = get_ve_by_id(veid);
++ if (dst_ve == NULL)
++ goto out;
++
++ dst_net = dst_ve->ve_netns;
++
++ rtnl_lock();
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_name(&init_net, dev_name);
++ read_unlock(&dev_base_lock);
++ if (dev == NULL)
++ goto out_unlock;
++
++ err = __dev_change_net_namespace(dev, dst_net, dev_name, get_exec_ub());
++out_unlock:
++ rtnl_unlock();
++ real_put_ve(dst_ve);
++
++ if (dev == NULL)
++ printk(KERN_WARNING "%s: device %s not found\n",
++ __func__, dev_name);
++out:
++ return err;
++}
++
++static int ve_dev_del(envid_t veid, char *dev_name)
++{
++ struct net_device *dev;
++ struct ve_struct *src_ve;
++ struct net *src_net;
++ int err = -ESRCH;
++
++ src_ve = get_ve_by_id(veid);
++ if (src_ve == NULL)
++ goto out;
++
++ src_net = src_ve->ve_netns;
++
++ rtnl_lock();
++
++ read_lock(&dev_base_lock);
++ dev = __dev_get_by_name(src_net, dev_name);
++ read_unlock(&dev_base_lock);
++ if (dev == NULL)
++ goto out_unlock;
++
++ err = __dev_change_net_namespace(dev, &init_net, dev_name,
++ netdev_bc(dev)->owner_ub);
++out_unlock:
++ rtnl_unlock();
++ real_put_ve(src_ve);
++
++ if (dev == NULL)
++ printk(KERN_WARNING "%s: device %s not found\n",
++ __func__, dev_name);
++out:
++ return err;
++}
++
++int real_ve_dev_map(envid_t veid, int op, char *dev_name)
++{
++ if (!capable(CAP_SETVEID))
++ return -EPERM;
++ switch (op) {
++ case VE_NETDEV_ADD:
++ return ve_dev_add(veid, dev_name);
++ case VE_NETDEV_DEL:
++ return ve_dev_del(veid, dev_name);
++ default:
++ return -EINVAL;
++ }
++}
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * VE information via /proc
++ *
++ **********************************************************************
++ **********************************************************************/
++#ifdef CONFIG_PROC_FS
++#if BITS_PER_LONG == 32
++#define VESTAT_LINE_WIDTH (6 * 11 + 6 * 21)
++#define VESTAT_LINE_FMT "%10u %10lu %10lu %10lu %10Lu %20Lu %20Lu %20Lu %20Lu %20Lu %20Lu %10lu\n"
++#define VESTAT_HEAD_FMT "%10s %10s %10s %10s %10s %20s %20s %20s %20s %20s %20s %10s\n"
++#else
++#define VESTAT_LINE_WIDTH (12 * 21)
++#define VESTAT_LINE_FMT "%20u %20lu %20lu %20lu %20Lu %20Lu %20Lu %20Lu %20Lu %20Lu %20Lu %20lu\n"
++#define VESTAT_HEAD_FMT "%20s %20s %20s %20s %20s %20s %20s %20s %20s %20s %20s %20s\n"
++#endif
++
++static int vestat_seq_show(struct seq_file *m, void *v)
++{
++ struct list_head *entry;
++ struct ve_struct *ve;
++ struct ve_struct *curve;
++ int cpu;
++ unsigned long user_ve, nice_ve, system_ve;
++ unsigned long long uptime;
++ cycles_t uptime_cycles, idle_time, strv_time, used;
++
++ entry = (struct list_head *)v;
++ ve = list_entry(entry, struct ve_struct, ve_list);
++
++ curve = get_exec_env();
++ if (entry == ve_list_head.next ||
++ (!ve_is_super(curve) && ve == curve)) {
++ /* print header */
++ seq_printf(m, "%-*s\n",
++ VESTAT_LINE_WIDTH - 1,
++ "Version: 2.2");
++ seq_printf(m, VESTAT_HEAD_FMT, "VEID",
++ "user", "nice", "system",
++ "uptime", "idle",
++ "strv", "uptime", "used",
++ "maxlat", "totlat", "numsched");
++ }
++
++ if (ve == get_ve0())
++ return 0;
++
++ user_ve = nice_ve = system_ve = 0;
++ idle_time = strv_time = used = 0;
++
++ for_each_online_cpu(cpu) {
++ struct ve_cpu_stats *st;
++
++ st = VE_CPU_STATS(ve, cpu);
++ user_ve += st->user;
++ nice_ve += st->nice;
++ system_ve += st->system;
++ used += st->used_time;
++ idle_time += ve_sched_get_idle_time(ve, cpu);
++ }
++ uptime_cycles = get_cycles() - ve->start_cycles;
++ uptime = get_jiffies_64() - ve->start_jiffies;
++
++ seq_printf(m, VESTAT_LINE_FMT, ve->veid,
++ user_ve, nice_ve, system_ve,
++ (unsigned long long)uptime,
++ (unsigned long long)idle_time,
++ (unsigned long long)strv_time,
++ (unsigned long long)uptime_cycles,
++ (unsigned long long)used,
++ (unsigned long long)ve->sched_lat_ve.last.maxlat,
++ (unsigned long long)ve->sched_lat_ve.last.totlat,
++ ve->sched_lat_ve.last.count);
++ return 0;
++}
++
++void *ve_seq_start(struct seq_file *m, loff_t *pos)
++{
++ struct ve_struct *curve;
++
++ curve = get_exec_env();
++ read_lock(&ve_list_lock);
++ if (!ve_is_super(curve)) {
++ if (*pos != 0)
++ return NULL;
++ return curve;
++ }
++
++ return seq_list_start(&ve_list_head, *pos);
++}
++EXPORT_SYMBOL(ve_seq_start);
++
++void *ve_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ if (!ve_is_super(get_exec_env()))
++ return NULL;
++ else
++ return seq_list_next(v, &ve_list_head, pos);
++}
++EXPORT_SYMBOL(ve_seq_next);
++
++void ve_seq_stop(struct seq_file *m, void *v)
++{
++ read_unlock(&ve_list_lock);
++}
++EXPORT_SYMBOL(ve_seq_stop);
++
++static struct seq_operations vestat_seq_op = {
++ .start = ve_seq_start,
++ .next = ve_seq_next,
++ .stop = ve_seq_stop,
++ .show = vestat_seq_show
++};
++
++static int vestat_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &vestat_seq_op);
++}
++
++static struct file_operations proc_vestat_operations = {
++ .open = vestat_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release
++};
++
++static struct seq_operations devperms_seq_op = {
++ .start = ve_seq_start,
++ .next = ve_seq_next,
++ .stop = ve_seq_stop,
++ .show = devperms_seq_show,
++};
++
++static int devperms_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &devperms_seq_op);
++}
++
++static struct file_operations proc_devperms_ops = {
++ .open = devperms_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static int vz_version_show(struct seq_file *file, void* v)
++{
++ static const char ver[] = VZVERSION "\n";
++
++ return seq_puts(file, ver);
++}
++
++static int vz_version_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, vz_version_show, NULL);
++}
++
++static struct file_operations proc_vz_version_oparations = {
++ .open = vz_version_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static inline unsigned long ve_used_mem(struct user_beancounter *ub)
++{
++ extern int glob_ve_meminfo;
++ return glob_ve_meminfo ? ub->ub_parms[UB_OOMGUARPAGES].held :
++ ub->ub_parms[UB_PRIVVMPAGES].held ;
++}
++
++static inline void ve_mi_replace(struct meminfo *mi)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ struct user_beancounter *ub;
++ unsigned long meminfo_val;
++ unsigned long nodettram;
++ unsigned long usedmem;
++
++ meminfo_val = get_exec_env()->meminfo_val;
++
++ if(!meminfo_val)
++ return; /* No virtualization */
++
++ nodettram = mi->si.totalram;
++ ub = current->mm->mm_ub;
++ usedmem = ve_used_mem(ub);
++
++ memset(mi, 0, sizeof(*mi));
++
++ mi->si.totalram = (meminfo_val > nodettram) ?
++ nodettram : meminfo_val;
++ mi->si.freeram = (mi->si.totalram > usedmem) ?
++ (mi->si.totalram - usedmem) : 0;
++#else
++ return;
++#endif
++}
++
++static int meminfo_call(struct vnotifier_block *self,
++ unsigned long event, void *arg, int old_ret)
++{
++ if (event != VIRTINFO_MEMINFO)
++ return old_ret;
++
++ ve_mi_replace((struct meminfo *)arg);
++
++ return NOTIFY_OK;
++}
++
++
++static struct vnotifier_block meminfo_notifier_block = {
++ .notifier_call = meminfo_call
++};
++
++static int __init init_vecalls_proc(void)
++{
++ struct proc_dir_entry *de;
++
++ de = proc_create("vestat", S_IFREG | S_IRUSR, proc_vz_dir,
++ &proc_vestat_operations);
++ if (!de)
++ printk(KERN_WARNING "VZMON: can't make vestat proc entry\n");
++
++ de = proc_create("devperms", S_IFREG | S_IRUSR, proc_vz_dir,
++ &proc_devperms_ops);
++ if (!de)
++ printk(KERN_WARNING "VZMON: can't make devperms proc entry\n");
++
++ de = proc_create("version", S_IFREG | S_IRUGO, proc_vz_dir,
++ &proc_vz_version_oparations);
++ if (!de)
++ printk(KERN_WARNING "VZMON: can't make version proc entry\n");
++
++ virtinfo_notifier_register(VITYPE_GENERAL, &meminfo_notifier_block);
++ return 0;
++}
++
++static void fini_vecalls_proc(void)
++{
++ remove_proc_entry("version", proc_vz_dir);
++ remove_proc_entry("devperms", proc_vz_dir);
++ remove_proc_entry("vestat", proc_vz_dir);
++ virtinfo_notifier_unregister(VITYPE_GENERAL, &meminfo_notifier_block);
++}
++#else
++#define init_vecalls_proc() (0)
++#define fini_vecalls_proc() do { } while (0)
++#endif /* CONFIG_PROC_FS */
++
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * User ctl
++ *
++ **********************************************************************
++ **********************************************************************/
++
++int vzcalls_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int err;
++
++ err = -ENOTTY;
++ switch(cmd) {
++ case VZCTL_MARK_ENV_TO_DOWN: {
++ /* Compatibility issue */
++ err = 0;
++ }
++ break;
++ case VZCTL_SETDEVPERMS: {
++ /* Device type was mistakenly declared as dev_t
++ * in the old user-kernel interface.
++ * That's wrong, dev_t is a kernel internal type.
++ * I use `unsigned' not having anything better in mind.
++ * 2001/08/11 SAW */
++ struct vzctl_setdevperms s;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = real_setdevperms(s.veid, s.type,
++ new_decode_dev(s.dev), s.mask);
++ }
++ break;
++#ifdef CONFIG_INET
++ case VZCTL_VE_NETDEV: {
++ struct vzctl_ve_netdev d;
++ char *s;
++ err = -EFAULT;
++ if (copy_from_user(&d, (void __user *)arg, sizeof(d)))
++ break;
++ err = -ENOMEM;
++ s = kmalloc(IFNAMSIZ+1, GFP_KERNEL);
++ if (s == NULL)
++ break;
++ err = -EFAULT;
++ if (strncpy_from_user(s, d.dev_name, IFNAMSIZ) > 0) {
++ s[IFNAMSIZ] = 0;
++ err = real_ve_dev_map(d.veid, d.op, s);
++ }
++ kfree(s);
++ }
++ break;
++#endif
++ case VZCTL_ENV_CREATE: {
++ struct vzctl_env_create s;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = real_env_create(s.veid, s.flags, s.class_id,
++ NULL, 0);
++ }
++ break;
++ case VZCTL_ENV_CREATE_DATA: {
++ struct vzctl_env_create_data s;
++ env_create_param_t *data;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err=-EINVAL;
++ if (s.datalen < VZCTL_ENV_CREATE_DATA_MINLEN ||
++ s.datalen > VZCTL_ENV_CREATE_DATA_MAXLEN ||
++ s.data == 0)
++ break;
++ err = -ENOMEM;
++ data = kzalloc(sizeof(*data), GFP_KERNEL);
++ if (!data)
++ break;
++
++ err = -EFAULT;
++ if (copy_from_user(data, (void __user *)s.data,
++ s.datalen))
++ goto free_data;
++ err = real_env_create(s.veid, s.flags, s.class_id,
++ data, s.datalen);
++free_data:
++ kfree(data);
++ }
++ break;
++ case VZCTL_GET_CPU_STAT: {
++ struct vzctl_cpustatctl s;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = ve_get_cpu_stat(s.veid, s.cpustat);
++ }
++ break;
++ case VZCTL_VE_MEMINFO: {
++ struct vzctl_ve_meminfo s;
++ err = -EFAULT;
++ if (copy_from_user(&s, (void __user *)arg, sizeof(s)))
++ break;
++ err = ve_set_meminfo(s.veid, s.val);
++ }
++ break;
++ }
++ return err;
++}
++
++#ifdef CONFIG_COMPAT
++int compat_vzcalls_ioctl(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ int err;
++
++ switch(cmd) {
++ case VZCTL_GET_CPU_STAT: {
++ /* FIXME */
++ }
++ case VZCTL_COMPAT_ENV_CREATE_DATA: {
++ struct compat_vzctl_env_create_data cs;
++ struct vzctl_env_create_data __user *s;
++
++ s = compat_alloc_user_space(sizeof(*s));
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++
++ if (put_user(cs.veid, &s->veid) ||
++ put_user(cs.flags, &s->flags) ||
++ put_user(cs.class_id, &s->class_id) ||
++ put_user(compat_ptr(cs.data), &s->data) ||
++ put_user(cs.datalen, &s->datalen))
++ break;
++ err = vzcalls_ioctl(file, VZCTL_ENV_CREATE_DATA,
++ (unsigned long)s);
++ break;
++ }
++#ifdef CONFIG_NET
++ case VZCTL_COMPAT_VE_NETDEV: {
++ struct compat_vzctl_ve_netdev cs;
++ struct vzctl_ve_netdev __user *s;
++
++ s = compat_alloc_user_space(sizeof(*s));
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++
++ if (put_user(cs.veid, &s->veid) ||
++ put_user(cs.op, &s->op) ||
++ put_user(compat_ptr(cs.dev_name), &s->dev_name))
++ break;
++ err = vzcalls_ioctl(file, VZCTL_VE_NETDEV, (unsigned long)s);
++ break;
++ }
++#endif
++ case VZCTL_COMPAT_VE_MEMINFO: {
++ struct compat_vzctl_ve_meminfo cs;
++ err = -EFAULT;
++ if (copy_from_user(&cs, (void *)arg, sizeof(cs)))
++ break;
++ err = ve_set_meminfo(cs.veid, cs.val);
++ break;
++ }
++ default:
++ err = vzcalls_ioctl(file, cmd, arg);
++ break;
++ }
++ return err;
++}
++#endif
++
++static struct vzioctlinfo vzcalls = {
++ .type = VZCTLTYPE,
++ .ioctl = vzcalls_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = compat_vzcalls_ioctl,
++#endif
++ .owner = THIS_MODULE,
++};
++
++
++/**********************************************************************
++ **********************************************************************
++ *
++ * Init/exit stuff
++ *
++ **********************************************************************
++ **********************************************************************/
++
++static int __init init_vecalls_symbols(void)
++{
++ KSYMRESOLVE(real_do_env_free);
++ KSYMMODRESOLVE(vzmon);
++ return 0;
++}
++
++static void fini_vecalls_symbols(void)
++{
++ KSYMMODUNRESOLVE(vzmon);
++ KSYMUNRESOLVE(real_do_env_free);
++}
++
++static inline __init int init_vecalls_ioctls(void)
++{
++ vzioctl_register(&vzcalls);
++ return 0;
++}
++
++static inline void fini_vecalls_ioctls(void)
++{
++ vzioctl_unregister(&vzcalls);
++}
++
++#ifdef CONFIG_SYSCTL
++static struct ctl_table_header *table_header;
++
++static ctl_table kernel_table[] = {
++ {
++ .procname = "ve_allow_kthreads",
++ .data = &ve_allow_kthreads,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ { 0 }
++};
++
++static ctl_table root_table[] = {
++ {CTL_KERN, "kernel", NULL, 0, 0555, kernel_table},
++ { 0 }
++};
++
++static int init_vecalls_sysctl(void)
++{
++ table_header = register_sysctl_table(root_table);
++ if (!table_header)
++ return -ENOMEM ;
++ return 0;
++}
++
++static void fini_vecalls_sysctl(void)
++{
++ unregister_sysctl_table(table_header);
++}
++#else
++static int init_vecalls_sysctl(void) { return 0; }
++static void fini_vecalls_sysctl(void) { ; }
++#endif
++
++static int __init vecalls_init(void)
++{
++ int err;
++
++ err = init_vecalls_sysctl();
++ if (err)
++ goto out_vzmond;
++
++ err = init_vzmond();
++ if (err < 0)
++ goto out_sysctl;
++
++ err = init_vecalls_symbols();
++ if (err < 0)
++ goto out_sym;
++
++ err = init_vecalls_proc();
++ if (err < 0)
++ goto out_proc;
++
++ err = init_vecalls_ioctls();
++ if (err < 0)
++ goto out_ioctls;
++
++ return 0;
++
++out_ioctls:
++ fini_vecalls_proc();
++out_proc:
++ fini_vecalls_symbols();
++out_sym:
++ fini_vzmond();
++out_sysctl:
++ fini_vecalls_sysctl();
++out_vzmond:
++ return err;
++}
++
++static void vecalls_exit(void)
++{
++ fini_vecalls_ioctls();
++ fini_vecalls_proc();
++ fini_vecalls_symbols();
++ fini_vzmond();
++ fini_vecalls_sysctl();
++}
++
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Virtuozzo Control");
++MODULE_LICENSE("GPL v2");
++
++module_init(vecalls_init)
++module_exit(vecalls_exit)
+diff --git a/kernel/ve/veowner.c b/kernel/ve/veowner.c
+new file mode 100644
+index 0000000..8774e9c
+--- /dev/null
++++ b/kernel/ve/veowner.c
+@@ -0,0 +1,149 @@
++/*
++ * kernel/ve/veowner.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/ve.h>
++#include <linux/ve_proto.h>
++#include <linux/ipc.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/inetdevice.h>
++#include <linux/pid_namespace.h>
++#include <asm/system.h>
++#include <asm/io.h>
++
++#include <net/tcp.h>
++
++void prepare_ve0_process(struct task_struct *tsk)
++{
++ VE_TASK_INFO(tsk)->exec_env = get_ve0();
++ VE_TASK_INFO(tsk)->owner_env = get_ve0();
++ VE_TASK_INFO(tsk)->sleep_time = 0;
++ VE_TASK_INFO(tsk)->wakeup_stamp = 0;
++ VE_TASK_INFO(tsk)->sched_time = 0;
++ seqcount_init(&VE_TASK_INFO(tsk)->wakeup_lock);
++
++ if (tsk->pid) {
++ list_add_rcu(&tsk->ve_task_info.vetask_list,
++ &get_ve0()->vetask_lh);
++ atomic_inc(&get_ve0()->pcounter);
++ }
++}
++
++/*
++ * ------------------------------------------------------------------------
++ * proc entries
++ * ------------------------------------------------------------------------
++ */
++
++#ifdef CONFIG_PROC_FS
++struct proc_dir_entry *proc_vz_dir;
++EXPORT_SYMBOL(proc_vz_dir);
++
++struct proc_dir_entry *glob_proc_vz_dir;
++EXPORT_SYMBOL(glob_proc_vz_dir);
++
++static void prepare_proc(void)
++{
++ proc_vz_dir = proc_mkdir("vz", NULL);
++ if (!proc_vz_dir)
++ panic("Can't create /proc/vz dir\n");
++
++ glob_proc_vz_dir = proc_mkdir("vz", &glob_proc_root);
++ if (!proc_vz_dir)
++ panic("Can't create /proc/vz dir\n");
++}
++#endif
++
++/*
++ * ------------------------------------------------------------------------
++ * OpenVZ sysctl
++ * ------------------------------------------------------------------------
++ */
++extern int ve_area_access_check;
++
++#ifdef CONFIG_INET
++static struct ctl_table vz_ipv4_route_table[] = {
++ {
++ .procname = "src_check",
++ .data = &ip_rt_src_check,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ { 0 }
++};
++
++static struct ctl_path net_ipv4_route_path[] = {
++ { .ctl_name = CTL_NET, .procname = "net", },
++ { .ctl_name = NET_IPV4, .procname = "ipv4", },
++ { .ctl_name = NET_IPV4_ROUTE, .procname = "route", },
++ { }
++};
++#endif
++
++static struct ctl_table vz_fs_table[] = {
++ {
++ .procname = "ve-area-access-check",
++ .data = &ve_area_access_check,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ { 0 }
++};
++
++static struct ctl_path fs_path[] = {
++ { .ctl_name = CTL_FS, .procname = "fs", },
++ { }
++};
++
++static void prepare_sysctl(void)
++{
++#ifdef CONFIG_INET
++ register_sysctl_paths(net_ipv4_route_path, vz_ipv4_route_table);
++#endif
++ register_sysctl_paths(fs_path, vz_fs_table);
++}
++
++/*
++ * ------------------------------------------------------------------------
++ * XXX init_ve_system
++ * ------------------------------------------------------------------------
++ */
++
++void init_ve_system(void)
++{
++ struct task_struct *init_entry;
++ struct ve_struct *ve;
++
++ ve = get_ve0();
++
++ init_entry = init_pid_ns.child_reaper;
++ /* if ve_move_task to VE0 (e.g. in cpt code) *
++ * occurs, ve_cap_bset on VE0 is required */
++ ve->ve_cap_bset = CAP_INIT_EFF_SET;
++
++ read_lock(&init_entry->fs->lock);
++ ve->root_path = init_entry->fs->root;
++ read_unlock(&init_entry->fs->lock);
++
++#ifdef CONFIG_PROC_FS
++ prepare_proc();
++#endif
++ prepare_sysctl();
++}
+diff --git a/kernel/ve/vzdev.c b/kernel/ve/vzdev.c
+new file mode 100644
+index 0000000..cc4b1b7
+--- /dev/null
++++ b/kernel/ve/vzdev.c
+@@ -0,0 +1,154 @@
++/*
++ * kernel/ve/vzdev.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/vzctl.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/vzcalluser.h>
++#include <asm/uaccess.h>
++#include <asm/pgalloc.h>
++#include <linux/device.h>
++#include <linux/smp_lock.h>
++
++#define VZCTL_MAJOR 126
++#define VZCTL_NAME "vzctl"
++
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Virtuozzo Interface");
++MODULE_LICENSE("GPL v2");
++
++static LIST_HEAD(ioctls);
++static spinlock_t ioctl_lock = SPIN_LOCK_UNLOCKED;
++
++static struct vzioctlinfo *vzctl_get_handler(unsigned int cmd)
++{
++ struct vzioctlinfo *h;
++
++ spin_lock(&ioctl_lock);
++ list_for_each_entry(h, &ioctls, list) {
++ if (h->type == _IOC_TYPE(cmd))
++ goto found;
++ }
++ h = NULL;
++found:
++ if (h && !try_module_get(h->owner))
++ h = NULL;
++ spin_unlock(&ioctl_lock);
++ return h;
++}
++
++static void vzctl_put_handler(struct vzioctlinfo *h)
++{
++ if (!h)
++ return;
++
++ module_put(h->owner);
++}
++
++long vzctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ struct vzioctlinfo *h;
++ int err;
++
++ err = -ENOTTY;
++ h = vzctl_get_handler(cmd);
++ if (h && h->ioctl)
++ err = (*h->ioctl)(file, cmd, arg);
++ vzctl_put_handler(h);
++
++ return err;
++}
++
++long compat_vzctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ struct vzioctlinfo *h;
++ int err;
++
++ err = -ENOIOCTLCMD;
++ h = vzctl_get_handler(cmd);
++ if (h && h->compat_ioctl)
++ err = (*h->compat_ioctl)(file, cmd, arg);
++ vzctl_put_handler(h);
++
++ return err;
++}
++
++void vzioctl_register(struct vzioctlinfo *inf)
++{
++ spin_lock(&ioctl_lock);
++ list_add(&inf->list, &ioctls);
++ spin_unlock(&ioctl_lock);
++}
++EXPORT_SYMBOL(vzioctl_register);
++
++void vzioctl_unregister(struct vzioctlinfo *inf)
++{
++ spin_lock(&ioctl_lock);
++ list_del_init(&inf->list);
++ spin_unlock(&ioctl_lock);
++}
++EXPORT_SYMBOL(vzioctl_unregister);
++
++/*
++ * Init/exit stuff.
++ */
++static struct file_operations vzctl_fops = {
++ .owner = THIS_MODULE,
++ .unlocked_ioctl = vzctl_ioctl,
++ .compat_ioctl = compat_vzctl_ioctl,
++};
++
++static struct class *vzctl_class;
++
++static void __exit vzctl_exit(void)
++{
++ device_destroy(vzctl_class, MKDEV(VZCTL_MAJOR, 0));
++ class_destroy(vzctl_class);
++ unregister_chrdev(VZCTL_MAJOR, VZCTL_NAME);
++}
++
++static int __init vzctl_init(void)
++{
++ int ret;
++ struct device *class_err;
++
++ ret = register_chrdev(VZCTL_MAJOR, VZCTL_NAME, &vzctl_fops);
++ if (ret < 0)
++ goto out;
++
++ vzctl_class = class_create(THIS_MODULE, "vzctl");
++ if (IS_ERR(vzctl_class)) {
++ ret = PTR_ERR(vzctl_class);
++ goto out_cleandev;
++ }
++
++ class_err = device_create(vzctl_class, NULL,
++ MKDEV(VZCTL_MAJOR, 0), NULL, VZCTL_NAME);
++ if (IS_ERR(class_err)) {
++ ret = PTR_ERR(class_err);
++ goto out_rmclass;
++ }
++
++ goto out;
++
++out_rmclass:
++ class_destroy(vzctl_class);
++out_cleandev:
++ unregister_chrdev(VZCTL_MAJOR, VZCTL_NAME);
++out:
++ return ret;
++}
++
++module_init(vzctl_init)
++module_exit(vzctl_exit);
+diff --git a/kernel/ve/vzevent.c b/kernel/ve/vzevent.c
+new file mode 100644
+index 0000000..554f169
+--- /dev/null
++++ b/kernel/ve/vzevent.c
+@@ -0,0 +1,125 @@
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/skbuff.h>
++#include <net/sock.h>
++#include <linux/netlink.h>
++#include <linux/errno.h>
++#include <linux/ve_proto.h>
++#include <linux/vzevent.h>
++
++#define NETLINK_UEVENT 31
++#define VZ_EVGRP_ALL 0x01
++
++/*
++ * NOTE: the original idea was to send events via kobject_uevent(),
++ * however, it turns out that it has negative consequences like
++ * start of /sbin/hotplug which tries to react on our events in inadequate manner.
++ */
++
++static struct sock *vzev_sock;
++
++static char *action_to_string(int action)
++{
++ switch (action) {
++ case KOBJ_MOUNT:
++ return "ve-mount";
++ case KOBJ_UMOUNT:
++ return "ve-umount";
++ case KOBJ_START:
++ return "ve-start";
++ case KOBJ_STOP:
++ return "ve-stop";
++ default:
++ return NULL;
++ }
++}
++
++static int do_vzevent_send(int event, char *msg, int len)
++{
++ struct sk_buff *skb;
++ char *buf, *action;
++ int alen;
++
++ action = action_to_string(event);
++ alen = strlen(action);
++
++ skb = alloc_skb(len + 1 + alen, GFP_KERNEL);
++ if (!skb)
++ return -ENOMEM;
++
++ buf = skb_put(skb, len + 1 + alen);
++ memcpy(buf, action, alen);
++ buf[alen] = '@';
++ memcpy(buf + alen + 1, msg, len);
++ (void)netlink_broadcast(vzev_sock, skb, 0, VZ_EVGRP_ALL, GFP_KERNEL);
++ return 0;
++}
++
++int vzevent_send(int event, const char *attrs_fmt, ...)
++{
++ va_list args;
++ int len, err;
++ struct ve_struct *ve;
++ char *page;
++
++ err = -ENOMEM;
++ page = (char *)__get_free_page(GFP_KERNEL);
++ if (!page)
++ goto out;
++
++ va_start(args, attrs_fmt);
++ len = vscnprintf(page, PAGE_SIZE, attrs_fmt, args);
++ va_end(args);
++
++ ve = set_exec_env(get_ve0());
++ err = do_vzevent_send(event, page, len);
++ (void)set_exec_env(ve);
++ free_page((unsigned long)page);
++out:
++ return err;
++}
++EXPORT_SYMBOL(vzevent_send);
++
++static int ve_start(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ vzevent_send(KOBJ_START, "%d", ve->veid);
++ return 0;
++}
++
++static void ve_stop(void *data)
++{
++ struct ve_struct *ve;
++
++ ve = (struct ve_struct *)data;
++ vzevent_send(KOBJ_STOP, "%d", ve->veid);
++}
++
++static struct ve_hook ve_start_stop_hook = {
++ .init = ve_start,
++ .fini = ve_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_AFTERALL,
++};
++
++static int __init init_vzevent(void)
++{
++ vzev_sock = netlink_kernel_create(NETLINK_UEVENT, 0, NULL, THIS_MODULE);
++ if (vzev_sock == NULL)
++ return -ENOMEM;
++ ve_hook_register(VE_SS_CHAIN, &ve_start_stop_hook);
++ return 0;
++}
++
++static void __exit exit_vzevent(void)
++{
++ ve_hook_unregister(&ve_start_stop_hook);
++ sock_release(vzev_sock->sk_socket);
++}
++
++MODULE_LICENSE("GPL");
++
++module_init(init_vzevent);
++module_exit(exit_vzevent);
+diff --git a/kernel/ve/vzwdog.c b/kernel/ve/vzwdog.c
+new file mode 100644
+index 0000000..c9a4024
+--- /dev/null
++++ b/kernel/ve/vzwdog.c
+@@ -0,0 +1,288 @@
++/*
++ * kernel/ve/vzwdog.c
++ *
++ * Copyright (C) 2000-2005 SWsoft
++ * All rights reserved.
++ *
++ * Licensing governed by "linux/COPYING.SWsoft" file.
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/ctype.h>
++#include <linux/kobject.h>
++#include <linux/genhd.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/kernel_stat.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/suspend.h>
++#include <linux/ve.h>
++#include <linux/vzstat.h>
++#include <asm/uaccess.h>
++#include <linux/kthread.h>
++#include <linux/freezer.h>
++
++/* Staff regading kernel thread polling VE validity */
++static int sleep_timeout = 60;
++static struct task_struct *wdog_thread_tsk;
++
++extern void show_mem(void);
++
++static struct file *intr_file;
++static char page[PAGE_SIZE];
++
++static void parse_irq_list(int len)
++{
++ int i, k, skip;
++ for (i = 0; i < len; ) {
++ k = i;
++ while (i < len && page[i] != '\n' && page[i] != ':')
++ i++;
++ skip = 0;
++ if (i < len && page[i] != '\n') {
++ i++; /* skip ':' */
++ while (i < len && (page[i] == ' ' || page[i] == '0'))
++ i++;
++ skip = (i < len && (page[i] < '0' || page[i] > '9'));
++ while (i < len && page[i] != '\n')
++ i++;
++ }
++ if (!skip)
++ printk("%.*s\n", i - k, page + k);
++ if (i < len)
++ i++; /* skip '\n' */
++ }
++}
++
++extern loff_t vfs_llseek(struct file *file, loff_t, int);
++extern ssize_t vfs_read(struct file *file, char __user *, size_t, loff_t *);
++extern struct file *filp_open(const char *filename, int flags, int mode);
++extern int filp_close(struct file *filp, fl_owner_t id);
++static void show_irq_list(void)
++{
++ mm_segment_t fs;
++ int r;
++
++ fs = get_fs();
++ set_fs(KERNEL_DS);
++ vfs_llseek(intr_file, 0, 0);
++ r = vfs_read(intr_file, (void __user *)page, sizeof(page),
++ &intr_file->f_pos);
++ set_fs(fs);
++
++ if (r > 0)
++ parse_irq_list(r);
++}
++
++static void show_alloc_latency(void)
++{
++ static const char *alloc_descr[KSTAT_ALLOCSTAT_NR] = {
++ "A0",
++ "L0",
++ "H0",
++ "L1",
++ "H1"
++ };
++ int i;
++
++ printk("lat: ");
++ for (i = 0; i < KSTAT_ALLOCSTAT_NR; i++) {
++ struct kstat_lat_struct *p;
++ cycles_t maxlat, avg0, avg1, avg2;
++
++ p = &kstat_glob.alloc_lat[i];
++ spin_lock_irq(&kstat_glb_lock);
++ maxlat = p->last.maxlat;
++ avg0 = p->avg[0];
++ avg1 = p->avg[1];
++ avg2 = p->avg[2];
++ spin_unlock_irq(&kstat_glb_lock);
++
++ printk("%s %Lu (%Lu %Lu %Lu)",
++ alloc_descr[i],
++ (unsigned long long)maxlat,
++ (unsigned long long)avg0,
++ (unsigned long long)avg1,
++ (unsigned long long)avg2);
++ }
++ printk("\n");
++}
++
++static void show_schedule_latency(void)
++{
++ struct kstat_lat_pcpu_struct *p;
++ cycles_t maxlat, totlat, avg0, avg1, avg2;
++ unsigned long count;
++
++ p = &kstat_glob.sched_lat;
++ spin_lock_irq(&kstat_glb_lock);
++ maxlat = p->last.maxlat;
++ totlat = p->last.totlat;
++ count = p->last.count;
++ avg0 = p->avg[0];
++ avg1 = p->avg[1];
++ avg2 = p->avg[2];
++ spin_unlock_irq(&kstat_glb_lock);
++
++ printk("sched lat: %Lu/%Lu/%lu (%Lu %Lu %Lu)\n",
++ (unsigned long long)maxlat,
++ (unsigned long long)totlat,
++ count,
++ (unsigned long long)avg0,
++ (unsigned long long)avg1,
++ (unsigned long long)avg2);
++}
++
++static void show_header(void)
++{
++ struct timeval tv;
++
++ do_gettimeofday(&tv);
++ preempt_disable();
++ printk("*** VZWDOG 1.14: time %lu.%06lu uptime %Lu CPU %d ***\n",
++ tv.tv_sec, (long)tv.tv_usec,
++ (unsigned long long)get_jiffies_64(),
++ smp_processor_id());
++#ifdef CONFIG_FAIRSCHED
++ printk("*** cycles_per_jiffy %lu jiffies_per_second %u ***\n",
++ cycles_per_jiffy, HZ);
++#else
++ printk("*** jiffies_per_second %u ***\n", HZ);
++#endif
++ preempt_enable();
++}
++
++static void show_pgdatinfo(void)
++{
++ pg_data_t *pgdat;
++
++ printk("pgdat:");
++ for_each_online_pgdat(pgdat) {
++ printk(" %d: %lu,%lu,%lu",
++ pgdat->node_id,
++ pgdat->node_start_pfn,
++ pgdat->node_present_pages,
++ pgdat->node_spanned_pages);
++#ifdef CONFIG_FLAT_NODE_MEM_MAP
++ printk(",%p", pgdat->node_mem_map);
++#endif
++ }
++ printk("\n");
++}
++
++static int show_partition_io(struct device *dev, void *x)
++{
++ char *name;
++ char buf[BDEVNAME_SIZE];
++ struct gendisk *gd;
++
++ gd = dev_to_disk(dev);
++
++ name = disk_name(gd, 0, buf);
++ if ((strlen(name) > 4) && (strncmp(name, "loop", 4) == 0) &&
++ isdigit(name[4]))
++ return 0;
++
++ if ((strlen(name) > 3) && (strncmp(name, "ram", 3) == 0) &&
++ isdigit(name[3]))
++ return 0;
++
++ printk("(%u,%u) %s r(%lu %lu %lu) w(%lu %lu %lu)\n",
++ gd->major, gd->first_minor,
++ name,
++ disk_stat_read(gd, ios[READ]),
++ disk_stat_read(gd, sectors[READ]),
++ disk_stat_read(gd, merges[READ]),
++ disk_stat_read(gd, ios[WRITE]),
++ disk_stat_read(gd, sectors[WRITE]),
++ disk_stat_read(gd, merges[WRITE]));
++
++ return 0;
++}
++
++static void show_diskio(void)
++{
++ printk("disk_io: ");
++ class_for_each_device(&block_class, NULL, NULL, show_partition_io);
++ printk("\n");
++}
++
++static void show_nrprocs(void)
++{
++ unsigned long _nr_running, _nr_sleeping,
++ _nr_unint, _nr_zombie, _nr_dead, _nr_stopped;
++
++ _nr_running = nr_running();
++ _nr_unint = nr_uninterruptible();
++ _nr_sleeping = nr_sleeping();
++ _nr_zombie = nr_zombie;
++ _nr_dead = atomic_read(&nr_dead);
++ _nr_stopped = nr_stopped();
++
++ printk("VEnum: %d, proc R %lu, S %lu, D %lu, "
++ "Z %lu, X %lu, T %lu (tot %d)\n",
++ nr_ve, _nr_running, _nr_sleeping, _nr_unint,
++ _nr_zombie, _nr_dead, _nr_stopped, nr_threads);
++}
++
++static void wdog_print(void)
++{
++ show_header();
++ show_irq_list();
++ show_pgdatinfo();
++ show_mem();
++ show_diskio();
++ show_schedule_latency();
++ show_alloc_latency();
++ show_nrprocs();
++}
++
++static int wdog_loop(void* data)
++{
++ while (1) {
++ wdog_print();
++ try_to_freeze();
++
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ if (kthread_should_stop())
++ break;
++ schedule_timeout(sleep_timeout*HZ);
++ }
++ return 0;
++}
++
++static int __init wdog_init(void)
++{
++ struct file *file;
++
++ file = filp_open("/proc/interrupts", 0, 0);
++ if (IS_ERR(file))
++ return PTR_ERR(file);
++ intr_file = file;
++
++ wdog_thread_tsk = kthread_run(wdog_loop, NULL, "vzwdog");
++ if (IS_ERR(wdog_thread_tsk)) {
++ filp_close(intr_file, NULL);
++ return -EBUSY;
++ }
++ return 0;
++}
++
++static void __exit wdog_exit(void)
++{
++ kthread_stop(wdog_thread_tsk);
++ filp_close(intr_file, NULL);
++}
++
++module_param(sleep_timeout, int, 0660);
++MODULE_AUTHOR("SWsoft <info@sw-soft.com>");
++MODULE_DESCRIPTION("Virtuozzo WDOG");
++MODULE_LICENSE("GPL v2");
++
++module_init(wdog_init)
++module_exit(wdog_exit)
+diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
+index 0b50481..7194076 100644
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -128,6 +128,15 @@ config DEBUG_SECTION_MISMATCH
+ - Enable verbose reporting from modpost to help solving
+ the section mismatches reported.
+
++config SYSRQ_DEBUG
++ bool "Debugging via sysrq keys"
++ depends on MAGIC_SYSRQ
++ default y
++ help
++ Say Y if you want to extend functionality of magic key. It will
++ provide you with some debugging facilities such as dumping and
++ writing memory, resolving symbols and some other.
++
+ config DEBUG_KERNEL
+ bool "Kernel debugging"
+ help
+diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
+index 3f91472..b3e8c6b 100644
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -38,6 +38,8 @@ static const char *kobject_actions[] = {
+ [KOBJ_REMOVE] = "remove",
+ [KOBJ_CHANGE] = "change",
+ [KOBJ_MOVE] = "move",
++ [KOBJ_START] = "start",
++ [KOBJ_STOP] = "stop",
+ [KOBJ_ONLINE] = "online",
+ [KOBJ_OFFLINE] = "offline",
+ };
+diff --git a/lib/show_mem.c b/lib/show_mem.c
+index 238e72a..57c038d 100644
+--- a/lib/show_mem.c
++++ b/lib/show_mem.c
+@@ -8,6 +8,7 @@
+ #include <linux/mm.h>
+ #include <linux/nmi.h>
+ #include <linux/quicklist.h>
++#include <linux/module.h>
+
+ void show_mem(void)
+ {
+@@ -61,3 +62,4 @@ void show_mem(void)
+ quicklist_total_size());
+ #endif
+ }
++EXPORT_SYMBOL_GPL(show_mem);
+diff --git a/mm/filemap.c b/mm/filemap.c
+index 876bc59..58eead0 100644
+--- a/mm/filemap.c
++++ b/mm/filemap.c
+@@ -42,6 +42,7 @@
+
+ #include <asm/mman.h>
+
++#include <bc/io_acct.h>
+
+ /*
+ * Shared mappings implemented 30.11.1994. It's not fully working yet,
+@@ -118,6 +119,7 @@ void __remove_from_page_cache(struct page *page)
+ mem_cgroup_uncharge_cache_page(page);
+ radix_tree_delete(&mapping->page_tree, page->index);
+ page->mapping = NULL;
++ ub_io_release_debug(page);
+ mapping->nrpages--;
+ __dec_zone_page_state(page, NR_FILE_PAGES);
+ BUG_ON(page_mapped(page));
+diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c
+index b5167df..82a9fc8 100644
+--- a/mm/filemap_xip.c
++++ b/mm/filemap_xip.c
+@@ -19,6 +19,7 @@
+ #include <linux/mutex.h>
+ #include <asm/tlbflush.h>
+ #include <asm/io.h>
++#include <bc/vmpages.h>
+
+ /*
+ * We do use our own empty page to avoid interference with other users
+@@ -194,6 +195,8 @@ retry:
+ flush_cache_page(vma, address, pte_pfn(*pte));
+ pteval = ptep_clear_flush_notify(vma, address, pte);
+ page_remove_rmap(page, vma);
++ pb_remove_ref(page, mm);
++ ub_unused_privvm_inc(mm, vma);
+ dec_mm_counter(mm, file_rss);
+ BUG_ON(pte_dirty(pteval));
+ pte_unmap_unlock(pte, ptl);
+diff --git a/mm/fremap.c b/mm/fremap.c
+index 7881638..b043155 100644
+--- a/mm/fremap.c
++++ b/mm/fremap.c
+@@ -21,6 +21,8 @@
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#include <bc/vmpages.h>
++
+ static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
+ {
+@@ -36,6 +38,7 @@ static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma,
+ if (pte_dirty(pte))
+ set_page_dirty(page);
+ page_remove_rmap(page, vma);
++ pb_remove_ref(page, mm);
+ page_cache_release(page);
+ update_hiwater_rss(mm);
+ dec_mm_counter(mm, file_rss);
+@@ -62,8 +65,10 @@ static int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma,
+ if (!pte)
+ goto out;
+
+- if (!pte_none(*pte))
++ if (!pte_none(*pte)) {
+ zap_pte(mm, vma, addr, pte);
++ ub_unused_privvm_inc(mm, vma);
++ }
+
+ set_pte_at(mm, addr, pte, pgoff_to_pte(pgoff));
+ /*
+@@ -240,4 +245,5 @@ out:
+
+ return err;
+ }
++EXPORT_SYMBOL_GPL(sys_remap_file_pages);
+
+diff --git a/mm/memory.c b/mm/memory.c
+index 1002f47..cf94817 100644
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -42,6 +42,9 @@
+ #include <linux/mm.h>
+ #include <linux/hugetlb.h>
+ #include <linux/mman.h>
++#include <linux/virtinfo.h>
++#include <linux/sched.h>
++#include <linux/vzstat.h>
+ #include <linux/swap.h>
+ #include <linux/highmem.h>
+ #include <linux/pagemap.h>
+@@ -62,6 +65,11 @@
+ #include <linux/swapops.h>
+ #include <linux/elf.h>
+
++#include <bc/beancounter.h>
++#include <bc/io_acct.h>
++#include <bc/kmem.h>
++#include <bc/vmpages.h>
++
+ #include "internal.h"
+
+ #ifndef CONFIG_NEED_MULTIPLE_NODES
+@@ -118,18 +126,21 @@ void pgd_clear_bad(pgd_t *pgd)
+ pgd_ERROR(*pgd);
+ pgd_clear(pgd);
+ }
++EXPORT_SYMBOL_GPL(pgd_clear_bad);
+
+ void pud_clear_bad(pud_t *pud)
+ {
+ pud_ERROR(*pud);
+ pud_clear(pud);
+ }
++EXPORT_SYMBOL_GPL(pud_clear_bad);
+
+ void pmd_clear_bad(pmd_t *pmd)
+ {
+ pmd_ERROR(*pmd);
+ pmd_clear(pmd);
+ }
++EXPORT_SYMBOL_GPL(pmd_clear_bad);
+
+ /*
+ * Note: this doesn't free the actual pages themselves. That
+@@ -340,6 +351,7 @@ int __pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
+ pte_free(mm, new);
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(__pte_alloc);
+
+ int __pte_alloc_kernel(pmd_t *pmd, unsigned long address)
+ {
+@@ -481,6 +493,7 @@ struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
+ out:
+ return pfn_to_page(pfn);
+ }
++EXPORT_SYMBOL_GPL(vm_normal_page);
+
+ /*
+ * copy one vm_area from one task to the other. Assumes the page tables
+@@ -491,7 +504,7 @@ out:
+ static inline void
+ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *vma,
+- unsigned long addr, int *rss)
++ unsigned long addr, int *rss, struct page_beancounter **pbc)
+ {
+ unsigned long vm_flags = vma->vm_flags;
+ pte_t pte = *src_pte;
+@@ -546,6 +559,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ if (page) {
+ get_page(page);
+ page_dup_rmap(page, vma, addr);
++ pb_dup_ref(page, dst_mm, pbc);
+ rss[!!PageAnon(page)]++;
+ }
+
+@@ -553,20 +567,35 @@ out_set_pte:
+ set_pte_at(dst_mm, addr, dst_pte, pte);
+ }
+
++#define pte_ptrs(a) (PTRS_PER_PTE - ((a >> PAGE_SHIFT)&(PTRS_PER_PTE - 1)))
++#ifdef CONFIG_BEANCOUNTERS
++#define same_ub(mm1, mm2) ((mm1)->mm_ub == (mm2)->mm_ub)
++#else
++#define same_ub(mm1, mm2) 1
++#endif
++
+ static int copy_pte_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+- pmd_t *dst_pmd, pmd_t *src_pmd, struct vm_area_struct *vma,
++ pmd_t *dst_pmd, pmd_t *src_pmd,
++ struct vm_area_struct *dst_vma,
++ struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+ {
+ pte_t *src_pte, *dst_pte;
+ spinlock_t *src_ptl, *dst_ptl;
+ int progress = 0;
+- int rss[2];
++ int rss[2], rss_tot;
++ struct page_beancounter *pbc;
++ int err;
+
++ err = -ENOMEM;
++ pbc = same_ub(src_mm, dst_mm) ? PBC_COPY_SAME : NULL;
+ again:
++ if (pbc != PBC_COPY_SAME && pb_alloc_list(&pbc, pte_ptrs(addr)))
++ goto out;
+ rss[1] = rss[0] = 0;
+ dst_pte = pte_alloc_map_lock(dst_mm, dst_pmd, addr, &dst_ptl);
+ if (!dst_pte)
+- return -ENOMEM;
++ goto out;
+ src_pte = pte_offset_map_nested(src_pmd, addr);
+ src_ptl = pte_lockptr(src_mm, src_pmd);
+ spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
+@@ -587,23 +616,32 @@ again:
+ progress++;
+ continue;
+ }
+- copy_one_pte(dst_mm, src_mm, dst_pte, src_pte, vma, addr, rss);
++ copy_one_pte(dst_mm, src_mm, dst_pte, src_pte, vma, addr, rss,
++ &pbc);
+ progress += 8;
+ } while (dst_pte++, src_pte++, addr += PAGE_SIZE, addr != end);
+
+ arch_leave_lazy_mmu_mode();
+ spin_unlock(src_ptl);
+ pte_unmap_nested(src_pte - 1);
++ rss_tot = rss[0] + rss[1];
++ ub_unused_privvm_sub(dst_mm, dst_vma, rss_tot);
+ add_mm_rss(dst_mm, rss[0], rss[1]);
+ pte_unmap_unlock(dst_pte - 1, dst_ptl);
+ cond_resched();
+ if (addr != end)
+ goto again;
+- return 0;
++
++ err = 0;
++out:
++ pb_free_list(&pbc);
++ return err;
+ }
+
+ static inline int copy_pmd_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+- pud_t *dst_pud, pud_t *src_pud, struct vm_area_struct *vma,
++ pud_t *dst_pud, pud_t *src_pud,
++ struct vm_area_struct *dst_vma,
++ struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+ {
+ pmd_t *src_pmd, *dst_pmd;
+@@ -618,14 +656,16 @@ static inline int copy_pmd_range(struct mm_struct *dst_mm, struct mm_struct *src
+ if (pmd_none_or_clear_bad(src_pmd))
+ continue;
+ if (copy_pte_range(dst_mm, src_mm, dst_pmd, src_pmd,
+- vma, addr, next))
++ dst_vma, vma, addr, next))
+ return -ENOMEM;
+ } while (dst_pmd++, src_pmd++, addr = next, addr != end);
+ return 0;
+ }
+
+ static inline int copy_pud_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+- pgd_t *dst_pgd, pgd_t *src_pgd, struct vm_area_struct *vma,
++ pgd_t *dst_pgd, pgd_t *src_pgd,
++ struct vm_area_struct *dst_vma,
++ struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+ {
+ pud_t *src_pud, *dst_pud;
+@@ -640,19 +680,21 @@ static inline int copy_pud_range(struct mm_struct *dst_mm, struct mm_struct *src
+ if (pud_none_or_clear_bad(src_pud))
+ continue;
+ if (copy_pmd_range(dst_mm, src_mm, dst_pud, src_pud,
+- vma, addr, next))
++ dst_vma, vma, addr, next))
+ return -ENOMEM;
+ } while (dst_pud++, src_pud++, addr = next, addr != end);
+ return 0;
+ }
+
+-int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+- struct vm_area_struct *vma)
++int __copy_page_range(struct vm_area_struct *dst_vma,
++ struct vm_area_struct *vma,
++ unsigned long addr, size_t size)
+ {
++ struct mm_struct *dst_mm = dst_vma->vm_mm;
++ struct mm_struct *src_mm = vma->vm_mm;
+ pgd_t *src_pgd, *dst_pgd;
+ unsigned long next;
+- unsigned long addr = vma->vm_start;
+- unsigned long end = vma->vm_end;
++ unsigned long end = addr + size;
+ int ret;
+
+ /*
+@@ -686,7 +728,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ if (pgd_none_or_clear_bad(src_pgd))
+ continue;
+ if (unlikely(copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd,
+- vma, addr, next))) {
++ dst_vma, vma, addr, next))) {
+ ret = -ENOMEM;
+ break;
+ }
+@@ -697,6 +739,17 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ vma->vm_start, end);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(__copy_page_range);
++
++int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
++ struct vm_area_struct *dst_vma, struct vm_area_struct *vma)
++{
++ if (dst_vma->vm_mm != dst)
++ BUG();
++ if (vma->vm_mm != src)
++ BUG();
++ return __copy_page_range(dst_vma, vma, vma->vm_start, vma->vm_end-vma->vm_start);
++}
+
+ static unsigned long zap_pte_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, pmd_t *pmd,
+@@ -708,6 +761,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
+ spinlock_t *ptl;
+ int file_rss = 0;
+ int anon_rss = 0;
++ int rss;
+
+ pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
+ arch_enter_lazy_mmu_mode();
+@@ -762,6 +816,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
+ file_rss--;
+ }
+ page_remove_rmap(page, vma);
++ pb_remove_ref(page, mm);
+ tlb_remove_page(tlb, page);
+ continue;
+ }
+@@ -776,6 +831,8 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
+ pte_clear_not_present_full(mm, addr, pte, tlb->fullmm);
+ } while (pte++, addr += PAGE_SIZE, (addr != end && *zap_work > 0));
+
++ rss = -(file_rss + anon_rss);
++ ub_unused_privvm_add(mm, vma, rss);
+ add_mm_rss(mm, file_rss, anon_rss);
+ arch_leave_lazy_mmu_mode();
+ pte_unmap_unlock(pte - 1, ptl);
+@@ -1768,6 +1825,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ int reuse = 0, ret = 0;
+ int page_mkwrite = 0;
+ struct page *dirty_page = NULL;
++ struct page_beancounter *pbc;
+
+ old_page = vm_normal_page(vma, address, orig_pte);
+ if (!old_page) {
+@@ -1839,6 +1897,7 @@ reuse:
+ flush_cache_page(vma, address, pte_pfn(orig_pte));
+ entry = pte_mkyoung(orig_pte);
+ entry = maybe_mkwrite(pte_mkdirty(entry), vma);
++ ClearPageCheckpointed(old_page);
+ if (ptep_set_access_flags(vma, address, page_table, entry,1))
+ update_mmu_cache(vma, address, entry);
+ ret |= VM_FAULT_WRITE;
+@@ -1852,6 +1911,9 @@ reuse:
+ gotten:
+ pte_unmap_unlock(page_table, ptl);
+
++ if (unlikely(pb_alloc(&pbc)))
++ goto oom_nopb;
++
+ if (unlikely(anon_vma_prepare(vma)))
+ goto oom;
+ VM_BUG_ON(old_page == ZERO_PAGE(0));
+@@ -1870,12 +1932,15 @@ gotten:
+ page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
+ if (likely(pte_same(*page_table, orig_pte))) {
+ if (old_page) {
++ pb_remove_ref(old_page, mm);
+ if (!PageAnon(old_page)) {
+ dec_mm_counter(mm, file_rss);
+ inc_mm_counter(mm, anon_rss);
+ }
+- } else
++ } else {
++ ub_unused_privvm_dec(mm, vma);
+ inc_mm_counter(mm, anon_rss);
++ }
+ flush_cache_page(vma, address, pte_pfn(orig_pte));
+ entry = mk_pte(new_page, vma->vm_page_prot);
+ entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+@@ -1890,6 +1955,7 @@ gotten:
+ update_mmu_cache(vma, address, entry);
+ lru_cache_add_active(new_page);
+ page_add_new_anon_rmap(new_page, vma, address);
++ pb_add_ref(new_page, mm, &pbc);
+
+ if (old_page) {
+ /*
+@@ -1927,6 +1993,7 @@ gotten:
+ page_cache_release(new_page);
+ if (old_page)
+ page_cache_release(old_page);
++ pb_free(&pbc);
+ unlock:
+ pte_unmap_unlock(page_table, ptl);
+ if (dirty_page) {
+@@ -1949,6 +2016,8 @@ unlock:
+ oom_free_new:
+ page_cache_release(new_page);
+ oom:
++ pb_free(&pbc);
++oom_nopb:
+ if (old_page)
+ page_cache_release(old_page);
+ return VM_FAULT_OOM;
+@@ -2256,10 +2325,16 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ swp_entry_t entry;
+ pte_t pte;
+ int ret = 0;
++ struct page_beancounter *pbc;
++ cycles_t start;
+
+ if (!pte_unmap_same(mm, pmd, page_table, orig_pte))
+- goto out;
++ goto out_nostat;
+
++ if (unlikely(pb_alloc(&pbc)))
++ return VM_FAULT_OOM;
++
++ start = get_cycles();
+ entry = pte_to_swp_entry(orig_pte);
+ if (is_migration_entry(entry)) {
+ migration_entry_wait(mm, pmd, address);
+@@ -2313,6 +2388,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ /* The page isn't present yet, go ahead with the fault. */
+
+ inc_mm_counter(mm, anon_rss);
++ ub_percpu_inc(mm->mm_ub, swapin);
+ pte = mk_pte(page, vma->vm_page_prot);
+ if (write_access && can_share_swap_page(page)) {
+ pte = maybe_mkwrite(pte_mkdirty(pte), vma);
+@@ -2322,10 +2398,11 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ flush_icache_page(vma, page);
+ set_pte_at(mm, address, page_table, pte);
+ page_add_anon_rmap(page, vma, address);
++ pb_add_ref(page, mm, &pbc);
++ ub_unused_privvm_dec(mm, vma);
+
+ swap_free(entry);
+- if (vm_swap_full())
+- remove_exclusive_swap_page(page);
++ try_to_remove_exclusive_swap_page(page);
+ unlock_page(page);
+
+ if (write_access) {
+@@ -2340,10 +2417,16 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ unlock:
+ pte_unmap_unlock(page_table, ptl);
+ out:
++ pb_free(&pbc);
++ spin_lock_irq(&kstat_glb_lock);
++ KSTAT_LAT_ADD(&kstat_glob.swap_in, get_cycles() - start);
++ spin_unlock_irq(&kstat_glb_lock);
++out_nostat:
+ return ret;
+ out_nomap:
+ mem_cgroup_uncharge_page(page);
+ pte_unmap_unlock(page_table, ptl);
++ pb_free(&pbc);
+ unlock_page(page);
+ page_cache_release(page);
+ return ret;
+@@ -2361,10 +2444,14 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ struct page *page;
+ spinlock_t *ptl;
+ pte_t entry;
++ struct page_beancounter *pbc;
+
+ /* Allocate our own private page. */
+ pte_unmap(page_table);
+
++ if (unlikely(pb_alloc(&pbc)))
++ goto oom_nopb;
++
+ if (unlikely(anon_vma_prepare(vma)))
+ goto oom;
+ page = alloc_zeroed_user_highpage_movable(vma, address);
+@@ -2384,11 +2471,14 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ inc_mm_counter(mm, anon_rss);
+ lru_cache_add_active(page);
+ page_add_new_anon_rmap(page, vma, address);
++ pb_add_ref(page, mm, &pbc);
++ ub_unused_privvm_dec(mm, vma);
+ set_pte_at(mm, address, page_table, entry);
+
+ /* No need to invalidate - it was non-present before */
+ update_mmu_cache(vma, address, entry);
+ unlock:
++ pb_free(&pbc);
+ pte_unmap_unlock(page_table, ptl);
+ return 0;
+ release:
+@@ -2398,6 +2488,8 @@ release:
+ oom_free_page:
+ page_cache_release(page);
+ oom:
++ pb_free(&pbc);
++oom_nopb:
+ return VM_FAULT_OOM;
+ }
+
+@@ -2424,6 +2516,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ pte_t entry;
+ int anon = 0;
+ struct page *dirty_page = NULL;
++ struct page_beancounter *pbc;
+ struct vm_fault vmf;
+ int ret;
+ int page_mkwrite = 0;
+@@ -2433,9 +2526,13 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ vmf.flags = flags;
+ vmf.page = NULL;
+
++ ret = VM_FAULT_OOM;
++ if (unlikely(pb_alloc(&pbc)))
++ goto oom_nopb;
++
+ ret = vma->vm_ops->fault(vma, &vmf);
+ if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))
+- return ret;
++ goto out_fault;
+
+ /*
+ * For consistency in subsequent calls, make the faulted page always
+@@ -2516,6 +2613,8 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ */
+ /* Only go through if we didn't race with anybody else... */
+ if (likely(pte_same(*page_table, orig_pte))) {
++ struct user_beancounter *ub;
++
+ flush_icache_page(vma, page);
+ entry = mk_pte(page, vma->vm_page_prot);
+ if (flags & FAULT_FLAG_WRITE)
+@@ -2533,6 +2632,25 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ get_page(dirty_page);
+ }
+ }
++ ub = page_ub(page);
++ if (ub != NULL &&
++#ifdef CONFIG_BC_IO_ACCOUNTING
++ !((unsigned long)ub & PAGE_IO_MARK) &&
++#endif
++ ub->ub_magic == UB_MAGIC) {
++ /*
++ * WOW: Page was already charged as page_ub. This may
++ * happens for example then some driver export its low
++ * memory pages to user space. We can't account page as
++ * page_ub and page_bp at the same time. So uncharge
++ * page from UB counter.
++ */
++ WARN_ON_ONCE(1);
++ ub_page_uncharge(page, 0);
++ }
++
++ pb_add_ref(page, mm, &pbc);
++ ub_unused_privvm_dec(mm, vma);
+
+ /* no need to invalidate: a not-present page won't be cached */
+ update_mmu_cache(vma, address, entry);
+@@ -2558,7 +2676,9 @@ out_unlocked:
+ set_page_dirty_balance(dirty_page, page_mkwrite);
+ put_page(dirty_page);
+ }
+-
++out_fault:
++ pb_free(&pbc);
++oom_nopb:
+ return ret;
+ }
+
+@@ -2684,6 +2804,27 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+ pmd_t *pmd;
+ pte_t *pte;
+
++#ifdef CONFIG_VZ_GENCALLS
++ do {
++ int ret;
++#ifdef CONFIG_BEANCOUNTERS
++ struct task_beancounter *tbc;
++
++ tbc = &current->task_bc;
++ if (!test_bit(UB_AFLAG_NOTIF_PAGEIN, &mm->mm_ub->ub_aflags) &&
++ tbc->pgfault_allot) {
++ tbc->pgfault_allot--;
++ break; /* skip notifier */
++ }
++#endif
++ ret = virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_PAGEIN,
++ (void *)1);
++ if (ret & NOTIFY_FAIL)
++ return VM_FAULT_SIGBUS;
++ if (ret & NOTIFY_OK)
++ return VM_FAULT_MINOR; /* retry */
++ } while (0);
++#endif
+ __set_current_state(TASK_RUNNING);
+
+ count_vm_event(PGFAULT);
+@@ -2728,6 +2869,8 @@ int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+ }
+ #endif /* __PAGETABLE_PUD_FOLDED */
+
++EXPORT_SYMBOL_GPL(__pud_alloc);
++
+ #ifndef __PAGETABLE_PMD_FOLDED
+ /*
+ * Allocate page middle directory.
+@@ -2758,6 +2901,8 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
+ }
+ #endif /* __PAGETABLE_PMD_FOLDED */
+
++EXPORT_SYMBOL_GPL(__pmd_alloc);
++
+ int make_pages_present(unsigned long addr, unsigned long end)
+ {
+ int ret, len, write;
+@@ -2787,6 +2932,8 @@ int make_pages_present(unsigned long addr, unsigned long end)
+ return ret == len ? 0 : -ENOMEM;
+ }
+
++EXPORT_SYMBOL(make_pages_present);
++
+ #if !defined(__HAVE_ARCH_GATE_AREA)
+
+ #if defined(AT_SYSINFO_EHDR)
+diff --git a/mm/mempool.c b/mm/mempool.c
+index a46eb1b..0e1a6bf 100644
+--- a/mm/mempool.c
++++ b/mm/mempool.c
+@@ -77,6 +77,8 @@ mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
+ init_waitqueue_head(&pool->wait);
+ pool->alloc = alloc_fn;
+ pool->free = free_fn;
++ if (alloc_fn == mempool_alloc_slab)
++ kmem_mark_nocharge((struct kmem_cache *)pool_data);
+
+ /*
+ * First pre-allocate the guaranteed number of buffers.
+@@ -118,6 +120,7 @@ int mempool_resize(mempool_t *pool, int new_min_nr, gfp_t gfp_mask)
+ unsigned long flags;
+
+ BUG_ON(new_min_nr <= 0);
++ gfp_mask &= ~__GFP_UBC;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ if (new_min_nr <= pool->min_nr) {
+@@ -211,6 +214,7 @@ void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
+ gfp_mask |= __GFP_NOMEMALLOC; /* don't allocate emergency reserves */
+ gfp_mask |= __GFP_NORETRY; /* don't loop in __alloc_pages */
+ gfp_mask |= __GFP_NOWARN; /* failures are OK */
++ gfp_mask &= ~__GFP_UBC;
+
+ gfp_temp = gfp_mask & ~(__GFP_WAIT|__GFP_IO);
+
+diff --git a/mm/mlock.c b/mm/mlock.c
+index 01fbe93..0488f60 100644
+--- a/mm/mlock.c
++++ b/mm/mlock.c
+@@ -8,10 +8,12 @@
+ #include <linux/capability.h>
+ #include <linux/mman.h>
+ #include <linux/mm.h>
++#include <linux/module.h>
+ #include <linux/mempolicy.h>
+ #include <linux/syscalls.h>
+ #include <linux/sched.h>
+ #include <linux/module.h>
++#include <bc/vmpages.h>
+
+ int can_do_mlock(void)
+ {
+@@ -36,6 +38,14 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
+ goto out;
+ }
+
++ if (newflags & VM_LOCKED) {
++ ret = ub_locked_charge(mm, end - start);
++ if (ret < 0) {
++ *prev = vma;
++ goto out;
++ }
++ }
++
+ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+ *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
+ vma->vm_file, pgoff, vma_policy(vma));
+@@ -49,13 +59,13 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
+ if (start != vma->vm_start) {
+ ret = split_vma(mm, vma, start, 1);
+ if (ret)
+- goto out;
++ goto out_uncharge;
+ }
+
+ if (end != vma->vm_end) {
+ ret = split_vma(mm, vma, end, 0);
+ if (ret)
+- goto out;
++ goto out_uncharge;
+ }
+
+ success:
+@@ -74,11 +84,17 @@ success:
+ pages = -pages;
+ if (!(newflags & VM_IO))
+ ret = make_pages_present(start, end);
+- }
++ } else
++ ub_locked_uncharge(mm, end - start);
+
+ mm->locked_vm -= pages;
+ out:
+ return ret;
++
++out_uncharge:
++ if (newflags & VM_LOCKED)
++ ub_locked_uncharge(mm, end - start);
++ goto out;
+ }
+
+ static int do_mlock(unsigned long start, size_t len, int on)
+@@ -155,6 +171,7 @@ asmlinkage long sys_mlock(unsigned long start, size_t len)
+ up_write(&current->mm->mmap_sem);
+ return error;
+ }
++EXPORT_SYMBOL_GPL(sys_mlock);
+
+ asmlinkage long sys_munlock(unsigned long start, size_t len)
+ {
+@@ -167,6 +184,7 @@ asmlinkage long sys_munlock(unsigned long start, size_t len)
+ up_write(&current->mm->mmap_sem);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(sys_munlock);
+
+ static int do_mlockall(int flags)
+ {
+diff --git a/mm/mmap.c b/mm/mmap.c
+index e7a5a68..0e47a12 100644
+--- a/mm/mmap.c
++++ b/mm/mmap.c
+@@ -27,6 +27,7 @@
+ #include <linux/mempolicy.h>
+ #include <linux/rmap.h>
+ #include <linux/mmu_notifier.h>
++#include <linux/virtinfo.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/cacheflush.h>
+@@ -39,10 +40,13 @@
+ #define arch_mmap_check(addr, len, flags) (0)
+ #endif
+
++#include <bc/vmpages.h>
++
+ #ifndef arch_rebalance_pgtables
+ #define arch_rebalance_pgtables(addr, len) (addr)
+ #endif
+
++static unsigned long __do_brk(unsigned long addr, unsigned long len, int soft);
+ static void unmap_region(struct mm_struct *mm,
+ struct vm_area_struct *vma, struct vm_area_struct *prev,
+ unsigned long start, unsigned long end);
+@@ -108,6 +112,18 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
+
+ vm_acct_memory(pages);
+
++#ifdef CONFIG_BEANCOUNTERS
++ switch (virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_ENOUGHMEM,
++ (void *)pages)
++ & (NOTIFY_OK | NOTIFY_FAIL)) {
++ case NOTIFY_OK:
++ return 0;
++ case NOTIFY_FAIL:
++ vm_unacct_memory(pages);
++ return -ENOMEM;
++ }
++#endif
++
+ /*
+ * Sometimes we want to use more memory than we have
+ */
+@@ -232,6 +248,9 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
+ struct vm_area_struct *next = vma->vm_next;
+
+ might_sleep();
++
++ ub_memory_uncharge(vma->vm_mm, vma->vm_end - vma->vm_start,
++ vma->vm_flags, vma->vm_file);
+ if (vma->vm_ops && vma->vm_ops->close)
+ vma->vm_ops->close(vma);
+ if (vma->vm_file) {
+@@ -289,7 +308,7 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
+ goto out;
+
+ /* Ok, looks good - let it rip. */
+- if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
++ if (__do_brk(oldbrk, newbrk-oldbrk, UB_HARD) != oldbrk)
+ goto out;
+ set_brk:
+ mm->brk = brk;
+@@ -1100,6 +1119,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
+ struct rb_node **rb_link, *rb_parent;
+ unsigned long charged = 0;
+ struct inode *inode = file ? file->f_path.dentry->d_inode : NULL;
++ unsigned long ub_charged = 0;
+
+ /* Clear old maps */
+ error = -ENOMEM;
+@@ -1134,6 +1154,11 @@ munmap_back:
+ }
+ }
+
++ if (ub_memory_charge(mm, len, vm_flags, file,
++ (flags & MAP_EXECPRIO ? UB_SOFT : UB_HARD)))
++ goto charge_error;
++ ub_charged = 1;
++
+ /*
+ * Can we just expand an old private anonymous mapping?
+ * The VM_SHARED test is necessary because shmem_zero_setup
+@@ -1149,7 +1174,8 @@ munmap_back:
+ * specific mapper. the address has already been validated, but
+ * not unmapped, but the maps are removed from the list.
+ */
+- vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
++ vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL |
++ (flags & MAP_EXECPRIO ? __GFP_SOFT_UBC : 0));
+ if (!vma) {
+ error = -ENOMEM;
+ goto unacct_error;
+@@ -1179,6 +1205,19 @@ munmap_back:
+ goto unmap_and_free_vma;
+ if (vm_flags & VM_EXECUTABLE)
+ added_exe_file_vma(mm);
++ if (vm_flags != vma->vm_flags) {
++ /*
++ * ->vm_flags has been changed in f_op->mmap method.
++ * We have to recharge ub memory.
++ */
++ ub_memory_uncharge(mm, len, vm_flags, file);
++ if (ub_memory_charge(mm, len, vma->vm_flags, file,
++ (flags & MAP_EXECPRIO ? UB_SOFT : UB_HARD))) {
++ ub_charged = 0;
++ error = -ENOMEM;
++ goto unmap_and_free_vma;
++ }
++ }
+ } else if (vm_flags & VM_SHARED) {
+ error = shmem_zero_setup(vma);
+ if (error)
+@@ -1243,6 +1282,9 @@ unmap_and_free_vma:
+ free_vma:
+ kmem_cache_free(vm_area_cachep, vma);
+ unacct_error:
++ if (ub_charged)
++ ub_memory_uncharge(mm, len, vm_flags, file);
++charge_error:
+ if (charged)
+ vm_unacct_memory(charged);
+ return error;
+@@ -1565,12 +1607,16 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
+ if (is_hugepage_only_range(vma->vm_mm, new_start, size))
+ return -EFAULT;
+
++ if (ub_memory_charge(mm, grow << PAGE_SHIFT, vma->vm_flags,
++ vma->vm_file, UB_SOFT))
++ goto fail_charge;
++
+ /*
+ * Overcommit.. This must be the final test, as it will
+ * update security statistics.
+ */
+ if (security_vm_enough_memory(grow))
+- return -ENOMEM;
++ goto fail_sec;
+
+ /* Ok, everything looks good - let it rip */
+ mm->total_vm += grow;
+@@ -1578,6 +1624,11 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
+ mm->locked_vm += grow;
+ vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow);
+ return 0;
++
++fail_sec:
++ ub_memory_uncharge(mm, grow << PAGE_SHIFT, vma->vm_flags, vma->vm_file);
++fail_charge:
++ return -ENOMEM;
+ }
+
+ #if defined(CONFIG_STACK_GROWSUP) || defined(CONFIG_IA64)
+@@ -1862,6 +1913,7 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(split_vma);
+
+ /* Munmap is split into 2 main parts -- this part which finds
+ * what needs doing, and the areas themselves, which do the
+@@ -1955,7 +2007,7 @@ static inline void verify_mm_writelocked(struct mm_struct *mm)
+ * anonymous maps. eventually we may be able to do some
+ * brk-specific accounting here.
+ */
+-unsigned long do_brk(unsigned long addr, unsigned long len)
++static unsigned long __do_brk(unsigned long addr, unsigned long len, int soft)
+ {
+ struct mm_struct * mm = current->mm;
+ struct vm_area_struct * vma, * prev;
+@@ -2021,8 +2073,11 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
+ if (mm->map_count > sysctl_max_map_count)
+ return -ENOMEM;
+
++ if (ub_memory_charge(mm, len, flags, NULL, soft))
++ goto fail_charge;
++
+ if (security_vm_enough_memory(len >> PAGE_SHIFT))
+- return -ENOMEM;
++ goto fail_sec;
+
+ /* Can we just expand an old private anonymous mapping? */
+ if (vma_merge(mm, prev, addr, addr + len, flags,
+@@ -2032,11 +2087,10 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
+ /*
+ * create a vma struct for an anonymous mapping
+ */
+- vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
+- if (!vma) {
+- vm_unacct_memory(len >> PAGE_SHIFT);
+- return -ENOMEM;
+- }
++ vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL |
++ (soft == UB_SOFT ? __GFP_SOFT_UBC : 0));
++ if (!vma)
++ goto fail_alloc;
+
+ vma->vm_mm = mm;
+ vma->vm_start = addr;
+@@ -2052,8 +2106,19 @@ out:
+ make_pages_present(addr, addr + len);
+ }
+ return addr;
++
++fail_alloc:
++ vm_unacct_memory(len >> PAGE_SHIFT);
++fail_sec:
++ ub_memory_uncharge(mm, len, flags, NULL);
++fail_charge:
++ return -ENOMEM;
+ }
+
++unsigned long do_brk(unsigned long addr, unsigned long len)
++{
++ return __do_brk(addr, len, UB_SOFT);
++}
+ EXPORT_SYMBOL(do_brk);
+
+ /* Release all mmaps. */
+@@ -2231,10 +2296,11 @@ static void special_mapping_close(struct vm_area_struct *vma)
+ {
+ }
+
+-static struct vm_operations_struct special_mapping_vmops = {
++struct vm_operations_struct special_mapping_vmops = {
+ .close = special_mapping_close,
+ .fault = special_mapping_fault,
+ };
++EXPORT_SYMBOL_GPL(special_mapping_vmops);
+
+ /*
+ * Called with mm->mmap_sem held for writing.
+diff --git a/mm/mmzone.c b/mm/mmzone.c
+index 16ce8b9..e9a5958 100644
+--- a/mm/mmzone.c
++++ b/mm/mmzone.c
+@@ -13,6 +13,7 @@ struct pglist_data *first_online_pgdat(void)
+ {
+ return NODE_DATA(first_online_node);
+ }
++EXPORT_SYMBOL_GPL(first_online_pgdat);
+
+ struct pglist_data *next_online_pgdat(struct pglist_data *pgdat)
+ {
+@@ -22,6 +23,7 @@ struct pglist_data *next_online_pgdat(struct pglist_data *pgdat)
+ return NULL;
+ return NODE_DATA(nid);
+ }
++EXPORT_SYMBOL_GPL(next_online_pgdat);
+
+ /*
+ * next_zone - helper magic for for_each_zone()
+diff --git a/mm/mprotect.c b/mm/mprotect.c
+index fded06f..93d0e7c 100644
+--- a/mm/mprotect.c
++++ b/mm/mprotect.c
+@@ -9,6 +9,7 @@
+ */
+
+ #include <linux/mm.h>
++#include <linux/module.h>
+ #include <linux/hugetlb.h>
+ #include <linux/slab.h>
+ #include <linux/shm.h>
+@@ -27,6 +28,8 @@
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#include <bc/vmpages.h>
++
+ #ifndef pgprot_modify
+ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
+ {
+@@ -143,6 +146,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
+ unsigned long charged = 0;
+ pgoff_t pgoff;
+ int error;
++ unsigned long ch_size;
++ int ch_dir;
+ int dirty_accountable = 0;
+
+ if (newflags == oldflags) {
+@@ -150,6 +155,12 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
+ return 0;
+ }
+
++ error = -ENOMEM;
++ ch_size = nrpages - pages_in_vma_range(vma, start, end);
++ ch_dir = ub_protected_charge(mm, ch_size, newflags, vma);
++ if (ch_dir == PRIVVM_ERROR)
++ goto fail_ch;
++
+ /*
+ * If we make a private mapping writable we increase our commit;
+ * but (without finer accounting) cannot reduce our commit if we
+@@ -160,7 +171,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
+ VM_SHARED|VM_NORESERVE))) {
+ charged = nrpages;
+ if (security_vm_enough_memory(charged))
+- return -ENOMEM;
++ goto fail_sec;
+ newflags |= VM_ACCOUNT;
+ }
+ }
+@@ -212,10 +223,16 @@ success:
+ mmu_notifier_invalidate_range_end(mm, start, end);
+ vm_stat_account(mm, oldflags, vma->vm_file, -nrpages);
+ vm_stat_account(mm, newflags, vma->vm_file, nrpages);
++ if (ch_dir == PRIVVM_TO_SHARED)
++ __ub_unused_privvm_dec(mm, ch_size);
+ return 0;
+
+ fail:
+ vm_unacct_memory(charged);
++fail_sec:
++ if (ch_dir == PRIVVM_TO_PRIVATE)
++ __ub_unused_privvm_dec(mm, ch_size);
++fail_ch:
+ return error;
+ }
+
+@@ -317,3 +334,4 @@ out:
+ up_write(&current->mm->mmap_sem);
+ return error;
+ }
++EXPORT_SYMBOL_GPL(sys_mprotect);
+diff --git a/mm/mremap.c b/mm/mremap.c
+index 1a77439..107144a 100644
+--- a/mm/mremap.c
++++ b/mm/mremap.c
+@@ -24,6 +24,8 @@
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#include <bc/vmpages.h>
++
+ static pmd_t *get_old_pmd(struct mm_struct *mm, unsigned long addr)
+ {
+ pgd_t *pgd;
+@@ -173,17 +175,21 @@ static unsigned long move_vma(struct vm_area_struct *vma,
+ unsigned long hiwater_vm;
+ int split = 0;
+
++ if (ub_memory_charge(mm, new_len, vm_flags,
++ vma->vm_file, UB_HARD))
++ goto err;
++
+ /*
+ * We'd prefer to avoid failure later on in do_munmap:
+ * which may split one vma into three before unmapping.
+ */
+ if (mm->map_count >= sysctl_max_map_count - 3)
+- return -ENOMEM;
++ goto err_nomem;
+
+ new_pgoff = vma->vm_pgoff + ((old_addr - vma->vm_start) >> PAGE_SHIFT);
+ new_vma = copy_vma(&vma, new_addr, new_len, new_pgoff);
+ if (!new_vma)
+- return -ENOMEM;
++ goto err_nomem;
+
+ moved_len = move_page_tables(vma, old_addr, new_vma, new_addr, old_len);
+ if (moved_len < old_len) {
+@@ -242,7 +248,13 @@ static unsigned long move_vma(struct vm_area_struct *vma,
+ new_addr + new_len);
+ }
+
+- return new_addr;
++ if (new_addr != -ENOMEM)
++ return new_addr;
++
++err_nomem:
++ ub_memory_uncharge(mm, new_len, vm_flags, vma->vm_file);
++err:
++ return -ENOMEM;
+ }
+
+ /*
+@@ -370,7 +382,15 @@ unsigned long do_mremap(unsigned long addr,
+ max_addr = vma->vm_next->vm_start;
+ /* can we just expand the current mapping? */
+ if (max_addr - addr >= new_len) {
+- int pages = (new_len - old_len) >> PAGE_SHIFT;
++ unsigned long len;
++ int pages;
++
++ len = new_len - old_len;
++ pages = len >> PAGE_SHIFT;
++ ret = -ENOMEM;
++ if (ub_memory_charge(mm, len, vma->vm_flags,
++ vma->vm_file, UB_HARD))
++ goto out;
+
+ vma_adjust(vma, vma->vm_start,
+ addr + new_len, vma->vm_pgoff, NULL);
+diff --git a/mm/oom_kill.c b/mm/oom_kill.c
+index 64e5b4b..365405b 100644
+--- a/mm/oom_kill.c
++++ b/mm/oom_kill.c
+@@ -19,6 +19,8 @@
+ #include <linux/mm.h>
+ #include <linux/err.h>
+ #include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/virtinfo.h>
+ #include <linux/swap.h>
+ #include <linux/timex.h>
+ #include <linux/jiffies.h>
+@@ -28,6 +30,9 @@
+ #include <linux/memcontrol.h>
+ #include <linux/security.h>
+
++#include <bc/beancounter.h>
++#include <bc/oom_kill.h>
++
+ int sysctl_panic_on_oom;
+ int sysctl_oom_kill_allocating_task;
+ int sysctl_oom_dump_tasks;
+@@ -200,16 +205,16 @@ static inline enum oom_constraint constrained_alloc(struct zonelist *zonelist,
+ *
+ * (not docbooked, we don't want this one cluttering up the manual)
+ */
+-static struct task_struct *select_bad_process(unsigned long *ppoints,
++struct task_struct *select_bad_process(struct user_beancounter *ub,
+ struct mem_cgroup *mem)
+ {
+ struct task_struct *g, *p;
+ struct task_struct *chosen = NULL;
+ struct timespec uptime;
+- *ppoints = 0;
++ unsigned long chosen_points = 0;
+
+ do_posix_clock_monotonic_gettime(&uptime);
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ unsigned long points;
+
+ /*
+@@ -223,6 +228,8 @@ static struct task_struct *select_bad_process(unsigned long *ppoints,
+ continue;
+ if (mem && !task_in_mem_cgroup(p, mem))
+ continue;
++ if (ub_oom_task_skip(ub, p))
++ continue;
+
+ /*
+ * This task already has access to memory reserves and is
+@@ -251,18 +258,18 @@ static struct task_struct *select_bad_process(unsigned long *ppoints,
+ return ERR_PTR(-1UL);
+
+ chosen = p;
+- *ppoints = ULONG_MAX;
++ chosen_points = ULONG_MAX;
+ }
+
+ if (p->oomkilladj == OOM_DISABLE)
+ continue;
+
+ points = badness(p, uptime.tv_sec);
+- if (points > *ppoints || !chosen) {
++ if (points > chosen_points || !chosen) {
+ chosen = p;
+- *ppoints = points;
++ chosen_points = points;
+ }
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+
+ return chosen;
+ }
+@@ -286,7 +293,7 @@ static void dump_tasks(const struct mem_cgroup *mem)
+
+ printk(KERN_INFO "[ pid ] uid tgid total_vm rss cpu oom_adj "
+ "name\n");
+- do_each_thread(g, p) {
++ do_each_thread_all(g, p) {
+ /*
+ * total_vm and rss sizes do not exist for tasks with a
+ * detached mm so there's no need to report them.
+@@ -302,7 +309,7 @@ static void dump_tasks(const struct mem_cgroup *mem)
+ get_mm_rss(p->mm), (int)task_cpu(p), p->oomkilladj,
+ p->comm);
+ task_unlock(p);
+- } while_each_thread(g, p);
++ } while_each_thread_all(g, p);
+ }
+
+ /*
+@@ -337,13 +344,16 @@ static void __oom_kill_task(struct task_struct *p, int verbose)
+ set_tsk_thread_flag(p, TIF_MEMDIE);
+
+ force_sig(SIGKILL, p);
++ ub_oom_task_killed(p);
+ }
+
+ static int oom_kill_task(struct task_struct *p)
+ {
+ struct mm_struct *mm;
++ struct user_beancounter *ub;
+ struct task_struct *g, *q;
+
++ task_lock(p);
+ mm = p->mm;
+
+ /* WARNING: mm may not be dereferenced since we did not obtain its
+@@ -355,16 +365,21 @@ static int oom_kill_task(struct task_struct *p)
+ * However, this is of no concern to us.
+ */
+
+- if (mm == NULL)
++ if (mm == NULL) {
++ task_unlock(p);
+ return 1;
++ }
++
++ ub = get_beancounter(mm_ub(mm));
++ task_unlock(p);
+
+ /*
+ * Don't kill the process if any threads are set to OOM_DISABLE
+ */
+- do_each_thread(g, q) {
++ do_each_thread_all(g, q) {
+ if (q->mm == mm && q->oomkilladj == OOM_DISABLE)
+ return 1;
+- } while_each_thread(g, q);
++ } while_each_thread_all(g, q);
+
+ __oom_kill_task(p, 1);
+
+@@ -373,17 +388,18 @@ static int oom_kill_task(struct task_struct *p)
+ * but are in a different thread group. Don't let them have access
+ * to memory reserves though, otherwise we might deplete all memory.
+ */
+- do_each_thread(g, q) {
++ do_each_thread_all(g, q) {
+ if (q->mm == mm && !same_thread_group(q, p))
+ force_sig(SIGKILL, q);
+- } while_each_thread(g, q);
++ } while_each_thread_all(g, q);
+
++ ub_oom_mm_killed(ub);
++ put_beancounter(ub);
+ return 0;
+ }
+
+-static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+- unsigned long points, struct mem_cgroup *mem,
+- const char *message)
++int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
++ struct mem_cgroup *mem, const char *message)
+ {
+ struct task_struct *c;
+
+@@ -406,8 +422,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+ return 0;
+ }
+
+- printk(KERN_ERR "%s: kill process %d (%s) score %li or a child\n",
+- message, task_pid_nr(p), p->comm, points);
++ printk(KERN_ERR "%s: kill process %d (%s) or a child\n",
++ message, task_pid_nr(p), p->comm);
+
+ /* Try to kill a child first */
+ list_for_each_entry(c, &p->children, sibling) {
+@@ -522,9 +538,9 @@ void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_mask)
+ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order)
+ {
+ struct task_struct *p;
+- unsigned long points = 0;
+ unsigned long freed = 0;
+ enum oom_constraint constraint;
++ struct user_beancounter *ub;
+
+ blocking_notifier_call_chain(&oom_notify_list, 0, &freed);
+ if (freed > 0)
+@@ -534,16 +550,34 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order)
+ if (sysctl_panic_on_oom == 2)
+ panic("out of memory. Compulsory panic_on_oom is selected.\n");
+
++ if (virtinfo_notifier_call(VITYPE_GENERAL, VIRTINFO_OUTOFMEM, NULL)
++ & (NOTIFY_OK | NOTIFY_FAIL))
++ return;
++
++ ub = NULL;
++ if (ub_oom_lock())
++ goto out_oom_lock;
++
++ read_lock(&tasklist_lock);
++
++ if (printk_ratelimit()) {
++ printk(KERN_WARNING "%s invoked oom-killer: "
++ "gfp_mask=0x%x, order=%d, oomkilladj=%d\n",
++ current->comm, gfp_mask, order, current->oomkilladj);
++ dump_stack();
++ show_mem();
++ show_slab_info();
++ }
++
+ /*
+ * Check if there were limitations on the allocation (only relevant for
+ * NUMA) that may require different handling.
+ */
+ constraint = constrained_alloc(zonelist, gfp_mask);
+- read_lock(&tasklist_lock);
+
+ switch (constraint) {
+ case CONSTRAINT_MEMORY_POLICY:
+- oom_kill_process(current, gfp_mask, order, points, NULL,
++ oom_kill_process(current, gfp_mask, order, NULL,
+ "No available memory (MPOL_BIND)");
+ break;
+
+@@ -553,27 +587,33 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order)
+ /* Fall-through */
+ case CONSTRAINT_CPUSET:
+ if (sysctl_oom_kill_allocating_task) {
+- oom_kill_process(current, gfp_mask, order, points, NULL,
++ oom_kill_process(current, gfp_mask, order, NULL,
+ "Out of memory (oom_kill_allocating_task)");
+ break;
+ }
+ retry:
++ put_beancounter(ub);
++
+ /*
+ * Rambo mode: Shoot down a process and hope it solves whatever
+ * issues we may have.
+ */
+- p = select_bad_process(&points, NULL);
++ ub = ub_oom_select_worst();
++ p = select_bad_process(ub, NULL);
+
+ if (PTR_ERR(p) == -1UL)
+ goto out;
+
+ /* Found nothing?!?! Either we hang forever, or we panic. */
+ if (!p) {
++ if (ub != NULL)
++ goto retry;
+ read_unlock(&tasklist_lock);
++ ub_oom_unlock();
+ panic("Out of memory and no killable processes...\n");
+ }
+
+- if (oom_kill_process(p, gfp_mask, order, points, NULL,
++ if (oom_kill_process(p, gfp_mask, order, NULL,
+ "Out of memory"))
+ goto retry;
+
+@@ -582,7 +622,10 @@ retry:
+
+ out:
+ read_unlock(&tasklist_lock);
++ ub_oom_unlock();
++ put_beancounter(ub);
+
++out_oom_lock:
+ /*
+ * Give "p" a good chance of killing itself before we
+ * retry to allocate memory unless "p" is current
+diff --git a/mm/page-writeback.c b/mm/page-writeback.c
+index 24de8b6..7e20345 100644
+--- a/mm/page-writeback.c
++++ b/mm/page-writeback.c
+@@ -35,6 +35,9 @@
+ #include <linux/buffer_head.h>
+ #include <linux/pagevec.h>
+
++#include <bc/io_prio.h>
++#include <bc/io_acct.h>
++
+ /*
+ * The maximum number of pages to writeout in a single bdflush/kupdate
+ * operation. We do this so we don't hold I_SYNC against an inode for
+@@ -903,6 +906,7 @@ retry:
+ scanned = 1;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = pvec.pages[i];
++ struct user_beancounter *old_ub;
+
+ /*
+ * At this point we hold neither mapping->tree_lock nor
+@@ -933,7 +937,9 @@ retry:
+ continue;
+ }
+
++ old_ub = bc_io_switch_context(page);
+ ret = (*writepage)(page, wbc, data);
++ bc_io_restore_context(old_ub);
+
+ if (unlikely(ret == AOP_WRITEPAGE_ACTIVATE)) {
+ unlock_page(page);
+@@ -1032,12 +1038,15 @@ int write_one_page(struct page *page, int wait)
+ .sync_mode = WB_SYNC_ALL,
+ .nr_to_write = 1,
+ };
++ struct user_beancounter *old_ub;
+
+ BUG_ON(!PageLocked(page));
+
+ if (wait)
+ wait_on_page_writeback(page);
+
++ old_ub = bc_io_switch_context(page);
++
+ if (clear_page_dirty_for_io(page)) {
+ page_cache_get(page);
+ ret = mapping->a_ops->writepage(page, &wbc);
+@@ -1050,6 +1059,9 @@ int write_one_page(struct page *page, int wait)
+ } else {
+ unlock_page(page);
+ }
++
++ bc_io_restore_context(old_ub);
++
+ return ret;
+ }
+ EXPORT_SYMBOL(write_one_page);
+@@ -1081,6 +1093,9 @@ int __set_page_dirty_no_writeback(struct page *page)
+ */
+ int __set_page_dirty_nobuffers(struct page *page)
+ {
++ int acct;
++
++ acct = 0;
+ if (!TestSetPageDirty(page)) {
+ struct address_space *mapping = page_mapping(page);
+ struct address_space *mapping2;
+@@ -1088,6 +1103,7 @@ int __set_page_dirty_nobuffers(struct page *page)
+ if (!mapping)
+ return 1;
+
++ acct = 0;
+ spin_lock_irq(&mapping->tree_lock);
+ mapping2 = page_mapping(page);
+ if (mapping2) { /* Race with truncate? */
+@@ -1097,12 +1113,14 @@ int __set_page_dirty_nobuffers(struct page *page)
+ __inc_zone_page_state(page, NR_FILE_DIRTY);
+ __inc_bdi_stat(mapping->backing_dev_info,
+ BDI_RECLAIMABLE);
+- task_io_account_write(PAGE_CACHE_SIZE);
++ acct = 1;
+ }
+ radix_tree_tag_set(&mapping->page_tree,
+ page_index(page), PAGECACHE_TAG_DIRTY);
+ }
+ spin_unlock_irq(&mapping->tree_lock);
++ if (acct)
++ task_io_account_write(page, PAGE_CACHE_SIZE, 0);
+ if (mapping->host) {
+ /* !PageAnon && !swapper_space */
+ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+@@ -1241,6 +1259,7 @@ int clear_page_dirty_for_io(struct page *page)
+ dec_zone_page_state(page, NR_FILE_DIRTY);
+ dec_bdi_stat(mapping->backing_dev_info,
+ BDI_RECLAIMABLE);
++ ub_io_release_context(page, PAGE_CACHE_SIZE);
+ return 1;
+ }
+ return 0;
+diff --git a/mm/page_alloc.c b/mm/page_alloc.c
+index 27b8681..0654364 100644
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -51,6 +51,9 @@
+ #include <asm/div64.h>
+ #include "internal.h"
+
++#include <bc/kmem.h>
++#include <bc/io_acct.h>
++
+ /*
+ * Array of node states.
+ */
+@@ -102,6 +105,7 @@ int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1] = {
+ 32,
+ };
+
++EXPORT_SYMBOL(nr_swap_pages);
+ EXPORT_SYMBOL(totalram_pages);
+
+ static char * const zone_names[MAX_NR_ZONES] = {
+@@ -460,8 +464,11 @@ static inline int free_pages_check(struct page *page)
+ (page_count(page) != 0) |
+ (page->flags & PAGE_FLAGS_CHECK_AT_FREE)))
+ bad_page(page);
+- if (PageDirty(page))
++ if (PageDirty(page)) {
++ ub_io_release_context(page, 0);
+ __ClearPageDirty(page);
++ } else
++ ub_io_release_debug(page);
+ /*
+ * For now, we report if PG_reserved was found set, but do not
+ * clear it, and do not free the page. But we shall soon need
+@@ -527,6 +534,7 @@ static void __free_pages_ok(struct page *page, unsigned int order)
+ arch_free_page(page, order);
+ kernel_map_pages(page, 1 << order, 0);
+
++ ub_page_uncharge(page, order);
+ local_irq_save(flags);
+ __count_vm_events(PGFREE, 1 << order);
+ free_one_page(page_zone(page), page, order);
+@@ -987,6 +995,7 @@ static void free_hot_cold_page(struct page *page, int cold)
+ kernel_map_pages(page, 1, 0);
+
+ pcp = &zone_pcp(zone, get_cpu())->pcp;
++ ub_page_uncharge(page, 0);
+ local_irq_save(flags);
+ __count_vm_event(PGFREE);
+ if (cold)
+@@ -1434,6 +1443,31 @@ try_next_zone:
+ return page;
+ }
+
++extern unsigned long cycles_per_jiffy;
++static void __alloc_collect_stats(gfp_t gfp_mask, unsigned int order,
++ struct page *page, cycles_t time)
++{
++#ifdef CONFIG_VE
++ int ind;
++ unsigned long flags;
++
++ time = (jiffies - time) * cycles_per_jiffy;
++ if (!(gfp_mask & __GFP_WAIT))
++ ind = 0;
++ else if (!(gfp_mask & __GFP_HIGHMEM))
++ ind = (order > 0 ? 2 : 1);
++ else
++ ind = (order > 0 ? 4 : 3);
++ spin_lock_irqsave(&kstat_glb_lock, flags);
++ KSTAT_LAT_ADD(&kstat_glob.alloc_lat[ind], time);
++ if (!page)
++ kstat_glob.alloc_fails[ind]++;
++ spin_unlock_irqrestore(&kstat_glb_lock, flags);
++#endif
++}
++
++int alloc_fail_warn;
++
+ /*
+ * This is the 'heart' of the zoned buddy allocator.
+ */
+@@ -1452,6 +1486,7 @@ __alloc_pages_internal(gfp_t gfp_mask, unsigned int order,
+ int alloc_flags;
+ unsigned long did_some_progress;
+ unsigned long pages_reclaimed = 0;
++ cycles_t start;
+
+ might_sleep_if(wait);
+
+@@ -1469,6 +1504,7 @@ restart:
+ return NULL;
+ }
+
++ start = jiffies;
+ page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,
+ zonelist, high_zoneidx, ALLOC_WMARK_LOW|ALLOC_CPUSET);
+ if (page)
+@@ -1625,19 +1661,32 @@ nofail_alloc:
+ do_retry = 1;
+ }
+ if (do_retry) {
++ if (total_swap_pages > 0 && nr_swap_pages == 0) {
++ out_of_memory(zonelist, gfp_mask, order);
++ goto restart;
++ }
+ congestion_wait(WRITE, HZ/50);
+ goto rebalance;
+ }
+
+ nopage:
+- if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
++ __alloc_collect_stats(gfp_mask, order, NULL, start);
++ if (alloc_fail_warn && !(gfp_mask & __GFP_NOWARN) &&
++ printk_ratelimit()) {
+ printk(KERN_WARNING "%s: page allocation failure."
+ " order:%d, mode:0x%x\n",
+ p->comm, order, gfp_mask);
+ dump_stack();
+ show_mem();
+ }
++ return NULL;
++
+ got_pg:
++ __alloc_collect_stats(gfp_mask, order, page, start);
++ if (ub_page_charge(page, order, gfp_mask)) {
++ __free_pages(page, order);
++ page = NULL;
++ }
+ return page;
+ }
+ EXPORT_SYMBOL(__alloc_pages_internal);
+diff --git a/mm/rmap.c b/mm/rmap.c
+index 0383acf..3523db0 100644
+--- a/mm/rmap.c
++++ b/mm/rmap.c
+@@ -51,6 +51,9 @@
+ #include <linux/memcontrol.h>
+ #include <linux/mmu_notifier.h>
+
++#include <bc/beancounter.h>
++#include <bc/vmpages.h>
++
+ #include <asm/tlbflush.h>
+
+ struct kmem_cache *anon_vma_cachep;
+@@ -94,6 +97,7 @@ int anon_vma_prepare(struct vm_area_struct *vma)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(anon_vma_prepare);
+
+ void __anon_vma_merge(struct vm_area_struct *vma, struct vm_area_struct *next)
+ {
+@@ -119,6 +123,7 @@ void anon_vma_link(struct vm_area_struct *vma)
+ spin_unlock(&anon_vma->lock);
+ }
+ }
++EXPORT_SYMBOL_GPL(anon_vma_link);
+
+ void anon_vma_unlink(struct vm_area_struct *vma)
+ {
+@@ -150,14 +155,14 @@ static void anon_vma_ctor(void *data)
+ void __init anon_vma_init(void)
+ {
+ anon_vma_cachep = kmem_cache_create("anon_vma", sizeof(struct anon_vma),
+- 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC, anon_vma_ctor);
++ 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC|SLAB_UBC, anon_vma_ctor);
+ }
+
+ /*
+ * Getting a lock on a stable anon_vma from a page off the LRU is
+ * tricky: page_lock_anon_vma rely on RCU to guard against the races.
+ */
+-static struct anon_vma *page_lock_anon_vma(struct page *page)
++struct anon_vma *page_lock_anon_vma(struct page *page)
+ {
+ struct anon_vma *anon_vma;
+ unsigned long anon_mapping;
+@@ -176,12 +181,14 @@ out:
+ rcu_read_unlock();
+ return NULL;
+ }
++EXPORT_SYMBOL_GPL(page_lock_anon_vma);
+
+-static void page_unlock_anon_vma(struct anon_vma *anon_vma)
++void page_unlock_anon_vma(struct anon_vma *anon_vma)
+ {
+ spin_unlock(&anon_vma->lock);
+ rcu_read_unlock();
+ }
++EXPORT_SYMBOL_GPL(page_unlock_anon_vma);
+
+ /*
+ * At what user virtual address is page expected in @vma?
+@@ -676,6 +683,12 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma)
+ }
+
+ mem_cgroup_uncharge_page(page);
++ /*
++ * Well, when a page is unmapped, we cannot keep PG_checkpointed
++ * flag, it is not accessible via process VM and we have no way
++ * to reset its state
++ */
++ ClearPageCheckpointed(page);
+ __dec_zone_page_state(page,
+ PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
+ /*
+@@ -776,6 +789,9 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
+
+
+ page_remove_rmap(page, vma);
++ ub_unused_privvm_inc(mm, vma);
++ ub_percpu_inc(mm->mm_ub, unmap);
++ pb_remove_ref(page, mm);
+ page_cache_release(page);
+
+ out_unmap:
+@@ -866,6 +882,9 @@ static void try_to_unmap_cluster(unsigned long cursor,
+ set_page_dirty(page);
+
+ page_remove_rmap(page, vma);
++ ub_percpu_inc(mm->mm_ub, unmap);
++ pb_remove_ref(page, mm);
++ ub_unused_privvm_inc(mm, vma);
+ page_cache_release(page);
+ dec_mm_counter(mm, file_rss);
+ (*mapcount)--;
+diff --git a/mm/shmem.c b/mm/shmem.c
+index 04fb4f1..87b813a 100644
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -55,6 +55,8 @@
+ #include <asm/div64.h>
+ #include <asm/pgtable.h>
+
++#include <bc/vmpages.h>
++
+ /* This magic number is used in glibc for posix shared memory */
+ #define TMPFS_MAGIC 0x01021994
+
+@@ -193,7 +195,7 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages)
+
+ static const struct super_operations shmem_ops;
+ static const struct address_space_operations shmem_aops;
+-static const struct file_operations shmem_file_operations;
++const struct file_operations shmem_file_operations;
+ static const struct inode_operations shmem_inode_operations;
+ static const struct inode_operations shmem_dir_inode_operations;
+ static const struct inode_operations shmem_special_inode_operations;
+@@ -256,7 +258,7 @@ static void shmem_free_inode(struct super_block *sb)
+ *
+ * It has to be called with the spinlock held.
+ */
+-static void shmem_recalc_inode(struct inode *inode)
++static void shmem_recalc_inode(struct inode *inode, long swp_freed)
+ {
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ long freed;
+@@ -266,6 +268,8 @@ static void shmem_recalc_inode(struct inode *inode)
+ info->alloced -= freed;
+ shmem_unacct_blocks(info->flags, freed);
+ shmem_free_blocks(inode, freed);
++ if (freed > swp_freed)
++ ub_tmpfs_respages_sub(info, freed - swp_freed);
+ }
+ }
+
+@@ -370,6 +374,11 @@ static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, uns
+ struct page *page = kmap_atomic_to_page(entry);
+ set_page_private(page, page_private(page) + incdec);
+ }
++
++ if (incdec == 1)
++ ub_tmpfs_respages_dec(info);
++ else
++ ub_tmpfs_respages_inc(info);
+ }
+
+ /**
+@@ -386,14 +395,24 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ struct page *page = NULL;
+ swp_entry_t *entry;
++ unsigned long ub_val;
+
+ if (sgp != SGP_WRITE &&
+ ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode))
+ return ERR_PTR(-EINVAL);
+
++ ub_val = 0;
++ if (info->next_index <= index) {
++ ub_val = index + 1 - info->next_index;
++ if (ub_shmpages_charge(info, ub_val))
++ return ERR_PTR(-ENOSPC);
++ }
++
+ while (!(entry = shmem_swp_entry(info, index, &page))) {
+- if (sgp == SGP_READ)
+- return shmem_swp_map(ZERO_PAGE(0));
++ if (sgp == SGP_READ) {
++ entry = shmem_swp_map(ZERO_PAGE(0));
++ goto out;
++ }
+ /*
+ * Test free_blocks against 1 not 0, since we have 1 data
+ * page (and perhaps indirect index pages) yet to allocate:
+@@ -403,7 +422,8 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long
+ spin_lock(&sbinfo->stat_lock);
+ if (sbinfo->free_blocks <= 1) {
+ spin_unlock(&sbinfo->stat_lock);
+- return ERR_PTR(-ENOSPC);
++ entry = ERR_PTR(-ENOSPC);
++ goto out;
+ }
+ sbinfo->free_blocks--;
+ inode->i_blocks += BLOCKS_PER_PAGE;
+@@ -411,31 +431,43 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long
+ }
+
+ spin_unlock(&info->lock);
+- page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping));
++ page = shmem_dir_alloc(mapping_gfp_mask(inode->i_mapping) |
++ __GFP_UBC);
+ if (page)
+ set_page_private(page, 0);
+ spin_lock(&info->lock);
+
+ if (!page) {
+- shmem_free_blocks(inode, 1);
+- return ERR_PTR(-ENOMEM);
++ entry = ERR_PTR(-ENOMEM);
++ goto out_block;
+ }
+ if (sgp != SGP_WRITE &&
+ ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+ entry = ERR_PTR(-EINVAL);
+- break;
++ goto out_dir;
+ }
+- if (info->next_index <= index)
++ if (info->next_index <= index) {
++ ub_val = 0;
+ info->next_index = index + 1;
++ }
+ }
+ if (page) {
+ /* another task gave its page, or truncated the file */
+ shmem_free_blocks(inode, 1);
+ shmem_dir_free(page);
+ }
+- if (info->next_index <= index && !IS_ERR(entry))
++ if (info->next_index <= index)
+ info->next_index = index + 1;
+ return entry;
++
++out_dir:
++ shmem_dir_free(page);
++out_block:
++ shmem_free_blocks(inode, 1);
++out:
++ if (ub_val)
++ ub_shmpages_uncharge(info, ub_val);
++ return entry;
+ }
+
+ /**
+@@ -543,6 +575,7 @@ static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
+ return;
+
+ spin_lock(&info->lock);
++ ub_shmpages_uncharge(info, info->next_index - idx);
+ info->flags |= SHMEM_TRUNCATE;
+ if (likely(end == (loff_t) -1)) {
+ limit = info->next_index;
+@@ -729,7 +762,7 @@ done2:
+ info->swapped -= nr_swaps_freed;
+ if (nr_pages_to_free)
+ shmem_free_blocks(inode, nr_pages_to_free);
+- shmem_recalc_inode(inode);
++ shmem_recalc_inode(inode, nr_swaps_freed);
+ spin_unlock(&info->lock);
+
+ /*
+@@ -812,6 +845,7 @@ static void shmem_delete_inode(struct inode *inode)
+ }
+ }
+ BUG_ON(inode->i_blocks);
++ shmi_ub_put(info);
+ shmem_free_inode(inode->i_sb);
+ clear_inode(inode);
+ }
+@@ -995,6 +1029,12 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
+ out: return found; /* 0 or 1 or -ENOMEM */
+ }
+
++#ifdef CONFIG_BEANCOUNTERS
++#define shm_get_swap_page(info) (get_swap_page((info)->shmi_ub))
++#else
++#define shm_get_swap_page(info) (get_swap_page(NULL))
++#endif
++
+ /*
+ * Move the page from the page cache to the swap cache.
+ */
+@@ -1025,7 +1065,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
+ * want to check if there's a redundant swappage to be discarded.
+ */
+ if (wbc->for_reclaim)
+- swap = get_swap_page();
++ swap = shm_get_swap_page(info);
+ else
+ swap.val = 0;
+
+@@ -1043,7 +1083,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
+ free_swap_and_cache(*entry);
+ shmem_swp_set(info, entry, 0);
+ }
+- shmem_recalc_inode(inode);
++ shmem_recalc_inode(inode, 0);
+
+ if (swap.val && add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
+ remove_from_page_cache(page);
+@@ -1081,6 +1121,54 @@ redirty:
+ return 0;
+ }
+
++/* Insert a swap entry to shmem inode address space. */
++int shmem_insertpage(struct inode * inode, unsigned long index,
++ swp_entry_t swap)
++{
++ struct shmem_inode_info *info;
++ swp_entry_t *entry;
++ int err;
++
++ info = SHMEM_I(inode);
++
++ spin_lock(&info->lock);
++ shmem_recalc_inode(inode, 0);
++ entry = shmem_swp_alloc(info, index, SGP_WRITE);
++ err = PTR_ERR(entry);
++ if (IS_ERR(entry))
++ goto unlock;
++
++ err = -EBUSY;
++ if (entry->val)
++ goto unlock_unmap;
++
++ err = -EINVAL;
++ if (!swap_duplicate(swap))
++ goto unlock_unmap;
++
++ info->alloced++;
++ ub_tmpfs_respages_inc(info);
++ inode->i_blocks += BLOCKS_PER_PAGE;
++ shmem_swp_set(info, entry, swap.val);
++ shmem_swp_unmap(entry);
++ spin_unlock(&info->lock);
++ if (list_empty(&info->swaplist)) {
++ mutex_lock(&shmem_swaplist_mutex);
++ /* move instead of add in case we're racing */
++ list_move_tail(&info->swaplist, &shmem_swaplist);
++ mutex_unlock(&shmem_swaplist_mutex);
++ }
++ return 0;
++
++unlock_unmap:
++ shmem_swp_unmap(entry);
++unlock:
++ spin_unlock(&info->lock);
++ return err;
++}
++EXPORT_SYMBOL(shmem_insertpage);
++
++
+ #ifdef CONFIG_NUMA
+ #ifdef CONFIG_TMPFS
+ static void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
+@@ -1223,7 +1311,7 @@ repeat:
+ }
+
+ spin_lock(&info->lock);
+- shmem_recalc_inode(inode);
++ shmem_recalc_inode(inode, 0);
+ entry = shmem_swp_alloc(info, idx, sgp);
+ if (IS_ERR(entry)) {
+ spin_unlock(&info->lock);
+@@ -1417,6 +1505,7 @@ repeat:
+ clear_highpage(filepage);
+ flush_dcache_page(filepage);
+ SetPageUptodate(filepage);
++ ub_tmpfs_respages_inc(info);
+ if (sgp == SGP_DIRTY)
+ set_page_dirty(filepage);
+ }
+@@ -1518,6 +1607,7 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
+ inode->i_generation = get_seconds();
+ info = SHMEM_I(inode);
+ memset(info, 0, (char *)inode - (char *)info);
++ shmi_ub_set(info, get_exec_ub());
+ spin_lock_init(&info->lock);
+ INIT_LIST_HEAD(&info->swaplist);
+
+@@ -2388,7 +2478,7 @@ static const struct address_space_operations shmem_aops = {
+ .migratepage = migrate_page,
+ };
+
+-static const struct file_operations shmem_file_operations = {
++const struct file_operations shmem_file_operations = {
+ .mmap = shmem_mmap,
+ #ifdef CONFIG_TMPFS
+ .llseek = generic_file_llseek,
+@@ -2401,6 +2491,7 @@ static const struct file_operations shmem_file_operations = {
+ .splice_write = generic_file_splice_write,
+ #endif
+ };
++EXPORT_SYMBOL_GPL(shmem_file_operations);
+
+ static const struct inode_operations shmem_inode_operations = {
+ .truncate = shmem_truncate,
+@@ -2470,6 +2561,10 @@ static struct vm_operations_struct shmem_vm_ops = {
+ #endif
+ };
+
++int is_shmem_mapping(struct address_space *map)
++{
++ return (map != NULL && map->a_ops == &shmem_aops);
++}
+
+ static int shmem_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+@@ -2477,13 +2572,19 @@ static int shmem_get_sb(struct file_system_type *fs_type,
+ return get_sb_nodev(fs_type, flags, data, shmem_fill_super, mnt);
+ }
+
+-static struct file_system_type tmpfs_fs_type = {
++struct file_system_type tmpfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "tmpfs",
+ .get_sb = shmem_get_sb,
+ .kill_sb = kill_litter_super,
+ };
++EXPORT_SYMBOL(tmpfs_fs_type);
++
++#ifdef CONFIG_VE
++#define shm_mnt (get_exec_env()->shmem_mnt)
++#else
+ static struct vfsmount *shm_mnt;
++#endif
+
+ static int __init init_tmpfs(void)
+ {
+@@ -2524,6 +2625,36 @@ out4:
+ }
+ module_init(init_tmpfs)
+
++static inline int shm_charge_ahead(struct inode *inode)
++{
++#ifdef CONFIG_BEANCOUNTERS
++ struct shmem_inode_info *info = SHMEM_I(inode);
++ unsigned long idx;
++ swp_entry_t *entry;
++
++ if (!inode->i_size)
++ return 0;
++ idx = (inode->i_size - 1) >> PAGE_CACHE_SHIFT;
++ /*
++ * Just touch info to allocate space for entry and
++ * make all UBC checks
++ */
++ spin_lock(&info->lock);
++ entry = shmem_swp_alloc(info, idx, SGP_CACHE);
++ if (IS_ERR(entry))
++ goto err;
++ shmem_swp_unmap(entry);
++ spin_unlock(&info->lock);
++ return 0;
++
++err:
++ spin_unlock(&info->lock);
++ return PTR_ERR(entry);
++#else
++ return 0;
++#endif
++}
++
+ /**
+ * shmem_file_setup - get an unlinked file living in tmpfs
+ * @name: name for dentry (to be seen in /proc/<pid>/maps
+@@ -2570,6 +2701,9 @@ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
+ d_instantiate(dentry, inode);
+ inode->i_size = size;
+ inode->i_nlink = 0; /* It is unlinked */
++ error = shm_charge_ahead(inode);
++ if (error)
++ goto close_file;
+ init_file(file, shm_mnt, dentry, FMODE_WRITE | FMODE_READ,
+ &shmem_file_operations);
+ return file;
+@@ -2582,6 +2716,7 @@ put_memory:
+ shmem_unacct_size(flags, size);
+ return ERR_PTR(error);
+ }
++EXPORT_SYMBOL_GPL(shmem_file_setup);
+
+ /**
+ * shmem_zero_setup - setup a shared anonymous mapping
+@@ -2598,6 +2733,8 @@ int shmem_zero_setup(struct vm_area_struct *vma)
+
+ if (vma->vm_file)
+ fput(vma->vm_file);
++ else if (vma->vm_flags & VM_WRITE)
++ __ub_unused_privvm_dec(vma->vm_mm, size >> PAGE_SHIFT);
+ vma->vm_file = file;
+ vma->vm_ops = &shmem_vm_ops;
+ return 0;
+diff --git a/mm/slab.c b/mm/slab.c
+index e76eee4..7cd5a15 100644
+--- a/mm/slab.c
++++ b/mm/slab.c
+@@ -111,30 +111,14 @@
+ #include <linux/rtmutex.h>
+ #include <linux/reciprocal_div.h>
+ #include <linux/debugobjects.h>
++#include <linux/nmi.h>
++#include <linux/vzstat.h>
+
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+ #include <asm/page.h>
+
+-/*
+- * DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON.
+- * 0 for faster, smaller code (especially in the critical paths).
+- *
+- * STATS - 1 to collect stats for /proc/slabinfo.
+- * 0 for faster, smaller code (especially in the critical paths).
+- *
+- * FORCED_DEBUG - 1 enables SLAB_RED_ZONE and SLAB_POISON (if possible)
+- */
+-
+-#ifdef CONFIG_DEBUG_SLAB
+-#define DEBUG 1
+-#define STATS 1
+-#define FORCED_DEBUG 1
+-#else
+-#define DEBUG 0
+-#define STATS 0
+-#define FORCED_DEBUG 0
+-#endif
++#include <bc/kmem.h>
+
+ /* Shouldn't this be in a header file somewhere? */
+ #define BYTES_PER_WORD sizeof(void *)
+@@ -169,19 +153,21 @@
+ #endif
+
+ /* Legal flag mask for kmem_cache_create(). */
+-#if DEBUG
++#if SLAB_DEBUG
+ # define CREATE_MASK (SLAB_RED_ZONE | \
+ SLAB_POISON | SLAB_HWCACHE_ALIGN | \
+ SLAB_CACHE_DMA | \
+ SLAB_STORE_USER | \
+ SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \
+ SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD | \
++ SLAB_UBC | SLAB_NO_CHARGE | \
+ SLAB_DEBUG_OBJECTS)
+ #else
+ # define CREATE_MASK (SLAB_HWCACHE_ALIGN | \
+ SLAB_CACHE_DMA | \
+ SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \
+ SLAB_DESTROY_BY_RCU | SLAB_MEM_SPREAD | \
++ SLAB_UBC | SLAB_NO_CHARGE | \
+ SLAB_DEBUG_OBJECTS)
+ #endif
+
+@@ -466,12 +452,14 @@ struct kmem_cache {
+ #define REAPTIMEOUT_CPUC (2*HZ)
+ #define REAPTIMEOUT_LIST3 (4*HZ)
+
+-#if STATS
++#define STATS_INC_GROWN(x) ((x)->grown++)
++#define STATS_ADD_REAPED(x,y) ((x)->reaped += (y))
++#define STATS_INC_SHRUNK(x) ((x)->shrunk++)
++
++#if SLAB_STATS
+ #define STATS_INC_ACTIVE(x) ((x)->num_active++)
+ #define STATS_DEC_ACTIVE(x) ((x)->num_active--)
+ #define STATS_INC_ALLOCED(x) ((x)->num_allocations++)
+-#define STATS_INC_GROWN(x) ((x)->grown++)
+-#define STATS_ADD_REAPED(x,y) ((x)->reaped += (y))
+ #define STATS_SET_HIGH(x) \
+ do { \
+ if ((x)->num_active > (x)->high_mark) \
+@@ -494,8 +482,6 @@ struct kmem_cache {
+ #define STATS_INC_ACTIVE(x) do { } while (0)
+ #define STATS_DEC_ACTIVE(x) do { } while (0)
+ #define STATS_INC_ALLOCED(x) do { } while (0)
+-#define STATS_INC_GROWN(x) do { } while (0)
+-#define STATS_ADD_REAPED(x,y) do { } while (0)
+ #define STATS_SET_HIGH(x) do { } while (0)
+ #define STATS_INC_ERR(x) do { } while (0)
+ #define STATS_INC_NODEALLOCS(x) do { } while (0)
+@@ -508,7 +494,7 @@ struct kmem_cache {
+ #define STATS_INC_FREEMISS(x) do { } while (0)
+ #endif
+
+-#if DEBUG
++#if SLAB_DEBUG
+
+ /*
+ * memory layout of objects:
+@@ -640,6 +626,8 @@ struct cache_sizes malloc_sizes[] = {
+ #define CACHE(x) { .cs_size = (x) },
+ #include <linux/kmalloc_sizes.h>
+ CACHE(ULONG_MAX)
++#include <linux/kmalloc_sizes.h>
++ CACHE(ULONG_MAX)
+ #undef CACHE
+ };
+ EXPORT_SYMBOL(malloc_sizes);
+@@ -653,10 +641,17 @@ struct cache_names {
+ static struct cache_names __initdata cache_names[] = {
+ #define CACHE(x) { .name = "size-" #x, .name_dma = "size-" #x "(DMA)" },
+ #include <linux/kmalloc_sizes.h>
++ {NULL,},
++#undef CACHE
++#define CACHE(x) { .name = "size-" #x "(UBC)", .name_dma = "size-" #x "(DMA,UBC)" },
++#include <linux/kmalloc_sizes.h>
+ {NULL,}
+ #undef CACHE
+ };
+
++int malloc_cache_num;
++EXPORT_SYMBOL(malloc_cache_num);
++
+ static struct arraycache_init initarray_cache __initdata =
+ { {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
+ static struct arraycache_init initarray_generic =
+@@ -733,6 +728,7 @@ static inline void init_lock_keys(void)
+ */
+ static DEFINE_MUTEX(cache_chain_mutex);
+ static struct list_head cache_chain;
++static spinlock_t cache_chain_lock;
+
+ /*
+ * chicken and egg problem: delay the per-cpu array allocation
+@@ -765,7 +761,9 @@ static inline struct kmem_cache *__find_general_cachep(size_t size,
+ {
+ struct cache_sizes *csizep = malloc_sizes;
+
+-#if DEBUG
++ if (gfpflags & __GFP_UBC)
++ csizep += malloc_cache_num;
++#if SLAB_DEBUG
+ /* This happens if someone tries to call
+ * kmem_cache_create(), or __kmalloc(), before
+ * the generic caches are initialized.
+@@ -795,9 +793,98 @@ static struct kmem_cache *kmem_find_general_cachep(size_t size, gfp_t gfpflags)
+ return __find_general_cachep(size, gfpflags);
+ }
+
+-static size_t slab_mgmt_size(size_t nr_objs, size_t align)
++static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
++{
++ return (kmem_bufctl_t *) (slabp + 1);
++}
++
++#ifdef CONFIG_BEANCOUNTERS
++#define init_slab_ubps(cachep, slabp) do { \
++ if (!((cachep)->flags & SLAB_UBC)) \
++ break; \
++ memset(slab_ubcs(cachep, slabp), 0, \
++ (cachep)->num * sizeof(void *)); \
++ } while (0)
++
++#define UB_ALIGN(flags) (flags & SLAB_UBC ? sizeof(void *) : 1)
++#define UB_EXTRA(flags) (flags & SLAB_UBC ? sizeof(void *) : 0)
++#define set_cache_objuse(cachep) do { \
++ (cachep)->objuse = ((PAGE_SIZE << (cachep)->gfporder) + \
++ (cachep)->num - 1) / (cachep)->num; \
++ if (!OFF_SLAB(cachep)) \
++ break; \
++ (cachep)->objuse += ((cachep)->slabp_cache->objuse + \
++ (cachep)->num - 1) / (cachep)->num; \
++ } while (0)
++
++void kmem_mark_nocharge(struct kmem_cache *cachep)
++{
++ cachep->flags |= SLAB_NO_CHARGE;
++}
++
++int kmem_cache_objuse(struct kmem_cache *cachep)
++{
++ return cachep->objuse;
++}
++
++EXPORT_SYMBOL(kmem_cache_objuse);
++
++int kmem_obj_objuse(void *obj)
++{
++ return virt_to_cache(obj)->objuse;
++}
++
++int kmem_dname_objuse(void *obj)
++{
++ return virt_to_cache(obj)->objuse;
++}
++
++unsigned long ub_cache_growth(struct kmem_cache *cachep)
++{
++ return (cachep->grown - cachep->reaped - cachep->shrunk)
++ << cachep->gfporder;
++}
++
++#define slab_ubcs(cachep, slabp) ((struct user_beancounter **)\
++ (ALIGN((unsigned long)(slab_bufctl(slabp) + (cachep)->num),\
++ sizeof(void *))))
++
++struct user_beancounter **ub_slab_ptr(struct kmem_cache *cachep, void *obj)
++{
++ struct slab *slabp;
++ int objnr;
++
++ BUG_ON(!(cachep->flags & SLAB_UBC));
++ slabp = virt_to_slab(obj);
++ objnr = (obj - slabp->s_mem) / cachep->buffer_size;
++ return slab_ubcs(cachep, slabp) + objnr;
++}
++
++struct user_beancounter *slab_ub(void *obj)
++{
++ return *ub_slab_ptr(virt_to_cache(obj), obj);
++}
++
++EXPORT_SYMBOL(slab_ub);
++
++#else
++#define UB_ALIGN(flags) 1
++#define UB_EXTRA(flags) 0
++#define set_cache_objuse(c) do { } while (0)
++#define init_slab_ubps(c, s) do { } while (0)
++#endif
++
++static size_t slab_mgmt_size_noalign(size_t nr_objs, int flags)
++{
++ size_t size_noub;
++
++ size_noub = sizeof(struct slab) + nr_objs * sizeof(kmem_bufctl_t);
++ return ALIGN(size_noub, UB_ALIGN(flags)) + nr_objs * UB_EXTRA(flags);
++}
++
++static size_t slab_mgmt_size(size_t nr_objs, size_t align, int flags)
+ {
+- return ALIGN(sizeof(struct slab)+nr_objs*sizeof(kmem_bufctl_t), align);
++ return ALIGN(slab_mgmt_size_noalign(nr_objs, flags), align);
+ }
+
+ /*
+@@ -842,20 +929,23 @@ static void cache_estimate(unsigned long gfporder, size_t buffer_size,
+ * into account.
+ */
+ nr_objs = (slab_size - sizeof(struct slab)) /
+- (buffer_size + sizeof(kmem_bufctl_t));
++ (buffer_size + sizeof(kmem_bufctl_t) +
++ UB_EXTRA(flags));
+
+ /*
+ * This calculated number will be either the right
+ * amount, or one greater than what we want.
+ */
+- if (slab_mgmt_size(nr_objs, align) + nr_objs*buffer_size
+- > slab_size)
++ if (slab_mgmt_size(nr_objs, align, flags) +
++ nr_objs * buffer_size > slab_size)
+ nr_objs--;
++ BUG_ON(slab_mgmt_size(nr_objs, align, flags) +
++ nr_objs * buffer_size > slab_size);
+
+ if (nr_objs > SLAB_LIMIT)
+ nr_objs = SLAB_LIMIT;
+
+- mgmt_size = slab_mgmt_size(nr_objs, align);
++ mgmt_size = slab_mgmt_size(nr_objs, align, flags);
+ }
+ *num = nr_objs;
+ *left_over = slab_size - nr_objs*buffer_size - mgmt_size;
+@@ -1403,6 +1493,7 @@ static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
+ cachep->nodelists[nodeid] = ptr;
+ local_irq_enable();
+ }
++static int offslab_limit;
+
+ /*
+ * For setting up all the kmem_list3s for cache whose buffer_size is same as
+@@ -1476,6 +1567,7 @@ void __init kmem_cache_init(void)
+
+ /* 1) create the cache_cache */
+ INIT_LIST_HEAD(&cache_chain);
++ spin_lock_init(&cache_chain_lock);
+ list_add(&cache_cache.next, &cache_chain);
+ cache_cache.colour_off = cache_line_size();
+ cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
+@@ -1487,7 +1579,7 @@ void __init kmem_cache_init(void)
+ */
+ cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
+ nr_node_ids * sizeof(struct kmem_list3 *);
+-#if DEBUG
++#if SLAB_DEBUG
+ cache_cache.obj_size = cache_cache.buffer_size;
+ #endif
+ cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
+@@ -1534,6 +1626,7 @@ void __init kmem_cache_init(void)
+
+ slab_early_init = 0;
+
++ for (i = 0; i < 2; i++) {
+ while (sizes->cs_size != ULONG_MAX) {
+ /*
+ * For performance, all the general caches are L1 aligned.
+@@ -1546,21 +1639,30 @@ void __init kmem_cache_init(void)
+ sizes->cs_cachep = kmem_cache_create(names->name,
+ sizes->cs_size,
+ ARCH_KMALLOC_MINALIGN,
+- ARCH_KMALLOC_FLAGS|SLAB_PANIC,
++ ARCH_KMALLOC_FLAGS|SLAB_PANIC|
++ (i ? SLAB_UBC : 0)|SLAB_NO_CHARGE,
+ NULL);
+ }
++ if (!(OFF_SLAB(sizes->cs_cachep)))
++ offslab_limit = sizes->cs_size;
+ #ifdef CONFIG_ZONE_DMA
+- sizes->cs_dmacachep = kmem_cache_create(
+- names->name_dma,
++ sizes->cs_dmacachep = kmem_cache_create(names->name_dma,
+ sizes->cs_size,
+ ARCH_KMALLOC_MINALIGN,
+ ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA|
++ (i ? SLAB_UBC : 0) | SLAB_NO_CHARGE|
+ SLAB_PANIC,
+ NULL);
+ #endif
+ sizes++;
+ names++;
+ }
++
++ sizes++;
++ names++;
++ if (!i)
++ malloc_cache_num = sizes - malloc_sizes;
++ }
+ /* 4) Replace the bootstrap head arrays */
+ {
+ struct array_cache *ptr;
+@@ -1730,7 +1832,7 @@ static void kmem_rcu_free(struct rcu_head *head)
+ kmem_cache_free(cachep->slabp_cache, slab_rcu);
+ }
+
+-#if DEBUG
++#if SLAB_DEBUG
+
+ #ifdef CONFIG_DEBUG_PAGEALLOC
+ static void store_stackinfo(struct kmem_cache *cachep, unsigned long *addr,
+@@ -1807,7 +1909,7 @@ static void dump_line(char *data, int offset, int limit)
+ }
+ #endif
+
+-#if DEBUG
++#if SLAB_DEBUG
+
+ static void print_objinfo(struct kmem_cache *cachep, void *objp, int lines)
+ {
+@@ -1900,7 +2002,7 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
+ }
+ #endif
+
+-#if DEBUG
++#if SLAB_DEBUG
+ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slabp)
+ {
+ int i;
+@@ -2000,7 +2102,6 @@ static void __kmem_cache_destroy(struct kmem_cache *cachep)
+ static size_t calculate_slab_order(struct kmem_cache *cachep,
+ size_t size, size_t align, unsigned long flags)
+ {
+- unsigned long offslab_limit;
+ size_t left_over = 0;
+ int gfporder;
+
+@@ -2013,15 +2114,10 @@ static size_t calculate_slab_order(struct kmem_cache *cachep,
+ continue;
+
+ if (flags & CFLGS_OFF_SLAB) {
+- /*
+- * Max number of objs-per-slab for caches which
+- * use off-slab slabs. Needed to avoid a possible
+- * looping condition in cache_grow().
+- */
+- offslab_limit = size - sizeof(struct slab);
+- offslab_limit /= sizeof(kmem_bufctl_t);
++ int slab_size;
+
+- if (num > offslab_limit)
++ slab_size = slab_mgmt_size_noalign(num, flags);
++ if (slab_size > offslab_limit)
+ break;
+ }
+
+@@ -2184,9 +2280,9 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+ }
+ }
+
+-#if DEBUG
++#if SLAB_DEBUG
+ WARN_ON(strchr(name, ' ')); /* It confuses parsers */
+-#if FORCED_DEBUG
++#if SLAB_FORCED_DEBUG
+ /*
+ * Enable redzoning and last user accounting, except for caches with
+ * large objects, if the increased size would increase the object size
+@@ -2271,7 +2367,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+ if (!cachep)
+ goto oops;
+
+-#if DEBUG
++#if SLAB_DEBUG
+ cachep->obj_size = size;
+
+ /*
+@@ -2293,7 +2389,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+ else
+ size += BYTES_PER_WORD;
+ }
+-#if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
++#if SLAB_FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC)
+ if (size >= malloc_sizes[INDEX_L3 + 1].cs_size
+ && cachep->obj_size > cache_line_size() && size < PAGE_SIZE) {
+ cachep->obj_offset += PAGE_SIZE - size;
+@@ -2325,8 +2421,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+ cachep = NULL;
+ goto oops;
+ }
+- slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
+- + sizeof(struct slab), align);
++ slab_size = slab_mgmt_size(cachep->num, align, flags);
+
+ /*
+ * If the slab has been placed off-slab, and we have enough space then
+@@ -2339,8 +2434,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+
+ if (flags & CFLGS_OFF_SLAB) {
+ /* really off slab. No need for manual alignment */
+- slab_size =
+- cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);
++ slab_size = slab_mgmt_size_noalign(cachep->num, flags);
+ }
+
+ cachep->colour_off = cache_line_size();
+@@ -2377,7 +2471,10 @@ kmem_cache_create (const char *name, size_t size, size_t align,
+ }
+
+ /* cache setup completed, link it into the list */
++ spin_lock(&cache_chain_lock);
+ list_add(&cachep->next, &cache_chain);
++ spin_unlock(&cache_chain_lock);
++ set_cache_objuse(cachep);
+ oops:
+ if (!cachep && (flags & SLAB_PANIC))
+ panic("kmem_cache_create(): failed to create slab `%s'\n",
+@@ -2388,7 +2485,7 @@ oops:
+ }
+ EXPORT_SYMBOL(kmem_cache_create);
+
+-#if DEBUG
++#if SLAB_DEBUG
+ static void check_irq_off(void)
+ {
+ BUG_ON(!irqs_disabled());
+@@ -2484,10 +2581,11 @@ static int drain_freelist(struct kmem_cache *cache,
+ }
+
+ slabp = list_entry(p, struct slab, list);
+-#if DEBUG
++#if SLAB_DEBUG
+ BUG_ON(slabp->inuse);
+ #endif
+ list_del(&slabp->list);
++ STATS_INC_SHRUNK(cache);
+ /*
+ * Safe to drop the lock. The slab is no longer linked
+ * to the cache.
+@@ -2570,10 +2668,14 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
+ /*
+ * the chain is never empty, cache_cache is never destroyed
+ */
++ spin_lock(&cache_chain_lock);
+ list_del(&cachep->next);
++ spin_unlock(&cache_chain_lock);
+ if (__cache_shrink(cachep)) {
+ slab_error(cachep, "Can't free all objects");
++ spin_lock(&cache_chain_lock);
+ list_add(&cachep->next, &cache_chain);
++ spin_unlock(&cache_chain_lock);
+ mutex_unlock(&cache_chain_mutex);
+ put_online_cpus();
+ return;
+@@ -2582,6 +2684,8 @@ void kmem_cache_destroy(struct kmem_cache *cachep)
+ if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU))
+ synchronize_rcu();
+
++
++ ub_kmemcache_free(cachep);
+ __kmem_cache_destroy(cachep);
+ mutex_unlock(&cache_chain_mutex);
+ put_online_cpus();
+@@ -2608,7 +2712,8 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
+ if (OFF_SLAB(cachep)) {
+ /* Slab management obj is off-slab. */
+ slabp = kmem_cache_alloc_node(cachep->slabp_cache,
+- local_flags & ~GFP_THISNODE, nodeid);
++ local_flags & (~(__GFP_UBC | GFP_THISNODE)),
++ nodeid);
+ if (!slabp)
+ return NULL;
+ } else {
+@@ -2620,14 +2725,10 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
+ slabp->s_mem = objp + colour_off;
+ slabp->nodeid = nodeid;
+ slabp->free = 0;
++ init_slab_ubps(cachep, slabp);
+ return slabp;
+ }
+
+-static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
+-{
+- return (kmem_bufctl_t *) (slabp + 1);
+-}
+-
+ static void cache_init_objs(struct kmem_cache *cachep,
+ struct slab *slabp)
+ {
+@@ -2635,7 +2736,7 @@ static void cache_init_objs(struct kmem_cache *cachep,
+
+ for (i = 0; i < cachep->num; i++) {
+ void *objp = index_to_obj(cachep, slabp, i);
+-#if DEBUG
++#if SLAB_DEBUG
+ /* need to poison the objs? */
+ if (cachep->flags & SLAB_POISON)
+ poison_obj(cachep, objp, POISON_FREE);
+@@ -2693,7 +2794,7 @@ static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp,
+
+ slabp->inuse++;
+ next = slab_bufctl(slabp)[slabp->free];
+-#if DEBUG
++#if SLAB_DEBUG
+ slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE;
+ WARN_ON(slabp->nodeid != nodeid);
+ #endif
+@@ -2707,7 +2808,7 @@ static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
+ {
+ unsigned int objnr = obj_to_index(cachep, slabp, objp);
+
+-#if DEBUG
++#if SLAB_DEBUG
+ /* Verify that the slab belongs to the intended node */
+ WARN_ON(slabp->nodeid != nodeid);
+
+@@ -2795,7 +2896,7 @@ static int cache_grow(struct kmem_cache *cachep,
+ * 'nodeid'.
+ */
+ if (!objp)
+- objp = kmem_getpages(cachep, local_flags, nodeid);
++ objp = kmem_getpages(cachep, local_flags & ~__GFP_UBC, nodeid);
+ if (!objp)
+ goto failed;
+
+@@ -2828,7 +2929,7 @@ failed:
+ return 0;
+ }
+
+-#if DEBUG
++#if SLAB_DEBUG
+
+ /*
+ * Perform extra freeing checks:
+@@ -3041,12 +3142,12 @@ static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep,
+ gfp_t flags)
+ {
+ might_sleep_if(flags & __GFP_WAIT);
+-#if DEBUG
++#if SLAB_DEBUG
+ kmem_flagcheck(cachep, flags);
+ #endif
+ }
+
+-#if DEBUG
++#if SLAB_DEBUG
+ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
+ gfp_t flags, void *objp, void *caller)
+ {
+@@ -3462,9 +3563,14 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller)
+ cache_alloc_debugcheck_before(cachep, flags);
+ local_irq_save(save_flags);
+ objp = __do_cache_alloc(cachep, flags);
+- local_irq_restore(save_flags);
+ objp = cache_alloc_debugcheck_after(cachep, flags, objp, caller);
+ prefetchw(objp);
++ if (objp && should_charge(cachep, flags) &&
++ ub_slab_charge(cachep, objp, flags)) {
++ kmem_cache_free(cachep, objp);
++ objp = NULL;
++ }
++ local_irq_restore(save_flags);
+
+ if (unlikely((flags & __GFP_ZERO) && objp))
+ memset(objp, 0, obj_size(cachep));
+@@ -3498,6 +3604,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
+ /* fixup slab chains */
+ if (slabp->inuse == 0) {
+ if (l3->free_objects > l3->free_limit) {
++ STATS_INC_SHRUNK(cachep);
+ l3->free_objects -= cachep->num;
+ /* No need to drop any previously held
+ * lock here, even if we have a off-slab slab
+@@ -3526,7 +3633,7 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
+ int node = numa_node_id();
+
+ batchcount = ac->batchcount;
+-#if DEBUG
++#if SLAB_DEBUG
+ BUG_ON(!batchcount || batchcount > ac->avail);
+ #endif
+ check_irq_off();
+@@ -3547,7 +3654,7 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
+
+ free_block(cachep, ac->entry, batchcount, node);
+ free_done:
+-#if STATS
++#if SLAB_STATS
+ {
+ int i = 0;
+ struct list_head *p;
+@@ -3581,6 +3688,9 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp)
+ check_irq_off();
+ objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
+
++ if (should_uncharge(cachep))
++ ub_slab_uncharge(cachep, objp);
++
+ /*
+ * Skip calling cache_free_alien() when the platform is not numa.
+ * This will avoid cache misses that happen while accessing slabp (which
+@@ -3989,7 +4099,7 @@ static int enable_cpucache(struct kmem_cache *cachep)
+ if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1)
+ shared = 8;
+
+-#if DEBUG
++#if SLAB_DEBUG
+ /*
+ * With debugging enabled, large batchcount lead to excessively long
+ * periods with disabled local interrupts. Limit the batchcount
+@@ -4057,6 +4167,7 @@ static void cache_reap(struct work_struct *w)
+ /* Give up. Setup the next iteration. */
+ goto out;
+
++ {KSTAT_PERF_ENTER(cache_reap)
+ list_for_each_entry(searchp, &cache_chain, next) {
+ check_irq_on();
+
+@@ -4097,6 +4208,7 @@ next:
+ check_irq_on();
+ mutex_unlock(&cache_chain_mutex);
+ next_reap_node();
++ KSTAT_PERF_LEAVE(cache_reap)}
+ out:
+ /* Set up the next iteration */
+ schedule_delayed_work(work, round_jiffies_relative(REAPTIMEOUT_CPUC));
+@@ -4110,7 +4222,7 @@ static void print_slabinfo_header(struct seq_file *m)
+ * Output format version, so at least we can change it
+ * without _too_ many complaints.
+ */
+-#if STATS
++#if SLAB_STATS
+ seq_puts(m, "slabinfo - version: 2.1 (statistics)\n");
+ #else
+ seq_puts(m, "slabinfo - version: 2.1\n");
+@@ -4119,14 +4231,82 @@ static void print_slabinfo_header(struct seq_file *m)
+ "<objperslab> <pagesperslab>");
+ seq_puts(m, " : tunables <limit> <batchcount> <sharedfactor>");
+ seq_puts(m, " : slabdata <active_slabs> <num_slabs> <sharedavail>");
+-#if STATS
++#if SLAB_STATS
+ seq_puts(m, " : globalstat <listallocs> <maxobjs> <grown> <reaped> "
+- "<error> <maxfreeable> <nodeallocs> <remotefrees> <alienoverflow>");
++ "<error> <maxfreeable> <nodeallocs> <remotefrees> <alienoverflow> <shrunk>");
+ seq_puts(m, " : cpustat <allochit> <allocmiss> <freehit> <freemiss>");
+ #endif
+ seq_putc(m, '\n');
+ }
+
++#define SHOW_TOP_SLABS 10
++
++static unsigned long get_cache_size(struct kmem_cache *cachep)
++{
++ unsigned long flags;
++ unsigned long slabs;
++ struct kmem_list3 *l3;
++ struct list_head *lh;
++ int node;
++
++ slabs = 0;
++
++ for_each_online_node (node) {
++ l3 = cachep->nodelists[node];
++ if (l3 == NULL)
++ continue;
++
++ spin_lock_irqsave(&l3->list_lock, flags);
++ list_for_each (lh, &l3->slabs_full)
++ slabs++;
++ list_for_each (lh, &l3->slabs_partial)
++ slabs++;
++ list_for_each (lh, &l3->slabs_free)
++ slabs++;
++ spin_unlock_irqrestore(&l3->list_lock, flags);
++ }
++
++ return slabs * (PAGE_SIZE << cachep->gfporder) +
++ (OFF_SLAB(cachep) ?
++ cachep->slabp_cache->buffer_size * slabs : 0);
++}
++
++void show_slab_info(void)
++{
++ int i, j;
++ unsigned long size;
++ struct kmem_cache *ptr;
++ unsigned long sizes[SHOW_TOP_SLABS];
++ struct kmem_cache *top[SHOW_TOP_SLABS];
++
++ memset(top, 0, sizeof(top));
++ memset(sizes, 0, sizeof(sizes));
++
++ printk("Top %d caches:\n", SHOW_TOP_SLABS);
++
++ spin_lock(&cache_chain_lock);
++ list_for_each_entry (ptr, &cache_chain, next) {
++ size = get_cache_size(ptr);
++
++ j = 0;
++ for (i = 1; i < SHOW_TOP_SLABS; i++)
++ if (sizes[i] < sizes[j])
++ j = i;
++
++ if (size > sizes[j]) {
++ sizes[j] = size;
++ top[j] = ptr;
++ }
++ }
++
++ for (i = 0; i < SHOW_TOP_SLABS; i++)
++ if (top[i])
++ printk("%-21s: size %10lu objsize %10u\n",
++ top[i]->name, sizes[i],
++ top[i]->buffer_size);
++ spin_unlock(&cache_chain_lock);
++}
++
+ static void *s_start(struct seq_file *m, loff_t *pos)
+ {
+ loff_t n = *pos;
+@@ -4205,19 +4385,20 @@ static int s_show(struct seq_file *m, void *p)
+ if (error)
+ printk(KERN_ERR "slab: cache %s error: %s\n", name, error);
+
+- seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d",
++ seq_printf(m, "%-21s %6lu %6lu %6u %4u %4d",
+ name, active_objs, num_objs, cachep->buffer_size,
+ cachep->num, (1 << cachep->gfporder));
+ seq_printf(m, " : tunables %4u %4u %4u",
+ cachep->limit, cachep->batchcount, cachep->shared);
+ seq_printf(m, " : slabdata %6lu %6lu %6lu",
+ active_slabs, num_slabs, shared_avail);
+-#if STATS
++#if SLAB_STATS
+ { /* list3 stats */
+ unsigned long high = cachep->high_mark;
+ unsigned long allocs = cachep->num_allocations;
+ unsigned long grown = cachep->grown;
+ unsigned long reaped = cachep->reaped;
++ unsigned long shrunk = cachep->shrunk;
+ unsigned long errors = cachep->errors;
+ unsigned long max_freeable = cachep->max_freeable;
+ unsigned long node_allocs = cachep->node_allocs;
+@@ -4225,9 +4406,10 @@ static int s_show(struct seq_file *m, void *p)
+ unsigned long overflows = cachep->node_overflow;
+
+ seq_printf(m, " : globalstat %7lu %6lu %5lu %4lu \
+- %4lu %4lu %4lu %4lu %4lu", allocs, high, grown,
++ %4lu %4lu %4lu %4lu %4lu %4lu",
++ allocs, high, grown,
+ reaped, errors, max_freeable, node_allocs,
+- node_frees, overflows);
++ node_frees, overflows, shrunk);
+ }
+ /* cpu stats */
+ {
+diff --git a/mm/slub.c b/mm/slub.c
+index 0c83e6a..713fb2c 100644
+--- a/mm/slub.c
++++ b/mm/slub.c
+@@ -24,6 +24,8 @@
+ #include <linux/memory.h>
+ #include <linux/math64.h>
+
++#include <bc/kmem.h>
++
+ /*
+ * Lock order:
+ * 1. slab_lock(page)
+@@ -137,9 +139,11 @@
+
+ /*
+ * Set of flags that will prevent slab merging
++ *
++ * FIXME - think over how to allow merging accountable slubs
+ */
+ #define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
+- SLAB_TRACE | SLAB_DESTROY_BY_RCU)
++ SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_UBC)
+
+ #define SLUB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \
+ SLAB_CACHE_DMA)
+@@ -305,6 +309,95 @@ static inline int oo_objects(struct kmem_cache_order_objects x)
+ return x.x & ((1 << 16) - 1);
+ }
+
++#ifdef CONFIG_BEANCOUNTERS
++static inline void inc_cache_grown(struct kmem_cache *s)
++{
++ atomic_inc(&s->grown);
++}
++
++static inline void dec_cache_grown(struct kmem_cache *s)
++{
++ atomic_dec(&s->grown);
++}
++
++unsigned long ub_cache_growth(struct kmem_cache *cachep)
++{
++ return atomic_read(&cachep->grown) << cachep->oo.x; /* XXX huh? */
++}
++
++static void __flush_cpu_slab(struct kmem_cache *s, int cpu);
++
++int kmem_cache_objuse(struct kmem_cache *cachep)
++{
++ return cachep->objuse;
++}
++
++EXPORT_SYMBOL(kmem_cache_objuse);
++
++int kmem_obj_objuse(void *obj)
++{
++ return kmem_cache_objuse(virt_to_head_page(obj)->slab);
++}
++
++EXPORT_SYMBOL(kmem_obj_objuse);
++
++int kmem_dname_objuse(void *obj)
++{
++ struct kmem_cache *s;
++
++ /*
++ * Allocations larger than PAGE_SIZE/2 go directly through
++ * __get_free_pages() and aren't associated with any cache.
++ */
++ s = virt_to_head_page(obj)->slab;
++ if (!s)
++ return PAGE_SIZE;
++ return kmem_cache_objuse(s);
++}
++
++#define page_ubs(pg) (pg->bc.slub_ubs)
++
++struct user_beancounter **ub_slab_ptr(struct kmem_cache *s, void *obj)
++{
++ struct page *pg;
++
++ BUG_ON(!(s->flags & SLAB_UBC));
++ pg = virt_to_head_page(obj);
++ return page_ubs(pg) + slab_index(obj, s, page_address(pg));
++}
++
++EXPORT_SYMBOL(ub_slab_ptr);
++
++struct user_beancounter *slab_ub(void *obj)
++{
++ struct page *pg;
++
++ pg = virt_to_head_page(obj);
++ BUG_ON(!(pg->slab->flags & SLAB_UBC));
++ return page_ubs(pg)[slab_index(obj, pg->slab, page_address(pg))];
++}
++
++EXPORT_SYMBOL(slab_ub);
++
++void kmem_mark_nocharge(struct kmem_cache *cachep)
++{
++ cachep->flags |= SLAB_NO_CHARGE;
++}
++#else
++static inline void inc_cache_grown(struct kmem_cache *s)
++{
++}
++
++static inline void dec_cache_grown(struct kmem_cache *s)
++{
++}
++#endif
++
++void show_slab_info(void)
++{
++ /* FIXME - show it */
++}
++
+ #ifdef CONFIG_SLUB_DEBUG
+ /*
+ * Debug settings:
+@@ -1073,6 +1166,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
+ struct page *page;
+ struct kmem_cache_order_objects oo = s->oo;
+
++ flags &= ~__GFP_UBC;
+ flags |= s->allocflags;
+
+ page = alloc_slab_page(flags | __GFP_NOWARN | __GFP_NORETRY, node,
+@@ -1095,9 +1189,12 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
+ NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE,
+ 1 << oo_order(oo));
+
++ inc_cache_grown(s);
+ return page;
+ }
+
++static void __free_slab(struct kmem_cache *s, struct page *page);
++
+ static void setup_object(struct kmem_cache *s, struct page *page,
+ void *object)
+ {
+@@ -1120,6 +1217,18 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
+ if (!page)
+ goto out;
+
++#ifdef CONFIG_BEANCOUNTERS
++ if (s->flags & SLAB_UBC) {
++ BUG_ON(page_ubs(page) != NULL);
++ page_ubs(page) = kzalloc(page->objects * sizeof(void *),
++ flags & ~__GFP_UBC);
++ if (page_ubs(page) == NULL) {
++ __free_slab(s, page);
++ page = NULL;
++ goto out;
++ }
++ }
++#endif
+ inc_slabs_node(s, page_to_nid(page), page->objects);
+ page->slab = s;
+ page->flags |= 1 << PG_slab;
+@@ -1169,6 +1278,13 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
+
+ __ClearPageSlab(page);
+ reset_page_mapcount(page);
++#ifdef CONFIG_BEANCOUNTERS
++ if (page_ubs(page) != NULL) {
++ BUG_ON(!(s->flags & SLAB_UBC));
++ kfree(page_ubs(page));
++ page_ubs(page) = NULL;
++ }
++#endif
+ __free_pages(page, order);
+ }
+
+@@ -1191,6 +1307,8 @@ static void free_slab(struct kmem_cache *s, struct page *page)
+ call_rcu(head, rcu_free_slab);
+ } else
+ __free_slab(s, page);
++
++ dec_cache_grown(s);
+ }
+
+ static void discard_slab(struct kmem_cache *s, struct page *page)
+@@ -1602,6 +1720,13 @@ static __always_inline void *slab_alloc(struct kmem_cache *s,
+ c->freelist = object[c->offset];
+ stat(c, ALLOC_FASTPATH);
+ }
++
++ if (object && should_charge(s, gfpflags) &&
++ ub_slab_charge(s, object, gfpflags)) {
++ kmem_cache_free(s, object);
++ object = NULL;
++ }
++
+ local_irq_restore(flags);
+
+ if (unlikely((gfpflags & __GFP_ZERO) && object))
+@@ -1712,6 +1837,9 @@ static __always_inline void slab_free(struct kmem_cache *s,
+ local_irq_save(flags);
+ c = get_cpu_slab(s, smp_processor_id());
+ debug_check_no_locks_freed(object, c->objsize);
++
++ if (should_uncharge(s))
++ ub_slab_uncharge(s, x);
+ if (!(s->flags & SLAB_DEBUG_OBJECTS))
+ debug_check_no_obj_freed(object, s->objsize);
+ if (likely(page == c->page && c->node >= 0)) {
+@@ -2315,6 +2443,9 @@ static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,
+ #ifdef CONFIG_NUMA
+ s->remote_node_defrag_ratio = 1000;
+ #endif
++#ifdef CONFIG_BEANCOUNTERS
++ s->objuse = s->size + (sizeof(struct page) / oo_objects(s->oo));
++#endif
+ if (!init_kmem_cache_nodes(s, gfpflags & ~SLUB_DMA))
+ goto error;
+
+@@ -2469,6 +2600,10 @@ EXPORT_SYMBOL(kmem_cache_destroy);
+
+ struct kmem_cache kmalloc_caches[PAGE_SHIFT + 1] __cacheline_aligned;
+ EXPORT_SYMBOL(kmalloc_caches);
++#ifdef CONFIG_BEANCOUNTERS
++struct kmem_cache ub_kmalloc_caches[KMALLOC_SHIFT_HIGH + 1] __cacheline_aligned;
++EXPORT_SYMBOL(ub_kmalloc_caches);
++#endif
+
+ static int __init setup_slub_min_order(char *str)
+ {
+@@ -2510,6 +2645,11 @@ static struct kmem_cache *create_kmalloc_cache(struct kmem_cache *s,
+ {
+ unsigned int flags = 0;
+
++ if (gfp_flags & __GFP_UBC) {
++ flags = SLAB_UBC | SLAB_NO_CHARGE;
++ gfp_flags &= ~__GFP_UBC;
++ }
++
+ if (gfp_flags & SLUB_DMA)
+ flags = SLAB_CACHE_DMA;
+
+@@ -2639,11 +2779,14 @@ static struct kmem_cache *get_slab(size_t size, gfp_t flags)
+ index = fls(size - 1);
+
+ #ifdef CONFIG_ZONE_DMA
+- if (unlikely((flags & SLUB_DMA)))
++ if (unlikely((flags & SLUB_DMA))) {
++ BUG_ON(flags & __GFP_UBC);
+ return dma_kmalloc_cache(index, flags);
++ }
+
+ #endif
+- return &kmalloc_caches[index];
++
++ return __kmalloc_cache(flags, index);
+ }
+
+ void *__kmalloc(size_t size, gfp_t flags)
+@@ -2957,6 +3100,11 @@ void __init kmem_cache_init(void)
+ create_kmalloc_cache(&kmalloc_caches[0], "kmem_cache_node",
+ sizeof(struct kmem_cache_node), GFP_KERNEL);
+ kmalloc_caches[0].refcount = -1;
++#ifdef CONFIG_BEANCOUNTERS
++ create_kmalloc_cache(&ub_kmalloc_caches[0], "kmem_cache_node_ubc",
++ sizeof(struct kmem_cache_node), GFP_KERNEL_UBC);
++ ub_kmalloc_caches[0].refcount = -1;
++#endif
+ caches++;
+
+ hotplug_memory_notifier(slab_memory_callback, SLAB_CALLBACK_PRI);
+@@ -2969,15 +3117,27 @@ void __init kmem_cache_init(void)
+ if (KMALLOC_MIN_SIZE <= 64) {
+ create_kmalloc_cache(&kmalloc_caches[1],
+ "kmalloc-96", 96, GFP_KERNEL);
++#ifdef CONFIG_BEANCOUNTERS
++ create_kmalloc_cache(&ub_kmalloc_caches[1],
++ "kmalloc-96-ubc", 96, GFP_KERNEL_UBC);
++#endif
+ caches++;
+ create_kmalloc_cache(&kmalloc_caches[2],
+ "kmalloc-192", 192, GFP_KERNEL);
++#ifdef CONFIG_BEANCOUNTERS
++ create_kmalloc_cache(&ub_kmalloc_caches[2],
++ "kmalloc-192-ubc", 192, GFP_KERNEL_UBC);
++#endif
+ caches++;
+ }
+
+ for (i = KMALLOC_SHIFT_LOW; i <= PAGE_SHIFT; i++) {
+ create_kmalloc_cache(&kmalloc_caches[i],
+ "kmalloc", 1 << i, GFP_KERNEL);
++#ifdef CONFIG_BEANCOUNTERS
++ create_kmalloc_cache(&ub_kmalloc_caches[i],
++ "kmalloc-ubc", 1 << i, GFP_KERNEL_UBC);
++#endif
+ caches++;
+ }
+
+@@ -3012,9 +3172,14 @@ void __init kmem_cache_init(void)
+ slab_state = UP;
+
+ /* Provide the correct kmalloc names now that the caches are up */
+- for (i = KMALLOC_SHIFT_LOW; i <= PAGE_SHIFT; i++)
++ for (i = KMALLOC_SHIFT_LOW; i <= PAGE_SHIFT; i++) {
+ kmalloc_caches[i]. name =
+ kasprintf(GFP_KERNEL, "kmalloc-%d", 1 << i);
++#ifdef CONFIG_BEANCOUNTERS
++ ub_kmalloc_caches[i].name =
++ kasprintf(GFP_KERNEL, "kmalloc-%d-ubc", 1 << i);
++#endif
++ }
+
+ #ifdef CONFIG_SMP
+ register_cpu_notifier(&slab_notifier);
+@@ -4280,6 +4445,8 @@ static char *create_unique_id(struct kmem_cache *s)
+ *p++ = 'a';
+ if (s->flags & SLAB_DEBUG_FREE)
+ *p++ = 'F';
++ if (s->flags & SLAB_UBC)
++ *p++ = 'b';
+ if (p != name + 1)
+ *p++ = '-';
+ p += sprintf(p, "%07d", s->size);
+diff --git a/mm/swap.c b/mm/swap.c
+index 9e0cb31..be5fc52 100644
+--- a/mm/swap.c
++++ b/mm/swap.c
+@@ -209,6 +209,7 @@ void lru_cache_add_active(struct page *page)
+ __pagevec_lru_add_active(pvec);
+ put_cpu_var(lru_add_active_pvecs);
+ }
++EXPORT_SYMBOL(lru_cache_add_active);
+
+ /*
+ * Drain pages out of the cpu's pagevecs.
+@@ -244,6 +245,8 @@ void lru_add_drain(void)
+ put_cpu();
+ }
+
++EXPORT_SYMBOL(lru_add_drain);
++
+ #ifdef CONFIG_NUMA
+ static void lru_add_drain_per_cpu(struct work_struct *dummy)
+ {
+diff --git a/mm/swap_state.c b/mm/swap_state.c
+index 797c383..e54c04c 100644
+--- a/mm/swap_state.c
++++ b/mm/swap_state.c
+@@ -20,6 +20,9 @@
+
+ #include <asm/pgtable.h>
+
++#include <bc/vmpages.h>
++#include <bc/io_acct.h>
++
+ /*
+ * swapper_space is a fiction, retained to simplify the path through
+ * vmscan's shrink_page_list, to make sync_page look nicer, and to allow
+@@ -44,6 +47,7 @@ struct address_space swapper_space = {
+ .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear),
+ .backing_dev_info = &swap_backing_dev_info,
+ };
++EXPORT_SYMBOL(swapper_space);
+
+ #define INC_CACHE_INFO(x) do { swap_cache_info.x++; } while (0)
+
+@@ -101,6 +105,8 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask)
+ return error;
+ }
+
++EXPORT_SYMBOL(add_to_swap_cache);
++
+ /*
+ * This must be called only on pages that have
+ * been verified to be in the swap cache.
+@@ -137,7 +143,14 @@ int add_to_swap(struct page * page, gfp_t gfp_mask)
+ BUG_ON(!PageUptodate(page));
+
+ for (;;) {
+- entry = get_swap_page();
++ struct user_beancounter *ub;
++
++ ub = pb_grab_page_ub(page);
++ if (IS_ERR(ub))
++ return 0;
++
++ entry = get_swap_page(ub);
++ put_beancounter(ub);
+ if (!entry.val)
+ return 0;
+
+@@ -321,6 +334,8 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
+ return found_page;
+ }
+
++EXPORT_SYMBOL(read_swap_cache_async);
++
+ /**
+ * swapin_readahead - swap in pages in hope we need them soon
+ * @entry: swap entry of this memory
+diff --git a/mm/swapfile.c b/mm/swapfile.c
+index 1e330f2..7ad8b1a 100644
+--- a/mm/swapfile.c
++++ b/mm/swapfile.c
+@@ -33,6 +33,8 @@
+ #include <asm/tlbflush.h>
+ #include <linux/swapops.h>
+
++#include <bc/vmpages.h>
++
+ static DEFINE_SPINLOCK(swap_lock);
+ static unsigned int nr_swapfiles;
+ long total_swap_pages;
+@@ -44,9 +46,13 @@ static const char Unused_file[] = "Unused swap file entry ";
+ static const char Bad_offset[] = "Bad swap offset entry ";
+ static const char Unused_offset[] = "Unused swap offset entry ";
+
+-static struct swap_list_t swap_list = {-1, -1};
++struct swap_list_t swap_list = {-1, -1};
+
+-static struct swap_info_struct swap_info[MAX_SWAPFILES];
++struct swap_info_struct swap_info[MAX_SWAPFILES];
++EXPORT_SYMBOL(total_swap_pages);
++EXPORT_SYMBOL(swap_lock);
++EXPORT_SYMBOL(swap_list);
++EXPORT_SYMBOL(swap_info);
+
+ static DEFINE_MUTEX(swapon_mutex);
+
+@@ -173,7 +179,7 @@ no_page:
+ return 0;
+ }
+
+-swp_entry_t get_swap_page(void)
++swp_entry_t get_swap_page(struct user_beancounter *ub)
+ {
+ struct swap_info_struct *si;
+ pgoff_t offset;
+@@ -194,6 +200,8 @@ swp_entry_t get_swap_page(void)
+ wrapped++;
+ }
+
++ if (si->flags & SWP_READONLY)
++ continue;
+ if (!si->highest_bit)
+ continue;
+ if (!(si->flags & SWP_WRITEOK))
+@@ -203,6 +211,7 @@ swp_entry_t get_swap_page(void)
+ offset = scan_swap_map(si);
+ if (offset) {
+ spin_unlock(&swap_lock);
++ ub_swapentry_inc(si, offset, ub);
+ return swp_entry(type, offset);
+ }
+ next = swap_list.next;
+@@ -214,6 +223,8 @@ noswap:
+ return (swp_entry_t) {0};
+ }
+
++EXPORT_SYMBOL(get_swap_page);
++
+ swp_entry_t get_swap_page_of_type(int type)
+ {
+ struct swap_info_struct *si;
+@@ -221,7 +232,7 @@ swp_entry_t get_swap_page_of_type(int type)
+
+ spin_lock(&swap_lock);
+ si = swap_info + type;
+- if (si->flags & SWP_WRITEOK) {
++ if (si->flags & SWP_WRITEOK && !(si->flags & SWP_READONLY)) {
+ nr_swap_pages--;
+ offset = scan_swap_map(si);
+ if (offset) {
+@@ -278,6 +289,7 @@ static int swap_entry_free(struct swap_info_struct *p, unsigned long offset)
+ count--;
+ p->swap_map[offset] = count;
+ if (!count) {
++ ub_swapentry_dec(p, offset);
+ if (offset < p->lowest_bit)
+ p->lowest_bit = offset;
+ if (offset > p->highest_bit)
+@@ -306,6 +318,8 @@ void swap_free(swp_entry_t entry)
+ }
+ }
+
++EXPORT_SYMBOL(swap_free);
++
+ /*
+ * How many references to page are currently swapped out?
+ */
+@@ -387,6 +401,55 @@ int remove_exclusive_swap_page(struct page *page)
+ return retval;
+ }
+
++int try_to_remove_exclusive_swap_page(struct page *page)
++{
++ int retval;
++ struct swap_info_struct * p;
++ swp_entry_t entry;
++
++ BUG_ON(PagePrivate(page));
++ BUG_ON(!PageLocked(page));
++
++ if (!PageSwapCache(page))
++ return 0;
++ if (PageWriteback(page))
++ return 0;
++ if (page_count(page) != 2) /* 2: us + cache */
++ return 0;
++
++ entry.val = page->private;
++ p = swap_info_get(entry);
++ if (!p)
++ return 0;
++
++ if (!vm_swap_full() &&
++ (p->flags & (SWP_ACTIVE|SWP_READONLY)) == SWP_ACTIVE) {
++ spin_unlock(&swap_lock);
++ return 0;
++ }
++
++ /* Is the only swap cache user the cache itself? */
++ retval = 0;
++ if (p->swap_map[swp_offset(entry)] == 1) {
++ /* Recheck the page count with the swapcache lock held.. */
++ spin_lock_irq(&swapper_space.tree_lock);
++ if ((page_count(page) == 2) && !PageWriteback(page)) {
++ __delete_from_swap_cache(page);
++ SetPageDirty(page);
++ retval = 1;
++ }
++ spin_unlock_irq(&swapper_space.tree_lock);
++ }
++ spin_unlock(&swap_lock);
++
++ if (retval) {
++ swap_free(entry);
++ page_cache_release(page);
++ }
++
++ return retval;
++}
++
+ /*
+ * Free the swap entry like above, but also try to
+ * free the page cache entry if it is the last user.
+@@ -426,6 +489,7 @@ void free_swap_and_cache(swp_entry_t entry)
+ page_cache_release(page);
+ }
+ }
++EXPORT_SYMBOL(free_swap_and_cache);
+
+ #ifdef CONFIG_HIBERNATION
+ /*
+@@ -509,11 +573,13 @@ unsigned int count_swap_pages(int type, int free)
+ * force COW, vm_page_prot omits write permission from any private vma.
+ */
+ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
+- unsigned long addr, swp_entry_t entry, struct page *page)
++ unsigned long addr, swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ spinlock_t *ptl;
+ pte_t *pte;
+ int ret = 1;
++ struct mm_struct *mm = vma->vm_mm;
+
+ if (mem_cgroup_charge(page, vma->vm_mm, GFP_KERNEL))
+ ret = -ENOMEM;
+@@ -526,9 +592,11 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
+ goto out;
+ }
+
+- inc_mm_counter(vma->vm_mm, anon_rss);
++ inc_mm_counter(mm, anon_rss);
++ ub_unused_privvm_dec(mm, vma);
++ pb_add_ref(page, mm, pb);
+ get_page(page);
+- set_pte_at(vma->vm_mm, addr, pte,
++ set_pte_at(mm, addr, pte,
+ pte_mkold(mk_pte(page, vma->vm_page_prot)));
+ page_add_anon_rmap(page, vma, addr);
+ swap_free(entry);
+@@ -544,7 +612,8 @@ out:
+
+ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
+ unsigned long addr, unsigned long end,
+- swp_entry_t entry, struct page *page)
++ swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ pte_t swp_pte = swp_entry_to_pte(entry);
+ pte_t *pte;
+@@ -567,7 +636,7 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
+ */
+ if (unlikely(pte_same(*pte, swp_pte))) {
+ pte_unmap(pte);
+- ret = unuse_pte(vma, pmd, addr, entry, page);
++ ret = unuse_pte(vma, pmd, addr, entry, page, pb);
+ if (ret)
+ goto out;
+ pte = pte_offset_map(pmd, addr);
+@@ -580,7 +649,8 @@ out:
+
+ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
+ unsigned long addr, unsigned long end,
+- swp_entry_t entry, struct page *page)
++ swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ pmd_t *pmd;
+ unsigned long next;
+@@ -591,7 +661,7 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
+ next = pmd_addr_end(addr, end);
+ if (pmd_none_or_clear_bad(pmd))
+ continue;
+- ret = unuse_pte_range(vma, pmd, addr, next, entry, page);
++ ret = unuse_pte_range(vma, pmd, addr, next, entry, page, pb);
+ if (ret)
+ return ret;
+ } while (pmd++, addr = next, addr != end);
+@@ -600,7 +670,8 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud,
+
+ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
+ unsigned long addr, unsigned long end,
+- swp_entry_t entry, struct page *page)
++ swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ pud_t *pud;
+ unsigned long next;
+@@ -611,7 +682,7 @@ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
+ next = pud_addr_end(addr, end);
+ if (pud_none_or_clear_bad(pud))
+ continue;
+- ret = unuse_pmd_range(vma, pud, addr, next, entry, page);
++ ret = unuse_pmd_range(vma, pud, addr, next, entry, page, pb);
+ if (ret)
+ return ret;
+ } while (pud++, addr = next, addr != end);
+@@ -619,7 +690,8 @@ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
+ }
+
+ static int unuse_vma(struct vm_area_struct *vma,
+- swp_entry_t entry, struct page *page)
++ swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ pgd_t *pgd;
+ unsigned long addr, end, next;
+@@ -641,7 +713,7 @@ static int unuse_vma(struct vm_area_struct *vma,
+ next = pgd_addr_end(addr, end);
+ if (pgd_none_or_clear_bad(pgd))
+ continue;
+- ret = unuse_pud_range(vma, pgd, addr, next, entry, page);
++ ret = unuse_pud_range(vma, pgd, addr, next, entry, page, pb);
+ if (ret)
+ return ret;
+ } while (pgd++, addr = next, addr != end);
+@@ -649,7 +721,8 @@ static int unuse_vma(struct vm_area_struct *vma,
+ }
+
+ static int unuse_mm(struct mm_struct *mm,
+- swp_entry_t entry, struct page *page)
++ swp_entry_t entry, struct page *page,
++ struct page_beancounter **pb)
+ {
+ struct vm_area_struct *vma;
+ int ret = 0;
+@@ -665,7 +738,7 @@ static int unuse_mm(struct mm_struct *mm,
+ lock_page(page);
+ }
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+- if (vma->anon_vma && (ret = unuse_vma(vma, entry, page)))
++ if (vma->anon_vma && (ret = unuse_vma(vma, entry, page, pb)))
+ break;
+ }
+ up_read(&mm->mmap_sem);
+@@ -727,6 +800,7 @@ static int try_to_unuse(unsigned int type)
+ int retval = 0;
+ int reset_overflow = 0;
+ int shmem;
++ struct page_beancounter *pb;
+
+ /*
+ * When searching mms for an entry, a good strategy is to
+@@ -779,6 +853,13 @@ static int try_to_unuse(unsigned int type)
+ break;
+ }
+
++ pb = NULL;
++ if (pb_alloc_all(&pb)) {
++ page_cache_release(page);
++ retval = -ENOMEM;
++ break;
++ }
++
+ /*
+ * Don't hold on to start_mm if it looks like exiting.
+ */
+@@ -801,6 +882,20 @@ static int try_to_unuse(unsigned int type)
+ lock_page(page);
+ wait_on_page_writeback(page);
+
++ /* If read failed we cannot map not-uptodate page to
++ * user space. Actually, we are in serious troubles,
++ * we do not even know what process to kill. So, the only
++ * variant remains: to stop swapoff() and allow someone
++ * to kill processes to zap invalid pages.
++ */
++ if (unlikely(!PageUptodate(page))) {
++ pb_free_list(&pb);
++ unlock_page(page);
++ page_cache_release(page);
++ retval = -EIO;
++ break;
++ }
++
+ /*
+ * Remove all references to entry.
+ * Whenever we reach init_mm, there's no address space
+@@ -812,7 +907,7 @@ static int try_to_unuse(unsigned int type)
+ if (start_mm == &init_mm)
+ shmem = shmem_unuse(entry, page);
+ else
+- retval = unuse_mm(start_mm, entry, page);
++ retval = unuse_mm(start_mm, entry, page, &pb);
+ }
+ if (*swap_map > 1) {
+ int set_start_mm = (*swap_map >= swcount);
+@@ -842,7 +937,7 @@ static int try_to_unuse(unsigned int type)
+ set_start_mm = 1;
+ shmem = shmem_unuse(entry, page);
+ } else
+- retval = unuse_mm(mm, entry, page);
++ retval = unuse_mm(mm, entry, page, &pb);
+ if (set_start_mm && *swap_map < swcount) {
+ mmput(new_start_mm);
+ atomic_inc(&mm->mm_users);
+@@ -863,6 +958,8 @@ static int try_to_unuse(unsigned int type)
+ retval = shmem;
+ break;
+ }
++
++ pb_free_list(&pb);
+ if (retval) {
+ unlock_page(page);
+ page_cache_release(page);
+@@ -1215,6 +1312,10 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
+ int i, type, prev;
+ int err;
+
++ /* VE admin check is just to be on the safe side, the admin may affect
++ * swaps only if he has access to special, i.e. if he has been granted
++ * access to the block device or if the swap file is in the area
++ * visible to him. */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+@@ -1324,6 +1425,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
+ spin_unlock(&swap_lock);
+ mutex_unlock(&swapon_mutex);
+ vfree(swap_map);
++ ub_swap_fini(p);
+ inode = mapping->host;
+ if (S_ISBLK(inode->i_mode)) {
+ struct block_device *bdev = I_BDEV(inode);
+@@ -1343,6 +1445,8 @@ out:
+ return err;
+ }
+
++EXPORT_SYMBOL(sys_swapoff);
++
+ #ifdef CONFIG_PROC_FS
+ /* iterator */
+ static void *swap_start(struct seq_file *swap, loff_t *pos)
+@@ -1437,7 +1541,7 @@ static const struct file_operations proc_swaps_operations = {
+
+ static int __init procswaps_init(void)
+ {
+- proc_create("swaps", 0, NULL, &proc_swaps_operations);
++ proc_create("swaps", 0, &glob_proc_root, &proc_swaps_operations);
+ return 0;
+ }
+ __initcall(procswaps_init);
+@@ -1669,6 +1773,11 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
+ goto bad_swap;
+ }
+
++ if (ub_swap_init(p, maxpages)) {
++ error = -ENOMEM;
++ goto bad_swap;
++ }
++
+ mutex_lock(&swapon_mutex);
+ spin_lock(&swap_lock);
+ if (swap_flags & SWAP_FLAG_PREFER)
+@@ -1678,6 +1787,8 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
+ p->prio = --least_priority;
+ p->swap_map = swap_map;
+ p->flags = SWP_ACTIVE;
++ if (swap_flags & SWAP_FLAG_READONLY)
++ p->flags |= SWP_READONLY;
+ nr_swap_pages += nr_good_pages;
+ total_swap_pages += nr_good_pages;
+
+@@ -1733,6 +1844,8 @@ out:
+ return error;
+ }
+
++EXPORT_SYMBOL(sys_swapon);
++
+ void si_swapinfo(struct sysinfo *val)
+ {
+ unsigned int i;
+@@ -1792,6 +1905,8 @@ bad_file:
+ goto out;
+ }
+
++EXPORT_SYMBOL(swap_duplicate);
++
+ struct swap_info_struct *
+ get_swap_info_struct(unsigned type)
+ {
+diff --git a/mm/truncate.c b/mm/truncate.c
+index 6650c1d..1c2719b 100644
+--- a/mm/truncate.c
++++ b/mm/truncate.c
+@@ -77,6 +77,7 @@ void cancel_dirty_page(struct page *page, unsigned int account_size)
+ BDI_RECLAIMABLE);
+ if (account_size)
+ task_io_account_cancelled_write(account_size);
++ ub_io_release_context(page, account_size);
+ }
+ }
+ }
+diff --git a/mm/vmalloc.c b/mm/vmalloc.c
+index 85b9a0d..78dba44 100644
+--- a/mm/vmalloc.c
++++ b/mm/vmalloc.c
+@@ -22,6 +22,9 @@
+ #include <asm/uaccess.h>
+ #include <asm/tlbflush.h>
+
++#include <bc/kmem.h>
++#include <bc/debug.h>
++
+
+ DEFINE_RWLOCK(vmlist_lock);
+ struct vm_struct *vmlist;
+@@ -334,6 +337,70 @@ static struct vm_struct *__find_vm_area(const void *addr)
+ return tmp;
+ }
+
++struct vm_struct * get_vm_area_best(unsigned long size, unsigned long flags)
++{
++ unsigned long addr, best_addr, delta, best_delta;
++ struct vm_struct **p, **best_p, *tmp, *area;
++
++ area = kmalloc(sizeof(*area), GFP_KERNEL);
++ if (!area)
++ return NULL;
++
++ size += PAGE_SIZE; /* one-page gap at the end */
++ addr = VMALLOC_START;
++ best_addr = 0UL;
++ best_p = NULL;
++ best_delta = PAGE_ALIGN(VMALLOC_END) - VMALLOC_START;
++
++ write_lock(&vmlist_lock);
++ for (p = &vmlist; (tmp = *p) &&
++ (tmp->addr <= (void *)PAGE_ALIGN(VMALLOC_END));
++ p = &tmp->next) {
++ if ((unsigned long)tmp->addr < addr)
++ continue;
++ if ((size + addr) < addr)
++ break;
++ delta = (unsigned long) tmp->addr - (size + addr);
++ if (delta < best_delta) {
++ best_delta = delta;
++ best_addr = addr;
++ best_p = p;
++ }
++ addr = tmp->size + (unsigned long)tmp->addr;
++ if (addr > VMALLOC_END-size)
++ break;
++ }
++
++ if (!tmp || (tmp->addr > (void *)PAGE_ALIGN(VMALLOC_END))) {
++ /* check free area after list end */
++ delta = (unsigned long) PAGE_ALIGN(VMALLOC_END) - (size + addr);
++ if (delta < best_delta) {
++ best_delta = delta;
++ best_addr = addr;
++ best_p = p;
++ }
++ }
++ if (best_addr) {
++ area->flags = flags;
++ /* allocate at the end of this area */
++ area->addr = (void *)(best_addr + best_delta);
++ area->size = size;
++ area->next = *best_p;
++ area->pages = NULL;
++ area->nr_pages = 0;
++ area->phys_addr = 0;
++ *best_p = area;
++ /* check like in __vunmap */
++ WARN_ON((PAGE_SIZE - 1) & (unsigned long)area->addr);
++ } else {
++ kfree(area);
++ area = NULL;
++ }
++ write_unlock(&vmlist_lock);
++
++ return area;
++}
++
+ /* Caller must hold vmlist_lock */
+ static struct vm_struct *__remove_vm_area(const void *addr)
+ {
+@@ -373,7 +440,7 @@ struct vm_struct *remove_vm_area(const void *addr)
+ return v;
+ }
+
+-static void __vunmap(const void *addr, int deallocate_pages)
++static void __vunmap(const void *addr, int deallocate_pages, int uncharge)
+ {
+ struct vm_struct *area;
+
+@@ -398,6 +465,8 @@ static void __vunmap(const void *addr, int deallocate_pages)
+ if (deallocate_pages) {
+ int i;
+
++ if (uncharge)
++ dec_vmalloc_charged(area);
+ for (i = 0; i < area->nr_pages; i++) {
+ struct page *page = area->pages[i];
+
+@@ -428,7 +497,7 @@ static void __vunmap(const void *addr, int deallocate_pages)
+ void vfree(const void *addr)
+ {
+ BUG_ON(in_interrupt());
+- __vunmap(addr, 1);
++ __vunmap(addr, 1, 1);
+ }
+ EXPORT_SYMBOL(vfree);
+
+@@ -444,7 +513,7 @@ EXPORT_SYMBOL(vfree);
+ void vunmap(const void *addr)
+ {
+ BUG_ON(in_interrupt());
+- __vunmap(addr, 0);
++ __vunmap(addr, 0, 0);
+ }
+ EXPORT_SYMBOL(vunmap);
+
+@@ -526,10 +595,12 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
+
+ if (map_vm_area(area, prot, &pages))
+ goto fail;
++
++ inc_vmalloc_charged(area, gfp_mask);
+ return area->addr;
+
+ fail:
+- vfree(area->addr);
++ __vunmap(area->addr, 1, 0);
+ return NULL;
+ }
+
+@@ -576,6 +647,22 @@ void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
+ }
+ EXPORT_SYMBOL(__vmalloc);
+
++static void *____vmalloc(unsigned long size, gfp_t mask, pgprot_t prot,
++ void *caller)
++{
++ struct vm_struct *area;
++
++ size = PAGE_ALIGN(size);
++ if (!size || (size >> PAGE_SHIFT) > num_physpages)
++ return NULL;
++
++ area = get_vm_area_best(size, VM_ALLOC);
++ if (!area)
++ return NULL;
++
++ return __vmalloc_area_node(area, mask, prot, -1, caller);
++}
++
+ /**
+ * vmalloc - allocate virtually contiguous memory
+ * @size: allocation size
+@@ -592,6 +679,28 @@ void *vmalloc(unsigned long size)
+ }
+ EXPORT_SYMBOL(vmalloc);
+
++void *ub_vmalloc(unsigned long size)
++{
++ return __vmalloc(size, GFP_KERNEL_UBC | __GFP_HIGHMEM, PAGE_KERNEL);
++}
++EXPORT_SYMBOL(ub_vmalloc);
++
++void *vmalloc_best(unsigned long size)
++{
++ return ____vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL,
++ __builtin_return_address(0));
++}
++
++EXPORT_SYMBOL(vmalloc_best);
++
++void *ub_vmalloc_best(unsigned long size)
++{
++ return ____vmalloc(size, GFP_KERNEL_UBC | __GFP_HIGHMEM, PAGE_KERNEL,
++ __builtin_return_address(0));
++}
++
++EXPORT_SYMBOL(ub_vmalloc_best);
++
+ /**
+ * vmalloc_user - allocate zeroed virtually contiguous memory for userspace
+ * @size: allocation size
+@@ -633,6 +742,13 @@ void *vmalloc_node(unsigned long size, int node)
+ }
+ EXPORT_SYMBOL(vmalloc_node);
+
++void *ub_vmalloc_node(unsigned long size, int node)
++{
++ return __vmalloc_node(size, GFP_KERNEL_UBC | __GFP_HIGHMEM, PAGE_KERNEL,
++ node, __builtin_return_address(0));
++}
++EXPORT_SYMBOL(ub_vmalloc_node);
++
+ #ifndef PAGE_KERNEL_EXEC
+ # define PAGE_KERNEL_EXEC PAGE_KERNEL
+ #endif
+@@ -896,6 +1012,39 @@ void free_vm_area(struct vm_struct *area)
+ }
+ EXPORT_SYMBOL_GPL(free_vm_area);
+
++void vprintstat(void)
++{
++ struct vm_struct *p, *last_p = NULL;
++ unsigned long addr, size, free_size, max_free_size;
++ int num;
++
++ addr = VMALLOC_START;
++ size = max_free_size = 0;
++ num = 0;
++
++ read_lock(&vmlist_lock);
++ for (p = vmlist; p; p = p->next) {
++ free_size = (unsigned long)p->addr - addr;
++ if (free_size > max_free_size)
++ max_free_size = free_size;
++ addr = (unsigned long)p->addr + p->size;
++ size += p->size;
++ ++num;
++ last_p = p;
++ }
++ if (last_p) {
++ free_size = VMALLOC_END -
++ ((unsigned long)last_p->addr + last_p->size);
++ if (free_size > max_free_size)
++ max_free_size = free_size;
++ }
++ read_unlock(&vmlist_lock);
++
++ printk("VMALLOC Used: %luKB Total: %luKB Entries: %d\n"
++ " Max_Free: %luKB Start: %lx End: %lx\n",
++ size/1024, (VMALLOC_END - VMALLOC_START)/1024, num,
++ max_free_size/1024, VMALLOC_START, VMALLOC_END);
++}
+
+ #ifdef CONFIG_PROC_FS
+ static void *s_start(struct seq_file *m, loff_t *pos)
+diff --git a/mm/vmscan.c b/mm/vmscan.c
+index 1ff1a58..0182a19 100644
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -40,10 +40,14 @@
+ #include <linux/memcontrol.h>
+ #include <linux/delayacct.h>
+
++#include <bc/oom_kill.h>
++#include <bc/io_acct.h>
++
+ #include <asm/tlbflush.h>
+ #include <asm/div64.h>
+
+ #include <linux/swapops.h>
++#include <linux/vzstat.h>
+
+ #include "internal.h"
+
+@@ -178,6 +182,9 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
+ if (scanned == 0)
+ scanned = SWAP_CLUSTER_MAX;
+
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE)))
++ return 1;
++
+ if (!down_read_trylock(&shrinker_rwsem))
+ return 1; /* Assume we'll be able to shrink next time */
+
+@@ -212,6 +219,9 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
+ int shrink_ret;
+ int nr_before;
+
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE)))
++ goto done;
++
+ nr_before = (*shrinker->shrink)(0, gfp_mask);
+ shrink_ret = (*shrinker->shrink)(this_scan, gfp_mask);
+ if (shrink_ret == -1)
+@@ -226,6 +236,7 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
+
+ shrinker->nr += total_scan;
+ }
++done:
+ up_read(&shrinker_rwsem);
+ return ret;
+ }
+@@ -339,6 +350,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
+ */
+ if (PagePrivate(page)) {
+ if (try_to_free_buffers(page)) {
++ ub_io_release_context(page, 0);
+ ClearPageDirty(page);
+ printk("%s: orphaned page\n", __func__);
+ return PAGE_CLEAN;
+@@ -1108,6 +1120,7 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
+ if (sc->may_swap)
+ reclaim_mapped = calc_reclaim_mapped(sc, zone, priority);
+
++ {KSTAT_PERF_ENTER(refill_inact)
+ lru_add_drain();
+ spin_lock_irq(&zone->lru_lock);
+ pgmoved = sc->isolate_pages(nr_pages, &l_hold, &pgscanned, sc->order,
+@@ -1197,6 +1210,7 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
+ spin_unlock_irq(&zone->lru_lock);
+
+ pagevec_release(&pvec);
++ KSTAT_PERF_LEAVE(refill_inact)}
+ }
+
+ /*
+@@ -1249,6 +1263,8 @@ static unsigned long shrink_zone(int priority, struct zone *zone,
+ nr_to_scan = min(nr_active,
+ (unsigned long)sc->swap_cluster_max);
+ nr_active -= nr_to_scan;
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE)))
++ goto done;
+ shrink_active_list(nr_to_scan, zone, sc, priority);
+ }
+
+@@ -1256,12 +1272,15 @@ static unsigned long shrink_zone(int priority, struct zone *zone,
+ nr_to_scan = min(nr_inactive,
+ (unsigned long)sc->swap_cluster_max);
+ nr_inactive -= nr_to_scan;
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE)))
++ goto done;
+ nr_reclaimed += shrink_inactive_list(nr_to_scan, zone,
+ sc);
+ }
+ }
+
+ throttle_vm_writeout(sc->gfp_mask);
++done:
+ return nr_reclaimed;
+ }
+
+@@ -1317,6 +1336,9 @@ static unsigned long shrink_zones(int priority, struct zonelist *zonelist,
+ }
+
+ nr_reclaimed += shrink_zone(priority, zone, sc);
++
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE)))
++ break;
+ }
+
+ return nr_reclaimed;
+@@ -1351,10 +1373,13 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
+ struct zone *zone;
+ enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask);
+
++ KSTAT_PERF_ENTER(ttfp);
+ delayacct_freepages_start();
+
+ if (scan_global_lru(sc))
+ count_vm_event(ALLOCSTALL);
++
++ ub_oom_start();
+ /*
+ * mem_cgroup will not do shrink_slab.
+ */
+@@ -1404,6 +1429,11 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
+ sc->may_writepage = 1;
+ }
+
++ if (unlikely(test_tsk_thread_flag(current, TIF_MEMDIE))) {
++ ret = 1;
++ goto out;
++ }
++
+ /* Take a nap, wait for some writeback to complete */
+ if (sc->nr_scanned && priority < DEF_PRIORITY - 2)
+ congestion_wait(WRITE, HZ/10);
+@@ -1435,6 +1465,7 @@ out:
+
+ delayacct_freepages_end();
+
++ KSTAT_PERF_LEAVE(ttfp);
+ return ret;
+ }
+
+diff --git a/mm/vmstat.c b/mm/vmstat.c
+index d7826af..f93d8df 100644
+--- a/mm/vmstat.c
++++ b/mm/vmstat.c
+@@ -15,6 +15,40 @@
+ #include <linux/cpu.h>
+ #include <linux/vmstat.h>
+ #include <linux/sched.h>
++#include <linux/virtinfo.h>
++
++void __get_zone_counts(unsigned long *active, unsigned long *inactive,
++ unsigned long *free, struct pglist_data *pgdat)
++{
++ struct zone *zones = pgdat->node_zones;
++ int i;
++
++ *active = 0;
++ *inactive = 0;
++ *free = 0;
++ for (i = 0; i < MAX_NR_ZONES; i++) {
++ *active += zone_page_state(&zones[i], NR_ACTIVE);
++ *inactive += zone_page_state(&zones[i], NR_INACTIVE);
++ *free += zone_page_state(&zones[i], NR_FREE_PAGES);
++ }
++}
++
++void get_zone_counts(unsigned long *active,
++ unsigned long *inactive, unsigned long *free)
++{
++ struct pglist_data *pgdat;
++
++ *active = 0;
++ *inactive = 0;
++ *free = 0;
++ for_each_online_pgdat(pgdat) {
++ unsigned long l, m, n;
++ __get_zone_counts(&l, &m, &n, pgdat);
++ *active += l;
++ *inactive += m;
++ *free += n;
++ }
++}
+
+ #ifdef CONFIG_VM_EVENT_COUNTERS
+ DEFINE_PER_CPU(struct vm_event_state, vm_event_states) = {{0}};
+@@ -35,6 +69,20 @@ static void sum_vm_events(unsigned long *ret, cpumask_t *cpumask)
+ }
+ }
+
++unsigned long vm_events(enum vm_event_item i)
++{
++ int cpu;
++ unsigned long sum;
++ struct vm_event_state *st;
++
++ sum = 0;
++ for_each_online_cpu(cpu) {
++ st = &per_cpu(vm_event_states, cpu);
++ sum += st->event[i];
++ }
++
++ return (sum < 0 ? 0 : sum);
++}
+ /*
+ * Accumulate the vm event counters across all CPUs.
+ * The result is unavoidably approximate - it can change
+@@ -763,30 +811,40 @@ static void *vmstat_start(struct seq_file *m, loff_t *pos)
+ unsigned long *v;
+ #ifdef CONFIG_VM_EVENT_COUNTERS
+ unsigned long *e;
++#define VMSTAT_BUFSIZE (NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long) + \
++ sizeof(struct vm_event_state))
++#else
++#define VMSTAT_BUFSIZE (NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long))
+ #endif
+ int i;
+
+ if (*pos >= ARRAY_SIZE(vmstat_text))
+ return NULL;
+
+-#ifdef CONFIG_VM_EVENT_COUNTERS
+- v = kmalloc(NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long)
+- + sizeof(struct vm_event_state), GFP_KERNEL);
+-#else
+- v = kmalloc(NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long),
+- GFP_KERNEL);
+-#endif
++ v = kmalloc(VMSTAT_BUFSIZE, GFP_KERNEL);
+ m->private = v;
+ if (!v)
+ return ERR_PTR(-ENOMEM);
+- for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+- v[i] = global_page_state(i);
++
++ if (ve_is_super(get_exec_env())) {
++ for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
++ v[i] = global_page_state(i);
+ #ifdef CONFIG_VM_EVENT_COUNTERS
+- e = v + NR_VM_ZONE_STAT_ITEMS;
+- all_vm_events(e);
+- e[PGPGIN] /= 2; /* sectors -> kbytes */
+- e[PGPGOUT] /= 2;
++ e = v + NR_VM_ZONE_STAT_ITEMS;
++ all_vm_events(e);
++ e[PGPGIN] /= 2; /* sectors -> kbytes */
++ e[PGPGOUT] /= 2;
+ #endif
++ } else
++ memset(v, 0, VMSTAT_BUFSIZE);
++
++ if (virtinfo_notifier_call(VITYPE_GENERAL,
++ VIRTINFO_VMSTAT, v) & NOTIFY_FAIL) {
++ kfree(v);
++ m->private = NULL;
++ return ERR_PTR(-ENOMSG);
++ }
++
+ return v + *pos;
+ }
+
+diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
+index b661f47..9150750 100644
+--- a/net/8021q/vlan.c
++++ b/net/8021q/vlan.c
+@@ -105,7 +105,7 @@ static struct vlan_group *vlan_group_alloc(struct net_device *real_dev)
+ {
+ struct vlan_group *grp;
+
+- grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL);
++ grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL_UBC);
+ if (!grp)
+ return NULL;
+
+@@ -127,7 +127,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
+ return 0;
+
+ size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN;
+- array = kzalloc(size, GFP_KERNEL);
++ array = kzalloc(size, GFP_KERNEL_UBC);
+ if (array == NULL)
+ return -ENOBUFS;
+
+@@ -146,6 +146,7 @@ void unregister_vlan_dev(struct net_device *dev)
+ struct net_device *real_dev = vlan->real_dev;
+ struct vlan_group *grp;
+ u16 vlan_id = vlan->vlan_id;
++ struct ve_struct *env;
+
+ ASSERT_RTNL();
+
+@@ -163,7 +164,9 @@ void unregister_vlan_dev(struct net_device *dev)
+
+ synchronize_net();
+
++ env = set_exec_env(dev->owner_env);
+ unregister_netdevice(dev);
++ set_exec_env(env);
+
+ /* If the group is now empty, kill off the group. */
+ if (grp->nr_vlans == 0) {
+@@ -532,6 +535,17 @@ static struct notifier_block vlan_notifier_block __read_mostly = {
+ .notifier_call = vlan_device_event,
+ };
+
++static inline int vlan_check_caps(void)
++{
++ if (capable(CAP_NET_ADMIN))
++ return 1;
++#ifdef CONFIG_VE
++ if (capable(CAP_VE_NET_ADMIN))
++ return 1;
++#endif
++ return 0;
++}
++
+ /*
+ * VLAN IOCTL handler.
+ * o execute requested action or pass command to the device driver
+@@ -573,7 +587,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
+ switch (args.cmd) {
+ case SET_VLAN_INGRESS_PRIORITY_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ vlan_dev_set_ingress_priority(dev,
+ args.u.skb_priority,
+@@ -583,7 +597,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
+
+ case SET_VLAN_EGRESS_PRIORITY_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ err = vlan_dev_set_egress_priority(dev,
+ args.u.skb_priority,
+@@ -592,7 +606,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
+
+ case SET_VLAN_FLAG_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ err = vlan_dev_change_flags(dev,
+ args.vlan_qos ? args.u.flag : 0,
+@@ -601,7 +615,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
+
+ case SET_VLAN_NAME_TYPE_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ if ((args.u.name_type >= 0) &&
+ (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
+@@ -617,14 +631,14 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
+
+ case ADD_VLAN_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ err = register_vlan_device(dev, args.u.VID);
+ break;
+
+ case DEL_VLAN_CMD:
+ err = -EPERM;
+- if (!capable(CAP_NET_ADMIN))
++ if (!vlan_check_caps())
+ break;
+ unregister_vlan_dev(dev);
+ err = 0;
+diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
+index 4bf014e..b3fe36b 100644
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -292,6 +292,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+
+ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
++ struct ve_struct *env;
+ struct net_device_stats *stats = &dev->stats;
+ struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
+
+@@ -323,13 +324,17 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ stats->tx_bytes += skb->len;
+
+ skb->dev = vlan_dev_info(dev)->real_dev;
++ skb->owner_env = skb->dev->owner_env;
++ env = set_exec_env(skb->owner_env);
+ dev_queue_xmit(skb);
++ set_exec_env(env);
+ return NETDEV_TX_OK;
+ }
+
+ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+ {
++ struct ve_struct *env;
+ struct net_device_stats *stats = &dev->stats;
+ u16 vlan_tci;
+
+@@ -341,7 +346,10 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
+ stats->tx_bytes += skb->len;
+
+ skb->dev = vlan_dev_info(dev)->real_dev;
++ skb->owner_env = skb->dev->owner_env;
++ env = set_exec_env(skb->owner_env);
+ dev_queue_xmit(skb);
++ set_exec_env(env);
+ return NETDEV_TX_OK;
+ }
+
+@@ -697,4 +705,6 @@ void vlan_setup(struct net_device *dev)
+ dev->ethtool_ops = &vlan_ethtool_ops;
+
+ memset(dev->broadcast, 0, ETH_ALEN);
++ if (!ve_is_super(get_exec_env()))
++ dev->features |= NETIF_F_VIRTUAL;
+ }
+diff --git a/net/Kconfig b/net/Kconfig
+index 7612cc8..29e7e09 100644
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -27,7 +27,7 @@ menu "Networking options"
+ config NET_NS
+ bool "Network namespace support"
+ default n
+- depends on EXPERIMENTAL && !SYSFS && NAMESPACES
++ depends on EXPERIMENTAL && NAMESPACES
+ help
+ Allow user space to create what appear to be multiple instances
+ of the network stack.
+diff --git a/net/bridge/br.c b/net/bridge/br.c
+index 573acdf..53e3e80 100644
+--- a/net/bridge/br.c
++++ b/net/bridge/br.c
+@@ -56,6 +56,7 @@ static int __init br_init(void)
+
+ brioctl_set(br_ioctl_deviceless_stub);
+ br_handle_frame_hook = br_handle_frame;
++ br_hard_xmit_hook = br_xmit;
+
+ br_fdb_get_hook = br_fdb_get;
+ br_fdb_put_hook = br_fdb_put;
+@@ -89,6 +90,7 @@ static void __exit br_deinit(void)
+ br_fdb_put_hook = NULL;
+
+ br_handle_frame_hook = NULL;
++ br_hard_xmit_hook = NULL;
+ br_fdb_fini();
+ }
+
+diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
+index 4f52c3d..db00163 100644
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -32,16 +32,47 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+ skb_reset_mac_header(skb);
+ skb_pull(skb, ETH_HLEN);
+
++ skb->brmark = BR_ALREADY_SEEN;
++
+ if (dest[0] & 1)
+ br_flood_deliver(br, skb);
+ else if ((dst = __br_fdb_get(br, dest)) != NULL)
+- br_deliver(dst->dst, skb);
++ br_deliver(dst->dst, skb, 1);
+ else
+ br_flood_deliver(br, skb);
+
+ return 0;
+ }
+
++int br_xmit(struct sk_buff *skb, struct net_bridge_port *port)
++{
++ struct net_bridge *br = port->br;
++ const unsigned char *dest = skb->data;
++ struct net_bridge_fdb_entry *dst;
++
++ if (!br->via_phys_dev)
++ return 0;
++
++ br->dev->stats.tx_packets++;
++ br->dev->stats.tx_bytes += skb->len;
++
++ skb_reset_mac_header(skb);
++ skb_pull(skb, ETH_HLEN);
++
++ skb->brmark = BR_ALREADY_SEEN;
++
++ if (dest[0] & 1)
++ br_xmit_deliver(br, port, skb);
++ else if ((dst = __br_fdb_get(br, dest)) != NULL)
++ br_deliver(dst->dst, skb, 0);
++ else
++ br_xmit_deliver(br, port, skb);
++
++ skb_push(skb, ETH_HLEN);
++
++ return 0;
++}
++
+ static int br_dev_open(struct net_device *dev)
+ {
+ struct net_bridge *br = netdev_priv(dev);
+diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
+index bdd9cce..0e2fb77 100644
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -76,14 +76,24 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
+ }
+
+ /* called with rcu_read_lock */
+-void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
++void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb, int free)
+ {
+ if (should_deliver(to, skb)) {
++ if (!free) {
++ struct sk_buff *skb2;
++
++ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
++ to->dev->stats.tx_dropped++;
++ return;
++ }
++ skb = skb2;
++ }
+ __br_deliver(to, skb);
+ return;
+ }
+
+- kfree_skb(skb);
++ if (free)
++ kfree_skb(skb);
+ }
+
+ /* called with rcu_read_lock */
+@@ -99,6 +109,7 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
+
+ /* called under bridge lock */
+ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
++ int free,
+ void (*__packet_hook)(const struct net_bridge_port *p,
+ struct sk_buff *skb))
+ {
+@@ -130,18 +141,41 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ return;
+ }
+
+- kfree_skb(skb);
++ if (free)
++ kfree_skb(skb);
+ }
+
+
+ /* called with rcu_read_lock */
+ void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
+ {
+- br_flood(br, skb, __br_deliver);
++ br_flood(br, skb, 1, __br_deliver);
++}
++
++/* called with rcu_read_lock */
++void br_xmit_deliver(struct net_bridge *br, struct net_bridge_port *port,
++ struct sk_buff *skb)
++{
++ struct net_bridge_port *p;
++
++ list_for_each_entry_rcu(p, &br->port_list, list) {
++ if (p == port)
++ continue;
++ if (should_deliver(p, skb)) {
++ struct sk_buff *skb2;
++
++ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
++ br->dev->stats.tx_dropped++;
++ return;
++ }
++ __br_deliver(p, skb2);
++ }
++ }
+ }
+
+ /* called under bridge lock */
+ void br_flood_forward(struct net_bridge *br, struct sk_buff *skb)
+ {
+- br_flood(br, skb, __br_forward);
++ skb->brmark = BR_ALREADY_SEEN;
++ br_flood(br, skb, 1, __br_forward);
+ }
+diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
+index 63c18aa..dbab3e8 100644
+--- a/net/bridge/br_if.c
++++ b/net/bridge/br_if.c
+@@ -12,6 +12,7 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/nsproxy.h>
+ #include <linux/netdevice.h>
+ #include <linux/ethtool.h>
+ #include <linux/if_arp.h>
+@@ -158,6 +159,11 @@ static void del_br(struct net_bridge *br)
+ {
+ struct net_bridge_port *p, *n;
+
++ if (br->master_dev) {
++ dev_put(br->master_dev);
++ br->master_dev = NULL;
++ }
++
+ list_for_each_entry_safe(p, n, &br->port_list, list) {
+ del_nbp(p);
+ }
+@@ -300,7 +306,7 @@ int br_del_bridge(const char *name)
+ int ret = 0;
+
+ rtnl_lock();
+- dev = __dev_get_by_name(&init_net, name);
++ dev = __dev_get_by_name(current->nsproxy->net_ns, name);
+ if (dev == NULL)
+ ret = -ENXIO; /* Could not find device */
+
+@@ -405,6 +411,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
+ if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
+ (br->dev->flags & IFF_UP))
+ br_stp_enable_port(p);
++ if (!(dev->features & NETIF_F_VIRTUAL)) {
++ dev_hold(dev);
++ br->master_dev = dev;
++ }
+ spin_unlock_bh(&br->lock);
+
+ br_ifinfo_notify(RTM_NEWLINK, p);
+@@ -440,6 +450,16 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
+ spin_lock_bh(&br->lock);
+ br_stp_recalculate_bridge_id(br);
+ br_features_recompute(br);
++ if (br->master_dev == dev) {
++ br->master_dev = NULL;
++ dev_put(dev);
++ list_for_each_entry(p, &br->port_list, list)
++ if (!(p->dev->features & NETIF_F_VIRTUAL)) {
++ dev_hold(p->dev);
++ br->master_dev = p->dev;
++ break;
++ }
++ }
+ spin_unlock_bh(&br->lock);
+
+ return 0;
+@@ -451,7 +471,7 @@ void __exit br_cleanup_bridges(void)
+
+ rtnl_lock();
+ restart:
+- for_each_netdev(&init_net, dev) {
++ for_each_netdev(current->nsproxy->net_ns, dev) {
+ if (dev->priv_flags & IFF_EBRIDGE) {
+ del_br(dev->priv);
+ goto restart;
+diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
+index 30b8877..44fb444 100644
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -28,7 +28,13 @@ static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+ brdev->stats.rx_bytes += skb->len;
+
+ indev = skb->dev;
+- skb->dev = brdev;
++ if (!br->via_phys_dev)
++ skb->dev = brdev;
++ else {
++ skb->brmark = BR_ALREADY_SEEN;
++ if (br->master_dev)
++ skb->dev = br->master_dev;
++ }
+
+ NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
+ netif_receive_skb);
+@@ -56,7 +62,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
+ /* The packet skb2 goes to the local host (NULL to skip). */
+ skb2 = NULL;
+
+- if (br->dev->flags & IFF_PROMISC)
++ if ((br->dev->flags & IFF_PROMISC) && !br->via_phys_dev)
+ skb2 = skb;
+
+ dst = NULL;
+@@ -142,6 +148,8 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
+ }
+
+ switch (p->state) {
++ struct net_device *out;
++
+ case BR_STATE_FORWARDING:
+ rhook = rcu_dereference(br_should_route_hook);
+ if (rhook != NULL) {
+@@ -151,7 +159,12 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
+ }
+ /* fall through */
+ case BR_STATE_LEARNING:
+- if (!compare_ether_addr(p->br->dev->dev_addr, dest))
++ if (skb->brmark == BR_ALREADY_SEEN)
++ return 0;
++
++ out = p->br->via_phys_dev ? p->br->master_dev : p->br->dev;
++
++ if (out && !compare_ether_addr(p->br->dev->dev_addr, dest))
+ skb->pkt_type = PACKET_HOST;
+
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
+index 5bbf073..ac612ab 100644
+--- a/net/bridge/br_ioctl.c
++++ b/net/bridge/br_ioctl.c
+@@ -15,6 +15,7 @@
+ #include <linux/kernel.h>
+ #include <linux/if_bridge.h>
+ #include <linux/netdevice.h>
++#include <linux/nsproxy.h>
+ #include <linux/times.h>
+ #include <net/net_namespace.h>
+ #include <asm/uaccess.h>
+@@ -26,7 +27,7 @@ static int get_bridge_ifindices(int *indices, int num)
+ struct net_device *dev;
+ int i = 0;
+
+- for_each_netdev(&init_net, dev) {
++ for_each_netdev(current->nsproxy->net_ns, dev) {
+ if (i >= num)
+ break;
+ if (dev->priv_flags & IFF_EBRIDGE)
+@@ -89,7 +90,7 @@ static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+- dev = dev_get_by_index(&init_net, ifindex);
++ dev = dev_get_by_index(current->nsproxy->net_ns, ifindex);
+ if (dev == NULL)
+ return -EINVAL;
+
+@@ -140,6 +141,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ b.root_port = br->root_port;
+
+ b.stp_enabled = (br->stp_enabled != BR_NO_STP);
++ b.via_phys_dev = br->via_phys_dev;
+ b.ageing_time = jiffies_to_clock_t(br->ageing_time);
+ b.hello_timer_value = br_timer_value(&br->hello_timer);
+ b.tcn_timer_value = br_timer_value(&br->tcn_timer);
+@@ -262,6 +264,13 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ br_stp_set_enabled(br, args[1]);
+ return 0;
+
++ case BRCTL_SET_VIA_ORIG_DEV:
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++
++ br->via_phys_dev = args[1] ? 1 : 0;
++ return 0;
++
+ case BRCTL_SET_BRIDGE_PRIORITY:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
+index f155e6c..e7a1b78 100644
+--- a/net/bridge/br_netlink.c
++++ b/net/bridge/br_netlink.c
+@@ -11,6 +11,7 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/nsproxy.h>
+ #include <net/rtnetlink.h>
+ #include <net/net_namespace.h>
+ #include <net/sock.h>
+@@ -97,10 +98,11 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port)
+ kfree_skb(skb);
+ goto errout;
+ }
+- err = rtnl_notify(skb, &init_net,0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
++ err = rtnl_notify(skb, dev_net(port->dev),0, RTNLGRP_LINK,
++ NULL, GFP_ATOMIC);
+ errout:
+ if (err < 0)
+- rtnl_set_sk_err(&init_net, RTNLGRP_LINK, err);
++ rtnl_set_sk_err(dev_net(port->dev), RTNLGRP_LINK, err);
+ }
+
+ /*
+@@ -112,11 +114,8 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+ struct net_device *dev;
+ int idx;
+
+- if (net != &init_net)
+- return 0;
+-
+ idx = 0;
+- for_each_netdev(&init_net, dev) {
++ for_each_netdev(net, dev) {
+ /* not a bridge port */
+ if (dev->br_port == NULL || idx < cb->args[0])
+ goto skip;
+@@ -165,7 +164,7 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+ if (new_state > BR_STATE_BLOCKING)
+ return -EINVAL;
+
+- dev = __dev_get_by_index(&init_net, ifm->ifi_index);
++ dev = __dev_get_by_index(current->nsproxy->net_ns, ifm->ifi_index);
+ if (!dev)
+ return -ENODEV;
+
+diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
+index c3dc18d..c4153d3 100644
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -89,6 +89,8 @@ struct net_bridge
+ spinlock_t lock;
+ struct list_head port_list;
+ struct net_device *dev;
++ struct net_device *master_dev;
++ unsigned char via_phys_dev;
+ spinlock_t hash_lock;
+ struct hlist_head hash[BR_HASH_SIZE];
+ struct list_head age_list;
+@@ -142,6 +144,7 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
+ /* br_device.c */
+ extern void br_dev_setup(struct net_device *dev);
+ extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
++extern int br_xmit(struct sk_buff *skb, struct net_bridge_port *port);
+
+ /* br_fdb.c */
+ extern int br_fdb_init(void);
+@@ -168,12 +171,13 @@ extern void br_fdb_update(struct net_bridge *br,
+
+ /* br_forward.c */
+ extern void br_deliver(const struct net_bridge_port *to,
+- struct sk_buff *skb);
++ struct sk_buff *skb, int free);
+ extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(const struct net_bridge_port *to,
+ struct sk_buff *skb);
+ extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb);
++extern void br_xmit_deliver(struct net_bridge *br, struct net_bridge_port *port, struct sk_buff *skb);
+ extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb);
+
+ /* br_if.c */
+diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
+index 158dee8..5b6f301 100644
+--- a/net/bridge/br_sysfs_br.c
++++ b/net/bridge/br_sysfs_br.c
+@@ -181,6 +181,27 @@ static ssize_t store_stp_state(struct device *d,
+ static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
+ store_stp_state);
+
++static ssize_t show_via_phys_dev_state(struct device *cd,
++ struct device_attribute *attr, char *buf)
++{
++ struct net_bridge *br = to_bridge(cd);
++ return sprintf(buf, "%d\n", br->via_phys_dev);
++}
++
++static void set_via_phys_dev_state(struct net_bridge *br, unsigned long val)
++{
++ br->via_phys_dev = val;
++}
++
++static ssize_t store_via_phys_dev_state(struct device *cd,
++ struct device_attribute *attr, const char *buf, size_t len)
++{
++ return store_bridge_parm(cd, buf, len, set_via_phys_dev_state);
++}
++
++static DEVICE_ATTR(via_phys_dev, S_IRUGO | S_IWUSR, show_via_phys_dev_state,
++ store_via_phys_dev_state);
++
+ static ssize_t show_priority(struct device *d, struct device_attribute *attr,
+ char *buf)
+ {
+@@ -350,6 +371,7 @@ static struct attribute *bridge_attrs[] = {
+ &dev_attr_max_age.attr,
+ &dev_attr_ageing_time.attr,
+ &dev_attr_stp_state.attr,
++ &dev_attr_via_phys_dev.attr,
+ &dev_attr_priority.attr,
+ &dev_attr_bridge_id.attr,
+ &dev_attr_root_id.attr,
+diff --git a/net/core/datagram.c b/net/core/datagram.c
+index 52f577a..a2e01c2 100644
+--- a/net/core/datagram.c
++++ b/net/core/datagram.c
+@@ -56,6 +56,8 @@
+ #include <net/sock.h>
+ #include <net/tcp_states.h>
+
++#include <bc/net.h>
++
+ /*
+ * Is a socket 'connection oriented' ?
+ */
+@@ -609,6 +611,7 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
+ {
+ struct sock *sk = sock->sk;
+ unsigned int mask;
++ int no_ubc_space;
+
+ poll_wait(file, sk->sk_sleep, wait);
+ mask = 0;
+@@ -618,8 +621,14 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
+ mask |= POLLERR;
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ mask |= POLLRDHUP;
+- if (sk->sk_shutdown == SHUTDOWN_MASK)
++ if (sk->sk_shutdown == SHUTDOWN_MASK) {
++ no_ubc_space = 0;
+ mask |= POLLHUP;
++ } else {
++ no_ubc_space = ub_sock_makewres_other(sk, SOCK_MIN_UBCSPACE_CH);
++ if (no_ubc_space)
++ ub_sock_sndqueueadd_other(sk, SOCK_MIN_UBCSPACE_CH);
++ }
+
+ /* readable? */
+ if (!skb_queue_empty(&sk->sk_receive_queue) ||
+@@ -636,7 +645,7 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
+ }
+
+ /* writable? */
+- if (sock_writeable(sk))
++ if (!no_ubc_space && sock_writeable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+diff --git a/net/core/dev.c b/net/core/dev.c
+index 0ae08d3..7fd03a6 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -130,6 +130,9 @@
+
+ #include "net-sysfs.h"
+
++#include <bc/beancounter.h>
++#include <bc/kmem.h>
++
+ /*
+ * The list of packet types we will receive (as opposed to discard)
+ * and the routines to invoke.
+@@ -207,20 +210,6 @@ DEFINE_RWLOCK(dev_base_lock);
+
+ EXPORT_SYMBOL(dev_base_lock);
+
+-#define NETDEV_HASHBITS 8
+-#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)
+-
+-static inline struct hlist_head *dev_name_hash(struct net *net, const char *name)
+-{
+- unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ));
+- return &net->dev_name_head[hash & ((1 << NETDEV_HASHBITS) - 1)];
+-}
+-
+-static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
+-{
+- return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)];
+-}
+-
+ /* Device list insertion */
+ static int list_netdevice(struct net_device *dev)
+ {
+@@ -1620,6 +1609,23 @@ static int dev_gso_segment(struct sk_buff *skb)
+ return 0;
+ }
+
++#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
++int (*br_hard_xmit_hook)(struct sk_buff *skb, struct net_bridge_port *port);
++static __inline__ int bridge_hard_start_xmit(struct sk_buff *skb,
++ struct net_device *dev)
++{
++ struct net_bridge_port *port;
++
++ if (((port = rcu_dereference(dev->br_port)) == NULL) ||
++ (skb->brmark == BR_ALREADY_SEEN))
++ return 0;
++
++ return br_hard_xmit_hook(skb, port);
++}
++#else
++#define bridge_hard_start_xmit(skb, dev) (0)
++#endif
++
+ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
+ struct netdev_queue *txq)
+ {
+@@ -1634,6 +1640,8 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
+ goto gso;
+ }
+
++ bridge_hard_start_xmit(skb, dev);
++
+ return dev->hard_start_xmit(skb, dev);
+ }
+
+@@ -1644,6 +1652,9 @@ gso:
+
+ skb->next = nskb->next;
+ nskb->next = NULL;
++
++ bridge_hard_start_xmit(skb, dev);
++
+ rc = dev->hard_start_xmit(nskb, dev);
+ if (unlikely(rc)) {
+ nskb->next = skb->next;
+@@ -2186,6 +2197,7 @@ int netif_receive_skb(struct sk_buff *skb)
+ struct net_device *null_or_orig;
+ int ret = NET_RX_DROP;
+ __be16 type;
++ struct ve_struct *old_ve;
+
+ /* if we've gotten here through NAPI, check netpoll */
+ if (netpoll_receive_skb(skb))
+@@ -2212,6 +2224,16 @@ int netif_receive_skb(struct sk_buff *skb)
+ skb_reset_transport_header(skb);
+ skb->mac_len = skb->network_header - skb->mac_header;
+
++#ifdef CONFIG_VE
++ /*
++ * Skb might be alloced in another VE context, than its device works.
++ * So, set the correct owner_env.
++ */
++ skb->owner_env = skb->dev->owner_env;
++ BUG_ON(skb->owner_env == NULL);
++#endif
++ old_ve = set_exec_env(skb->owner_env);
++
+ pt_prev = NULL;
+
+ rcu_read_lock();
+@@ -2274,6 +2296,7 @@ ncls:
+
+ out:
+ rcu_read_unlock();
++ (void)set_exec_env(old_ve);
+ return ret;
+ }
+
+@@ -2947,8 +2970,13 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
+ return -EOVERFLOW;
+ }
+ }
+- if (dev->flags != old_flags) {
+- printk(KERN_INFO "device %s %s promiscuous mode\n",
++ /*
++ * Promiscous mode on LOOPBACK/POINTTOPOINT devices does
++ * not mean anything
++ */
++ if ((dev->flags != old_flags) &&
++ !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) {
++ ve_printk(VE_LOG, KERN_INFO "device %s %s promiscuous mode\n",
+ dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
+ "left");
+ if (audit_enabled)
+@@ -3731,11 +3759,20 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+ * - require strict serialization.
+ * - do not return a value
+ */
++ case SIOCSIFMTU:
++ case SIOCSIFHWADDR:
+ case SIOCSIFFLAGS:
++ if (!capable(CAP_NET_ADMIN) &&
++ !capable(CAP_VE_NET_ADMIN))
++ return -EPERM;
++ dev_load(net, ifr.ifr_name);
++ rtnl_lock();
++ ret = dev_ifsioc(net, &ifr, cmd);
++ rtnl_unlock();
++ return ret;
++
+ case SIOCSIFMETRIC:
+- case SIOCSIFMTU:
+ case SIOCSIFMAP:
+- case SIOCSIFHWADDR:
+ case SIOCSIFSLAVE:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+@@ -3802,12 +3839,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+ */
+ static int dev_new_index(struct net *net)
+ {
+- static int ifindex;
+ for (;;) {
+- if (++ifindex <= 0)
+- ifindex = 1;
+- if (!__dev_get_by_index(net, ifindex))
+- return ifindex;
++ if (++net->ifindex <= 0)
++ net->ifindex = 1;
++ if (!__dev_get_by_index(net, net->ifindex))
++ return net->ifindex;
+ }
+ }
+
+@@ -3922,6 +3958,10 @@ int register_netdevice(struct net_device *dev)
+ BUG_ON(!dev_net(dev));
+ net = dev_net(dev);
+
++ ret = -EPERM;
++ if (!ve_is_super(get_exec_env()) && ve_is_dev_movable(dev))
++ goto out;
++
+ spin_lock_init(&dev->addr_list_lock);
+ netdev_set_addr_lockdep_class(dev);
+ netdev_init_queue_locks(dev);
+@@ -4021,6 +4061,10 @@ int register_netdevice(struct net_device *dev)
+
+ set_bit(__LINK_STATE_PRESENT, &dev->state);
+
++ dev->owner_env = get_exec_env();
++ netdev_bc(dev)->owner_ub = get_beancounter(get_exec_ub());
++ netdev_bc(dev)->exec_ub = get_beancounter(get_exec_ub());
++
+ dev_init_scheduler(dev);
+ dev_hold(dev);
+ list_netdevice(dev);
+@@ -4156,12 +4200,14 @@ static void netdev_wait_allrefs(struct net_device *dev)
+ void netdev_run_todo(void)
+ {
+ struct list_head list;
++ struct ve_struct *old_ve;
+
+ /* Snapshot list, allow later requests */
+ list_replace_init(&net_todo_list, &list);
+
+ __rtnl_unlock();
+
++ old_ve = get_exec_env();
+ while (!list_empty(&list)) {
+ struct net_device *dev
+ = list_entry(list.next, struct net_device, todo_list);
+@@ -4174,6 +4220,7 @@ void netdev_run_todo(void)
+ continue;
+ }
+
++ (void)set_exec_env(dev->owner_env);
+ dev->reg_state = NETREG_UNREGISTERED;
+
+ on_each_cpu(flush_backlog, dev, 1);
+@@ -4186,12 +4233,21 @@ void netdev_run_todo(void)
+ WARN_ON(dev->ip6_ptr);
+ WARN_ON(dev->dn_ptr);
+
++ put_beancounter(netdev_bc(dev)->exec_ub);
++ put_beancounter(netdev_bc(dev)->owner_ub);
++ netdev_bc(dev)->exec_ub = NULL;
++ netdev_bc(dev)->owner_ub = NULL;
++
++ /* It must be the very last action,
++ * after this 'dev' may point to freed up memory.
++ */
+ if (dev->destructor)
+ dev->destructor(dev);
+
+ /* Free network device */
+ kobject_put(&dev->dev.kobj);
+ }
++ (void)set_exec_env(old_ve);
+ }
+
+ static struct net_device_stats *internal_stats(struct net_device *dev)
+@@ -4243,7 +4299,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
+ /* ensure 32-byte alignment of whole construct */
+ alloc_size += NETDEV_ALIGN_CONST;
+
+- p = kzalloc(alloc_size, GFP_KERNEL);
++ p = kzalloc(alloc_size, GFP_KERNEL_UBC);
+ if (!p) {
+ printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
+ return NULL;
+@@ -4372,11 +4428,18 @@ EXPORT_SYMBOL(unregister_netdev);
+ * Callers must hold the rtnl semaphore.
+ */
+
+-int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
++int __dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat,
++ struct user_beancounter *exec_ub)
+ {
+ char buf[IFNAMSIZ];
+ const char *destname;
+ int err;
++ struct user_beancounter *tmp_ub;
++#ifdef CONFIG_VE
++ struct ve_struct *cur_ve = get_exec_env();
++ struct ve_struct *src_ve = dev->owner_env;
++ struct ve_struct *dst_ve = net->owner_ve;
++#endif
+
+ ASSERT_RTNL();
+
+@@ -4427,6 +4490,11 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
+ err = -ENODEV;
+ unlist_netdevice(dev);
+
++ dev->owner_env = dst_ve;
++ tmp_ub = netdev_bc(dev)->exec_ub;
++ netdev_bc(dev)->exec_ub = get_beancounter(exec_ub);
++ put_beancounter(tmp_ub);
++
+ synchronize_net();
+
+ /* Shutdown queueing discipline. */
+@@ -4435,7 +4503,9 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
+ /* Notify protocols, that we are about to destroy
+ this device. They should clean all the things.
+ */
++ set_exec_env(src_ve);
+ call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
++ (void)set_exec_env(cur_ve);
+
+ /*
+ * Flush the unicast and multicast chains
+@@ -4458,15 +4528,20 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
+ }
+
+ /* Fixup kobjects */
++ set_exec_env(src_ve);
+ netdev_unregister_kobject(dev);
++ set_exec_env(dst_ve);
+ err = netdev_register_kobject(dev);
++ set_exec_env(cur_ve);
+ WARN_ON(err);
+
+ /* Add the device back in the hashes */
+ list_netdevice(dev);
+
+ /* Notify protocols, that a new device appeared. */
++ set_exec_env(dst_ve);
+ call_netdevice_notifiers(NETDEV_REGISTER, dev);
++ (void)set_exec_env(cur_ve);
+
+ synchronize_net();
+ err = 0;
+@@ -4474,6 +4549,13 @@ out:
+ return err;
+ }
+
++int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
++{
++ struct user_beancounter *ub = get_exec_ub();
++
++ return __dev_change_net_namespace(dev, net, pat, ub);
++}
++
+ static int dev_cpu_callback(struct notifier_block *nfb,
+ unsigned long action,
+ void *ocpu)
+@@ -4679,7 +4761,7 @@ static struct hlist_head *netdev_create_hash(void)
+ int i;
+ struct hlist_head *hash;
+
+- hash = kmalloc(sizeof(*hash) * NETDEV_HASHENTRIES, GFP_KERNEL);
++ hash = kmalloc(sizeof(*hash) * NETDEV_HASHENTRIES, GFP_KERNEL_UBC);
+ if (hash != NULL)
+ for (i = 0; i < NETDEV_HASHENTRIES; i++)
+ INIT_HLIST_HEAD(&hash[i]);
+@@ -4843,6 +4925,7 @@ EXPORT_SYMBOL(__dev_remove_pack);
+ EXPORT_SYMBOL(dev_valid_name);
+ EXPORT_SYMBOL(dev_add_pack);
+ EXPORT_SYMBOL(dev_alloc_name);
++EXPORT_SYMBOL(__dev_change_net_namespace);
+ EXPORT_SYMBOL(dev_close);
+ EXPORT_SYMBOL(dev_get_by_flags);
+ EXPORT_SYMBOL(dev_get_by_index);
+@@ -4874,6 +4957,7 @@ EXPORT_SYMBOL(dev_get_flags);
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++EXPORT_SYMBOL(br_hard_xmit_hook);
+ EXPORT_SYMBOL(br_fdb_get_hook);
+ EXPORT_SYMBOL(br_fdb_put_hook);
+ #endif
+diff --git a/net/core/dst.c b/net/core/dst.c
+index fe03266..ce92751 100644
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -308,6 +308,7 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event, void
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ case NETDEV_DOWN:
++ dst_gc_task(NULL);
+ mutex_lock(&dst_gc_mutex);
+ for (dst = dst_busy_list; dst; dst = dst->next) {
+ last = dst;
+diff --git a/net/core/ethtool.c b/net/core/ethtool.c
+index 14ada53..15db122 100644
+--- a/net/core/ethtool.c
++++ b/net/core/ethtool.c
+@@ -859,7 +859,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
+ case ETHTOOL_GRXFH:
+ break;
+ default:
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) && !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+ }
+
+diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
+index 79de3b1..ee4499f 100644
+--- a/net/core/fib_rules.c
++++ b/net/core/fib_rules.c
+@@ -20,7 +20,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops,
+ {
+ struct fib_rule *r;
+
+- r = kzalloc(ops->rule_size, GFP_KERNEL);
++ r = kzalloc(ops->rule_size, GFP_KERNEL_UBC);
+ if (r == NULL)
+ return -ENOMEM;
+
+@@ -238,7 +238,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+ if (err < 0)
+ goto errout;
+
+- rule = kzalloc(ops->rule_size, GFP_KERNEL);
++ rule = kzalloc(ops->rule_size, GFP_KERNEL_UBC);
+ if (rule == NULL) {
+ err = -ENOMEM;
+ goto errout;
+diff --git a/net/core/filter.c b/net/core/filter.c
+index df37443..8a4933c 100644
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -478,7 +478,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+ if (fprog->filter == NULL)
+ return -EINVAL;
+
+- fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
++ fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL_UBC);
+ if (!fp)
+ return -ENOMEM;
+ if (copy_from_user(fp->insns, fprog->filter, fsize)) {
+diff --git a/net/core/neighbour.c b/net/core/neighbour.c
+index 9d92e41..5d08fab 100644
+--- a/net/core/neighbour.c
++++ b/net/core/neighbour.c
+@@ -21,6 +21,7 @@
+ #include <linux/socket.h>
+ #include <linux/netdevice.h>
+ #include <linux/proc_fs.h>
++#include <linux/ve.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+ #endif
+@@ -35,6 +36,7 @@
+ #include <linux/random.h>
+ #include <linux/string.h>
+ #include <linux/log2.h>
++#include <bc/beancounter.h>
+
+ #define NEIGH_DEBUG 1
+
+@@ -264,6 +266,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
+ int entries;
+
+ entries = atomic_inc_return(&tbl->entries) - 1;
++ n = ERR_PTR(-ENOBUFS);
+ if (entries >= tbl->gc_thresh3 ||
+ (entries >= tbl->gc_thresh2 &&
+ time_after(now, tbl->last_flush + 5 * HZ))) {
+@@ -274,7 +277,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
+
+ n = kmem_cache_zalloc(tbl->kmem_cachep, GFP_ATOMIC);
+ if (!n)
+- goto out_entries;
++ goto out_nomem;
+
+ skb_queue_head_init(&n->arp_queue);
+ rwlock_init(&n->lock);
+@@ -291,6 +294,8 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
+ out:
+ return n;
+
++out_nomem:
++ n = ERR_PTR(-ENOMEM);
+ out_entries:
+ atomic_dec(&tbl->entries);
+ goto out;
+@@ -409,12 +414,11 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
+ u32 hash_val;
+ int key_len = tbl->key_len;
+ int error;
+- struct neighbour *n1, *rc, *n = neigh_alloc(tbl);
++ struct neighbour *n1, *rc, *n;
+
+- if (!n) {
+- rc = ERR_PTR(-ENOBUFS);
++ rc = n = neigh_alloc(tbl);
++ if (IS_ERR(n))
+ goto out;
+- }
+
+ memcpy(n->primary_key, pkey, key_len);
+ n->dev = dev;
+@@ -736,10 +740,21 @@ static void neigh_periodic_timer(unsigned long arg)
+ if (atomic_read(&n->refcnt) == 1 &&
+ (state == NUD_FAILED ||
+ time_after(now, n->used + n->parms->gc_staletime))) {
++ struct net_device *dev = n->dev;
++ struct ve_struct *ve;
++ struct user_beancounter *ub;
++
+ *np = n->next;
+ n->dead = 1;
+ write_unlock(&n->lock);
++
++ ve = set_exec_env(dev->owner_env);
++ ub = set_exec_ub(netdev_bc(dev)->owner_ub);
++
+ neigh_cleanup_and_release(n);
++
++ set_exec_ub(ub);
++ set_exec_env(ve);
+ continue;
+ }
+ write_unlock(&n->lock);
+@@ -781,6 +796,11 @@ static void neigh_timer_handler(unsigned long arg)
+ struct neighbour *neigh = (struct neighbour *)arg;
+ unsigned state;
+ int notify = 0;
++ struct ve_struct *env;
++ struct user_beancounter *ub;
++
++ env = set_exec_env(neigh->dev->owner_env);
++ ub = set_exec_ub(netdev_bc(neigh->dev)->exec_ub);
+
+ write_lock(&neigh->lock);
+
+@@ -884,6 +904,8 @@ out:
+ neigh_update_notify(neigh);
+
+ neigh_release(neigh);
++ (void)set_exec_ub(ub);
++ (void)set_exec_env(env);
+ }
+
+ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
+@@ -1273,9 +1295,16 @@ static void neigh_proxy_process(unsigned long arg)
+ if (tdif <= 0) {
+ struct net_device *dev = back->dev;
+ __skb_unlink(back, &tbl->proxy_queue);
+- if (tbl->proxy_redo && netif_running(dev))
++ if (tbl->proxy_redo && netif_running(dev)) {
++ struct ve_struct *ve;
++ struct user_beancounter *ub;
++
++ ve = set_exec_env(dev->owner_env);
++ ub = set_exec_ub(netdev_bc(dev)->owner_ub);
+ tbl->proxy_redo(back);
+- else
++ set_exec_ub(ub);
++ set_exec_env(ve);
++ } else
+ kfree_skb(back);
+
+ dev_put(dev);
+diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
+index c1f4e0d..cafed96 100644
+--- a/net/core/net-sysfs.c
++++ b/net/core/net-sysfs.c
+@@ -229,6 +229,27 @@ static struct device_attribute net_class_attributes[] = {
+ {}
+ };
+
++#ifdef CONFIG_VE
++struct device_attribute ve_net_class_attributes[] = {
++ __ATTR(addr_len, S_IRUGO, show_addr_len, NULL),
++ __ATTR(iflink, S_IRUGO, show_iflink, NULL),
++ __ATTR(ifindex, S_IRUGO, show_ifindex, NULL),
++ __ATTR(features, S_IRUGO, show_features, NULL),
++ __ATTR(type, S_IRUGO, show_type, NULL),
++ __ATTR(link_mode, S_IRUGO, show_link_mode, NULL),
++ __ATTR(address, S_IRUGO, show_address, NULL),
++ __ATTR(broadcast, S_IRUGO, show_broadcast, NULL),
++ __ATTR(carrier, S_IRUGO, show_carrier, NULL),
++ __ATTR(dormant, S_IRUGO, show_dormant, NULL),
++ __ATTR(operstate, S_IRUGO, show_operstate, NULL),
++ __ATTR(mtu, S_IRUGO, show_mtu, NULL),
++ __ATTR(flags, S_IRUGO, show_flags, NULL),
++ __ATTR(tx_queue_len, S_IRUGO, show_tx_queue_len, NULL),
++ {}
++};
++EXPORT_SYMBOL(ve_net_class_attributes);
++#endif
++
+ /* Show a given an attribute in the statistics group */
+ static ssize_t netstat_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+@@ -421,7 +442,7 @@ static void netdev_release(struct device *d)
+ kfree((char *)dev - dev->padded);
+ }
+
+-static struct class net_class = {
++struct class net_class = {
+ .name = "net",
+ .dev_release = netdev_release,
+ #ifdef CONFIG_SYSFS
+@@ -431,6 +452,13 @@ static struct class net_class = {
+ .dev_uevent = netdev_uevent,
+ #endif
+ };
++EXPORT_SYMBOL(net_class);
++
++#ifndef CONFIG_VE
++#define visible_net_class net_class
++#else
++#define visible_net_class (*get_exec_env()->net_class)
++#endif
+
+ /* Delete sysfs entries but hold kobject reference until after all
+ * netdev references are gone.
+@@ -449,7 +477,7 @@ int netdev_register_kobject(struct net_device *net)
+ struct device *dev = &(net->dev);
+ struct attribute_group **groups = net->sysfs_groups;
+
+- dev->class = &net_class;
++ dev->class = &visible_net_class;
+ dev->platform_data = net;
+ dev->groups = groups;
+
+@@ -487,7 +515,15 @@ void netdev_initialize_kobject(struct net_device *net)
+ device_initialize(device);
+ }
+
++void prepare_sysfs_netdev(void)
++{
++#ifdef CONFIG_VE
++ get_ve0()->net_class = &net_class;
++#endif
++}
++
+ int netdev_kobject_init(void)
+ {
++ prepare_sysfs_netdev();
+ return class_register(&net_class);
+ }
+diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
+index 7c52fe2..4ccdf17 100644
+--- a/net/core/net_namespace.c
++++ b/net/core/net_namespace.c
+@@ -1,6 +1,7 @@
+ #include <linux/workqueue.h>
+ #include <linux/rtnetlink.h>
+ #include <linux/cache.h>
++#include <linux/proc_fs.h>
+ #include <linux/slab.h>
+ #include <linux/list.h>
+ #include <linux/delay.h>
+@@ -34,6 +35,10 @@ static __net_init int setup_net(struct net *net)
+ int error;
+ struct net_generic *ng;
+
++#ifdef CONFIG_VE
++ net->owner_ve = get_exec_env();
++#endif
++
+ atomic_set(&net->count, 1);
+ #ifdef NETNS_REFCNT_DEBUG
+ atomic_set(&net->use_count, 0);
+@@ -85,6 +90,8 @@ static struct net *net_alloc(void)
+
+ static void net_free(struct net *net)
+ {
++ struct completion *sysfs_completion;
++
+ if (!net)
+ return;
+
+@@ -95,8 +102,11 @@ static void net_free(struct net *net)
+ return;
+ }
+ #endif
+-
++ kfree(net->gen);
++ sysfs_completion = net->sysfs_completion;
+ kmem_cache_free(net_cachep, net);
++ if (sysfs_completion)
++ complete(sysfs_completion);
+ }
+
+ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
+@@ -139,6 +149,7 @@ static void cleanup_net(struct work_struct *work)
+ {
+ struct pernet_operations *ops;
+ struct net *net;
++ struct ve_struct *old_ve;
+
+ /* Be very certain incoming network packets will not find us */
+ rcu_barrier();
+@@ -152,11 +163,13 @@ static void cleanup_net(struct work_struct *work)
+ list_del(&net->list);
+ rtnl_unlock();
+
++ old_ve = set_exec_env(net->owner_ve);
+ /* Run all of the network namespace exit methods */
+ list_for_each_entry_reverse(ops, &pernet_list, list) {
+ if (ops->exit)
+ ops->exit(net);
+ }
++ (void)set_exec_env(old_ve);
+
+ mutex_unlock(&net_mutex);
+
+diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
+index d6381c2..b16c5c1 100644
+--- a/net/core/rtnetlink.c
++++ b/net/core/rtnetlink.c
+@@ -1203,6 +1203,8 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
+ if (rtnl_msg_handlers[idx] == NULL ||
+ rtnl_msg_handlers[idx][type].dumpit == NULL)
+ continue;
++ if (vz_security_family_check(idx))
++ continue;
+ if (idx > s_idx)
+ memset(&cb->args[0], 0, sizeof(cb->args));
+ if (rtnl_msg_handlers[idx][type].dumpit(skb, cb))
+@@ -1263,13 +1265,13 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ return 0;
+
+ family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
+- if (family >= NPROTO)
++ if (family >= NPROTO || vz_security_family_check(family))
+ return -EAFNOSUPPORT;
+
+ sz_idx = type>>2;
+ kind = type&3;
+
+- if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (kind != 2 && security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
+diff --git a/net/core/scm.c b/net/core/scm.c
+index 10f5c65..65e0983 100644
+--- a/net/core/scm.c
++++ b/net/core/scm.c
+@@ -36,6 +36,7 @@
+ #include <net/compat.h>
+ #include <net/scm.h>
+
++#include <bc/kmem.h>
+
+ /*
+ * Only allow a user to send credentials, that they could set with
+@@ -44,7 +45,9 @@
+
+ static __inline__ int scm_check_creds(struct ucred *creds)
+ {
+- if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
++ if ((creds->pid == task_tgid_vnr(current) ||
++ creds->pid == current->tgid ||
++ capable(CAP_VE_SYS_ADMIN)) &&
+ ((creds->uid == current->uid || creds->uid == current->euid ||
+ creds->uid == current->suid) || capable(CAP_SETUID)) &&
+ ((creds->gid == current->gid || creds->gid == current->egid ||
+@@ -71,7 +74,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+
+ if (!fpl)
+ {
+- fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
++ fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_UBC);
+ if (!fpl)
+ return -ENOMEM;
+ *fplp = fpl;
+@@ -282,7 +285,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
+ if (!fpl)
+ return NULL;
+
+- new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
++ new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL_UBC);
+ if (new_fpl) {
+ for (i=fpl->count-1; i>=0; i--)
+ get_file(fpl->fp[i]);
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index ca1ccdf..4058ec2 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -65,6 +65,8 @@
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+
++#include <bc/net.h>
++
+ #include "kmap_skb.h"
+
+ static struct kmem_cache *skbuff_head_cache __read_mostly;
+@@ -191,6 +193,10 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
+ if (!skb)
+ goto out;
+
++ if (ub_skb_alloc_bc(skb, gfp_mask & ~__GFP_DMA))
++ goto nobc;
++
++ /* Get the DATA. Size must match skb_add_mtu(). */
+ size = SKB_DATA_ALIGN(size);
+ data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
+ gfp_mask, node);
+@@ -209,6 +215,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
+ skb->data = data;
+ skb_reset_tail_pointer(skb);
+ skb->end = skb->tail + size;
++ skb->owner_env = get_exec_env();
+ /* make sure we initialize shinfo sequentially */
+ shinfo = skb_shinfo(skb);
+ atomic_set(&shinfo->dataref, 1);
+@@ -231,6 +238,8 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
+ out:
+ return skb;
+ nodata:
++ ub_skb_free_bc(skb);
++nobc:
+ kmem_cache_free(cache, skb);
+ skb = NULL;
+ goto out;
+@@ -337,6 +346,7 @@ static void kfree_skbmem(struct sk_buff *skb)
+ struct sk_buff *other;
+ atomic_t *fclone_ref;
+
++ ub_skb_free_bc(skb);
+ switch (skb->fclone) {
+ case SKB_FCLONE_UNAVAILABLE:
+ kmem_cache_free(skbuff_head_cache, skb);
+@@ -370,6 +380,7 @@ static void skb_release_all(struct sk_buff *skb)
+ #ifdef CONFIG_XFRM
+ secpath_put(skb->sp);
+ #endif
++ ub_skb_uncharge(skb);
+ if (skb->destructor) {
+ WARN_ON(in_irq());
+ skb->destructor(skb);
+@@ -461,6 +472,11 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+ #endif
+ new->vlan_tci = old->vlan_tci;
+
++#ifdef CONFIG_VE
++ new->accounted = old->accounted;
++ new->redirected = old->redirected;
++#endif
++ skb_copy_brmark(new, old);
+ skb_copy_secmark(new, old);
+ }
+
+@@ -478,6 +494,10 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
+ n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
+ n->cloned = 1;
+ n->nohdr = 0;
++ C(owner_env);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++ C(brmark);
++#endif
+ n->destructor = NULL;
+ C(iif);
+ C(tail);
+@@ -490,6 +510,11 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
+ #endif
+ atomic_set(&n->users, 1);
+
++#ifdef CONFIG_VE
++ C(accounted);
++ C(redirected);
++#endif
++
+ atomic_inc(&(skb_shinfo(skb)->dataref));
+ skb->cloned = 1;
+
+@@ -545,6 +570,10 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
+ n->fclone = SKB_FCLONE_UNAVAILABLE;
+ }
+
++ if (ub_skb_alloc_bc(n, gfp_mask)) {
++ kmem_cache_free(skbuff_head_cache, n);
++ return NULL;
++ }
+ return __skb_clone(n, skb);
+ }
+
+diff --git a/net/core/sock.c b/net/core/sock.c
+index 91f8bbc..a908502 100644
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -123,6 +123,9 @@
+ #include <net/xfrm.h>
+ #include <linux/ipsec.h>
+
++#include <bc/net.h>
++#include <bc/beancounter.h>
++
+ #include <linux/filter.h>
+
+ #ifdef CONFIG_INET
+@@ -248,7 +251,7 @@ static void sock_warn_obsolete_bsdism(const char *name)
+ static char warncomm[TASK_COMM_LEN];
+ if (strcmp(warncomm, current->comm) && warned < 5) {
+ strcpy(warncomm, current->comm);
+- printk(KERN_WARNING "process `%s' is using obsolete "
++ ve_printk(VE_LOG, KERN_WARNING "process `%s' is using obsolete "
+ "%s SO_BSDCOMPAT\n", warncomm, name);
+ warned++;
+ }
+@@ -281,7 +284,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+ if (err)
+ goto out;
+
+- if (!sk_rmem_schedule(sk, skb->truesize)) {
++ if (!sk_rmem_schedule(sk, skb)) {
+ err = -ENOBUFS;
+ goto out;
+ }
+@@ -919,6 +922,7 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
+ slab = prot->slab;
+
+ security_sk_free(sk);
++ ub_sock_uncharge(sk);
+ if (slab != NULL)
+ kmem_cache_free(slab, sk);
+ else
+@@ -947,6 +951,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
+ */
+ sk->sk_prot = sk->sk_prot_creator = prot;
+ sock_lock_init(sk);
++ sk->owner_env = get_exec_env();
+ sock_net_set(sk, get_net(net));
+ }
+
+@@ -1041,14 +1046,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
+ if (filter != NULL)
+ sk_filter_charge(newsk, filter);
+
+- if (unlikely(xfrm_sk_clone_policy(newsk))) {
+- /* It is still raw copy of parent, so invalidate
+- * destructor and make plain sk_free() */
+- newsk->sk_destruct = NULL;
+- sk_free(newsk);
+- newsk = NULL;
+- goto out;
+- }
++ if (ub_sock_charge(newsk, newsk->sk_family, newsk->sk_type) < 0)
++ goto out_err;
++
++ if (unlikely(xfrm_sk_clone_policy(newsk)))
++ goto out_err;
+
+ newsk->sk_err = 0;
+ newsk->sk_priority = 0;
+@@ -1072,14 +1074,23 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
+ if (newsk->sk_prot->sockets_allocated)
+ atomic_inc(newsk->sk_prot->sockets_allocated);
+ }
+-out:
+ return newsk;
++
++out_err:
++ /* It is still raw copy of parent, so invalidate
++ * destructor and make plain sk_free() */
++ sock_reset_flag(newsk, SOCK_TIMESTAMP);
++ newsk->sk_destruct = NULL;
++ sk_free(newsk);
++ return NULL;
+ }
+
+ EXPORT_SYMBOL_GPL(sk_clone);
+
+ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
+ {
++ extern int sysctl_tcp_use_sg;
++
+ __sk_dst_set(sk, dst);
+ sk->sk_route_caps = dst->dev->features;
+ if (sk->sk_route_caps & NETIF_F_GSO)
+@@ -1092,6 +1103,8 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
+ sk->sk_gso_max_size = dst->dev->gso_max_size;
+ }
+ }
++ if (!sysctl_tcp_use_sg)
++ sk->sk_route_caps &= ~NETIF_F_SG;
+ }
+ EXPORT_SYMBOL_GPL(sk_setup_caps);
+
+@@ -1252,11 +1265,9 @@ static long sock_wait_for_wmem(struct sock * sk, long timeo)
+ /*
+ * Generic send/receive buffer handlers
+ */
+-
+-static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
+- unsigned long header_len,
+- unsigned long data_len,
+- int noblock, int *errcode)
++struct sk_buff *sock_alloc_send_skb2(struct sock *sk, unsigned long size,
++ unsigned long size2, int noblock,
++ int *errcode)
+ {
+ struct sk_buff *skb;
+ gfp_t gfp_mask;
+@@ -1277,46 +1288,35 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
+ if (sk->sk_shutdown & SEND_SHUTDOWN)
+ goto failure;
+
+- if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
+- skb = alloc_skb(header_len, gfp_mask);
+- if (skb) {
+- int npages;
+- int i;
+-
+- /* No pages, we're done... */
+- if (!data_len)
+- break;
+-
+- npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+- skb->truesize += data_len;
+- skb_shinfo(skb)->nr_frags = npages;
+- for (i = 0; i < npages; i++) {
+- struct page *page;
+- skb_frag_t *frag;
+-
+- page = alloc_pages(sk->sk_allocation, 0);
+- if (!page) {
+- err = -ENOBUFS;
+- skb_shinfo(skb)->nr_frags = i;
+- kfree_skb(skb);
+- goto failure;
+- }
+-
+- frag = &skb_shinfo(skb)->frags[i];
+- frag->page = page;
+- frag->page_offset = 0;
+- frag->size = (data_len >= PAGE_SIZE ?
+- PAGE_SIZE :
+- data_len);
+- data_len -= PAGE_SIZE;
+- }
++ if (ub_sock_getwres_other(sk, skb_charge_size(size))) {
++ if (size2 < size) {
++ size = size2;
++ continue;
++ }
++ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
++ err = -EAGAIN;
++ if (!timeo)
++ goto failure;
++ if (signal_pending(current))
++ goto interrupted;
++ timeo = ub_sock_wait_for_space(sk, timeo,
++ skb_charge_size(size));
++ continue;
++ }
+
++ if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
++ skb = alloc_skb(size, gfp_mask);
++ if (skb)
+ /* Full success... */
+ break;
+- }
++ ub_sock_retwres_other(sk, skb_charge_size(size),
++ SOCK_MIN_UBCSPACE_CH);
+ err = -ENOBUFS;
+ goto failure;
+ }
++ ub_sock_retwres_other(sk,
++ skb_charge_size(size),
++ SOCK_MIN_UBCSPACE_CH);
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ err = -EAGAIN;
+@@ -1327,6 +1327,7 @@ static struct sk_buff *sock_alloc_send_pskb(struct sock *sk,
+ timeo = sock_wait_for_wmem(sk, timeo);
+ }
+
++ ub_skb_set_charge(skb, sk, skb_charge_size(size), UB_OTHERSOCKBUF);
+ skb_set_owner_w(skb, sk);
+ return skb;
+
+@@ -1336,11 +1337,12 @@ failure:
+ *errcode = err;
+ return NULL;
+ }
++EXPORT_SYMBOL(sock_alloc_send_skb2);
+
+ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
+ int noblock, int *errcode)
+ {
+- return sock_alloc_send_pskb(sk, size, 0, noblock, errcode);
++ return sock_alloc_send_skb2(sk, size, size, noblock, errcode);
+ }
+
+ static void __lock_sock(struct sock *sk)
+@@ -1750,10 +1752,12 @@ void lock_sock_nested(struct sock *sk, int subclass)
+ __lock_sock(sk);
+ sk->sk_lock.owned = 1;
+ spin_unlock(&sk->sk_lock.slock);
++#if !defined(CONFIG_VZ_CHECKPOINT) && !defined(CONFIG_VZ_CHECKPOINT_MODULE)
+ /*
+ * The sk_lock has mutex_lock() semantics here:
+ */
+ mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
++#endif
+ local_bh_enable();
+ }
+
+@@ -1761,11 +1765,12 @@ EXPORT_SYMBOL(lock_sock_nested);
+
+ void release_sock(struct sock *sk)
+ {
++#if !defined(CONFIG_VZ_CHECKPOINT) && !defined(CONFIG_VZ_CHECKPOINT_MODULE)
+ /*
+ * The sk_lock has mutex_unlock() semantics:
+ */
+ mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_);
+-
++#endif
+ spin_lock_bh(&sk->sk_lock.slock);
+ if (sk->sk_backlog.tail)
+ __release_sock(sk);
+@@ -2039,7 +2044,7 @@ int proto_register(struct proto *prot, int alloc_slab)
+
+ if (alloc_slab) {
+ prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
+- SLAB_HWCACHE_ALIGN, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_UBC, NULL);
+
+ if (prot->slab == NULL) {
+ printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
+@@ -2057,7 +2062,7 @@ int proto_register(struct proto *prot, int alloc_slab)
+ sprintf(request_sock_slab_name, mask, prot->name);
+ prot->rsk_prot->slab = kmem_cache_create(request_sock_slab_name,
+ prot->rsk_prot->obj_size, 0,
+- SLAB_HWCACHE_ALIGN, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_UBC, NULL);
+
+ if (prot->rsk_prot->slab == NULL) {
+ printk(KERN_CRIT "%s: Can't create request sock SLAB cache!\n",
+@@ -2078,7 +2083,7 @@ int proto_register(struct proto *prot, int alloc_slab)
+ prot->twsk_prot->twsk_slab =
+ kmem_cache_create(timewait_sock_slab_name,
+ prot->twsk_prot->twsk_obj_size,
+- 0, SLAB_HWCACHE_ALIGN,
++ 0, SLAB_HWCACHE_ALIGN|SLAB_UBC,
+ NULL);
+ if (prot->twsk_prot->twsk_slab == NULL)
+ goto out_free_timewait_sock_slab_name;
+@@ -2235,10 +2240,26 @@ static const struct file_operations proto_seq_fops = {
+ .release = seq_release,
+ };
+
++static int proto_net_init(struct net *net)
++{
++ if (!proc_net_fops_create(net, "protocols", S_IRUGO, &proto_seq_fops))
++ return -ENOBUFS;
++ return 0;
++}
++
++static void proto_net_exit(struct net *net)
++{
++ proc_net_remove(net, "protocols");
++}
++
++static struct pernet_operations proto_net_ops = {
++ .init = proto_net_init,
++ .exit = proto_net_exit,
++};
++
+ static int __init proto_init(void)
+ {
+- /* register /proc/net/protocols */
+- return proc_net_fops_create(&init_net, "protocols", S_IRUGO, &proto_seq_fops) == NULL ? -ENOBUFS : 0;
++ return register_pernet_subsys(&proto_net_ops);
+ }
+
+ subsys_initcall(proto_init);
+diff --git a/net/core/stream.c b/net/core/stream.c
+index a6b3437..100f16e 100644
+--- a/net/core/stream.c
++++ b/net/core/stream.c
+@@ -111,8 +111,10 @@ EXPORT_SYMBOL(sk_stream_wait_close);
+ * sk_stream_wait_memory - Wait for more memory for a socket
+ * @sk: socket to wait for memory
+ * @timeo_p: for how long
++ * @amount - amount of memory to wait for (in UB space!)
+ */
+-int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
++int __sk_stream_wait_memory(struct sock *sk, long *timeo_p,
++ unsigned long amount)
+ {
+ int err = 0;
+ long vm_wait = 0;
+@@ -134,8 +136,11 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
+ if (signal_pending(current))
+ goto do_interrupted;
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+- if (sk_stream_memory_free(sk) && !vm_wait)
+- break;
++ if (amount == 0) {
++ if (sk_stream_memory_free(sk) && !vm_wait)
++ break;
++ } else
++ ub_sock_sndqueueadd_tcp(sk, amount);
+
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ sk->sk_write_pending++;
+@@ -144,6 +149,8 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
+ sk_stream_memory_free(sk) &&
+ vm_wait);
+ sk->sk_write_pending--;
++ if (amount > 0)
++ ub_sock_sndqueuedel(sk);
+
+ if (vm_wait) {
+ vm_wait -= current_timeo;
+@@ -170,6 +177,10 @@ do_interrupted:
+ goto out;
+ }
+
++int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
++{
++ return __sk_stream_wait_memory(sk, timeo_p, 0);
++}
+ EXPORT_SYMBOL(sk_stream_wait_memory);
+
+ int sk_stream_error(struct sock *sk, int flags, int err)
+diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
+index 5e1ee0d..5f57513 100644
+--- a/net/dccp/ipv6.c
++++ b/net/dccp/ipv6.c
+@@ -582,6 +582,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
+ __ip6_dst_store(newsk, dst, NULL, NULL);
+ newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
+ NETIF_F_TSO);
++ if (!sysctl_tcp_use_sg)
++ newsk->sk_route_caps &= ~NETIF_F_SG;
+ newdp6 = (struct dccp6_sock *)newsk;
+ newinet = inet_sk(newsk);
+ newinet->pinet6 = &newdp6->inet6;
+diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
+index b2804e2..92dab28 100644
+--- a/net/dccp/minisocks.c
++++ b/net/dccp/minisocks.c
+@@ -19,6 +19,8 @@
+ #include <net/xfrm.h>
+ #include <net/inet_timewait_sock.h>
+
++#include <bc/sock_orphan.h>
++
+ #include "ackvec.h"
+ #include "ccid.h"
+ #include "dccp.h"
+@@ -56,7 +58,8 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
+ {
+ struct inet_timewait_sock *tw = NULL;
+
+- if (dccp_death_row.tw_count < dccp_death_row.sysctl_max_tw_buckets)
++ if (dccp_death_row.tw_count < dccp_death_row.sysctl_max_tw_buckets &&
++ ub_timewait_check(sk, &dccp_death_row))
+ tw = inet_twsk_alloc(sk, state);
+
+ if (tw != NULL) {
+diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c
+index 6d2bd32..45567e3 100644
+--- a/net/decnet/netfilter/dn_rtmsg.c
++++ b/net/decnet/netfilter/dn_rtmsg.c
+@@ -107,7 +107,7 @@ static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
+ if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+ return;
+
+- if (security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ RCV_SKB_FAIL(-EPERM);
+
+ /* Eventually we might send routing messages too */
+diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
+index 8a3ac1f..cd4d09d 100644
+--- a/net/ipv4/af_inet.c
++++ b/net/ipv4/af_inet.c
+@@ -114,6 +114,7 @@
+ #ifdef CONFIG_IP_MROUTE
+ #include <linux/mroute.h>
+ #endif
++#include <bc/net.h>
+
+ extern void ip_mc_drop_socket(struct sock *sk);
+
+@@ -324,6 +325,10 @@ lookup_protocol:
+ goto out_rcu_unlock;
+ }
+
++ err = vz_security_protocol_check(answer->protocol);
++ if (err < 0)
++ goto out_rcu_unlock;
++
+ err = -EPERM;
+ if (answer->capability > 0 && !capable(answer->capability))
+ goto out_rcu_unlock;
+@@ -345,6 +350,13 @@ lookup_protocol:
+ if (sk == NULL)
+ goto out;
+
++ err = -ENOBUFS;
++ if (ub_sock_charge(sk, PF_INET, sock->type))
++ goto out_sk_free;
++ /* if charge was successful, sock_init_data() MUST be called to
++ * set sk->sk_type. otherwise sk will be uncharged to wrong resource
++ */
++
+ err = 0;
+ sk->sk_no_check = answer_no_check;
+ if (INET_PROTOSW_REUSE & answer_flags)
+@@ -402,6 +414,9 @@ out:
+ out_rcu_unlock:
+ rcu_read_unlock();
+ goto out;
++out_sk_free:
++ sk_free(sk);
++ return err;
+ }
+
+
+@@ -416,6 +431,9 @@ int inet_release(struct socket *sock)
+
+ if (sk) {
+ long timeout;
++ struct ve_struct *saved_env;
++
++ saved_env = set_exec_env(sk->owner_env);
+
+ /* Applications forget to leave groups before exiting */
+ ip_mc_drop_socket(sk);
+@@ -433,6 +451,8 @@ int inet_release(struct socket *sock)
+ timeout = sk->sk_lingertime;
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, timeout);
++
++ (void)set_exec_env(saved_env);
+ }
+ return 0;
+ }
+diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
+index b043eda..31f84a0 100644
+--- a/net/ipv4/arp.c
++++ b/net/ipv4/arp.c
+@@ -1136,7 +1136,8 @@ int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+ switch (cmd) {
+ case SIOCDARP:
+ case SIOCSARP:
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) &&
++ !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+ case SIOCGARP:
+ err = copy_from_user(&r, arg, sizeof(struct arpreq));
+diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
+index b12dae2..1ad68cf 100644
+--- a/net/ipv4/devinet.c
++++ b/net/ipv4/devinet.c
+@@ -110,9 +110,9 @@ static inline void devinet_sysctl_unregister(struct in_device *idev)
+
+ /* Locks all the inet devices. */
+
+-static struct in_ifaddr *inet_alloc_ifa(void)
++struct in_ifaddr *inet_alloc_ifa(void)
+ {
+- struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
++ struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL_UBC);
+
+ if (ifa) {
+ INIT_RCU_HEAD(&ifa->rcu_head);
+@@ -120,6 +120,7 @@ static struct in_ifaddr *inet_alloc_ifa(void)
+
+ return ifa;
+ }
++EXPORT_SYMBOL_GPL(inet_alloc_ifa);
+
+ static void inet_rcu_free_ifa(struct rcu_head *head)
+ {
+@@ -152,7 +153,7 @@ void in_dev_finish_destroy(struct in_device *idev)
+ }
+ }
+
+-static struct in_device *inetdev_init(struct net_device *dev)
++struct in_device *inetdev_init(struct net_device *dev)
+ {
+ struct in_device *in_dev;
+
+@@ -189,6 +190,7 @@ out_kfree:
+ in_dev = NULL;
+ goto out;
+ }
++EXPORT_SYMBOL_GPL(inetdev_init);
+
+ static void in_dev_rcu_put(struct rcu_head *head)
+ {
+@@ -382,7 +384,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
+ return 0;
+ }
+
+-static int inet_insert_ifa(struct in_ifaddr *ifa)
++int inet_insert_ifa(struct in_ifaddr *ifa)
+ {
+ return __inet_insert_ifa(ifa, NULL, 0);
+ }
+@@ -433,6 +435,7 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
+ } endfor_ifa(in_dev);
+ return NULL;
+ }
++EXPORT_SYMBOL_GPL(inet_insert_ifa);
+
+ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+ {
+@@ -633,7 +636,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+
+ case SIOCSIFFLAGS:
+ ret = -EACCES;
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ goto out;
+ break;
+ case SIOCSIFADDR: /* Set interface address (and family) */
+@@ -641,7 +644,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+ case SIOCSIFDSTADDR: /* Set the destination address */
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ ret = -EACCES;
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ goto out;
+ ret = -EINVAL;
+ if (sin->sin_family != AF_INET)
+diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
+index 65c1503..1e87cfd 100644
+--- a/net/ipv4/fib_frontend.c
++++ b/net/ipv4/fib_frontend.c
+@@ -258,7 +258,8 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
+ net = dev_net(dev);
+ if (fib_lookup(net, &fl, &res))
+ goto last_resort;
+- if (res.type != RTN_UNICAST)
++ if (res.type != RTN_UNICAST &&
++ (!(dev->features & NETIF_F_VENET) || res.type != RTN_LOCAL))
+ goto e_inval_res;
+ *spec_dst = FIB_RES_PREFSRC(res);
+ fib_combine_itag(itag, &res);
+@@ -460,7 +461,7 @@ int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
+ switch (cmd) {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&rt, arg, sizeof(rt)))
+diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
+index c8cac6c..c21d89f 100644
+--- a/net/ipv4/fib_hash.c
++++ b/net/ipv4/fib_hash.c
+@@ -770,10 +770,10 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
+ void __init fib_hash_init(void)
+ {
+ fn_hash_kmem = kmem_cache_create("ip_fib_hash", sizeof(struct fib_node),
+- 0, SLAB_PANIC, NULL);
++ 0, SLAB_PANIC | SLAB_UBC, NULL);
+
+ fn_alias_kmem = kmem_cache_create("ip_fib_alias", sizeof(struct fib_alias),
+- 0, SLAB_PANIC, NULL);
++ 0, SLAB_PANIC | SLAB_UBC, NULL);
+
+ }
+
+diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
+index f70fac6..fe43dd5 100644
+--- a/net/ipv4/igmp.c
++++ b/net/ipv4/igmp.c
+@@ -2272,6 +2272,7 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p
+
+ #if defined(CONFIG_PROC_FS)
+ struct igmp_mc_iter_state {
++ struct seq_net_private p;
+ struct net_device *dev;
+ struct in_device *in_dev;
+ };
+@@ -2282,9 +2283,10 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
+ {
+ struct ip_mc_list *im = NULL;
+ struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
++ struct net *net = seq_file_net(seq);
+
+ state->in_dev = NULL;
+- for_each_netdev(&init_net, state->dev) {
++ for_each_netdev(net, state->dev) {
+ struct in_device *in_dev;
+ in_dev = in_dev_get(state->dev);
+ if (!in_dev)
+@@ -2405,7 +2407,7 @@ static const struct seq_operations igmp_mc_seq_ops = {
+
+ static int igmp_mc_seq_open(struct inode *inode, struct file *file)
+ {
+- return seq_open_private(file, &igmp_mc_seq_ops,
++ return seq_open_net(inode, file, &igmp_mc_seq_ops,
+ sizeof(struct igmp_mc_iter_state));
+ }
+
+@@ -2414,10 +2416,11 @@ static const struct file_operations igmp_mc_seq_fops = {
+ .open = igmp_mc_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+- .release = seq_release_private,
++ .release = seq_release_net,
+ };
+
+ struct igmp_mcf_iter_state {
++ struct seq_net_private p;
+ struct net_device *dev;
+ struct in_device *idev;
+ struct ip_mc_list *im;
+@@ -2430,10 +2433,11 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
+ struct ip_sf_list *psf = NULL;
+ struct ip_mc_list *im = NULL;
+ struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
++ struct net *net = seq_file_net(seq);
+
+ state->idev = NULL;
+ state->im = NULL;
+- for_each_netdev(&init_net, state->dev) {
++ for_each_netdev(net, state->dev) {
+ struct in_device *idev;
+ idev = in_dev_get(state->dev);
+ if (unlikely(idev == NULL))
+@@ -2564,7 +2568,7 @@ static const struct seq_operations igmp_mcf_seq_ops = {
+
+ static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
+ {
+- return seq_open_private(file, &igmp_mcf_seq_ops,
++ return seq_open_net(inode, file, &igmp_mcf_seq_ops,
+ sizeof(struct igmp_mcf_iter_state));
+ }
+
+@@ -2573,14 +2577,37 @@ static const struct file_operations igmp_mcf_seq_fops = {
+ .open = igmp_mcf_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+- .release = seq_release_private,
++ .release = seq_release_net,
+ };
+
+-int __init igmp_mc_proc_init(void)
++static int igmp_net_init(struct net *net)
+ {
+- proc_net_fops_create(&init_net, "igmp", S_IRUGO, &igmp_mc_seq_fops);
+- proc_net_fops_create(&init_net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops);
++ if (!proc_net_fops_create(net, "igmp", S_IRUGO, &igmp_mc_seq_fops))
++ goto out_igmp;
++ if (!proc_net_fops_create(net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops))
++ goto out_mcfilter;
+ return 0;
++
++out_mcfilter:
++ proc_net_remove(net, "igmp");
++out_igmp:
++ return -ENOMEM;
++}
++
++static void igmp_net_exit(struct net *net)
++{
++ proc_net_remove(net, "igmp");
++ proc_net_remove(net, "mcfilter");
++}
++
++static struct pernet_operations igmp_net_ops = {
++ .init = igmp_net_init,
++ .exit = igmp_net_exit,
++};
++
++int __init igmp_mc_proc_init(void)
++{
++ return register_pernet_subsys(&igmp_net_ops);
+ }
+ #endif
+
+diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
+index 0c1ae68..f88f2dc 100644
+--- a/net/ipv4/inet_connection_sock.c
++++ b/net/ipv4/inet_connection_sock.c
+@@ -24,6 +24,9 @@
+ #include <net/tcp_states.h>
+ #include <net/xfrm.h>
+
++#include <bc/net.h>
++#include <bc/sock_orphan.h>
++
+ #ifdef INET_CSK_DEBUG
+ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n";
+ EXPORT_SYMBOL(inet_csk_timer_bug_msg);
+@@ -142,6 +145,8 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
+ goto tb_not_found;
+ tb_found:
+ if (!hlist_empty(&tb->owners)) {
++ if (sk->sk_reuse > 1)
++ goto success;
+ if (tb->fastreuse > 0 &&
+ sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
+ goto success;
+@@ -154,7 +159,7 @@ tb_found:
+ tb_not_found:
+ ret = 1;
+ if (!tb && (tb = inet_bind_bucket_create(hashinfo->bind_bucket_cachep,
+- net, head, snum)) == NULL)
++ net, head, snum, sk->owner_env)) == NULL)
+ goto fail_unlock;
+ if (hlist_empty(&tb->owners)) {
+ if (sk->sk_reuse && sk->sk_state != TCP_LISTEN)
+@@ -556,7 +561,7 @@ void inet_csk_destroy_sock(struct sock *sk)
+
+ sk_refcnt_debug_release(sk);
+
+- atomic_dec(sk->sk_prot->orphan_count);
++ ub_dec_orphan_count(sk);
+ sock_put(sk);
+ }
+
+@@ -636,7 +641,7 @@ void inet_csk_listen_stop(struct sock *sk)
+
+ sock_orphan(child);
+
+- atomic_inc(sk->sk_prot->orphan_count);
++ ub_inc_orphan_count(sk);
+
+ inet_csk_destroy_sock(child);
+
+diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
+index c10036e..758114b 100644
+--- a/net/ipv4/inet_diag.c
++++ b/net/ipv4/inet_diag.c
+@@ -706,6 +706,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
+ struct inet_diag_req *r = NLMSG_DATA(cb->nlh);
+ const struct inet_diag_handler *handler;
+ struct inet_hashinfo *hashinfo;
++ struct ve_struct *ve = get_exec_env();
+
+ handler = inet_diag_lock_handler(cb->nlh->nlmsg_type);
+ if (IS_ERR(handler))
+@@ -729,6 +730,8 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
+ sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
+ struct inet_sock *inet = inet_sk(sk);
+
++ if (!ve_accessible(sk->owner_env, ve))
++ continue;
+ if (num < s_num) {
+ num++;
+ continue;
+@@ -790,6 +793,8 @@ skip_listen_ht:
+ sk_for_each(sk, node, &head->chain) {
+ struct inet_sock *inet = inet_sk(sk);
+
++ if (!ve_accessible(sk->owner_env, ve))
++ continue;
+ if (num < s_num)
+ goto next_normal;
+ if (!(r->idiag_states & (1 << sk->sk_state)))
+@@ -814,6 +819,8 @@ next_normal:
+ inet_twsk_for_each(tw, node,
+ &head->twchain) {
+
++ if (!ve_accessible_veid(tw->tw_owner_env, VEID(ve)))
++ continue;
+ if (num < s_num)
+ goto next_dying;
+ if (r->id.idiag_sport != tw->tw_sport &&
+diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
+index 6c52e08..2039811 100644
+--- a/net/ipv4/inet_fragment.c
++++ b/net/ipv4/inet_fragment.c
+@@ -249,6 +249,9 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
+ spin_lock_init(&q->lock);
+ atomic_set(&q->refcnt, 1);
+ q->net = nf;
++#ifdef CONFIG_VE
++ q->owner_ve = get_exec_env();
++#endif
+
+ return q;
+ }
+diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
+index 4498190..e71191c 100644
+--- a/net/ipv4/inet_hashtables.c
++++ b/net/ipv4/inet_hashtables.c
+@@ -30,7 +30,8 @@
+ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
+ struct net *net,
+ struct inet_bind_hashbucket *head,
+- const unsigned short snum)
++ const unsigned short snum,
++ struct ve_struct *ve)
+ {
+ struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
+
+@@ -39,6 +40,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
+ tb->port = snum;
+ tb->fastreuse = 0;
+ INIT_HLIST_HEAD(&tb->owners);
++ tb->owner_env = ve;
+ hlist_add_head(&tb->node, &head->chain);
+ }
+ return tb;
+@@ -461,7 +463,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
+- net, head, port);
++ net, head, port, sk->owner_env);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
+index 743f011..4534cdf 100644
+--- a/net/ipv4/inet_timewait_sock.c
++++ b/net/ipv4/inet_timewait_sock.c
+@@ -13,6 +13,8 @@
+ #include <net/inet_timewait_sock.h>
+ #include <net/ip.h>
+
++#include <bc/sock_orphan.h>
++
+ /* Must be called with locally disabled BHs. */
+ static void __inet_twsk_kill(struct inet_timewait_sock *tw,
+ struct inet_hashinfo *hashinfo)
+@@ -107,9 +109,14 @@ EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
+
+ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
+ {
+- struct inet_timewait_sock *tw =
+- kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
+- GFP_ATOMIC);
++ struct user_beancounter *ub;
++ struct inet_timewait_sock *tw;
++
++ ub = set_exec_ub(sock_bc(sk)->ub);
++ tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
++ GFP_ATOMIC);
++ (void)set_exec_ub(ub);
++
+ if (tw != NULL) {
+ const struct inet_sock *inet = inet_sk(sk);
+
+@@ -158,6 +165,7 @@ static int inet_twdr_do_twkill_work(struct inet_timewait_death_row *twdr,
+ rescan:
+ inet_twsk_for_each_inmate(tw, node, &twdr->cells[slot]) {
+ __inet_twsk_del_dead_node(tw);
++ ub_timewait_dec(tw, twdr);
+ spin_unlock(&twdr->death_lock);
+ __inet_twsk_kill(tw, twdr->hashinfo);
+ #ifdef CONFIG_NET_NS
+@@ -258,6 +266,7 @@ void inet_twsk_deschedule(struct inet_timewait_sock *tw,
+ {
+ spin_lock(&twdr->death_lock);
+ if (inet_twsk_del_dead_node(tw)) {
++ ub_timewait_dec(tw, twdr);
+ inet_twsk_put(tw);
+ if (--twdr->tw_count == 0)
+ del_timer(&twdr->tw_timer);
+@@ -304,9 +313,10 @@ void inet_twsk_schedule(struct inet_timewait_sock *tw,
+ spin_lock(&twdr->death_lock);
+
+ /* Unlink it, if it was scheduled */
+- if (inet_twsk_del_dead_node(tw))
++ if (inet_twsk_del_dead_node(tw)) {
++ ub_timewait_dec(tw, twdr);
+ twdr->tw_count--;
+- else
++ } else
+ atomic_inc(&tw->tw_refcnt);
+
+ if (slot >= INET_TWDR_RECYCLE_SLOTS) {
+@@ -342,6 +352,7 @@ void inet_twsk_schedule(struct inet_timewait_sock *tw,
+
+ hlist_add_head(&tw->tw_death_node, list);
+
++ ub_timewait_inc(tw, twdr);
+ if (twdr->tw_count++ == 0)
+ mod_timer(&twdr->tw_timer, jiffies + twdr->period);
+ spin_unlock(&twdr->death_lock);
+@@ -376,6 +387,7 @@ void inet_twdr_twcal_tick(unsigned long data)
+ &twdr->twcal_row[slot]) {
+ __inet_twsk_del_dead_node(tw);
+ __inet_twsk_kill(tw, twdr->hashinfo);
++ ub_timewait_dec(tw, twdr);
+ #ifdef CONFIG_NET_NS
+ NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED);
+ #endif
+diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
+index 450016b..962de13 100644
+--- a/net/ipv4/ip_forward.c
++++ b/net/ipv4/ip_forward.c
+@@ -94,6 +94,24 @@ int ip_forward(struct sk_buff *skb)
+ goto drop;
+ }
+
++ /*
++ * We try to optimize forwarding of VE packets:
++ * do not decrement TTL (and so save skb_cow)
++ * during forwarding of outgoing pkts from VE.
++ * For incoming pkts we still do ttl decr,
++ * since such skb is not cloned and does not require
++ * actual cow. So, there is at least one place
++ * in pkts path with mandatory ttl decr, that is
++ * sufficient to prevent routing loops.
++ */
++ iph = ip_hdr(skb);
++ if (
++#ifdef CONFIG_IP_ROUTE_NAT
++ (rt->rt_flags & RTCF_NAT) == 0 && /* no NAT mangling expected */
++#endif /* and */
++ (skb->dev->features & NETIF_F_VENET)) /* src is VENET device */
++ goto no_ttl_decr;
++
+ /* We are about to mangle packet. Copy it! */
+ if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
+ goto drop;
+@@ -102,6 +120,8 @@ int ip_forward(struct sk_buff *skb)
+ /* Decrease ttl after skb cow done */
+ ip_decrease_ttl(iph);
+
++no_ttl_decr:
++
+ /*
+ * We now generate an ICMP HOST REDIRECT giving the route
+ * we calculated.
+diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
+index 2152d22..31abfb9 100644
+--- a/net/ipv4/ip_fragment.c
++++ b/net/ipv4/ip_fragment.c
+@@ -186,10 +186,13 @@ static void ip_evictor(struct net *net)
+ */
+ static void ip_expire(unsigned long arg)
+ {
++ struct inet_frag_queue *q = (struct inet_frag_queue *)arg;
+ struct ipq *qp;
+ struct net *net;
++ struct ve_struct *old_ve;
+
+- qp = container_of((struct inet_frag_queue *) arg, struct ipq, q);
++ qp = container_of(q, struct ipq, q);
++ old_ve = set_exec_env(q->owner_ve);
+ net = container_of(qp->q.net, struct net, ipv4.frags);
+
+ spin_lock(&qp->q.lock);
+@@ -214,6 +217,8 @@ static void ip_expire(unsigned long arg)
+ out:
+ spin_unlock(&qp->q.lock);
+ ipq_put(qp);
++
++ (void)set_exec_env(old_ve);
+ }
+
+ /* Find the correct entry in the "incomplete datagrams" queue for
+@@ -524,6 +529,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
+ clone->csum = 0;
+ clone->ip_summed = head->ip_summed;
+ atomic_add(clone->truesize, &qp->q.net->mem);
++ clone->owner_env = head->owner_env;
+ }
+
+ skb_shinfo(head)->frag_list = head->next;
+diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
+index e0bed56..04035d2 100644
+--- a/net/ipv4/ip_input.c
++++ b/net/ipv4/ip_input.c
+@@ -193,6 +193,8 @@ static int ip_local_deliver_finish(struct sk_buff *skb)
+ {
+ struct net *net = dev_net(skb->dev);
+
++ if (skb->destructor)
++ skb_orphan(skb);
+ __skb_pull(skb, ip_hdrlen(skb));
+
+ /* Point into the IP datagram, just past the header. */
+diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+index d533a89..76eee76 100644
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -1345,12 +1345,13 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
+ char data[40];
+ } replyopts;
+ struct ipcm_cookie ipc;
+- __be32 daddr;
++ __be32 saddr, daddr;
+ struct rtable *rt = skb->rtable;
+
+ if (ip_options_echo(&replyopts.opt, skb))
+ return;
+
++ saddr = ip_hdr(skb)->daddr;
+ daddr = ipc.addr = rt->rt_src;
+ ipc.opt = NULL;
+
+@@ -1365,7 +1366,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
+ struct flowi fl = { .oif = arg->bound_dev_if,
+ .nl_u = { .ip4_u =
+ { .daddr = daddr,
+- .saddr = rt->rt_spec_dst,
++ .saddr = saddr,
+ .tos = RT_TOS(ip_hdr(skb)->tos) } },
+ /* Not quite clean, but right. */
+ .uli_u = { .ports =
+diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
+index 42065ff..160fe1f 100644
+--- a/net/ipv4/ipconfig.c
++++ b/net/ipv4/ipconfig.c
+@@ -187,19 +187,20 @@ static int __init ic_open_devs(void)
+ struct ic_device *d, **last;
+ struct net_device *dev;
+ unsigned short oflags;
++ struct net *net = get_exec_env()->ve_netns;
+
+ last = &ic_first_dev;
+ rtnl_lock();
+
+ /* bring loopback device up first */
+- for_each_netdev(&init_net, dev) {
++ for_each_netdev(net, dev) {
+ if (!(dev->flags & IFF_LOOPBACK))
+ continue;
+ if (dev_change_flags(dev, dev->flags | IFF_UP) < 0)
+ printk(KERN_ERR "IP-Config: Failed to open %s\n", dev->name);
+ }
+
+- for_each_netdev(&init_net, dev) {
++ for_each_netdev(net, dev) {
+ if (dev->flags & IFF_LOOPBACK)
+ continue;
+ if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
+@@ -432,9 +433,6 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
+ unsigned char *sha, *tha; /* s for "source", t for "target" */
+ struct ic_device *d;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- goto drop;
+-
+ if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+ return NET_RX_DROP;
+
+@@ -852,9 +850,6 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
+ struct ic_device *d;
+ int len, ext_len;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- goto drop;
+-
+ /* Perform verifications before taking the lock. */
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop;
+diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
+index 4c6d2ca..572a117 100644
+--- a/net/ipv4/ipip.c
++++ b/net/ipv4/ipip.c
+@@ -106,6 +106,7 @@
+ #include <linux/init.h>
+ #include <linux/netfilter_ipv4.h>
+ #include <linux/if_ether.h>
++#include <linux/vzcalluser.h>
+
+ #include <net/sock.h>
+ #include <net/ip.h>
+@@ -144,6 +145,9 @@ static struct ip_tunnel * ipip_tunnel_lookup(struct net *net,
+ struct ip_tunnel *t;
+ struct ipip_net *ipn = net_generic(net, ipip_net_id);
+
++ if (ipn == NULL)
++ return NULL;
++
+ for (t = ipn->tunnels_r_l[h0^h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr &&
+ remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
+@@ -771,6 +775,9 @@ static int ipip_init_net(struct net *net)
+ int err;
+ struct ipip_net *ipn;
+
++ if (!(get_exec_env()->features & VE_FEATURE_IPIP))
++ return 0;
++
+ err = -ENOMEM;
+ ipn = kzalloc(sizeof(struct ipip_net), GFP_KERNEL);
+ if (ipn == NULL)
+@@ -816,6 +823,9 @@ static void ipip_exit_net(struct net *net)
+ struct ipip_net *ipn;
+
+ ipn = net_generic(net, ipip_net_id);
++ if (ipn == NULL) /* no VE_FEATURE_IPIP */
++ return;
++
+ rtnl_lock();
+ ipip_destroy_tunnels(ipn);
+ unregister_netdevice(ipn->fb_tunnel_dev);
+diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
+index c519b8d..e8abf43 100644
+--- a/net/ipv4/ipmr.c
++++ b/net/ipv4/ipmr.c
+@@ -40,6 +40,7 @@
+ #include <linux/in.h>
+ #include <linux/inet.h>
+ #include <linux/netdevice.h>
++#include <linux/nsproxy.h>
+ #include <linux/inetdevice.h>
+ #include <linux/igmp.h>
+ #include <linux/proc_fs.h>
+@@ -146,9 +147,10 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
+ static
+ struct net_device *ipmr_new_tunnel(struct vifctl *v)
+ {
++ struct net *net = get_exec_env()->ve_netns;
+ struct net_device *dev;
+
+- dev = __dev_get_by_name(&init_net, "tunl0");
++ dev = __dev_get_by_name(net, "tunl0");
+
+ if (dev) {
+ int err;
+@@ -172,7 +174,7 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v)
+
+ dev = NULL;
+
+- if (err == 0 && (dev = __dev_get_by_name(&init_net, p.name)) != NULL) {
++ if (err == 0 && (dev = __dev_get_by_name(net, p.name)) != NULL) {
+ dev->flags |= IFF_MULTICAST;
+
+ in_dev = __in_dev_get_rtnl(dev);
+@@ -1124,9 +1126,6 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
+ struct vif_device *v;
+ int ct;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+ if (event != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+ v=&vif_table[0];
+diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c
+index 44a6872..dec8193 100644
+--- a/net/ipv4/ipvs/ip_vs_conn.c
++++ b/net/ipv4/ipvs/ip_vs_conn.c
+@@ -979,7 +979,7 @@ int __init ip_vs_conn_init(void)
+ /* Allocate ip_vs_conn slab cache */
+ ip_vs_conn_cachep = kmem_cache_create("ip_vs_conn",
+ sizeof(struct ip_vs_conn), 0,
+- SLAB_HWCACHE_ALIGN, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_UBC, NULL);
+ if (!ip_vs_conn_cachep) {
+ vfree(ip_vs_conn_tab);
+ return -ENOMEM;
+diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c
+index a652da2..2b4c316 100644
+--- a/net/ipv4/ipvs/ip_vs_sync.c
++++ b/net/ipv4/ipvs/ip_vs_sync.c
+@@ -21,6 +21,7 @@
+ #include <linux/slab.h>
+ #include <linux/inetdevice.h>
+ #include <linux/net.h>
++#include <linux/nsproxy.h>
+ #include <linux/completion.h>
+ #include <linux/delay.h>
+ #include <linux/skbuff.h>
+@@ -475,7 +476,8 @@ static int set_mcast_if(struct sock *sk, char *ifname)
+ struct net_device *dev;
+ struct inet_sock *inet = inet_sk(sk);
+
+- if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL)
++ dev = __dev_get_by_name(get_exec_env()->ve_netns, ifname);
++ if (!dev)
+ return -ENODEV;
+
+ if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
+@@ -496,11 +498,12 @@ static int set_mcast_if(struct sock *sk, char *ifname)
+ */
+ static int set_sync_mesg_maxlen(int sync_state)
+ {
++ struct net *net = get_exec_env()->ve_netns;
+ struct net_device *dev;
+ int num;
+
+ if (sync_state == IP_VS_STATE_MASTER) {
+- if ((dev = __dev_get_by_name(&init_net, ip_vs_master_mcast_ifn)) == NULL)
++ if ((dev = __dev_get_by_name(net, ip_vs_master_mcast_ifn)) == NULL)
+ return -ENODEV;
+
+ num = (dev->mtu - sizeof(struct iphdr) -
+@@ -511,7 +514,7 @@ static int set_sync_mesg_maxlen(int sync_state)
+ IP_VS_DBG(7, "setting the maximum length of sync sending "
+ "message %d.\n", sync_send_mesg_maxlen);
+ } else if (sync_state == IP_VS_STATE_BACKUP) {
+- if ((dev = __dev_get_by_name(&init_net, ip_vs_backup_mcast_ifn)) == NULL)
++ if ((dev = __dev_get_by_name(net, ip_vs_backup_mcast_ifn)) == NULL)
+ return -ENODEV;
+
+ sync_recv_mesg_maxlen = dev->mtu -
+@@ -539,7 +542,8 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname)
+ memset(&mreq, 0, sizeof(mreq));
+ memcpy(&mreq.imr_multiaddr, addr, sizeof(struct in_addr));
+
+- if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL)
++ dev = __dev_get_by_name(get_exec_env()->ve_netns, ifname);
++ if (!dev)
+ return -ENODEV;
+ if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
+ return -EINVAL;
+@@ -560,7 +564,8 @@ static int bind_mcastif_addr(struct socket *sock, char *ifname)
+ __be32 addr;
+ struct sockaddr_in sin;
+
+- if ((dev = __dev_get_by_name(&init_net, ifname)) == NULL)
++ dev = __dev_get_by_name(get_exec_env()->ve_netns, ifname);
++ if (!dev)
+ return -ENODEV;
+
+ addr = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
+diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
+index 432ce9d..ac3b375 100644
+--- a/net/ipv4/netfilter/ip_queue.c
++++ b/net/ipv4/netfilter/ip_queue.c
+@@ -436,7 +436,7 @@ __ipq_rcv_skb(struct sk_buff *skb)
+ if (type <= IPQM_BASE)
+ return;
+
+- if (security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ RCV_SKB_FAIL(-EPERM);
+
+ write_lock_bh(&queue_lock);
+@@ -466,8 +466,12 @@ __ipq_rcv_skb(struct sk_buff *skb)
+ static void
+ ipq_rcv_skb(struct sk_buff *skb)
+ {
++ struct ve_struct *old_ve;
++
+ mutex_lock(&ipqnl_mutex);
++ old_ve = set_exec_env(skb->owner_env);
+ __ipq_rcv_skb(skb);
++ (void)set_exec_env(old_ve);
+ mutex_unlock(&ipqnl_mutex);
+ }
+
+@@ -477,9 +481,6 @@ ipq_rcv_dev_event(struct notifier_block *this,
+ {
+ struct net_device *dev = ptr;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+ /* Drop any packets associated with the downed device */
+ if (event == NETDEV_DOWN)
+ ipq_dev_drop(dev->ifindex);
+@@ -499,7 +500,7 @@ ipq_rcv_nl_event(struct notifier_block *this,
+ if (event == NETLINK_URELEASE &&
+ n->protocol == NETLINK_FIREWALL && n->pid) {
+ write_lock_bh(&queue_lock);
+- if ((n->net == &init_net) && (n->pid == peer_pid))
++ if (n->pid == peer_pid)
+ __ipq_reset();
+ write_unlock_bh(&queue_lock);
+ }
+diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
+index 4e7c719..18e2717 100644
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -337,6 +337,9 @@ ipt_do_table(struct sk_buff *skb,
+ struct ipt_entry *e, *back;
+ struct xt_table_info *private;
+
++ if (!table) /* VE is not allowed to have this xtable */
++ return NF_ACCEPT;
++
+ /* Initialization */
+ ip = ip_hdr(skb);
+ datalen = skb->len - ip->ihl * 4;
+@@ -488,8 +491,8 @@ mark_source_chains(struct xt_table_info *newinfo,
+ int visited = e->comefrom & (1 << hook);
+
+ if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
+- printk("iptables: loop hook %u pos %u %08X.\n",
+- hook, pos, e->comefrom);
++ ve_printk(VE_LOG, "iptables: loop hook %u pos "
++ "%u %08X.\n", hook, pos, e->comefrom);
+ return 0;
+ }
+ e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
+@@ -932,7 +935,7 @@ static struct xt_counters * alloc_counters(struct xt_table *table)
+ (other than comefrom, which userspace doesn't care
+ about). */
+ countersize = sizeof(struct xt_counters) * private->number;
+- counters = vmalloc_node(countersize, numa_node_id());
++ counters = ub_vmalloc_node(countersize, numa_node_id());
+
+ if (counters == NULL)
+ return ERR_PTR(-ENOMEM);
+@@ -1202,7 +1205,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
+ void *loc_cpu_old_entry;
+
+ ret = 0;
+- counters = vmalloc(num_counters * sizeof(struct xt_counters));
++ counters = ub_vmalloc_best(num_counters * sizeof(struct xt_counters));
+ if (!counters) {
+ ret = -ENOMEM;
+ goto out;
+@@ -1374,7 +1377,7 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat
+ if (len != size + num_counters * sizeof(struct xt_counters))
+ return -EINVAL;
+
+- paddc = vmalloc_node(len - size, numa_node_id());
++ paddc = ub_vmalloc_node(len - size, numa_node_id());
+ if (!paddc)
+ return -ENOMEM;
+
+@@ -1841,13 +1844,15 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
+ return ret;
+ }
+
++static int do_ipt_set_ctl(struct sock *, int, void __user *, unsigned int);
++
+ static int
+ compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
+ unsigned int len)
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) && !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -1860,8 +1865,7 @@ compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
+ break;
+
+ default:
+- duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
+- ret = -EINVAL;
++ ret = do_ipt_set_ctl(sk, cmd, user, len);
+ }
+
+ return ret;
+@@ -1958,7 +1962,7 @@ compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) && !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -1980,7 +1984,7 @@ do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) && !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -2005,7 +2009,7 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_NET_ADMIN) && !capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -2057,7 +2061,7 @@ struct xt_table *ipt_register_table(struct net *net, struct xt_table *table,
+ int ret;
+ struct xt_table_info *newinfo;
+ struct xt_table_info bootstrap
+- = { 0, 0, 0, { 0 }, { 0 }, { } };
++ = { 0, 0, 0, 0, { 0 }, { 0 }, { } };
+ void *loc_cpu_entry;
+ struct xt_table *new_table;
+
+@@ -2216,11 +2220,22 @@ static struct xt_match icmp_matchstruct __read_mostly = {
+
+ static int __net_init ip_tables_net_init(struct net *net)
+ {
+- return xt_proto_init(net, AF_INET);
++ int res;
++
++ if (!net_ipt_module_permitted(net, VE_IP_IPTABLES))
++ return 0;
++
++ res = xt_proto_init(net, AF_INET);
++ if (!res)
++ net_ipt_module_set(net, VE_IP_IPTABLES);
++ return res;
+ }
+
+ static void __net_exit ip_tables_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_IPTABLES))
++ return;
++
+ xt_proto_fini(net, AF_INET);
+ }
+
+diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
+index fafe8eb..1563e5c 100644
+--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
++++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
+@@ -20,6 +20,7 @@
+ #include <linux/icmp.h>
+ #include <linux/if_arp.h>
+ #include <linux/seq_file.h>
++#include <linux/nsproxy.h>
+ #include <linux/netfilter_arp.h>
+ #include <linux/netfilter/x_tables.h>
+ #include <linux/netfilter_ipv4/ip_tables.h>
+@@ -388,7 +389,8 @@ clusterip_tg_check(const char *tablename, const void *e_void,
+ return false;
+ }
+
+- dev = dev_get_by_name(&init_net, e->ip.iniface);
++ dev = dev_get_by_name(get_exec_env()->ve_netns,
++ e->ip.iniface);
+ if (!dev) {
+ printk(KERN_WARNING "CLUSTERIP: no such interface %s\n", e->ip.iniface);
+ return false;
+diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
+index 0af1413..08a4bcd 100644
+--- a/net/ipv4/netfilter/ipt_LOG.c
++++ b/net/ipv4/netfilter/ipt_LOG.c
+@@ -47,32 +47,32 @@ static void dump_packet(const struct nf_loginfo *info,
+
+ ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph);
+ if (ih == NULL) {
+- printk("TRUNCATED");
++ ve_printk(VE_LOG, "TRUNCATED");
+ return;
+ }
+
+ /* Important fields:
+ * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+ /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+- printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
++ ve_printk(VE_LOG, "SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
+ NIPQUAD(ih->saddr), NIPQUAD(ih->daddr));
+
+ /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+- printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
++ ve_printk(VE_LOG, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+ ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK,
+ ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id));
+
+ /* Max length: 6 "CE DF MF " */
+ if (ntohs(ih->frag_off) & IP_CE)
+- printk("CE ");
++ ve_printk(VE_LOG, "CE ");
+ if (ntohs(ih->frag_off) & IP_DF)
+- printk("DF ");
++ ve_printk(VE_LOG, "DF ");
+ if (ntohs(ih->frag_off) & IP_MF)
+- printk("MF ");
++ ve_printk(VE_LOG, "MF ");
+
+ /* Max length: 11 "FRAG:65535 " */
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+- printk("FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET);
++ ve_printk(VE_LOG, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET);
+
+ if ((logflags & IPT_LOG_IPOPT)
+ && ih->ihl * 4 > sizeof(struct iphdr)) {
+@@ -84,15 +84,15 @@ static void dump_packet(const struct nf_loginfo *info,
+ op = skb_header_pointer(skb, iphoff+sizeof(_iph),
+ optsize, _opt);
+ if (op == NULL) {
+- printk("TRUNCATED");
++ ve_printk(VE_LOG, "TRUNCATED");
+ return;
+ }
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+- printk("OPT (");
++ ve_printk(VE_LOG, "OPT (");
+ for (i = 0; i < optsize; i++)
+- printk("%02X", op[i]);
+- printk(") ");
++ ve_printk(VE_LOG, "%02X", op[i]);
++ ve_printk(VE_LOG, ") ");
+ }
+
+ switch (ih->protocol) {
+@@ -101,7 +101,7 @@ static void dump_packet(const struct nf_loginfo *info,
+ const struct tcphdr *th;
+
+ /* Max length: 10 "PROTO=TCP " */
+- printk("PROTO=TCP ");
++ ve_printk(VE_LOG, "PROTO=TCP ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+@@ -110,41 +110,41 @@ static void dump_packet(const struct nf_loginfo *info,
+ th = skb_header_pointer(skb, iphoff + ih->ihl * 4,
+ sizeof(_tcph), &_tcph);
+ if (th == NULL) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+- printk("SPT=%u DPT=%u ",
++ ve_printk(VE_LOG, "SPT=%u DPT=%u ",
+ ntohs(th->source), ntohs(th->dest));
+ /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+ if (logflags & IPT_LOG_TCPSEQ)
+- printk("SEQ=%u ACK=%u ",
++ ve_printk(VE_LOG, "SEQ=%u ACK=%u ",
+ ntohl(th->seq), ntohl(th->ack_seq));
+ /* Max length: 13 "WINDOW=65535 " */
+- printk("WINDOW=%u ", ntohs(th->window));
++ ve_printk(VE_LOG, "WINDOW=%u ", ntohs(th->window));
+ /* Max length: 9 "RES=0x3F " */
+- printk("RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22));
++ ve_printk(VE_LOG, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22));
+ /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
+ if (th->cwr)
+- printk("CWR ");
++ ve_printk(VE_LOG, "CWR ");
+ if (th->ece)
+- printk("ECE ");
++ ve_printk(VE_LOG, "ECE ");
+ if (th->urg)
+- printk("URG ");
++ ve_printk(VE_LOG, "URG ");
+ if (th->ack)
+- printk("ACK ");
++ ve_printk(VE_LOG, "ACK ");
+ if (th->psh)
+- printk("PSH ");
++ ve_printk(VE_LOG, "PSH ");
+ if (th->rst)
+- printk("RST ");
++ ve_printk(VE_LOG, "RST ");
+ if (th->syn)
+- printk("SYN ");
++ ve_printk(VE_LOG, "SYN ");
+ if (th->fin)
+- printk("FIN ");
++ ve_printk(VE_LOG, "FIN ");
+ /* Max length: 11 "URGP=65535 " */
+- printk("URGP=%u ", ntohs(th->urg_ptr));
++ ve_printk(VE_LOG, "URGP=%u ", ntohs(th->urg_ptr));
+
+ if ((logflags & IPT_LOG_TCPOPT)
+ && th->doff * 4 > sizeof(struct tcphdr)) {
+@@ -157,15 +157,15 @@ static void dump_packet(const struct nf_loginfo *info,
+ iphoff+ih->ihl*4+sizeof(_tcph),
+ optsize, _opt);
+ if (op == NULL) {
+- printk("TRUNCATED");
++ ve_printk(VE_LOG, "TRUNCATED");
+ return;
+ }
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+- printk("OPT (");
++ ve_printk(VE_LOG, "OPT (");
+ for (i = 0; i < optsize; i++)
+- printk("%02X", op[i]);
+- printk(") ");
++ ve_printk(VE_LOG, "%02X", op[i]);
++ ve_printk(VE_LOG, ") ");
+ }
+ break;
+ }
+@@ -176,9 +176,9 @@ static void dump_packet(const struct nf_loginfo *info,
+
+ if (ih->protocol == IPPROTO_UDP)
+ /* Max length: 10 "PROTO=UDP " */
+- printk("PROTO=UDP " );
++ ve_printk(VE_LOG, "PROTO=UDP " );
+ else /* Max length: 14 "PROTO=UDPLITE " */
+- printk("PROTO=UDPLITE ");
++ ve_printk(VE_LOG, "PROTO=UDPLITE ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+@@ -187,13 +187,13 @@ static void dump_packet(const struct nf_loginfo *info,
+ uh = skb_header_pointer(skb, iphoff+ih->ihl*4,
+ sizeof(_udph), &_udph);
+ if (uh == NULL) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+- printk("SPT=%u DPT=%u LEN=%u ",
++ ve_printk(VE_LOG, "SPT=%u DPT=%u LEN=%u ",
+ ntohs(uh->source), ntohs(uh->dest),
+ ntohs(uh->len));
+ break;
+@@ -220,7 +220,7 @@ static void dump_packet(const struct nf_loginfo *info,
+ [ICMP_ADDRESSREPLY] = 12 };
+
+ /* Max length: 11 "PROTO=ICMP " */
+- printk("PROTO=ICMP ");
++ ve_printk(VE_LOG, "PROTO=ICMP ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+@@ -229,19 +229,19 @@ static void dump_packet(const struct nf_loginfo *info,
+ ich = skb_header_pointer(skb, iphoff + ih->ihl * 4,
+ sizeof(_icmph), &_icmph);
+ if (ich == NULL) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Max length: 18 "TYPE=255 CODE=255 " */
+- printk("TYPE=%u CODE=%u ", ich->type, ich->code);
++ ve_printk(VE_LOG, "TYPE=%u CODE=%u ", ich->type, ich->code);
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (ich->type <= NR_ICMP_TYPES
+ && required_len[ich->type]
+ && skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+@@ -250,19 +250,19 @@ static void dump_packet(const struct nf_loginfo *info,
+ case ICMP_ECHOREPLY:
+ case ICMP_ECHO:
+ /* Max length: 19 "ID=65535 SEQ=65535 " */
+- printk("ID=%u SEQ=%u ",
++ ve_printk(VE_LOG, "ID=%u SEQ=%u ",
+ ntohs(ich->un.echo.id),
+ ntohs(ich->un.echo.sequence));
+ break;
+
+ case ICMP_PARAMETERPROB:
+ /* Max length: 14 "PARAMETER=255 " */
+- printk("PARAMETER=%u ",
++ ve_printk(VE_LOG, "PARAMETER=%u ",
+ ntohl(ich->un.gateway) >> 24);
+ break;
+ case ICMP_REDIRECT:
+ /* Max length: 24 "GATEWAY=255.255.255.255 " */
+- printk("GATEWAY=%u.%u.%u.%u ",
++ ve_printk(VE_LOG, "GATEWAY=%u.%u.%u.%u ",
+ NIPQUAD(ich->un.gateway));
+ /* Fall through */
+ case ICMP_DEST_UNREACH:
+@@ -270,16 +270,16 @@ static void dump_packet(const struct nf_loginfo *info,
+ case ICMP_TIME_EXCEEDED:
+ /* Max length: 3+maxlen */
+ if (!iphoff) { /* Only recurse once. */
+- printk("[");
++ ve_printk(VE_LOG, "[");
+ dump_packet(info, skb,
+ iphoff + ih->ihl*4+sizeof(_icmph));
+- printk("] ");
++ ve_printk(VE_LOG, "] ");
+ }
+
+ /* Max length: 10 "MTU=65535 " */
+ if (ich->type == ICMP_DEST_UNREACH
+ && ich->code == ICMP_FRAG_NEEDED)
+- printk("MTU=%u ", ntohs(ich->un.frag.mtu));
++ ve_printk(VE_LOG, "MTU=%u ", ntohs(ich->un.frag.mtu));
+ }
+ break;
+ }
+@@ -292,19 +292,19 @@ static void dump_packet(const struct nf_loginfo *info,
+ break;
+
+ /* Max length: 9 "PROTO=AH " */
+- printk("PROTO=AH ");
++ ve_printk(VE_LOG, "PROTO=AH ");
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ ah = skb_header_pointer(skb, iphoff+ih->ihl*4,
+ sizeof(_ahdr), &_ahdr);
+ if (ah == NULL) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 " */
+- printk("SPI=0x%x ", ntohl(ah->spi));
++ ve_printk(VE_LOG, "SPI=0x%x ", ntohl(ah->spi));
+ break;
+ }
+ case IPPROTO_ESP: {
+@@ -312,7 +312,7 @@ static void dump_packet(const struct nf_loginfo *info,
+ const struct ip_esp_hdr *eh;
+
+ /* Max length: 10 "PROTO=ESP " */
+- printk("PROTO=ESP ");
++ ve_printk(VE_LOG, "PROTO=ESP ");
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+@@ -321,25 +321,25 @@ static void dump_packet(const struct nf_loginfo *info,
+ eh = skb_header_pointer(skb, iphoff+ih->ihl*4,
+ sizeof(_esph), &_esph);
+ if (eh == NULL) {
+- printk("INCOMPLETE [%u bytes] ",
++ ve_printk(VE_LOG, "INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - ih->ihl*4);
+ break;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 " */
+- printk("SPI=0x%x ", ntohl(eh->spi));
++ ve_printk(VE_LOG, "SPI=0x%x ", ntohl(eh->spi));
+ break;
+ }
+ /* Max length: 10 "PROTO 255 " */
+ default:
+- printk("PROTO=%u ", ih->protocol);
++ ve_printk(VE_LOG, "PROTO=%u ", ih->protocol);
+ }
+
+ /* Max length: 15 "UID=4294967295 " */
+ if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) {
+ read_lock_bh(&skb->sk->sk_callback_lock);
+ if (skb->sk->sk_socket && skb->sk->sk_socket->file)
+- printk("UID=%u GID=%u ",
++ ve_printk(VE_LOG, "UID=%u GID=%u ",
+ skb->sk->sk_socket->file->f_uid,
+ skb->sk->sk_socket->file->f_gid);
+ read_unlock_bh(&skb->sk->sk_callback_lock);
+@@ -387,7 +387,7 @@ ipt_log_packet(unsigned int pf,
+ loginfo = &default_loginfo;
+
+ spin_lock_bh(&log_lock);
+- printk("<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
++ ve_printk(VE_LOG, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
+ prefix,
+ in ? in->name : "",
+ out ? out->name : "");
+@@ -398,30 +398,30 @@ ipt_log_packet(unsigned int pf,
+
+ physindev = skb->nf_bridge->physindev;
+ if (physindev && in != physindev)
+- printk("PHYSIN=%s ", physindev->name);
++ ve_printk(VE_LOG, "PHYSIN=%s ", physindev->name);
+ physoutdev = skb->nf_bridge->physoutdev;
+ if (physoutdev && out != physoutdev)
+- printk("PHYSOUT=%s ", physoutdev->name);
++ ve_printk(VE_LOG, "PHYSOUT=%s ", physoutdev->name);
+ }
+ #endif
+
+ if (in && !out) {
+ /* MAC logging for input chain only. */
+- printk("MAC=");
++ ve_printk(VE_LOG, "MAC=");
+ if (skb->dev && skb->dev->hard_header_len
+ && skb->mac_header != skb->network_header) {
+ int i;
+ const unsigned char *p = skb_mac_header(skb);
+ for (i = 0; i < skb->dev->hard_header_len; i++,p++)
+- printk("%02x%c", *p,
++ ve_printk(VE_LOG, "%02x%c", *p,
+ i==skb->dev->hard_header_len - 1
+ ? ' ':':');
+ } else
+- printk(" ");
++ ve_printk(VE_LOG, " ");
+ }
+
+ dump_packet(loginfo, skb, 0);
+- printk("\n");
++ ve_printk(VE_LOG, "\n");
+ spin_unlock_bh(&log_lock);
+ }
+
+diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
+index 0841aef..85e4a69 100644
+--- a/net/ipv4/netfilter/ipt_MASQUERADE.c
++++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
+@@ -98,6 +98,7 @@ masquerade_tg(struct sk_buff *skb, const struct net_device *in,
+ return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_SRC);
+ }
+
++#if 0
+ static int
+ device_cmp(struct nf_conn *i, void *ifindex)
+ {
+@@ -120,9 +121,6 @@ static int masq_device_event(struct notifier_block *this,
+ {
+ const struct net_device *dev = ptr;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+ if (event == NETDEV_DOWN) {
+ /* Device was downed. Search entire table for
+ conntracks which were associated with that device,
+@@ -150,6 +148,7 @@ static struct notifier_block masq_dev_notifier = {
+ static struct notifier_block masq_inet_notifier = {
+ .notifier_call = masq_inet_event,
+ };
++#endif
+
+ static struct xt_target masquerade_tg_reg __read_mostly = {
+ .name = "MASQUERADE",
+@@ -168,12 +167,16 @@ static int __init masquerade_tg_init(void)
+
+ ret = xt_register_target(&masquerade_tg_reg);
+
++#if 0
++/* These notifiers are unnecessary and may
++ lead to oops in virtual environments */
+ if (ret == 0) {
+ /* Register for device down reports */
+ register_netdevice_notifier(&masq_dev_notifier);
+ /* Register IP address change reports */
+ register_inetaddr_notifier(&masq_inet_notifier);
+ }
++#endif
+
+ return ret;
+ }
+@@ -181,8 +184,8 @@ static int __init masquerade_tg_init(void)
+ static void __exit masquerade_tg_exit(void)
+ {
+ xt_unregister_target(&masquerade_tg_reg);
+- unregister_netdevice_notifier(&masq_dev_notifier);
+- unregister_inetaddr_notifier(&masq_inet_notifier);
++/* unregister_netdevice_notifier(&masq_dev_notifier);
++ unregister_inetaddr_notifier(&masq_inet_notifier);*/
+ }
+
+ module_init(masquerade_tg_init);
+diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c
+index 5c62924..99dfc92 100644
+--- a/net/ipv4/netfilter/ipt_REDIRECT.c
++++ b/net/ipv4/netfilter/ipt_REDIRECT.c
+@@ -72,8 +72,13 @@ redirect_tg(struct sk_buff *skb, const struct net_device *in,
+
+ rcu_read_lock();
+ indev = __in_dev_get_rcu(skb->dev);
+- if (indev && (ifa = indev->ifa_list))
++ if (indev && (ifa = indev->ifa_list)) {
++ /* because of venet device specific, we should use
++ * second ifa in the list */
++ if (IN_LOOPBACK(ntohl(ifa->ifa_local)) && ifa->ifa_next)
++ ifa = ifa->ifa_next;
+ newdst = ifa->ifa_local;
++ }
+ rcu_read_unlock();
+
+ if (!newdst)
+diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
+index 2639872..6b1fcf8 100644
+--- a/net/ipv4/netfilter/ipt_REJECT.c
++++ b/net/ipv4/netfilter/ipt_REJECT.c
+@@ -186,13 +186,13 @@ reject_tg_check(const char *tablename, const void *e_void,
+ const struct ipt_entry *e = e_void;
+
+ if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
+- printk("ipt_REJECT: ECHOREPLY no longer supported.\n");
++ ve_printk(VE_LOG, "ipt_REJECT: ECHOREPLY no longer supported.\n");
+ return false;
+ } else if (rejinfo->with == IPT_TCP_RESET) {
+ /* Must specify that it's a TCP packet */
+ if (e->ip.proto != IPPROTO_TCP
+ || (e->ip.invflags & XT_INV_PROTO)) {
+- printk("ipt_REJECT: TCP_RESET invalid for non-tcp\n");
++ ve_printk(VE_LOG, "ipt_REJECT: TCP_RESET invalid for non-tcp\n");
+ return false;
+ }
+ }
+diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c
+index 3974d7c..11ae8fd 100644
+--- a/net/ipv4/netfilter/ipt_recent.c
++++ b/net/ipv4/netfilter/ipt_recent.c
+@@ -14,6 +14,7 @@
+ #include <linux/init.h>
+ #include <linux/ip.h>
+ #include <linux/moduleparam.h>
++#include <linux/nsproxy.h>
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
+ #include <linux/string.h>
+@@ -52,6 +53,19 @@ MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files");
+ MODULE_PARM_DESC(ip_list_uid,"owner of /proc/net/ipt_recent/* files");
+ MODULE_PARM_DESC(ip_list_gid,"owning group of /proc/net/ipt_recent/* files");
+
++#include <linux/sched.h>
++
++#if defined(CONFIG_VE_IPTABLES)
++#define tables (get_exec_env()->_ipt_recent->tables)
++#define proc_dir (get_exec_env()->_ipt_recent->proc_dir)
++#else
++static LIST_HEAD(tables);
++static struct proc_dir_entry *proc_dir;
++#endif /* CONFIG_VE_IPTABLES */
++
++static int init_ipt_recent(struct ve_struct *ve);
++static void fini_ipt_recent(struct ve_struct *ve);
++
+ struct recent_entry {
+ struct list_head list;
+ struct list_head lru_list;
+@@ -74,12 +88,10 @@ struct recent_table {
+ struct list_head iphash[0];
+ };
+
+-static LIST_HEAD(tables);
+ static DEFINE_SPINLOCK(recent_lock);
+ static DEFINE_MUTEX(recent_mutex);
+
+ #ifdef CONFIG_PROC_FS
+-static struct proc_dir_entry *proc_dir;
+ static const struct file_operations recent_fops;
+ #endif
+
+@@ -258,6 +270,9 @@ recent_mt_check(const char *tablename, const void *ip,
+ strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN)
+ return false;
+
++ if (init_ipt_recent(get_exec_env()))
++ return 0;
++
+ mutex_lock(&recent_mutex);
+ t = recent_table_lookup(info->name);
+ if (t != NULL) {
+@@ -298,6 +313,13 @@ static void recent_mt_destroy(const struct xt_match *match, void *matchinfo)
+ {
+ const struct ipt_recent_info *info = matchinfo;
+ struct recent_table *t;
++ struct ve_struct *ve;
++
++ ve = get_exec_env();
++#ifdef CONFIG_VE_IPTABLES
++ if (!ve->_ipt_recent)
++ return;
++#endif
+
+ mutex_lock(&recent_mutex);
+ t = recent_table_lookup(info->name);
+@@ -312,6 +334,8 @@ static void recent_mt_destroy(const struct xt_match *match, void *matchinfo)
+ kfree(t);
+ }
+ mutex_unlock(&recent_mutex);
++ if (!ve_is_super(ve) && list_empty(&tables))
++ fini_ipt_recent(ve);
+ }
+
+ #ifdef CONFIG_PROC_FS
+@@ -467,6 +491,49 @@ static struct xt_match recent_mt_reg __read_mostly = {
+ .me = THIS_MODULE,
+ };
+
++static int init_ipt_recent(struct ve_struct *ve)
++{
++ int err = 0;
++
++#ifdef CONFIG_VE_IPTABLES
++ if (ve->_ipt_recent)
++ return 0;
++
++ ve->_ipt_recent = kzalloc(sizeof(struct ve_ipt_recent), GFP_KERNEL);
++ if (!ve->_ipt_recent) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ INIT_LIST_HEAD(&tables);
++#endif
++#ifdef CONFIG_PROC_FS
++ if (err)
++ return err;
++ proc_dir = proc_mkdir("ipt_recent", ve->ve_netns->proc_net);
++ if (proc_dir == NULL) {
++ err = -ENOMEM;
++ goto out_mem;
++ }
++#endif
++out:
++ return err;
++out_mem:
++#ifdef CONFIG_VE_IPTABLES
++ kfree(ve->_ipt_recent);
++#endif
++ goto out;
++}
++
++static void fini_ipt_recent(struct ve_struct *ve)
++{
++ remove_proc_entry("ipt_recent", ve->ve_netns->proc_net);
++#ifdef CONFIG_VE_IPTABLES
++ kfree(ve->_ipt_recent);
++ ve->_ipt_recent = NULL;
++#endif
++}
++
+ static int __init recent_mt_init(void)
+ {
+ int err;
+@@ -476,25 +543,24 @@ static int __init recent_mt_init(void)
+ ip_list_hash_size = 1 << fls(ip_list_tot);
+
+ err = xt_register_match(&recent_mt_reg);
+-#ifdef CONFIG_PROC_FS
+ if (err)
+ return err;
+- proc_dir = proc_mkdir("ipt_recent", init_net.proc_net);
+- if (proc_dir == NULL) {
++
++ err = init_ipt_recent(&ve0);
++ if (err) {
+ xt_unregister_match(&recent_mt_reg);
+- err = -ENOMEM;
++ return err;
+ }
+-#endif
+- return err;
++
++ return 0;
+ }
+
+ static void __exit recent_mt_exit(void)
+ {
+ BUG_ON(!list_empty(&tables));
++
++ fini_ipt_recent(&ve0);
+ xt_unregister_match(&recent_mt_reg);
+-#ifdef CONFIG_PROC_FS
+- remove_proc_entry("ipt_recent", init_net.proc_net);
+-#endif
+ }
+
+ module_init(recent_mt_init);
+diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
+index 1ea677d..12c4c2b 100644
+--- a/net/ipv4/netfilter/iptable_filter.c
++++ b/net/ipv4/netfilter/iptable_filter.c
+@@ -134,16 +134,24 @@ module_param(forward, bool, 0000);
+
+ static int __net_init iptable_filter_net_init(struct net *net)
+ {
++ if (!net_ipt_module_permitted(net, VE_IP_FILTER))
++ return 0;
++
+ /* Register table */
+ net->ipv4.iptable_filter =
+ ipt_register_table(net, &packet_filter, &initial_table.repl);
+ if (IS_ERR(net->ipv4.iptable_filter))
+ return PTR_ERR(net->ipv4.iptable_filter);
++
++ net_ipt_module_set(net, VE_IP_FILTER);
+ return 0;
+ }
+
+ static void __net_exit iptable_filter_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_FILTER))
++ return;
++
+ ipt_unregister_table(net->ipv4.iptable_filter);
+ }
+
+diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
+index da59182..f6343d8 100644
+--- a/net/ipv4/netfilter/iptable_mangle.c
++++ b/net/ipv4/netfilter/iptable_mangle.c
+@@ -203,16 +203,24 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = {
+
+ static int __net_init iptable_mangle_net_init(struct net *net)
+ {
++ if (!net_ipt_module_permitted(net, VE_IP_MANGLE))
++ return 0;
++
+ /* Register table */
+ net->ipv4.iptable_mangle =
+ ipt_register_table(net, &packet_mangler, &initial_table.repl);
+ if (IS_ERR(net->ipv4.iptable_mangle))
+ return PTR_ERR(net->ipv4.iptable_mangle);
++
++ net_ipt_module_set(net, VE_IP_MANGLE);
+ return 0;
+ }
+
+ static void __net_exit iptable_mangle_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_MANGLE))
++ return;
++
+ ipt_unregister_table(net->ipv4.iptable_mangle);
+ }
+
+diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+index 5a955c4..b4bb436 100644
+--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
++++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+@@ -15,6 +15,7 @@
+ #include <linux/sysctl.h>
+ #include <net/route.h>
+ #include <net/ip.h>
++#include <linux/nfcalls.h>
+
+ #include <linux/netfilter_ipv4.h>
+ #include <net/netfilter/nf_conntrack.h>
+@@ -417,66 +418,214 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET));
+ MODULE_ALIAS("ip_conntrack");
+ MODULE_LICENSE("GPL");
+
+-static int __init nf_conntrack_l3proto_ipv4_init(void)
++#ifdef CONFIG_VE_IPTABLES
++#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
++static int nf_ct_proto_ipv4_sysctl_init(void)
+ {
+- int ret = 0;
++ struct nf_conntrack_l3proto *ipv4 = ve_nf_conntrack_l3proto_ipv4;
++ struct ctl_table *ct_table;
++ struct net *net = get_exec_env()->ve_netns;
+
+- need_conntrack();
++ ct_table = ip_ct_sysctl_table;
+
+- ret = nf_register_sockopt(&so_getorigdst);
+- if (ret < 0) {
+- printk(KERN_ERR "Unable to register netfilter socket option\n");
+- return ret;
++ if (net != &init_net) {
++ ct_table = kmemdup(ct_table, sizeof(ip_ct_sysctl_table),
++ GFP_KERNEL);
++ if (!ct_table)
++ return -ENOMEM;
++ }
++
++ ipv4->ctl_table_header = NULL;
++ ipv4->ctl_table_path = nf_net_ipv4_netfilter_sysctl_path;
++ ipv4->ctl_table = ct_table;
++
++ ipv4->ctl_table[0].data = &ve_nf_conntrack_max;
++ ipv4->ctl_table[1].data = &ve_nf_conntrack_count;
++ ipv4->ctl_table[3].data = &ve_nf_conntrack_checksum;
++ ipv4->ctl_table[4].data = &ve_nf_ct_log_invalid;
++
++ return 0;
++}
++
++static void nf_ct_proto_ipv4_sysctl_cleanup(void)
++{
++ struct net *net = get_exec_env()->ve_netns;
++
++ if (net != &init_net) {
++ kfree(ve_nf_conntrack_l3proto_ipv4->ctl_table);
++ }
++}
++#else
++static inline int nf_ct_proto_ipv4_sysctl_init(void)
++{
++ return 0;
++}
++static inline void nf_ct_proto_ipv4_sysctl_cleanup(void)
++{
++}
++#endif /* SYSCTL && NF_CONNTRACK_PROC_COMPAT */
++
++/*
++ * Functions init/fini_nf_ct_l3proto_ipv4 glue distributed nf_conntrack
++ * virtualization efforts. They are to be called from 2 places:
++ *
++ * 1) on loading/unloading module nf_conntrack_ipv4 from
++ * nf_conntrack_l3proto_ipv4_init/fini
++ * 2) on start/stop ve - from do_ve_iptables
++ */
++static int nf_ct_proto_ipv4_init(void)
++{
++ struct nf_conntrack_l3proto *ipv4;
++
++ if (ve_is_super(get_exec_env())) {
++ ipv4 = &nf_conntrack_l3proto_ipv4;
++ goto out;
+ }
++ ipv4 = kmemdup(&nf_conntrack_l3proto_ipv4,
++ sizeof(struct nf_conntrack_l3proto), GFP_KERNEL);
++ if (!ipv4)
++ return -ENOMEM;
++out:
++ ve_nf_conntrack_l3proto_ipv4 = ipv4;
++ return 0;
++}
++
++static void nf_ct_proto_ipv4_fini(void)
++{
++ if (!ve_is_super(get_exec_env()))
++ kfree(ve_nf_conntrack_l3proto_ipv4);
++}
++#endif
++
++int init_nf_ct_l3proto_ipv4(void)
++{
++ int ret = -ENOMEM;
++ int do_hooks = ve_is_super(get_exec_env());
++
++#ifdef CONFIG_VE_IPTABLES
++ if (!ve_is_super(get_exec_env()))
++ __module_get(THIS_MODULE);
+
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp4);
++ ret = nf_ct_proto_ipv4_init();
++ if (ret < 0)
++ goto err_out;
++ ret = nf_ct_proto_ipv4_sysctl_init();
++ if (ret < 0)
++ goto no_mem_ipv4;
++ ret = nf_ct_proto_icmp_sysctl_init();
++ if (ret < 0)
++ goto no_mem_icmp;
++#endif /* CONFIG_VE_IPTABLES */
++
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_tcp4);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv4: can't register tcp.\n");
+- goto cleanup_sockopt;
++ goto cleanup_sys;
+ }
+
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp4);
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_udp4);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv4: can't register udp.\n");
+- goto cleanup_tcp;
++ goto unreg_tcp;
+ }
+
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmp);
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_icmp);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv4: can't register icmp.\n");
+- goto cleanup_udp;
++ goto unreg_udp;
+ }
+
+- ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);
++ ret = nf_conntrack_l3proto_register(ve_nf_conntrack_l3proto_ipv4);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv4: can't register ipv4\n");
+- goto cleanup_icmp;
++ goto unreg_icmp;
+ }
+
+- ret = nf_register_hooks(ipv4_conntrack_ops,
+- ARRAY_SIZE(ipv4_conntrack_ops));
+- if (ret < 0) {
+- printk("nf_conntrack_ipv4: can't register hooks.\n");
+- goto cleanup_ipv4;
++ if (do_hooks) {
++ ret = nf_register_hooks(ipv4_conntrack_ops,
++ ARRAY_SIZE(ipv4_conntrack_ops));
++ if (ret < 0) {
++ printk("nf_conntrack_ipv4: can't register hooks.\n");
++ goto unreg_ipv4;
++ }
+ }
+-#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
+ ret = nf_conntrack_ipv4_compat_init();
+ if (ret < 0)
+- goto cleanup_hooks;
+-#endif
++ goto unreg_hooks;
++ return 0;
++
++unreg_hooks:
++ if (do_hooks)
++ nf_unregister_hooks(ipv4_conntrack_ops,
++ ARRAY_SIZE(ipv4_conntrack_ops));
++unreg_ipv4:
++ nf_conntrack_l3proto_unregister(ve_nf_conntrack_l3proto_ipv4);
++unreg_icmp:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_icmp);
++unreg_udp:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_udp4);
++unreg_tcp:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_tcp4);
++cleanup_sys:
++#ifdef CONFIG_VE_IPTABLES
++no_mem_icmp:
++ nf_ct_proto_ipv4_sysctl_cleanup();
++no_mem_ipv4:
++ nf_ct_proto_ipv4_fini();
++err_out:
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++#endif /* CONFIG_VE_IPTABLES */
+ return ret;
+-#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
+- cleanup_hooks:
+- nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
+-#endif
+- cleanup_ipv4:
+- nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
+- cleanup_icmp:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp);
+- cleanup_udp:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4);
+- cleanup_tcp:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
++}
++EXPORT_SYMBOL(init_nf_ct_l3proto_ipv4);
++
++void fini_nf_ct_l3proto_ipv4(void)
++{
++ int do_hooks = ve_is_super(get_exec_env());
++
++ nf_conntrack_ipv4_compat_fini();
++ if (do_hooks)
++ nf_unregister_hooks(ipv4_conntrack_ops,
++ ARRAY_SIZE(ipv4_conntrack_ops));
++
++ nf_conntrack_l3proto_unregister(ve_nf_conntrack_l3proto_ipv4);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_icmp);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_udp4);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_tcp4);
++
++#ifdef CONFIG_VE_IPTABLES
++ nf_ct_proto_icmp_sysctl_cleanup();
++ nf_ct_proto_ipv4_sysctl_cleanup();
++ nf_ct_proto_ipv4_fini();
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++#endif /* CONFIG_VE_IPTABLES */
++}
++EXPORT_SYMBOL(fini_nf_ct_l3proto_ipv4);
++
++static int __init nf_conntrack_l3proto_ipv4_init(void)
++{
++ int ret = 0;
++
++ need_conntrack();
++
++ ret = nf_register_sockopt(&so_getorigdst);
++ if (ret < 0) {
++ printk(KERN_ERR "Unable to register netfilter socket option\n");
++ return ret;
++ }
++
++ ret = init_nf_ct_l3proto_ipv4();
++ if (ret < 0) {
++ printk(KERN_ERR "Unable to initialize netfilter protocols\n");
++ goto cleanup_sockopt;
++ }
++ KSYMRESOLVE(init_nf_ct_l3proto_ipv4);
++ KSYMRESOLVE(fini_nf_ct_l3proto_ipv4);
++ KSYMMODRESOLVE(nf_conntrack_ipv4);
++ return ret;
++
+ cleanup_sockopt:
+ nf_unregister_sockopt(&so_getorigdst);
+ return ret;
+@@ -485,14 +634,12 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
+ static void __exit nf_conntrack_l3proto_ipv4_fini(void)
+ {
+ synchronize_net();
+-#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
+- nf_conntrack_ipv4_compat_fini();
+-#endif
+- nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
+- nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
++
++ KSYMMODUNRESOLVE(nf_conntrack_ipv4);
++ KSYMUNRESOLVE(init_nf_ct_l3proto_ipv4);
++ KSYMUNRESOLVE(fini_nf_ct_l3proto_ipv4);
++
++ fini_nf_ct_l3proto_ipv4();
+ nf_unregister_sockopt(&so_getorigdst);
+ }
+
+diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
+index 3a02072..7a3129b 100644
+--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
++++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
+@@ -9,7 +9,9 @@
+ */
+ #include <linux/types.h>
+ #include <linux/proc_fs.h>
++#include <linux/nsproxy.h>
+ #include <linux/seq_file.h>
++#include <linux/sysctl.h>
+ #include <linux/percpu.h>
+ #include <net/net_namespace.h>
+
+@@ -32,7 +34,7 @@ static struct hlist_node *ct_get_first(struct seq_file *seq)
+ for (st->bucket = 0;
+ st->bucket < nf_conntrack_htable_size;
+ st->bucket++) {
+- n = rcu_dereference(nf_conntrack_hash[st->bucket].first);
++ n = rcu_dereference(ve_nf_conntrack_hash[st->bucket].first);
+ if (n)
+ return n;
+ }
+@@ -48,7 +50,7 @@ static struct hlist_node *ct_get_next(struct seq_file *seq,
+ while (head == NULL) {
+ if (++st->bucket >= nf_conntrack_htable_size)
+ return NULL;
+- head = rcu_dereference(nf_conntrack_hash[st->bucket].first);
++ head = rcu_dereference(ve_nf_conntrack_hash[st->bucket].first);
+ }
+ return head;
+ }
+@@ -181,7 +183,7 @@ static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
+ struct hlist_node *n;
+
+ for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
+- n = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
++ n = rcu_dereference(ve_nf_ct_expect_hash[st->bucket].first);
+ if (n)
+ return n;
+ }
+@@ -197,7 +199,7 @@ static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
+ while (head == NULL) {
+ if (++st->bucket >= nf_ct_expect_hsize)
+ return NULL;
+- head = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
++ head = rcu_dereference(ve_nf_ct_expect_hash[st->bucket].first);
+ }
+ return head;
+ }
+@@ -314,7 +316,7 @@ static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
+
+ static int ct_cpu_seq_show(struct seq_file *seq, void *v)
+ {
+- unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
++ unsigned int nr_conntracks = atomic_read(&ve_nf_conntrack_count);
+ const struct ip_conntrack_stat *st = v;
+
+ if (v == SEQ_START_TOKEN) {
+@@ -365,36 +367,91 @@ static const struct file_operations ct_cpu_seq_fops = {
+ .release = seq_release,
+ };
+
+-int __init nf_conntrack_ipv4_compat_init(void)
++#ifdef CONFIG_VE_IPTABLES
++#define ve_ip_ct_netfilter_table (get_exec_env()->_nf_conntrack->_ip_ct_netfilter_table)
++#define ve_ip_ct_sysctl_header (get_exec_env()->_nf_conntrack->_ip_ct_sysctl_header)
++#else
++#define ve_ip_ct_netfilter_table ip_ct_netfilter_table
++#define ve_ip_ct_sysctl_header ip_ct_sysctl_header
++#endif
++
++static ctl_table ip_ct_netfilter_table[] = {
++ {
++ .procname = "ip_conntrack_max",
++ .data = &nf_conntrack_max,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {}
++};
++
++static struct ctl_path ip_ct_net_table_path[] = {
++ { .procname = "net", .ctl_name = CTL_NET, },
++ { .procname = "ipv4", .ctl_name = NET_IPV4, },
++ {},
++};
++
++int nf_conntrack_ipv4_compat_init(void)
+ {
++ struct net *net = get_exec_env()->ve_netns;
+ struct proc_dir_entry *proc, *proc_exp, *proc_stat;
++ static ctl_table *table;
+
+- proc = proc_net_fops_create(&init_net, "ip_conntrack", 0440, &ct_file_ops);
++ proc = proc_net_fops_create(net, "ip_conntrack", 0440, &ct_file_ops);
+ if (!proc)
+ goto err1;
+
+- proc_exp = proc_net_fops_create(&init_net, "ip_conntrack_expect", 0440,
++ proc_exp = proc_net_fops_create(net, "ip_conntrack_expect", 0440,
+ &ip_exp_file_ops);
+ if (!proc_exp)
+ goto err2;
+
+ proc_stat = proc_create("ip_conntrack", S_IRUGO,
+- init_net.proc_net_stat, &ct_cpu_seq_fops);
++ net->proc_net_stat, &ct_cpu_seq_fops);
+ if (!proc_stat)
+ goto err3;
++
++ table = ip_ct_netfilter_table;
++ if (net != &init_net) {
++ table = kmemdup(table,
++ sizeof(ip_ct_netfilter_table),
++ GFP_KERNEL);
++ if (!table)
++ goto err4;
++ }
++
++ table[0].data = &ve_nf_conntrack_max;
++ ve_ip_ct_sysctl_header = register_net_sysctl_table(net,
++ ip_ct_net_table_path,
++ table);
++ if (!ve_ip_ct_sysctl_header)
++ goto err5;
++
+ return 0;
+
++err5:
++ if (net != &init_net)
++ kfree(table);
++err4:
++ remove_proc_entry("ip_conntrack", net->proc_net_stat);
+ err3:
+- proc_net_remove(&init_net, "ip_conntrack_expect");
++ proc_net_remove(net, "ip_conntrack_expect");
+ err2:
+- proc_net_remove(&init_net, "ip_conntrack");
++ proc_net_remove(net, "ip_conntrack");
+ err1:
+ return -ENOMEM;
+ }
+
+-void __exit nf_conntrack_ipv4_compat_fini(void)
++void nf_conntrack_ipv4_compat_fini(void)
+ {
+- remove_proc_entry("ip_conntrack", init_net.proc_net_stat);
+- proc_net_remove(&init_net, "ip_conntrack_expect");
+- proc_net_remove(&init_net, "ip_conntrack");
++ struct net *net = get_exec_env()->ve_netns;
++ struct ctl_table *table = ve_ip_ct_sysctl_header->ctl_table_arg;
++
++ unregister_net_sysctl_table(ve_ip_ct_sysctl_header);
++ if (net != &init_net)
++ kfree(table);
++ remove_proc_entry("ip_conntrack", net->proc_net_stat);
++ proc_net_remove(net, "ip_conntrack_expect");
++ proc_net_remove(net, "ip_conntrack");
+ }
+diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+index 9779104..df39929 100644
+--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
++++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include <linux/types.h>
++#include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/netfilter.h>
+ #include <linux/in.h>
+@@ -20,7 +21,7 @@
+ #include <net/netfilter/nf_conntrack_core.h>
+ #include <net/netfilter/nf_log.h>
+
+-static unsigned long nf_ct_icmp_timeout __read_mostly = 30*HZ;
++unsigned long nf_ct_icmp_timeout __read_mostly = 30*HZ;
+
+ static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
+ struct nf_conntrack_tuple *tuple)
+@@ -92,7 +93,7 @@ static int icmp_packet(struct nf_conn *ct,
+ } else {
+ atomic_inc(&ct->proto.icmp.count);
+ nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
+- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
++ nf_ct_refresh_acct(ct, ctinfo, skb, ve_nf_ct_icmp_timeout);
+ }
+
+ return NF_ACCEPT;
+@@ -148,7 +149,7 @@ icmp_error_message(struct sk_buff *skb,
+ /* Ordinarily, we'd expect the inverted tupleproto, but it's
+ been preserved inside the ICMP. */
+ if (!nf_ct_invert_tuple(&innertuple, &origtuple,
+- &nf_conntrack_l3proto_ipv4, innerproto)) {
++ ve_nf_conntrack_l3proto_ipv4, innerproto)) {
+ pr_debug("icmp_error_message: no match\n");
+ return -NF_ACCEPT;
+ }
+@@ -320,3 +321,64 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
+ #endif
+ #endif
+ };
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++int nf_ct_proto_icmp_sysctl_init(void)
++{
++ struct nf_conntrack_l4proto *icmp;
++
++ if (ve_is_super(get_exec_env())) {
++ icmp = &nf_conntrack_l4proto_icmp;
++ goto out;
++ }
++
++ icmp = kmemdup(&nf_conntrack_l4proto_icmp,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (!icmp)
++ goto no_mem_ct;
++
++ icmp->ctl_table_header = &ve_icmp_sysctl_header;
++ icmp->ctl_table = kmemdup(icmp_sysctl_table,
++ sizeof(icmp_sysctl_table), GFP_KERNEL);
++ if (icmp->ctl_table == NULL)
++ goto no_mem_sys;
++ icmp->ctl_table[0].data = &ve_nf_ct_icmp_timeout;
++
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ icmp->ctl_compat_table_header = ve_icmp_compat_sysctl_header;
++ icmp->ctl_compat_table = kmemdup(icmp_compat_sysctl_table,
++ sizeof(icmp_compat_sysctl_table),
++ GFP_KERNEL);
++ if (icmp->ctl_compat_table == NULL)
++ goto no_mem_compat;
++ icmp->ctl_compat_table[0].data = &ve_nf_ct_icmp_timeout;
++#endif
++out:
++ ve_nf_ct_icmp_timeout = nf_ct_icmp_timeout;
++
++ ve_nf_conntrack_l4proto_icmp = icmp;
++ return 0;
++
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++no_mem_compat:
++ kfree(icmp->ctl_table);
++#endif
++no_mem_sys:
++ kfree(icmp);
++no_mem_ct:
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(nf_ct_proto_icmp_sysctl_init);
++
++void nf_ct_proto_icmp_sysctl_cleanup(void)
++{
++ if (!ve_is_super(get_exec_env())) {
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(ve_nf_conntrack_l4proto_icmp->ctl_compat_table);
++#endif
++ kfree(ve_nf_conntrack_l4proto_icmp->ctl_table);
++ kfree(ve_nf_conntrack_l4proto_icmp);
++ }
++}
++EXPORT_SYMBOL(nf_ct_proto_icmp_sysctl_cleanup);
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
+diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
+index 6c6a3cb..94c86f5 100644
+--- a/net/ipv4/netfilter/nf_nat_core.c
++++ b/net/ipv4/netfilter/nf_nat_core.c
+@@ -19,6 +19,8 @@
+ #include <linux/icmp.h>
+ #include <linux/udp.h>
+ #include <linux/jhash.h>
++#include <linux/nfcalls.h>
++#include <bc/kmem.h>
+
+ #include <linux/netfilter_ipv4.h>
+ #include <net/netfilter/nf_conntrack.h>
+@@ -33,22 +35,34 @@
+
+ static DEFINE_SPINLOCK(nf_nat_lock);
+
+-static struct nf_conntrack_l3proto *l3proto __read_mostly;
+
+ /* Calculated at init based on memory size */
+ static unsigned int nf_nat_htable_size __read_mostly;
+-static int nf_nat_vmalloced;
+
++#define MAX_IP_NAT_PROTO 256
++
++#ifdef CONFIG_VE_IPTABLES
++#define ve_nf_nat_protos (get_exec_env()->_nf_conntrack->_nf_nat_protos)
++#define ve_nf_nat_l3proto (get_exec_env()->_nf_conntrack->_nf_nat_l3proto)
++#define ve_bysource (get_exec_env()->_nf_conntrack->_bysource)
++#define ve_nf_nat_vmalloced (get_exec_env()->_nf_conntrack->_nf_nat_vmalloced)
++#else
++static struct nf_conntrack_l3proto *l3proto __read_mostly;
++static int nf_nat_vmalloced;
+ static struct hlist_head *bysource __read_mostly;
+
+-#define MAX_IP_NAT_PROTO 256
+ static const struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO]
+ __read_mostly;
++#define ve_nf_nat_protos nf_nat_protos
++#define ve_nf_nat_l3proto l3proto
++#define ve_bysource bysource
++#define ve_nf_nat_vmalloced nf_nat_vmalloced
++#endif
+
+ static inline const struct nf_nat_protocol *
+ __nf_nat_proto_find(u_int8_t protonum)
+ {
+- return rcu_dereference(nf_nat_protos[protonum]);
++ return rcu_dereference(ve_nf_nat_protos[protonum]);
+ }
+
+ const struct nf_nat_protocol *
+@@ -155,7 +169,7 @@ find_appropriate_src(const struct nf_conntrack_tuple *tuple,
+ const struct hlist_node *n;
+
+ rcu_read_lock();
+- hlist_for_each_entry_rcu(nat, n, &bysource[h], bysource) {
++ hlist_for_each_entry_rcu(nat, n, &ve_bysource[h], bysource) {
+ ct = nat->ct;
+ if (same_src(ct, tuple)) {
+ /* Copy source part from reply tuple. */
+@@ -278,6 +292,22 @@ out:
+ rcu_read_unlock();
+ }
+
++void nf_nat_hash_conntrack(struct nf_conn *ct)
++{
++ struct nf_conn_nat *nat;
++ unsigned int srchash;
++
++ srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
++ spin_lock_bh(&nf_nat_lock);
++ /* nf_conntrack_alter_reply might re-allocate exntension aera */
++ nat = nfct_nat(ct);
++ nat->ct = ct;
++ hlist_add_head_rcu(&nat->bysource, &ve_bysource[srchash]);
++ spin_unlock_bh(&nf_nat_lock);
++
++}
++EXPORT_SYMBOL_GPL(nf_nat_hash_conntrack);
++
+ unsigned int
+ nf_nat_setup_info(struct nf_conn *ct,
+ const struct nf_nat_range *range,
+@@ -326,17 +356,8 @@ nf_nat_setup_info(struct nf_conn *ct,
+ }
+
+ /* Place in source hash if this is the first time. */
+- if (have_to_hash) {
+- unsigned int srchash;
+-
+- srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+- spin_lock_bh(&nf_nat_lock);
+- /* nf_conntrack_alter_reply might re-allocate exntension aera */
+- nat = nfct_nat(ct);
+- nat->ct = ct;
+- hlist_add_head_rcu(&nat->bysource, &bysource[srchash]);
+- spin_unlock_bh(&nf_nat_lock);
+- }
++ if (have_to_hash)
++ nf_nat_hash_conntrack(ct);
+
+ /* It's done. */
+ if (maniptype == IP_NAT_MANIP_DST)
+@@ -426,7 +447,6 @@ int nf_nat_icmp_reply_translation(struct nf_conn *ct,
+ struct icmphdr icmp;
+ struct iphdr ip;
+ } *inside;
+- const struct nf_conntrack_l4proto *l4proto;
+ struct nf_conntrack_tuple inner, target;
+ int hdrlen = ip_hdrlen(skb);
+ enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+@@ -463,16 +483,14 @@ int nf_nat_icmp_reply_translation(struct nf_conn *ct,
+ "dir %s\n", skb, manip,
+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
+
+- /* rcu_read_lock()ed by nf_hook_slow */
+- l4proto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol);
+-
+ if (!nf_ct_get_tuple(skb,
+ ip_hdrlen(skb) + sizeof(struct icmphdr),
+ (ip_hdrlen(skb) +
+ sizeof(struct icmphdr) + inside->ip.ihl * 4),
+ (u_int16_t)AF_INET,
+ inside->ip.protocol,
+- &inner, l3proto, l4proto))
++ &inner, ve_nf_nat_l3proto,
++ __nf_ct_l4proto_find(PF_INET, inside->ip.protocol)))
+ return 0;
+
+ /* Change inner back to look like incoming packet. We do the
+@@ -522,11 +540,11 @@ int nf_nat_protocol_register(const struct nf_nat_protocol *proto)
+ int ret = 0;
+
+ spin_lock_bh(&nf_nat_lock);
+- if (nf_nat_protos[proto->protonum] != &nf_nat_unknown_protocol) {
++ if (ve_nf_nat_protos[proto->protonum] != &nf_nat_unknown_protocol) {
+ ret = -EBUSY;
+ goto out;
+ }
+- rcu_assign_pointer(nf_nat_protos[proto->protonum], proto);
++ rcu_assign_pointer(ve_nf_nat_protos[proto->protonum], proto);
+ out:
+ spin_unlock_bh(&nf_nat_lock);
+ return ret;
+@@ -537,7 +555,7 @@ EXPORT_SYMBOL(nf_nat_protocol_register);
+ void nf_nat_protocol_unregister(const struct nf_nat_protocol *proto)
+ {
+ spin_lock_bh(&nf_nat_lock);
+- rcu_assign_pointer(nf_nat_protos[proto->protonum],
++ rcu_assign_pointer(ve_nf_nat_protos[proto->protonum],
+ &nf_nat_unknown_protocol);
+ spin_unlock_bh(&nf_nat_lock);
+ synchronize_rcu();
+@@ -583,47 +601,62 @@ static struct nf_ct_ext_type nat_extend __read_mostly = {
+ .flags = NF_CT_EXT_F_PREALLOC,
+ };
+
+-static int __init nf_nat_init(void)
++int nf_nat_init(void)
+ {
+ size_t i;
+ int ret;
+
+ need_ipv4_conntrack();
+
+- ret = nf_ct_extend_register(&nat_extend);
+- if (ret < 0) {
+- printk(KERN_ERR "nf_nat_core: Unable to register extension\n");
+- return ret;
++ if (ve_is_super(get_exec_env())) {
++ ret = nf_ct_extend_register(&nat_extend);
++ if (ret < 0) {
++ printk(KERN_ERR "nf_nat_core: Unable to register extension\n");
++ return ret;
++ }
+ }
+
+ /* Leave them the same for the moment. */
+ nf_nat_htable_size = nf_conntrack_htable_size;
+
+- bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size,
+- &nf_nat_vmalloced);
+- if (!bysource) {
++ ve_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size,
++ &ve_nf_nat_vmalloced);
++ if (!ve_bysource) {
+ ret = -ENOMEM;
+ goto cleanup_extend;
+ }
+
++#ifdef CONFIG_VE_IPTABLES
++ ve_nf_nat_protos = kcalloc(MAX_IP_NAT_PROTO, sizeof(void *), GFP_KERNEL);
++ if (!ve_nf_nat_protos) {
++ ret = -ENOMEM;
++ goto cleanup_hash;
++ }
++#endif
+ /* Sew in builtin protocols. */
+ spin_lock_bh(&nf_nat_lock);
+ for (i = 0; i < MAX_IP_NAT_PROTO; i++)
+- rcu_assign_pointer(nf_nat_protos[i], &nf_nat_unknown_protocol);
+- rcu_assign_pointer(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp);
+- rcu_assign_pointer(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp);
+- rcu_assign_pointer(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp);
++ rcu_assign_pointer(ve_nf_nat_protos[i], &nf_nat_unknown_protocol);
++ rcu_assign_pointer(ve_nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp);
++ rcu_assign_pointer(ve_nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp);
++ rcu_assign_pointer(ve_nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp);
+ spin_unlock_bh(&nf_nat_lock);
+
+- /* Initialize fake conntrack so that NAT will skip it */
+- nf_conntrack_untracked.status |= IPS_NAT_DONE_MASK;
++ if (ve_is_super(get_exec_env())) {
++ /* Initialize fake conntrack so that NAT will skip it */
++ nf_conntrack_untracked.status |= IPS_NAT_DONE_MASK;
++ BUG_ON(nf_nat_seq_adjust_hook != NULL);
++ rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust);
++ }
+
+- l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET);
++ ve_nf_nat_l3proto = nf_ct_l3proto_find_get((u_int16_t)AF_INET);
+
+- BUG_ON(nf_nat_seq_adjust_hook != NULL);
+- rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust);
+ return 0;
+
++#ifdef CONFIG_VE_IPTABLES
++cleanup_hash:
++#endif
++ nf_ct_free_hashtable(ve_bysource, ve_nf_nat_vmalloced, nf_nat_htable_size);
+ cleanup_extend:
+ nf_ct_extend_unregister(&nat_extend);
+ return ret;
+@@ -641,18 +674,46 @@ static int clean_nat(struct nf_conn *i, void *data)
+ return 0;
+ }
+
+-static void __exit nf_nat_cleanup(void)
++void nf_nat_cleanup(void)
+ {
+ nf_ct_iterate_cleanup(&clean_nat, NULL);
+ synchronize_rcu();
+- nf_ct_free_hashtable(bysource, nf_nat_vmalloced, nf_nat_htable_size);
+- nf_ct_l3proto_put(l3proto);
+- nf_ct_extend_unregister(&nat_extend);
+- rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL);
++ nf_ct_free_hashtable(ve_bysource, ve_nf_nat_vmalloced, nf_nat_htable_size);
++ nf_ct_l3proto_put(ve_nf_nat_l3proto);
++#ifdef CONFIG_VE_IPTABLES
++ kfree(ve_nf_nat_protos);
++#endif
++ if (ve_is_super(get_exec_env())) {
++ nf_ct_extend_unregister(&nat_extend);
++ rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL);
++ }
+ synchronize_net();
+ }
+
++static int __init init(void)
++{
++ int rv;
++
++ rv = nf_nat_init();
++ if (rv < 0)
++ return rv;
++
++ KSYMRESOLVE(nf_nat_init);
++ KSYMRESOLVE(nf_nat_cleanup);
++ KSYMMODRESOLVE(nf_nat);
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ KSYMMODUNRESOLVE(nf_nat);
++ KSYMUNRESOLVE(nf_nat_cleanup);
++ KSYMUNRESOLVE(nf_nat_init);
++
++ nf_nat_cleanup();
++}
++
+ MODULE_LICENSE("GPL");
+
+-module_init(nf_nat_init);
+-module_exit(nf_nat_cleanup);
++module_init(init);
++module_exit(fini);
+diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c
+index e8b4d0d..f301178 100644
+--- a/net/ipv4/netfilter/nf_nat_rule.c
++++ b/net/ipv4/netfilter/nf_nat_rule.c
+@@ -15,6 +15,7 @@
+ #include <linux/kmod.h>
+ #include <linux/skbuff.h>
+ #include <linux/proc_fs.h>
++#include <linux/nsproxy.h>
+ #include <net/checksum.h>
+ #include <net/route.h>
+ #include <linux/bitops.h>
+@@ -33,7 +34,7 @@ static struct
+ struct ipt_replace repl;
+ struct ipt_standard entries[3];
+ struct ipt_error term;
+-} nat_initial_table __initdata = {
++} nat_initial_table = {
+ .repl = {
+ .name = "nat",
+ .valid_hooks = NAT_VALID_HOOKS,
+@@ -65,7 +66,12 @@ static struct xt_table __nat_table = {
+ .me = THIS_MODULE,
+ .af = AF_INET,
+ };
++#ifdef CONFIG_VE_IPTABLES
++#define nat_table \
++ (get_exec_env()->_nf_conntrack->_nf_nat_table)
++#else
+ static struct xt_table *nat_table;
++#endif
+
+ /* Source NAT */
+ static unsigned int ipt_snat_target(struct sk_buff *skb,
+@@ -226,14 +232,20 @@ static struct xt_target ipt_dnat_reg __read_mostly = {
+ .family = AF_INET,
+ };
+
+-int __init nf_nat_rule_init(void)
++int nf_nat_rule_init(void)
+ {
+ int ret;
++ struct net *net = get_exec_env()->ve_netns;
+
+- nat_table = ipt_register_table(&init_net, &__nat_table,
++ nat_table = ipt_register_table(net, &__nat_table,
+ &nat_initial_table.repl);
+ if (IS_ERR(nat_table))
+ return PTR_ERR(nat_table);
++
++ ret = 0;
++ if (!ve_is_super(get_exec_env()))
++ goto done;
++
+ ret = xt_register_target(&ipt_snat_reg);
+ if (ret != 0)
+ goto unregister_table;
+@@ -242,19 +254,26 @@ int __init nf_nat_rule_init(void)
+ if (ret != 0)
+ goto unregister_snat;
+
++done:
+ return ret;
+
+ unregister_snat:
+ xt_unregister_target(&ipt_snat_reg);
+ unregister_table:
+ ipt_unregister_table(nat_table);
++ nat_table = NULL;
+
+ return ret;
+ }
+
+ void nf_nat_rule_cleanup(void)
+ {
++ if (!ve_is_super(get_exec_env()))
++ goto skip;
++
+ xt_unregister_target(&ipt_dnat_reg);
+ xt_unregister_target(&ipt_snat_reg);
++skip:
+ ipt_unregister_table(nat_table);
++ nat_table = NULL;
+ }
+diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c
+index b7dd695..72f45db 100644
+--- a/net/ipv4/netfilter/nf_nat_standalone.c
++++ b/net/ipv4/netfilter/nf_nat_standalone.c
+@@ -16,6 +16,7 @@
+ #include <net/ip.h>
+ #include <net/checksum.h>
+ #include <linux/spinlock.h>
++#include <linux/nfcalls.h>
+
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -282,6 +283,45 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
+ },
+ };
+
++int init_nftable_nat(void)
++{
++ int ret;
++
++ if (!ve_is_super(get_exec_env()))
++ __module_get(THIS_MODULE);
++
++ ret = nf_nat_rule_init();
++ if (ret < 0) {
++ printk("nf_nat_init: can't setup rules.\n");
++ goto out_modput;
++ }
++
++ if (ve_is_super(get_exec_env())) {
++ ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
++ if (ret < 0) {
++ printk("nf_nat_init: can't register hooks.\n");
++ goto cleanup_rule_init;
++ }
++ }
++ return 0;
++
++cleanup_rule_init:
++ nf_nat_rule_cleanup();
++out_modput:
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++ return ret;
++}
++
++void fini_nftable_nat(void)
++{
++ if (ve_is_super(get_exec_env()))
++ nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
++ nf_nat_rule_cleanup();
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++}
++
+ static int __init nf_nat_standalone_init(void)
+ {
+ int ret = 0;
+@@ -292,20 +332,19 @@ static int __init nf_nat_standalone_init(void)
+ BUG_ON(ip_nat_decode_session != NULL);
+ rcu_assign_pointer(ip_nat_decode_session, nat_decode_session);
+ #endif
+- ret = nf_nat_rule_init();
+- if (ret < 0) {
+- printk("nf_nat_init: can't setup rules.\n");
+- goto cleanup_decode_session;
+- }
+- ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
+- if (ret < 0) {
+- printk("nf_nat_init: can't register hooks.\n");
+- goto cleanup_rule_init;
++
++ if (!ip_conntrack_disable_ve0) {
++ ret = init_nftable_nat();
++ if (ret < 0)
++ goto cleanup_decode_session;
+ }
++
++ KSYMRESOLVE(init_nftable_nat);
++ KSYMRESOLVE(fini_nftable_nat);
++ KSYMMODRESOLVE(iptable_nat);
++
+ return ret;
+
+- cleanup_rule_init:
+- nf_nat_rule_cleanup();
+ cleanup_decode_session:
+ #ifdef CONFIG_XFRM
+ rcu_assign_pointer(ip_nat_decode_session, NULL);
+@@ -316,8 +355,12 @@ static int __init nf_nat_standalone_init(void)
+
+ static void __exit nf_nat_standalone_fini(void)
+ {
+- nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
+- nf_nat_rule_cleanup();
++ KSYMMODUNRESOLVE(iptable_nat);
++ KSYMUNRESOLVE(init_nftable_nat);
++ KSYMUNRESOLVE(fini_nftable_nat);
++
++ if (!ip_conntrack_disable_ve0)
++ fini_nftable_nat();
+ #ifdef CONFIG_XFRM
+ rcu_assign_pointer(ip_nat_decode_session, NULL);
+ synchronize_net();
+diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
+index 8f5a403..c42d9a5 100644
+--- a/net/ipv4/proc.c
++++ b/net/ipv4/proc.c
+@@ -51,6 +51,9 @@ static int sockstat_seq_show(struct seq_file *seq, void *v)
+ {
+ struct net *net = seq->private;
+
++ if (!ve_is_super(get_exec_env()))
++ return 0;
++
+ socket_seq_show(seq);
+ seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
+ sock_prot_inuse_get(net, &tcp_prot),
+diff --git a/net/ipv4/route.c b/net/ipv4/route.c
+index 6ee5354..3bbe823 100644
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -69,6 +69,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/mm.h>
++#include <linux/nsproxy.h>
+ #include <linux/bootmem.h>
+ #include <linux/string.h>
+ #include <linux/socket.h>
+@@ -115,6 +116,7 @@
+
+ #define RT_GC_TIMEOUT (300*HZ)
+
++int ip_rt_src_check = 1;
+ static int ip_rt_max_size;
+ static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;
+ static int ip_rt_gc_interval __read_mostly = 60 * HZ;
+@@ -1272,6 +1274,9 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
+ rt->u.dst.hh = NULL;
+ rt->u.dst.xfrm = NULL;
+ rt->rt_genid = rt_genid(net);
++#ifdef CONFIG_VE
++ rt->fl.owner_env = get_exec_env();
++#endif
+ rt->rt_flags |= RTCF_REDIRECTED;
+
+ /* Gateway is different ... */
+@@ -1729,9 +1734,12 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
+ #ifdef CONFIG_NET_CLS_ROUTE
+ rth->u.dst.tclassid = itag;
+ #endif
++#ifdef CONFIG_VE
++ rth->fl.owner_env = get_exec_env();
++#endif
+ rth->rt_iif =
+ rth->fl.iif = dev->ifindex;
+- rth->u.dst.dev = init_net.loopback_dev;
++ rth->u.dst.dev = get_exec_env()->ve_netns->loopback_dev;
+ dev_hold(rth->u.dst.dev);
+ rth->idev = in_dev_get(rth->u.dst.dev);
+ rth->fl.oif = 0;
+@@ -1868,6 +1876,9 @@ static int __mkroute_input(struct sk_buff *skb,
+ rth->fl.fl4_src = saddr;
+ rth->rt_src = saddr;
+ rth->rt_gateway = daddr;
++#ifdef CONFIG_VE
++ rth->fl.owner_env = get_exec_env();
++#endif
+ rth->rt_iif =
+ rth->fl.iif = in_dev->dev->ifindex;
+ rth->u.dst.dev = (out_dev)->dev;
+@@ -2062,6 +2073,9 @@ local_input:
+ rth->idev = in_dev_get(rth->u.dst.dev);
+ rth->rt_gateway = daddr;
+ rth->rt_spec_dst= spec_dst;
++#ifdef CONFIG_VE
++ rth->fl.owner_env = get_exec_env();
++#endif
+ rth->u.dst.input= ip_local_deliver;
+ rth->rt_flags = flags|RTCF_LOCAL;
+ if (res.type == RTN_UNREACHABLE) {
+@@ -2251,6 +2265,9 @@ static int __mkroute_output(struct rtable **result,
+ rth->fl.mark = oldflp->mark;
+ rth->rt_dst = fl->fl4_dst;
+ rth->rt_src = fl->fl4_src;
++#ifdef CONFIG_VE
++ rth->fl.owner_env = get_exec_env();
++#endif
+ rth->rt_iif = oldflp->oif ? : dev_out->ifindex;
+ /* get references to the devices that are to be hold by the routing
+ cache entry */
+@@ -2356,10 +2373,13 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
+ ipv4_is_zeronet(oldflp->fl4_src))
+ goto out;
+
+- /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
+- dev_out = ip_dev_find(net, oldflp->fl4_src);
+- if (dev_out == NULL)
+- goto out;
++ if (ip_rt_src_check) {
++ /* It is equivalent to
++ inet_addr_type(saddr) == RTN_LOCAL */
++ dev_out = ip_dev_find(net, oldflp->fl4_src);
++ if (dev_out == NULL)
++ goto out;
++ }
+
+ /* I removed check for oif == dev_out->oif here.
+ It was wrong for two reasons:
+@@ -2387,6 +2407,12 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp,
+ Luckily, this hack is good workaround.
+ */
+
++ if (dev_out == NULL) {
++ dev_out = ip_dev_find(net, oldflp->fl4_src);
++ if (dev_out == NULL)
++ goto out;
++ }
++
+ fl.oif = dev_out->ifindex;
+ goto make_route;
+ }
+diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
+index e0689fd..f6c0adf 100644
+--- a/net/ipv4/sysctl_net_ipv4.c
++++ b/net/ipv4/sysctl_net_ipv4.c
+@@ -26,6 +26,9 @@ static int tcp_retr1_max = 255;
+ static int ip_local_port_range_min[] = { 1, 1 };
+ static int ip_local_port_range_max[] = { 65535, 65535 };
+
++int sysctl_tcp_use_sg = 1;
++EXPORT_SYMBOL(sysctl_tcp_use_sg);
++
+ extern seqlock_t sysctl_port_range_lock;
+ extern int sysctl_local_port_range[2];
+
+@@ -411,6 +414,13 @@ static struct ctl_table ipv4_table[] = {
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
++ {
++ .procname = "tcp_use_sg",
++ .data = &sysctl_tcp_use_sg,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
+
+ #endif
+ {
+@@ -578,6 +588,20 @@ static struct ctl_table ipv4_table[] = {
+ .proc_handler = &proc_dointvec
+ },
+ {
++ .procname = "tcp_max_tw_kmem_fraction",
++ .data = &sysctl_tcp_max_tw_kmem_fraction,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {
++ .procname = "tcp_max_tw_buckets_ub",
++ .data = &sysctl_tcp_max_tw_buckets_ub,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = proc_dointvec,
++ },
++ {
+ .ctl_name = NET_TCP_NO_METRICS_SAVE,
+ .procname = "tcp_no_metrics_save",
+ .data = &sysctl_tcp_nometrics_save,
+diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
+index 1ab341e..8387637 100644
+--- a/net/ipv4/tcp.c
++++ b/net/ipv4/tcp.c
+@@ -272,6 +272,10 @@
+ #include <net/netdma.h>
+ #include <net/sock.h>
+
++#include <bc/sock_orphan.h>
++#include <bc/net.h>
++#include <bc/tcp.h>
++
+ #include <asm/uaccess.h>
+ #include <asm/ioctls.h>
+
+@@ -336,6 +340,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
+ unsigned int mask;
+ struct sock *sk = sock->sk;
+ struct tcp_sock *tp = tcp_sk(sk);
++ int check_send_space;
+
+ poll_wait(file, sk->sk_sleep, wait);
+ if (sk->sk_state == TCP_LISTEN)
+@@ -350,6 +355,21 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
+ if (sk->sk_err)
+ mask = POLLERR;
+
++ check_send_space = 1;
++#ifdef CONFIG_BEANCOUNTERS
++ if (!(sk->sk_shutdown & SEND_SHUTDOWN) && sock_has_ubc(sk)) {
++ unsigned long size;
++ size = MAX_TCP_HEADER + tp->mss_cache;
++ if (size > SOCK_MIN_UBCSPACE)
++ size = SOCK_MIN_UBCSPACE;
++ size = skb_charge_size(size);
++ if (ub_sock_makewres_tcp(sk, size)) {
++ check_send_space = 0;
++ ub_sock_sndqueueadd_tcp(sk, size);
++ }
++ }
++#endif
++
+ /*
+ * POLLHUP is certainly not done right. But poll() doesn't
+ * have a notion of HUP in just one direction, and for a
+@@ -393,7 +413,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
+ sock_flag(sk, SOCK_URGINLINE) || !tp->urg_data))
+ mask |= POLLIN | POLLRDNORM;
+
+- if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
++ if (check_send_space && !(sk->sk_shutdown & SEND_SHUTDOWN)) {
+ if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
+ mask |= POLLOUT | POLLWRNORM;
+ } else { /* send SIGIO later */
+@@ -637,7 +657,7 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp)
+
+ skb = alloc_skb_fclone(size + sk->sk_prot->max_header, gfp);
+ if (skb) {
+- if (sk_wmem_schedule(sk, skb->truesize)) {
++ if (sk_wmem_schedule(sk, skb->truesize, skb)) {
+ /*
+ * Make sure that we have exactly size bytes
+ * available to the caller, no more, no less.
+@@ -683,15 +703,22 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
+ int copy, i, can_coalesce;
+ int offset = poffset % PAGE_SIZE;
+ int size = min_t(size_t, psize, PAGE_SIZE - offset);
++ unsigned long chargesize = 0;
+
+ if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) {
+ new_segment:
++ chargesize = 0;
+ if (!sk_stream_memory_free(sk))
+ goto wait_for_sndbuf;
+
++ chargesize = skb_charge_size(MAX_TCP_HEADER +
++ tp->mss_cache);
++ if (ub_sock_getwres_tcp(sk, chargesize) < 0)
++ goto wait_for_ubspace;
+ skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
+ if (!skb)
+ goto wait_for_memory;
++ ub_skb_set_charge(skb, sk, chargesize, UB_TCPSNDBUF);
+
+ skb_entail(sk, skb);
+ copy = size_goal;
+@@ -706,7 +733,7 @@ new_segment:
+ tcp_mark_push(tp, skb);
+ goto new_segment;
+ }
+- if (!sk_wmem_schedule(sk, copy))
++ if (!sk_wmem_schedule(sk, copy, skb))
+ goto wait_for_memory;
+
+ if (can_coalesce) {
+@@ -747,10 +774,15 @@ new_segment:
+ wait_for_sndbuf:
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ wait_for_memory:
++ ub_sock_retwres_tcp(sk, chargesize,
++ skb_charge_size(MAX_TCP_HEADER + tp->mss_cache));
++ chargesize = 0;
++wait_for_ubspace:
+ if (copied)
+ tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+
+- if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
++ err = __sk_stream_wait_memory(sk, &timeo, chargesize);
++ if (err != 0)
+ goto do_error;
+
+ mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+@@ -787,12 +819,8 @@ ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset,
+ return res;
+ }
+
+-#define TCP_PAGE(sk) (sk->sk_sndmsg_page)
+-#define TCP_OFF(sk) (sk->sk_sndmsg_off)
+-
+-static inline int select_size(struct sock *sk)
++static inline int select_size(struct sock *sk, struct tcp_sock *tp)
+ {
+- struct tcp_sock *tp = tcp_sk(sk);
+ int tmp = tp->mss_cache;
+
+ if (sk->sk_route_caps & NETIF_F_SG) {
+@@ -851,6 +879,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
+ while (--iovlen >= 0) {
+ int seglen = iov->iov_len;
+ unsigned char __user *from = iov->iov_base;
++ unsigned long chargesize = 0;
+
+ iov++;
+
+@@ -861,18 +890,27 @@ int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
+
+ if (!tcp_send_head(sk) ||
+ (copy = size_goal - skb->len) <= 0) {
++ unsigned long size;
+
+ new_segment:
+ /* Allocate new segment. If the interface is SG,
+ * allocate skb fitting to single page.
+ */
++ chargesize = 0;
+ if (!sk_stream_memory_free(sk))
+ goto wait_for_sndbuf;
+
+- skb = sk_stream_alloc_skb(sk, select_size(sk),
++ size = select_size(sk, tp);
++ chargesize = skb_charge_size(MAX_TCP_HEADER +
++ size);
++ if (ub_sock_getwres_tcp(sk, chargesize) < 0)
++ goto wait_for_ubspace;
++ skb = sk_stream_alloc_skb(sk, size,
+ sk->sk_allocation);
+ if (!skb)
+ goto wait_for_memory;
++ ub_skb_set_charge(skb, sk, chargesize,
++ UB_TCPSNDBUF);
+
+ /*
+ * Check whether we can use HW checksum.
+@@ -918,6 +956,7 @@ new_segment:
+ } else if (page) {
+ if (off == PAGE_SIZE) {
+ put_page(page);
++ ub_sock_tcp_detachpage(sk);
+ TCP_PAGE(sk) = page = NULL;
+ off = 0;
+ }
+@@ -927,10 +966,13 @@ new_segment:
+ if (copy > PAGE_SIZE - off)
+ copy = PAGE_SIZE - off;
+
+- if (!sk_wmem_schedule(sk, copy))
++ if (!sk_wmem_schedule(sk, copy, skb))
+ goto wait_for_memory;
+
+ if (!page) {
++ chargesize = PAGE_SIZE;
++ if (ub_sock_tcp_chargepage(sk) < 0)
++ goto wait_for_ubspace;
+ /* Allocate new cache page. */
+ if (!(page = sk_stream_alloc_page(sk)))
+ goto wait_for_memory;
+@@ -962,7 +1004,8 @@ new_segment:
+ } else if (off + copy < PAGE_SIZE) {
+ get_page(page);
+ TCP_PAGE(sk) = page;
+- }
++ } else
++ ub_sock_tcp_detachpage(sk);
+ }
+
+ TCP_OFF(sk) = off + copy;
+@@ -993,10 +1036,15 @@ new_segment:
+ wait_for_sndbuf:
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ wait_for_memory:
++ ub_sock_retwres_tcp(sk, chargesize,
++ skb_charge_size(MAX_TCP_HEADER+tp->mss_cache));
++ chargesize = 0;
++wait_for_ubspace:
+ if (copied)
+ tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+
+- if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
++ err = __sk_stream_wait_memory(sk, &timeo, chargesize);
++ if (err != 0)
+ goto do_error;
+
+ mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
+@@ -1096,7 +1144,18 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied)
+ #if TCP_DEBUG
+ struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
+
+- WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
++ if (WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq))) {
++ printk("KERNEL: assertion: skb==NULL || "
++ "before(tp->copied_seq, skb->end_seq)\n");
++ printk("VE%u pid %d comm %.16s\n",
++ (get_exec_env() ? VEID(get_exec_env()) : 0),
++ current->pid, current->comm);
++ printk("copied=%d, copied_seq=%d, rcv_nxt=%d\n", copied,
++ tp->copied_seq, tp->rcv_nxt);
++ printk("skb->len=%d, skb->seq=%d, skb->end_seq=%d\n",
++ skb->len, TCP_SKB_CB(skb)->seq,
++ TCP_SKB_CB(skb)->end_seq);
++ }
+ #endif
+
+ if (inet_csk_ack_scheduled(sk)) {
+@@ -1358,7 +1417,23 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+ goto found_ok_skb;
+ if (tcp_hdr(skb)->fin)
+ goto found_fin_ok;
+- WARN_ON(!(flags & MSG_PEEK));
++ if (WARN_ON(!(flags & MSG_PEEK))) {
++ printk("KERNEL: assertion: flags&MSG_PEEK\n");
++ printk("VE%u pid %d comm %.16s\n",
++ (get_exec_env() ?
++ VEID(get_exec_env()) : 0),
++ current->pid, current->comm);
++ printk("flags=0x%x, len=%d, copied_seq=%d, "
++ "rcv_nxt=%d\n", flags,
++ (int)len, tp->copied_seq,
++ tp->rcv_nxt);
++ printk("skb->len=%d, *seq=%d, skb->seq=%d, "
++ "skb->end_seq=%d, offset=%d\n",
++ skb->len, *seq,
++ TCP_SKB_CB(skb)->seq,
++ TCP_SKB_CB(skb)->end_seq,
++ offset);
++ }
+ skb = skb->next;
+ } while (skb != (struct sk_buff *)&sk->sk_receive_queue);
+
+@@ -1421,8 +1496,19 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
+
+ tp->ucopy.len = len;
+
+- WARN_ON(tp->copied_seq != tp->rcv_nxt &&
+- !(flags & (MSG_PEEK | MSG_TRUNC)));
++ if (WARN_ON(tp->copied_seq != tp->rcv_nxt &&
++ !(flags & (MSG_PEEK | MSG_TRUNC)))) {
++ printk("KERNEL: assertion: tp->copied_seq == "
++ "tp->rcv_nxt || ...\n");
++ printk("VE%u pid %d comm %.16s\n",
++ (get_exec_env() ?
++ VEID(get_exec_env()) : 0),
++ current->pid, current->comm);
++ printk("flags=0x%x, len=%d, copied_seq=%d, "
++ "rcv_nxt=%d\n", flags,
++ (int)len, tp->copied_seq,
++ tp->rcv_nxt);
++ }
+
+ /* Ugly... If prequeue is not empty, we have to
+ * process it before releasing socket, otherwise
+@@ -1833,7 +1919,7 @@ adjudge_to_death:
+ state = sk->sk_state;
+ sock_hold(sk);
+ sock_orphan(sk);
+- atomic_inc(sk->sk_prot->orphan_count);
++ ub_inc_orphan_count(sk);
+
+ /* It is the last release_sock in its life. It will remove backlog. */
+ release_sock(sk);
+@@ -1884,12 +1970,19 @@ adjudge_to_death:
+ }
+ }
+ if (sk->sk_state != TCP_CLOSE) {
++ int orphans = ub_get_orphan_count(sk);
++
+ sk_mem_reclaim(sk);
+- if (tcp_too_many_orphans(sk,
+- atomic_read(sk->sk_prot->orphan_count))) {
+- if (net_ratelimit())
++ if (ub_too_many_orphans(sk, orphans)) {
++ if (net_ratelimit()) {
++ int ubid = 0;
++#ifdef CONFIG_USER_RESOURCE
++ ubid = sock_has_ubc(sk) ?
++ top_beancounter(sock_bc(sk)->ub)->ub_uid : 0;
++#endif
+ printk(KERN_INFO "TCP: too many of orphaned "
+- "sockets\n");
++ "sockets (%d in CT%d)\n", orphans, ubid);
++ }
+ tcp_set_state(sk, TCP_CLOSE);
+ tcp_send_active_reset(sk, GFP_ATOMIC);
+ NET_INC_STATS_BH(sock_net(sk),
+@@ -1966,6 +2059,7 @@ int tcp_disconnect(struct sock *sk, int flags)
+ tp->snd_ssthresh = 0x7fffffff;
+ tp->snd_cwnd_cnt = 0;
+ tp->bytes_acked = 0;
++ tp->advmss = 65535;
+ tcp_set_ca_state(sk, TCP_CA_Open);
+ tcp_clear_retrans(tp);
+ inet_csk_delack_init(sk);
+@@ -2687,7 +2781,7 @@ void __init tcp_init(void)
+ tcp_hashinfo.bind_bucket_cachep =
+ kmem_cache_create("tcp_bind_bucket",
+ sizeof(struct inet_bind_bucket), 0,
+- SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
++ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_UBC, NULL);
+
+ /* Size and allocate the main established and bind bucket
+ * hash tables.
+@@ -2756,6 +2850,11 @@ void __init tcp_init(void)
+ sysctl_tcp_mem[1] = limit;
+ sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2;
+
++ if (sysctl_tcp_mem[2] - sysctl_tcp_mem[1] > 4096)
++ sysctl_tcp_mem[1] = sysctl_tcp_mem[2] - 4096;
++ if (sysctl_tcp_mem[1] - sysctl_tcp_mem[0] > 4096)
++ sysctl_tcp_mem[0] = sysctl_tcp_mem[1] - 4096;
++
+ /* Set per-socket limits to no more than 1/128 the pressure threshold */
+ limit = ((unsigned long)sysctl_tcp_mem[1]) << (PAGE_SHIFT - 7);
+ max_share = min(4UL*1024*1024, limit);
+diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
+index 7abc6b8..84c400c 100644
+--- a/net/ipv4/tcp_input.c
++++ b/net/ipv4/tcp_input.c
+@@ -71,6 +71,8 @@
+ #include <asm/unaligned.h>
+ #include <net/netdma.h>
+
++#include <bc/tcp.h>
++
+ int sysctl_tcp_timestamps __read_mostly = 1;
+ int sysctl_tcp_window_scaling __read_mostly = 1;
+ int sysctl_tcp_sack __read_mostly = 1;
+@@ -306,7 +308,7 @@ static void tcp_grow_window(struct sock *sk, struct sk_buff *skb)
+ /* Check #1 */
+ if (tp->rcv_ssthresh < tp->window_clamp &&
+ (int)tp->rcv_ssthresh < tcp_space(sk) &&
+- !tcp_memory_pressure) {
++ ub_tcp_rmem_allows_expand(sk)) {
+ int incr;
+
+ /* Check #2. Increase window, if skb with such overhead
+@@ -376,6 +378,8 @@ static void tcp_init_buffer_space(struct sock *sk)
+
+ tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp);
+ tp->snd_cwnd_stamp = tcp_time_stamp;
++
++ ub_tcp_update_maxadvmss(sk);
+ }
+
+ /* 5. Recalculate window clamp after socket hit its memory bounds. */
+@@ -388,7 +392,7 @@ static void tcp_clamp_window(struct sock *sk)
+
+ if (sk->sk_rcvbuf < sysctl_tcp_rmem[2] &&
+ !(sk->sk_userlocks & SOCK_RCVBUF_LOCK) &&
+- !tcp_memory_pressure &&
++ !ub_tcp_memory_pressure(sk) &&
+ atomic_read(&tcp_memory_allocated) < sysctl_tcp_mem[0]) {
+ sk->sk_rcvbuf = min(atomic_read(&sk->sk_rmem_alloc),
+ sysctl_tcp_rmem[2]);
+@@ -3936,19 +3940,19 @@ static void tcp_ofo_queue(struct sock *sk)
+ static int tcp_prune_ofo_queue(struct sock *sk);
+ static int tcp_prune_queue(struct sock *sk);
+
+-static inline int tcp_try_rmem_schedule(struct sock *sk, unsigned int size)
++static inline int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb)
+ {
+ if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
+- !sk_rmem_schedule(sk, size)) {
++ !sk_rmem_schedule(sk, skb)) {
+
+ if (tcp_prune_queue(sk) < 0)
+ return -1;
+
+- if (!sk_rmem_schedule(sk, size)) {
++ if (!sk_rmem_schedule(sk, skb)) {
+ if (!tcp_prune_ofo_queue(sk))
+ return -1;
+
+- if (!sk_rmem_schedule(sk, size))
++ if (!sk_rmem_schedule(sk, skb))
+ return -1;
+ }
+ }
+@@ -4003,8 +4007,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
+ if (eaten <= 0) {
+ queue_and_out:
+ if (eaten < 0 &&
+- tcp_try_rmem_schedule(sk, skb->truesize))
+- goto drop;
++ tcp_try_rmem_schedule(sk, skb))
++ goto drop_part;
+
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+@@ -4048,6 +4052,12 @@ out_of_window:
+ drop:
+ __kfree_skb(skb);
+ return;
++
++drop_part:
++ if (after(tp->copied_seq, tp->rcv_nxt))
++ tp->rcv_nxt = tp->copied_seq;
++ __kfree_skb(skb);
++ return;
+ }
+
+ /* Out of window. F.e. zero window probe. */
+@@ -4074,7 +4084,7 @@ drop:
+
+ TCP_ECN_check_ce(tp, skb);
+
+- if (tcp_try_rmem_schedule(sk, skb->truesize))
++ if (tcp_try_rmem_schedule(sk, skb))
+ goto drop;
+
+ /* Disable header prediction. */
+@@ -4218,6 +4228,10 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list,
+ nskb = alloc_skb(copy + header, GFP_ATOMIC);
+ if (!nskb)
+ return;
++ if (ub_tcprcvbuf_charge_forced(skb->sk, nskb) < 0) {
++ kfree_skb(nskb);
++ return;
++ }
+
+ skb_set_mac_header(nskb, skb_mac_header(skb) - skb->head);
+ skb_set_network_header(nskb, (skb_network_header(skb) -
+@@ -4345,7 +4359,7 @@ static int tcp_prune_queue(struct sock *sk)
+
+ if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
+ tcp_clamp_window(sk);
+- else if (tcp_memory_pressure)
++ else if (ub_tcp_memory_pressure(sk))
+ tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);
+
+ tcp_collapse_ofo_queue(sk);
+@@ -4410,7 +4424,7 @@ static int tcp_should_expand_sndbuf(struct sock *sk)
+ return 0;
+
+ /* If we are under global TCP memory pressure, do not expand. */
+- if (tcp_memory_pressure)
++ if (ub_tcp_memory_pressure(sk))
+ return 0;
+
+ /* If we are under soft global TCP memory pressure, do not expand. */
+@@ -4859,6 +4873,10 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+
+ if ((int)skb->truesize > sk->sk_forward_alloc)
+ goto step5;
++ /* This is OK not to try to free memory here.
++ * Do this below on slow path. Den */
++ if (ub_tcprcvbuf_charge(sk, skb) < 0)
++ goto step5;
+
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS);
+
+diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
+index 011478e..153901c 100644
+--- a/net/ipv4/tcp_ipv4.c
++++ b/net/ipv4/tcp_ipv4.c
+@@ -71,6 +71,8 @@
+ #include <net/xfrm.h>
+ #include <net/netdma.h>
+
++#include <bc/tcp.h>
++
+ #include <linux/inet.h>
+ #include <linux/ipv6.h>
+ #include <linux/stddef.h>
+@@ -678,7 +680,8 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
+ struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
+
+ tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+- tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
++ tcptw->tw_rcv_wnd >>
++ (tw->tw_rcv_wscale & TW_WSCALE_MASK),
+ tcptw->tw_ts_recent,
+ tw->tw_bound_dev_if,
+ tcp_twsk_md5_key(tcptw)
+@@ -1155,6 +1158,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
+ .destructor = tcp_v4_reqsk_destructor,
+ .send_reset = tcp_v4_send_reset,
+ };
++EXPORT_SYMBOL_GPL(tcp_request_sock_ops);
+
+ #ifdef CONFIG_TCP_MD5SIG
+ static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
+@@ -1460,6 +1464,10 @@ static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
+ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ {
+ struct sock *rsk;
++ struct user_beancounter *ub;
++
++ ub = set_exec_ub(sock_bc(sk)->ub);
++
+ #ifdef CONFIG_TCP_MD5SIG
+ /*
+ * We really want to reject the packet as early as possible
+@@ -1478,7 +1486,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ goto reset;
+ }
+ TCP_CHECK_TIMER(sk);
+- return 0;
++ goto restore_context;
+ }
+
+ if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
+@@ -1494,7 +1502,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ rsk = nsk;
+ goto reset;
+ }
+- return 0;
++ goto restore_context;
+ }
+ }
+
+@@ -1504,6 +1512,9 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ goto reset;
+ }
+ TCP_CHECK_TIMER(sk);
++
++restore_context:
++ (void)set_exec_ub(ub);
+ return 0;
+
+ reset:
+@@ -1515,7 +1526,7 @@ discard:
+ * might be destroyed here. This current version compiles correctly,
+ * but you have been warned.
+ */
+- return 0;
++ goto restore_context;
+
+ csum_err:
+ TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
+@@ -1778,6 +1789,8 @@ static int tcp_v4_init_sock(struct sock *sk)
+ tp->snd_cwnd_clamp = ~0;
+ tp->mss_cache = 536;
+
++ tp->advmss = 65535; /* max value */
++
+ tp->reordering = sysctl_tcp_reordering;
+ icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+
+@@ -1839,6 +1852,8 @@ void tcp_v4_destroy_sock(struct sock *sk)
+ * If sendmsg cached page exists, toss it.
+ */
+ if (sk->sk_sndmsg_page) {
++ /* queue is empty, uncharge */
++ ub_sock_tcp_detachpage(sk);
+ __free_page(sk->sk_sndmsg_page);
+ sk->sk_sndmsg_page = NULL;
+ }
+@@ -2390,6 +2405,87 @@ void __init tcp_v4_init(void)
+ panic("Failed to create the TCP control socket.\n");
+ }
+
++#ifdef CONFIG_VE
++static void tcp_kill_ve_onesk(struct sock *sk)
++{
++ struct tcp_sock *tp = tcp_sk(sk);
++
++ /* Check the assumed state of the socket. */
++ if (!sock_flag(sk, SOCK_DEAD)) {
++ static int printed;
++invalid:
++ if (!printed)
++ printk(KERN_DEBUG "Killing sk: dead %d, state %d, "
++ "wrseq %u unseq %u, wrqu %d.\n",
++ sock_flag(sk, SOCK_DEAD), sk->sk_state,
++ tp->write_seq, tp->snd_una,
++ !skb_queue_empty(&sk->sk_write_queue));
++ printed = 1;
++ return;
++ }
++
++ tcp_send_active_reset(sk, GFP_ATOMIC);
++ switch (sk->sk_state) {
++ case TCP_FIN_WAIT1:
++ case TCP_CLOSING:
++ /* In these 2 states the peer may want us to retransmit
++ * some data and/or FIN. Entering "resetting mode"
++ * instead.
++ */
++ tcp_time_wait(sk, TCP_CLOSE, 0);
++ break;
++ case TCP_FIN_WAIT2:
++ /* By some reason the socket may stay in this state
++ * without turning into a TW bucket. Fix it.
++ */
++ tcp_time_wait(sk, TCP_FIN_WAIT2, 0);
++ break;
++ case TCP_LAST_ACK:
++ /* Just jump into CLOSED state. */
++ tcp_done(sk);
++ break;
++ default:
++ /* The socket must be already close()d. */
++ goto invalid;
++ }
++}
++
++void tcp_v4_kill_ve_sockets(struct ve_struct *envid)
++{
++ struct inet_ehash_bucket *head;
++ int i;
++
++ /* alive */
++ local_bh_disable();
++ head = tcp_hashinfo.ehash;
++ for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
++ struct sock *sk;
++ struct hlist_node *node;
++ rwlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, i);
++more_work:
++ write_lock(lock);
++ sk_for_each(sk, node, &head[i].chain) {
++ if (ve_accessible_strict(sk->owner_env, envid)) {
++ sock_hold(sk);
++ write_unlock(lock);
++
++ bh_lock_sock(sk);
++ /* sk might have disappeared from the hash before
++ * we got the lock */
++ if (sk->sk_state != TCP_CLOSE)
++ tcp_kill_ve_onesk(sk);
++ bh_unlock_sock(sk);
++ sock_put(sk);
++ goto more_work;
++ }
++ }
++ write_unlock(lock);
++ }
++ local_bh_enable();
++}
++EXPORT_SYMBOL(tcp_v4_kill_ve_sockets);
++#endif
++
+ EXPORT_SYMBOL(ipv4_specific);
+ EXPORT_SYMBOL(tcp_hashinfo);
+ EXPORT_SYMBOL(tcp_prot);
+diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
+index f976fc5..5ce52dd 100644
+--- a/net/ipv4/tcp_minisocks.c
++++ b/net/ipv4/tcp_minisocks.c
+@@ -26,6 +26,9 @@
+ #include <net/inet_common.h>
+ #include <net/xfrm.h>
+
++#include <bc/net.h>
++#include <bc/sock_orphan.h>
++
+ #ifdef CONFIG_SYSCTL
+ #define SYNC_INIT 0 /* let the user enable it */
+ #else
+@@ -36,6 +39,11 @@ int sysctl_tcp_syncookies __read_mostly = SYNC_INIT;
+ EXPORT_SYMBOL(sysctl_tcp_syncookies);
+
+ int sysctl_tcp_abort_on_overflow __read_mostly;
++int sysctl_tcp_max_tw_kmem_fraction __read_mostly = 384;
++int sysctl_tcp_max_tw_buckets_ub __read_mostly = 16536;
++
++EXPORT_SYMBOL(sysctl_tcp_max_tw_kmem_fraction);
++EXPORT_SYMBOL(sysctl_tcp_max_tw_buckets_ub);
+
+ struct inet_timewait_death_row tcp_death_row = {
+ .sysctl_max_tw_buckets = NR_FILE * 2,
+@@ -51,6 +59,7 @@ struct inet_timewait_death_row tcp_death_row = {
+ .twcal_hand = -1,
+ .twcal_timer = TIMER_INITIALIZER(inet_twdr_twcal_tick, 0,
+ (unsigned long)&tcp_death_row),
++ .ub_managed = 1,
+ };
+
+ EXPORT_SYMBOL_GPL(tcp_death_row);
+@@ -279,7 +288,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
+ if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
+ recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);
+
+- if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
++ if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets &&
++ ub_timewait_check(sk, &tcp_death_row))
+ tw = inet_twsk_alloc(sk, state);
+
+ if (tw != NULL) {
+@@ -292,6 +302,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
+ tcptw->tw_rcv_wnd = tcp_receive_window(tp);
+ tcptw->tw_ts_recent = tp->rx_opt.ts_recent;
+ tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp;
++ if (sk->sk_user_data != NULL)
++ tw->tw_rcv_wscale |= TW_WSCALE_SPEC;
+
+ #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (tw->tw_family == PF_INET6) {
+@@ -326,6 +338,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
+ }
+ } while (0);
+ #endif
++ tw->tw_owner_env = VEID(sk->owner_env);
+
+ /* Linkage updates. */
+ __inet_twsk_hashdance(tw, sk, &tcp_hashinfo);
+@@ -346,11 +359,16 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
+ TCP_TIMEWAIT_LEN);
+ inet_twsk_put(tw);
+ } else {
++ int ubid = 0;
+ /* Sorry, if we're out of memory, just CLOSE this
+ * socket up. We've got bigger problems than
+ * non-graceful socket closings.
+ */
+- LIMIT_NETDEBUG(KERN_INFO "TCP: time wait bucket table overflow\n");
++#ifdef CONFIG_BEANCOUNTERS
++ if (sock_has_ubc(sk))
++ ubid = top_beancounter(sock_bc(sk)->ub)->ub_uid;
++#endif
++ LIMIT_NETDEBUG(KERN_INFO "TCP: time wait bucket table overflow (CT%d)\n", ubid);
+ }
+
+ tcp_update_metrics(sk);
+@@ -391,6 +409,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
+ struct tcp_sock *newtp;
+
+ /* Now setup tcp_sock */
++ newsk->owner_env = sk->owner_env;
++
+ newtp = tcp_sk(newsk);
+ newtp->pred_flags = 0;
+ newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1;
+diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
+index 8165f5a..b46e764 100644
+--- a/net/ipv4/tcp_output.c
++++ b/net/ipv4/tcp_output.c
+@@ -39,6 +39,9 @@
+ #include <linux/compiler.h>
+ #include <linux/module.h>
+
++#include <bc/net.h>
++#include <bc/tcp.h>
++
+ /* People can turn this off for buggy TCP's found in printers etc. */
+ int sysctl_tcp_retrans_collapse __read_mostly = 1;
+
+@@ -565,6 +568,13 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb,
+ return size;
+ }
+
++static int skb_header_size(struct sock *sk, int tcp_hlen)
++{
++ struct ip_options *opt = inet_sk(sk)->opt;
++ return tcp_hlen + sizeof(struct iphdr) +
++ (opt ? opt->optlen : 0) + ETH_HLEN /* For hard header */;
++}
++
+ /* This routine actually transmits TCP packets queued in by
+ * tcp_do_sendmsg(). This is used by both the initial
+ * transmission and possible later retransmissions.
+@@ -589,6 +599,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
+ __u8 *md5_hash_location;
+ struct tcphdr *th;
+ int err;
++ int header_size;
+
+ BUG_ON(!skb || !tcp_skb_pcount(skb));
+
+@@ -619,6 +630,20 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
+ &md5);
+ tcp_header_size = tcp_options_size + sizeof(struct tcphdr);
+
++ /* Unfortunately, we can have skb from outside world here
++ * with size insufficient for header. It is impossible to make
++ * guess when we queue skb, so the decision should be made
++ * here. Den
++ */
++ header_size = skb_header_size(sk, tcp_header_size);
++ if (skb->data - header_size < skb->head) {
++ int delta = header_size - skb_headroom(skb);
++ err = pskb_expand_head(skb, SKB_DATA_ALIGN(delta),
++ 0, GFP_ATOMIC);
++ if (err)
++ return err;
++ }
++
+ if (tcp_packets_in_flight(tp) == 0)
+ tcp_ca_event(sk, CA_EVENT_TX_START);
+
+@@ -755,15 +780,21 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
+ if (nsize < 0)
+ nsize = 0;
+
+- if (skb_cloned(skb) &&
+- skb_is_nonlinear(skb) &&
+- pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+- return -ENOMEM;
++ if (skb_cloned(skb) && skb_is_nonlinear(skb)) {
++ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
++ return -ENOMEM;
++ ub_skb_uncharge(skb);
++ ub_tcpsndbuf_charge_forced(sk, skb);
++ }
+
+ /* Get a new skb... force flag on. */
+ buff = sk_stream_alloc_skb(sk, nsize, GFP_ATOMIC);
+ if (buff == NULL)
+ return -ENOMEM; /* We'll just try again later. */
++ if (ub_tcpsndbuf_charge(sk, buff) < 0) {
++ kfree_skb(buff);
++ return -ENOMEM;
++ }
+
+ sk->sk_wmem_queued += buff->truesize;
+ sk_mem_charge(sk, buff->truesize);
+@@ -1270,6 +1301,11 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
+ if (unlikely(buff == NULL))
+ return -ENOMEM;
+
++ if (ub_tcpsndbuf_charge(sk, buff) < 0) {
++ kfree_skb(buff);
++ return -ENOMEM;
++ }
++
+ sk->sk_wmem_queued += buff->truesize;
+ sk_mem_charge(sk, buff->truesize);
+ buff->truesize += nlen;
+@@ -1705,7 +1741,7 @@ u32 __tcp_select_window(struct sock *sk)
+ if (free_space < (full_space >> 1)) {
+ icsk->icsk_ack.quick = 0;
+
+- if (tcp_memory_pressure)
++ if (ub_tcp_shrink_rcvbuf(sk))
+ tp->rcv_ssthresh = min(tp->rcv_ssthresh,
+ 4U * tp->advmss);
+
+@@ -2153,6 +2189,7 @@ void tcp_send_fin(struct sock *sk)
+ break;
+ yield();
+ }
++ ub_tcpsndbuf_charge_forced(sk, skb);
+
+ /* Reserve space for headers and prepare control bits. */
+ skb_reserve(skb, MAX_TCP_HEADER);
+@@ -2211,6 +2248,10 @@ int tcp_send_synack(struct sock *sk)
+ struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
+ if (nskb == NULL)
+ return -ENOMEM;
++ if (ub_tcpsndbuf_charge(sk, skb) < 0) {
++ kfree_skb(nskb);
++ return -ENOMEM;
++ }
+ tcp_unlink_write_queue(skb, sk);
+ skb_header_release(nskb);
+ __tcp_add_write_queue_head(sk, nskb);
+@@ -2320,6 +2361,7 @@ static void tcp_connect_init(struct sock *sk)
+ struct dst_entry *dst = __sk_dst_get(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ __u8 rcv_wscale;
++ static int once = 0;
+
+ /* We'll fix this up when we get a response from the other end.
+ * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
+@@ -2339,9 +2381,23 @@ static void tcp_connect_init(struct sock *sk)
+ tcp_mtup_init(sk);
+ tcp_sync_mss(sk, dst_mtu(dst));
+
++ if (!once && dst_metric(dst, RTAX_ADVMSS) == 0) {
++ once = 1;
++
++ printk("Oops in connect_init! dst->advmss=%d\n",
++ dst_metric(dst, RTAX_ADVMSS));
++ printk("dst: pmtu=%u\n", dst_metric(dst, RTAX_MTU));
++ printk("sk->state=%d, tp: ack.rcv_mss=%d, mss_cache=%d, "
++ "advmss=%d, user_mss=%d\n",
++ sk->sk_state, inet_csk(sk)->icsk_ack.rcv_mss,
++ tp->mss_cache, tp->advmss, tp->rx_opt.user_mss);
++ }
++
+ if (!tp->window_clamp)
+ tp->window_clamp = dst_metric(dst, RTAX_WINDOW);
+ tp->advmss = dst_metric(dst, RTAX_ADVMSS);
++ if (tp->advmss == 0)
++ tp->advmss = 1460;
+ tcp_initialize_rcv_mss(sk);
+
+ tcp_select_initial_window(tcp_full_space(sk),
+@@ -2382,6 +2438,10 @@ int tcp_connect(struct sock *sk)
+ buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
+ if (unlikely(buff == NULL))
+ return -ENOBUFS;
++ if (ub_tcpsndbuf_charge(sk, buff) < 0) {
++ kfree_skb(buff);
++ return -ENOBUFS;
++ }
+
+ /* Reserve space for headers. */
+ skb_reserve(buff, MAX_TCP_HEADER);
+diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
+index 5ab6ba1..25ed21e 100644
+--- a/net/ipv4/tcp_timer.c
++++ b/net/ipv4/tcp_timer.c
+@@ -20,6 +20,8 @@
+
+ #include <linux/module.h>
+ #include <net/tcp.h>
++#include <bc/sock_orphan.h>
++#include <bc/tcp.h>
+
+ int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES;
+ int sysctl_tcp_synack_retries __read_mostly = TCP_SYNACK_RETRIES;
+@@ -65,7 +67,8 @@ static void tcp_write_err(struct sock *sk)
+ static int tcp_out_of_resources(struct sock *sk, int do_reset)
+ {
+ struct tcp_sock *tp = tcp_sk(sk);
+- int orphans = atomic_read(&tcp_orphan_count);
++ int orphans = ub_get_orphan_count(sk);
++ int orph = orphans;
+
+ /* If peer does not open window for long time, or did not transmit
+ * anything for long time, penalize it. */
+@@ -76,10 +79,16 @@ static int tcp_out_of_resources(struct sock *sk, int do_reset)
+ if (sk->sk_err_soft)
+ orphans <<= 1;
+
+- if (tcp_too_many_orphans(sk, orphans)) {
+- if (net_ratelimit())
+- printk(KERN_INFO "Out of socket memory\n");
+-
++ if (ub_too_many_orphans(sk, orphans)) {
++ if (net_ratelimit()) {
++ int ubid = 0;
++#ifdef CONFIG_USER_RESOURCE
++ ubid = sock_has_ubc(sk) ?
++ top_beancounter(sock_bc(sk)->ub)->ub_uid : 0;
++#endif
++ printk(KERN_INFO "Orphaned socket dropped "
++ "(%d,%d in CT%d)\n", orph, orphans, ubid);
++ }
+ /* Catch exceptional cases, when connection requires reset.
+ * 1. Last segment was sent recently. */
+ if ((s32)(tcp_time_stamp - tp->lsndtime) <= TCP_TIMEWAIT_LEN ||
+@@ -172,9 +181,12 @@ static int tcp_write_timeout(struct sock *sk)
+ static void tcp_delack_timer(unsigned long data)
+ {
+ struct sock *sk = (struct sock*)data;
++ struct ve_struct *env;
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
+
++ env = set_exec_env(sk->owner_env);
++
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Try again later. */
+@@ -223,11 +235,12 @@ static void tcp_delack_timer(unsigned long data)
+ TCP_CHECK_TIMER(sk);
+
+ out:
+- if (tcp_memory_pressure)
++ if (ub_tcp_memory_pressure(sk))
+ sk_mem_reclaim(sk);
+ out_unlock:
+ bh_unlock_sock(sk);
+ sock_put(sk);
++ (void)set_exec_env(env);
+ }
+
+ static void tcp_probe_timer(struct sock *sk)
+@@ -282,8 +295,11 @@ static void tcp_probe_timer(struct sock *sk)
+ static void tcp_retransmit_timer(struct sock *sk)
+ {
+ struct tcp_sock *tp = tcp_sk(sk);
++ struct ve_struct *env;
+ struct inet_connection_sock *icsk = inet_csk(sk);
+
++ env = set_exec_env(sk->owner_env);
++
+ if (!tp->packets_out)
+ goto out;
+
+@@ -391,15 +407,19 @@ out_reset_timer:
+ if (icsk->icsk_retransmits > sysctl_tcp_retries1)
+ __sk_dst_reset(sk);
+
+-out:;
++out:
++ (void)set_exec_env(env);
+ }
+
+ static void tcp_write_timer(unsigned long data)
+ {
+ struct sock *sk = (struct sock*)data;
++ struct ve_struct *env;
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ int event;
+
++ env = set_exec_env(sk->owner_env);
++
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Try again later */
+@@ -433,6 +453,7 @@ out:
+ out_unlock:
+ bh_unlock_sock(sk);
+ sock_put(sk);
++ (void)set_exec_env(env);
+ }
+
+ /*
+@@ -460,10 +481,13 @@ void tcp_set_keepalive(struct sock *sk, int val)
+ static void tcp_keepalive_timer (unsigned long data)
+ {
+ struct sock *sk = (struct sock *) data;
++ struct ve_struct *env;
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ __u32 elapsed;
+
++ env = set_exec_env(sk->owner_env);
++
+ /* Only process if socket is not in use. */
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+@@ -535,4 +559,5 @@ death:
+ out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
++ (void)set_exec_env(env);
+ }
+diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
+index 7b6a584..03be399 100644
+--- a/net/ipv6/addrconf.c
++++ b/net/ipv6/addrconf.c
+@@ -404,9 +404,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
+ dev->type == ARPHRD_TUNNEL6 ||
+ dev->type == ARPHRD_SIT ||
+ dev->type == ARPHRD_NONE) {
+- printk(KERN_INFO
+- "%s: Disabled Privacy Extensions\n",
+- dev->name);
++ ADBG((KERN_INFO "%s: Disabled Privacy Extensions\n",
++ dev->name));
+ ndev->cnf.use_tempaddr = -1;
+ } else {
+ in6_dev_hold(ndev);
+@@ -612,7 +611,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
+ goto out;
+ }
+
+- ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
++ ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC_UBC);
+
+ if (ifa == NULL) {
+ ADBG(("ipv6_add_addr: malloc failed\n"));
+@@ -2070,7 +2069,7 @@ err_exit:
+ /*
+ * Manual configuration of address on an interface
+ */
+-static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
++int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
+ unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
+ __u32 valid_lft)
+ {
+@@ -2142,6 +2141,7 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
+
+ return PTR_ERR(ifp);
+ }
++EXPORT_SYMBOL_GPL(inet6_addr_add);
+
+ static int inet6_addr_del(struct net *net, int ifindex, struct in6_addr *pfx,
+ unsigned int plen)
+@@ -2187,7 +2187,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
+ struct in6_ifreq ireq;
+ int err;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+@@ -2206,7 +2206,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
+ struct in6_ifreq ireq;
+ int err;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+@@ -2709,6 +2709,9 @@ static int addrconf_ifdown(struct net_device *dev, int how)
+ static void addrconf_rs_timer(unsigned long data)
+ {
+ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
++ struct ve_struct *old_env;
++
++ old_env = set_exec_env(ifp->idev->dev->owner_env);
+
+ if (ifp->idev->cnf.forwarding)
+ goto out;
+@@ -2743,6 +2746,7 @@ static void addrconf_rs_timer(unsigned long data)
+
+ out:
+ in6_ifa_put(ifp);
++ (void)set_exec_env(old_env);
+ }
+
+ /*
+@@ -2819,7 +2823,9 @@ static void addrconf_dad_timer(unsigned long data)
+ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+ struct inet6_dev *idev = ifp->idev;
+ struct in6_addr mcaddr;
++ struct ve_struct *old_env;
+
++ old_env = set_exec_env(ifp->idev->dev->owner_env);
+ read_lock_bh(&idev->lock);
+ if (idev->dead) {
+ read_unlock_bh(&idev->lock);
+@@ -2855,6 +2861,7 @@ static void addrconf_dad_timer(unsigned long data)
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any);
+ out:
+ in6_ifa_put(ifp);
++ (void)set_exec_env(old_env);
+ }
+
+ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+@@ -3077,6 +3084,7 @@ static void addrconf_verify(unsigned long foo)
+ struct inet6_ifaddr *ifp;
+ unsigned long now, next;
+ int i;
++ struct ve_struct *old_env;
+
+ spin_lock_bh(&addrconf_verify_lock);
+ now = jiffies;
+@@ -3097,6 +3105,8 @@ restart:
+ if (ifp->flags & IFA_F_PERMANENT)
+ continue;
+
++ old_env = set_exec_env(ifp->idev->dev->owner_env);
++
+ spin_lock(&ifp->lock);
+ age = (now - ifp->tstamp) / HZ;
+
+@@ -3112,9 +3122,11 @@ restart:
+ in6_ifa_hold(ifp);
+ read_unlock(&addrconf_hash_lock);
+ ipv6_del_addr(ifp);
++ (void)set_exec_env(old_env);
+ goto restart;
+ } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
+ spin_unlock(&ifp->lock);
++ set_exec_env(old_env);
+ continue;
+ } else if (age >= ifp->prefered_lft) {
+ /* jiffies - ifp->tsamp > age >= ifp->prefered_lft */
+@@ -3136,6 +3148,7 @@ restart:
+
+ ipv6_ifa_notify(0, ifp);
+ in6_ifa_put(ifp);
++ (void)set_exec_env(old_env);
+ goto restart;
+ }
+ #ifdef CONFIG_IPV6_PRIVACY
+@@ -3157,6 +3170,7 @@ restart:
+ ipv6_create_tempaddr(ifpub, ifp);
+ in6_ifa_put(ifpub);
+ in6_ifa_put(ifp);
++ (void)set_exec_env(old_env);
+ goto restart;
+ }
+ } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
+@@ -3169,6 +3183,7 @@ restart:
+ next = ifp->tstamp + ifp->prefered_lft * HZ;
+ spin_unlock(&ifp->lock);
+ }
++ (void)set_exec_env(old_env);
+ }
+ read_unlock(&addrconf_hash_lock);
+ }
+diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
+index 95055f8..58036a8 100644
+--- a/net/ipv6/af_inet6.c
++++ b/net/ipv6/af_inet6.c
+@@ -56,6 +56,10 @@
+ #ifdef CONFIG_IPV6_TUNNEL
+ #include <net/ip6_tunnel.h>
+ #endif
++#ifdef CONFIG_IPV6_MIP6
++#include <net/mip6.h>
++#endif
++#include <bc/net.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -140,6 +144,10 @@ lookup_protocol:
+ goto out_rcu_unlock;
+ }
+
++ err = vz_security_protocol_check(answer->protocol);
++ if (err < 0)
++ goto out_rcu_unlock;
++
+ err = -EPERM;
+ if (answer->capability > 0 && !capable(answer->capability))
+ goto out_rcu_unlock;
+@@ -157,6 +165,13 @@ lookup_protocol:
+ if (sk == NULL)
+ goto out;
+
++ err = -ENOBUFS;
++ if (ub_sock_charge(sk, PF_INET6, sock->type))
++ goto out_sk_free;
++ /* if charge was successful, sock_init_data() MUST be called to
++ * set sk->sk_type. otherwise sk will be uncharged to wrong resource
++ */
++
+ sock_init_data(sock, sk);
+
+ err = 0;
+@@ -231,6 +246,9 @@ out:
+ out_rcu_unlock:
+ rcu_read_unlock();
+ goto out;
++out_sk_free:
++ sk_free(sk);
++ return err;
+ }
+
+
+@@ -794,45 +812,48 @@ static void ipv6_packet_cleanup(void)
+ dev_remove_pack(&ipv6_packet_type);
+ }
+
+-static int __init init_ipv6_mibs(void)
++int init_ipv6_mibs(void)
+ {
+- if (snmp_mib_init((void **)ipv6_statistics,
++ if (snmp_mib_init((void **)ve_ipv6_statistics,
+ sizeof(struct ipstats_mib)) < 0)
+ goto err_ip_mib;
+- if (snmp_mib_init((void **)icmpv6_statistics,
++ if (snmp_mib_init((void **)ve_icmpv6_statistics,
+ sizeof(struct icmpv6_mib)) < 0)
+ goto err_icmp_mib;
+- if (snmp_mib_init((void **)icmpv6msg_statistics,
++ if (snmp_mib_init((void **)ve_icmpv6msg_statistics,
+ sizeof(struct icmpv6msg_mib)) < 0)
+ goto err_icmpmsg_mib;
+- if (snmp_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib)) < 0)
++ if (snmp_mib_init((void **)ve_udp_stats_in6,
++ sizeof (struct udp_mib)) < 0)
+ goto err_udp_mib;
+- if (snmp_mib_init((void **)udplite_stats_in6,
++ if (snmp_mib_init((void **)ve_udplite_stats_in6,
+ sizeof (struct udp_mib)) < 0)
+ goto err_udplite_mib;
+ return 0;
+
+ err_udplite_mib:
+- snmp_mib_free((void **)udp_stats_in6);
++ snmp_mib_free((void **)ve_udp_stats_in6);
+ err_udp_mib:
+- snmp_mib_free((void **)icmpv6msg_statistics);
++ snmp_mib_free((void **)ve_icmpv6msg_statistics);
+ err_icmpmsg_mib:
+- snmp_mib_free((void **)icmpv6_statistics);
++ snmp_mib_free((void **)ve_icmpv6_statistics);
+ err_icmp_mib:
+- snmp_mib_free((void **)ipv6_statistics);
++ snmp_mib_free((void **)ve_ipv6_statistics);
+ err_ip_mib:
+ return -ENOMEM;
+
+ }
++EXPORT_SYMBOL(init_ipv6_mibs);
+
+-static void cleanup_ipv6_mibs(void)
++void cleanup_ipv6_mibs(void)
+ {
+- snmp_mib_free((void **)ipv6_statistics);
+- snmp_mib_free((void **)icmpv6_statistics);
+- snmp_mib_free((void **)icmpv6msg_statistics);
+- snmp_mib_free((void **)udp_stats_in6);
+- snmp_mib_free((void **)udplite_stats_in6);
++ snmp_mib_free((void **)ve_ipv6_statistics);
++ snmp_mib_free((void **)ve_icmpv6_statistics);
++ snmp_mib_free((void **)ve_icmpv6msg_statistics);
++ snmp_mib_free((void **)ve_udp_stats_in6);
++ snmp_mib_free((void **)ve_udplite_stats_in6);
+ }
++EXPORT_SYMBOL(cleanup_ipv6_mibs);
+
+ static int inet6_net_init(struct net *net)
+ {
+diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
+index 29c7c99..2990d66 100644
+--- a/net/ipv6/ip6_fib.c
++++ b/net/ipv6/ip6_fib.c
+@@ -182,11 +182,9 @@ static void fib6_link_table(struct net *net, struct fib6_table *tb)
+
+ h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1);
+
+- /*
+- * No protection necessary, this is the only list mutatation
+- * operation, tables never disappear once they exist.
+- */
++ write_lock_bh(&tb->tb6_lock);
+ hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
++ write_unlock_bh(&tb->tb6_lock);
+ }
+
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+@@ -1370,10 +1368,14 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
+ for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
+ head = &net->ipv6.fib_table_hash[h];
+ hlist_for_each_entry_rcu(table, node, head, tb6_hlist) {
++ struct ve_struct *old_env;
++
++ old_env = set_exec_env(table->owner_env);
+ write_lock_bh(&table->tb6_lock);
+ fib6_clean_tree(net, &table->tb6_root,
+ func, prune, arg);
+ write_unlock_bh(&table->tb6_lock);
++ (void)set_exec_env(old_env);
+ }
+ }
+ rcu_read_unlock();
+@@ -1493,6 +1495,9 @@ static int fib6_net_init(struct net *net)
+ if (!net->ipv6.fib6_main_tbl)
+ goto out_fib_table_hash;
+
++#ifdef CONFIG_VE
++ net->ipv6.fib6_main_tbl->owner_env = get_exec_env();
++#endif
+ net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
+ net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
+ net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
+@@ -1503,6 +1508,10 @@ static int fib6_net_init(struct net *net)
+ GFP_KERNEL);
+ if (!net->ipv6.fib6_local_tbl)
+ goto out_fib6_main_tbl;
++
++#ifdef CONFIG_VE
++ net->ipv6.fib6_local_tbl->owner_env = get_exec_env();
++#endif
+ net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
+ net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
+ net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
+@@ -1548,7 +1557,7 @@ int __init fib6_init(void)
+
+ fib6_node_kmem = kmem_cache_create("fib6_nodes",
+ sizeof(struct fib6_node),
+- 0, SLAB_HWCACHE_ALIGN,
++ 0, SLAB_HWCACHE_ALIGN|SLAB_UBC,
+ NULL);
+ if (!fib6_node_kmem)
+ goto out;
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 3df2c44..19dcecc 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -529,6 +529,20 @@ int ip6_forward(struct sk_buff *skb)
+ return -EMSGSIZE;
+ }
+
++ /*
++ * We try to optimize forwarding of VE packets:
++ * do not decrement TTL (and so save skb_cow)
++ * during forwarding of outgoing pkts from VE.
++ * For incoming pkts we still do ttl decr,
++ * since such skb is not cloned and does not require
++ * actual cow. So, there is at least one place
++ * in pkts path with mandatory ttl decr, that is
++ * sufficient to prevent routing loops.
++ */
++ hdr = ipv6_hdr(skb);
++ if (skb->dev->features & NETIF_F_VENET) /* src is VENET device */
++ goto no_ttl_decr;
++
+ if (skb_cow(skb, dst->dev->hard_header_len)) {
+ IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS);
+ goto drop;
+@@ -540,6 +554,7 @@ int ip6_forward(struct sk_buff *skb)
+
+ hdr->hop_limit--;
+
++no_ttl_decr:
+ IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
+ return NF_HOOK(PF_INET6, NF_INET_FORWARD, skb, skb->dev, dst->dev,
+ ip6_forward_finish);
+diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
+index e7c03bc..614a5b4 100644
+--- a/net/ipv6/mcast.c
++++ b/net/ipv6/mcast.c
+@@ -243,6 +243,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(ipv6_sock_mc_join);
+
+ /*
+ * socket leave on multicast group
+@@ -2195,15 +2196,18 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma)
+ static void mld_gq_timer_expire(unsigned long data)
+ {
+ struct inet6_dev *idev = (struct inet6_dev *)data;
++ struct ve_struct *old_env = set_exec_env(idev->dev->owner_env);
+
+ idev->mc_gq_running = 0;
+ mld_send_report(idev, NULL);
+ __in6_dev_put(idev);
++ set_exec_env(old_env);
+ }
+
+ static void mld_ifc_timer_expire(unsigned long data)
+ {
+ struct inet6_dev *idev = (struct inet6_dev *)data;
++ struct ve_struct *old_env = set_exec_env(idev->dev->owner_env);
+
+ mld_send_cr(idev);
+ if (idev->mc_ifc_count) {
+@@ -2212,6 +2216,7 @@ static void mld_ifc_timer_expire(unsigned long data)
+ mld_ifc_start_timer(idev, idev->mc_maxdelay);
+ }
+ __in6_dev_put(idev);
++ set_exec_env(old_env);
+ }
+
+ static void mld_ifc_event(struct inet6_dev *idev)
+@@ -2226,6 +2231,7 @@ static void mld_ifc_event(struct inet6_dev *idev)
+ static void igmp6_timer_handler(unsigned long data)
+ {
+ struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
++ struct ve_struct *old_env = set_exec_env(ma->idev->dev->owner_env);
+
+ if (MLD_V1_SEEN(ma->idev))
+ igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
+@@ -2237,6 +2243,7 @@ static void igmp6_timer_handler(unsigned long data)
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+ spin_unlock(&ma->mca_lock);
+ ma_put(ma);
++ set_exec_env(old_env);
+ }
+
+ /* Device going down */
+diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
+index 5859c04..5bbe792 100644
+--- a/net/ipv6/netfilter/ip6_queue.c
++++ b/net/ipv6/netfilter/ip6_queue.c
+@@ -439,7 +439,7 @@ __ipq_rcv_skb(struct sk_buff *skb)
+ if (type <= IPQM_BASE)
+ return;
+
+- if (security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ RCV_SKB_FAIL(-EPERM);
+
+ write_lock_bh(&queue_lock);
+@@ -469,8 +469,12 @@ __ipq_rcv_skb(struct sk_buff *skb)
+ static void
+ ipq_rcv_skb(struct sk_buff *skb)
+ {
++ struct ve_struct *old_ve;
++
+ mutex_lock(&ipqnl_mutex);
++ old_ve = set_exec_env(skb->owner_env);
+ __ipq_rcv_skb(skb);
++ (void)set_exec_env(old_ve);
+ mutex_unlock(&ipqnl_mutex);
+ }
+
+@@ -480,9 +484,6 @@ ipq_rcv_dev_event(struct notifier_block *this,
+ {
+ struct net_device *dev = ptr;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+ /* Drop any packets associated with the downed device */
+ if (event == NETDEV_DOWN)
+ ipq_dev_drop(dev->ifindex);
+@@ -502,7 +503,7 @@ ipq_rcv_nl_event(struct notifier_block *this,
+ if (event == NETLINK_URELEASE &&
+ n->protocol == NETLINK_IP6_FW && n->pid) {
+ write_lock_bh(&queue_lock);
+- if ((n->net == &init_net) && (n->pid == peer_pid))
++ if (n->pid == peer_pid)
+ __ipq_reset();
+ write_unlock_bh(&queue_lock);
+ }
+diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
+index 0b4557e..8244d02 100644
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -365,6 +365,9 @@ ip6t_do_table(struct sk_buff *skb,
+ struct ip6t_entry *e, *back;
+ struct xt_table_info *private;
+
++ if (!table) /* VE is not allowed to have this xtable */
++ return NF_ACCEPT;
++
+ /* Initialization */
+ indev = in ? in->name : nulldevname;
+ outdev = out ? out->name : nulldevname;
+@@ -1874,7 +1877,7 @@ compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -1985,7 +1988,7 @@ compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+ {
+ int ret;
+
+- if (!capable(CAP_NET_ADMIN))
++ if (!capable(CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+@@ -2084,7 +2087,7 @@ struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table,
+ int ret;
+ struct xt_table_info *newinfo;
+ struct xt_table_info bootstrap
+- = { 0, 0, 0, { 0 }, { 0 }, { } };
++ = { 0, 0, 0, 0, { 0 }, { 0 }, { } };
+ void *loc_cpu_entry;
+ struct xt_table *new_table;
+
+@@ -2241,11 +2244,22 @@ static struct xt_match icmp6_matchstruct __read_mostly = {
+
+ static int __net_init ip6_tables_net_init(struct net *net)
+ {
+- return xt_proto_init(net, AF_INET6);
++ int res;
++
++ if (!net_ipt_module_permitted(net, VE_IP_IPTABLES6))
++ return 0;
++
++ res = xt_proto_init(net, AF_INET6);
++ if (!res)
++ net_ipt_module_set(net, VE_IP_IPTABLES6);
++ return res;
+ }
+
+ static void __net_exit ip6_tables_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_IPTABLES6))
++ return;
++
+ xt_proto_fini(net, AF_INET6);
+ }
+
+diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
+index 55a2c29..91bcf27 100644
+--- a/net/ipv6/netfilter/ip6table_filter.c
++++ b/net/ipv6/netfilter/ip6table_filter.c
+@@ -133,16 +133,24 @@ module_param(forward, bool, 0000);
+
+ static int __net_init ip6table_filter_net_init(struct net *net)
+ {
++ if (!net_ipt_module_permitted(net, VE_IP_FILTER6))
++ return 0;
++
+ /* Register table */
+ net->ipv6.ip6table_filter =
+ ip6t_register_table(net, &packet_filter, &initial_table.repl);
+ if (IS_ERR(net->ipv6.ip6table_filter))
+ return PTR_ERR(net->ipv6.ip6table_filter);
++
++ net_ipt_module_set(net, VE_IP_FILTER6);
+ return 0;
+ }
+
+ static void __net_exit ip6table_filter_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_FILTER6))
++ return;
++
+ ip6t_unregister_table(net->ipv6.ip6table_filter);
+ }
+
+diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c
+index f405cea..a4727b3 100644
+--- a/net/ipv6/netfilter/ip6table_mangle.c
++++ b/net/ipv6/netfilter/ip6table_mangle.c
+@@ -160,16 +160,24 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = {
+
+ static int __net_init ip6table_mangle_net_init(struct net *net)
+ {
++ if (!net_ipt_module_permitted(net, VE_IP_MANGLE6))
++ return 0;
++
+ /* Register table */
+ net->ipv6.ip6table_mangle =
+ ip6t_register_table(net, &packet_mangler, &initial_table.repl);
+ if (IS_ERR(net->ipv6.ip6table_mangle))
+ return PTR_ERR(net->ipv6.ip6table_mangle);
++
++ net_ipt_module_set(net, VE_IP_MANGLE6);
+ return 0;
+ }
+
+ static void __net_exit ip6table_mangle_net_exit(struct net *net)
+ {
++ if (!net_is_ipt_module_set(net, VE_IP_MANGLE6))
++ return;
++
+ ip6t_unregister_table(net->ipv6.ip6table_mangle);
+ }
+
+diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+index 85050c0..a782710 100644
+--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
++++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+@@ -14,6 +14,7 @@
+ #include <linux/in6.h>
+ #include <linux/netfilter.h>
+ #include <linux/module.h>
++#include <linux/nfcalls.h>
+ #include <linux/skbuff.h>
+ #include <linux/icmp.h>
+ #include <linux/sysctl.h>
+@@ -359,72 +360,157 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
+
+-static int __init nf_conntrack_l3proto_ipv6_init(void)
++static int nf_ct_proto_ipv6_init_net(struct net *net)
+ {
+- int ret = 0;
++ struct nf_conntrack_l3proto *ipv6;
++
++ ipv6 = &nf_conntrack_l3proto_ipv6;
++ if (net != &init_net) {
++ ipv6 = kmemdup(ipv6,
++ sizeof(struct nf_conntrack_l3proto), GFP_KERNEL);
++ if (!ipv6)
++ return -ENOMEM;
++ }
+
+- need_conntrack();
++ net->ipv6.nf_conntrack_l3proto_ipv6 = ipv6;
++ return 0;
++}
+
+- ret = nf_ct_frag6_init();
+- if (ret < 0) {
+- printk("nf_conntrack_ipv6: can't initialize frag6.\n");
+- return ret;
+- }
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6);
++static void nf_ct_proto_ipv6_exit_net(struct net *net)
++{
++ if (net != &init_net)
++ kfree(net->ipv6.nf_conntrack_l3proto_ipv6);
++}
++
++static struct pernet_operations nf_ct_ipv6_ops = {
++ .init = nf_ct_proto_ipv6_init_net,
++ .exit = nf_ct_proto_ipv6_exit_net,
++};
++
++int init_nf_ct_l3proto_ipv6(void)
++{
++ struct net *net = get_exec_env()->ve_netns;
++
++ int ret = -ENOMEM;
++
++#ifdef CONFIG_VE_IPTABLES
++ if (!ve_is_super(get_exec_env()))
++ __module_get(THIS_MODULE);
++
++ ret = nf_ct_proto_icmpv6_sysctl_init();
++ if (ret < 0)
++ goto no_mem_icmp;
++#endif /* CONFIG_VE_IPTABLES */
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_tcp6);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv6: can't register tcp.\n");
+- goto cleanup_frag6;
++ goto cleanup_sys;
+ }
+
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6);
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_udp6);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv6: can't register udp.\n");
+- goto cleanup_tcp;
++ goto unreg_tcp;
+ }
+
+- ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmpv6);
++ ret = nf_conntrack_l4proto_register(ve_nf_conntrack_l4proto_icmpv6);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv6: can't register icmpv6.\n");
+- goto cleanup_udp;
++ goto unreg_udp;
+ }
+
+- ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
++ ret = nf_conntrack_l3proto_register(net->ipv6.nf_conntrack_l3proto_ipv6);
+ if (ret < 0) {
+ printk("nf_conntrack_ipv6: can't register ipv6\n");
+- goto cleanup_icmpv6;
++ goto unreg_icmpv6;
++ }
++
++ return 0;
++
++unreg_icmpv6:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_icmpv6);
++unreg_udp:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_udp6);
++unreg_tcp:
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_tcp6);
++cleanup_sys:
++#ifdef CONFIG_VE_IPTABLES
++no_mem_icmp:
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++#endif /* CONFIG_VE_IPTABLES */
++ return ret;
++}
++EXPORT_SYMBOL(init_nf_ct_l3proto_ipv6);
++
++void fini_nf_ct_l3proto_ipv6(void)
++{
++ struct net *net = get_exec_env()->ve_netns;
++
++ nf_conntrack_l3proto_unregister(net->ipv6.nf_conntrack_l3proto_ipv6);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_icmpv6);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_udp6);
++ nf_conntrack_l4proto_unregister(ve_nf_conntrack_l4proto_tcp6);
++
++#ifdef CONFIG_VE_IPTABLES
++ nf_ct_proto_icmpv6_sysctl_cleanup();
++ if (!ve_is_super(get_exec_env()))
++ module_put(THIS_MODULE);
++#endif /* CONFIG_VE_IPTABLES */
++}
++EXPORT_SYMBOL(fini_nf_ct_l3proto_ipv6);
++
++static int __init nf_conntrack_l3proto_ipv6_init(void)
++{
++ int ret = 0;
++
++ need_conntrack();
++
++ register_pernet_subsys(&nf_ct_ipv6_ops);
++
++ ret = nf_ct_frag6_init();
++ if (ret < 0) {
++ printk("nf_conntrack_ipv6: can't initialize frag6.\n");
++ goto unreg_subsys;
++ }
++
++ ret = init_nf_ct_l3proto_ipv6();
++ if (ret < 0) {
++ printk(KERN_ERR "Unable to initialize netfilter protocols\n");
++ goto cleanup_frag6;
+ }
+
+ ret = nf_register_hooks(ipv6_conntrack_ops,
+ ARRAY_SIZE(ipv6_conntrack_ops));
+ if (ret < 0) {
+- printk("nf_conntrack_ipv6: can't register pre-routing defrag "
+- "hook.\n");
+- goto cleanup_ipv6;
++ printk(KERN_ERR "nf_conntrack_ipv6: can't register pre-routing "
++ "defrag hook.\n");
++ goto cleanup_l3proto;
+ }
+- return ret;
++ KSYMRESOLVE(init_nf_ct_l3proto_ipv6);
++ KSYMRESOLVE(fini_nf_ct_l3proto_ipv6);
++ KSYMMODRESOLVE(nf_conntrack_ipv6);
++ return 0;
+
+- cleanup_ipv6:
+- nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
+- cleanup_icmpv6:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
+- cleanup_udp:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
+- cleanup_tcp:
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
+- cleanup_frag6:
++cleanup_l3proto:
++ fini_nf_ct_l3proto_ipv6();
++cleanup_frag6:
+ nf_ct_frag6_cleanup();
++unreg_subsys:
++ unregister_pernet_subsys(&nf_ct_ipv6_ops);
+ return ret;
+ }
+
+ static void __exit nf_conntrack_l3proto_ipv6_fini(void)
+ {
+ synchronize_net();
++ KSYMMODUNRESOLVE(nf_conntrack_ipv6);
++ KSYMUNRESOLVE(init_nf_ct_l3proto_ipv6);
++ KSYMUNRESOLVE(fini_nf_ct_l3proto_ipv6);
+ nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
+- nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6);
+- nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
++ fini_nf_ct_l3proto_ipv6();
+ nf_ct_frag6_cleanup();
++ unregister_pernet_subsys(&nf_ct_ipv6_ops);
+ }
+
+ module_init(nf_conntrack_l3proto_ipv6_init);
+diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+index 14d47d8..438b543 100644
+--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
++++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+@@ -10,6 +10,7 @@
+ */
+
+ #include <linux/types.h>
++#include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/module.h>
+ #include <linux/netfilter.h>
+@@ -94,7 +95,7 @@ static int icmpv6_packet(struct nf_conn *ct,
+ } else {
+ atomic_inc(&ct->proto.icmp.count);
+ nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
+- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout);
++ nf_ct_refresh_acct(ct, ctinfo, skb, ve_nf_ct_icmpv6_timeout);
+ }
+
+ return NF_ACCEPT;
+@@ -130,6 +131,7 @@ icmpv6_error_message(struct sk_buff *skb,
+ struct nf_conntrack_tuple intuple, origtuple;
+ const struct nf_conntrack_tuple_hash *h;
+ const struct nf_conntrack_l4proto *inproto;
++ struct net *net = get_exec_env()->ve_netns;
+
+ NF_CT_ASSERT(skb->nfct == NULL);
+
+@@ -149,7 +151,7 @@ icmpv6_error_message(struct sk_buff *skb,
+ /* Ordinarily, we'd expect the inverted tupleproto, but it's
+ been preserved inside the ICMP. */
+ if (!nf_ct_invert_tuple(&intuple, &origtuple,
+- &nf_conntrack_l3proto_ipv6, inproto)) {
++ net->ipv6.nf_conntrack_l3proto_ipv6, inproto)) {
+ pr_debug("icmpv6_error: Can't invert tuple\n");
+ return -NF_ACCEPT;
+ }
+@@ -281,3 +283,48 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly =
+ .ctl_table = icmpv6_sysctl_table,
+ #endif
+ };
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++int nf_ct_proto_icmpv6_sysctl_init(void)
++{
++ struct nf_conntrack_l4proto *icmp6;
++
++ if (ve_is_super(get_exec_env())) {
++ icmp6 = &nf_conntrack_l4proto_icmpv6;
++ goto out;
++ }
++
++ icmp6 = kmemdup(&nf_conntrack_l4proto_icmpv6,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (!icmp6)
++ goto no_mem_ct;
++
++ icmp6->ctl_table_header = &ve_icmpv6_sysctl_header;
++ icmp6->ctl_table = kmemdup(icmpv6_sysctl_table,
++ sizeof(icmpv6_sysctl_table), GFP_KERNEL);
++ if (!icmp6->ctl_table)
++ goto no_mem_sys;
++
++ icmp6->ctl_table[0].data = &ve_nf_ct_icmpv6_timeout;
++out:
++ ve_nf_ct_icmpv6_timeout = nf_ct_icmpv6_timeout;
++
++ ve_nf_conntrack_l4proto_icmpv6 = icmp6;
++ return 0;
++
++no_mem_sys:
++ kfree(icmp6);
++no_mem_ct:
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(nf_ct_proto_icmpv6_sysctl_init);
++
++void nf_ct_proto_icmpv6_sysctl_cleanup(void)
++{
++ if (!ve_is_super(get_exec_env())) {
++ kfree(ve_nf_conntrack_l4proto_icmpv6->ctl_table);
++ kfree(ve_nf_conntrack_l4proto_icmpv6);
++ }
++}
++EXPORT_SYMBOL(nf_ct_proto_icmpv6_sysctl_cleanup);
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
+diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
+index 52d06dd..fa67851 100644
+--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
++++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
+@@ -40,6 +40,7 @@
+ #include <net/ndisc.h>
+ #include <net/addrconf.h>
+ #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
++#include <net/netfilter/nf_conntrack_l3proto.h>
+ #include <linux/sysctl.h>
+ #include <linux/netfilter.h>
+ #include <linux/netfilter_ipv6.h>
+@@ -145,11 +146,12 @@ static void nf_skb_free(struct sk_buff *skb)
+ }
+
+ /* Memory Tracking Functions. */
+-static inline void frag_kfree_skb(struct sk_buff *skb, unsigned int *work)
++static inline void frag_kfree_skb(struct netns_frags *nf,
++ struct sk_buff *skb, unsigned int *work)
+ {
+ if (work)
+ *work -= skb->truesize;
+- atomic_sub(skb->truesize, &nf_init_frags.mem);
++ atomic_sub(skb->truesize, &nf->mem);
+ nf_skb_free(skb);
+ kfree_skb(skb);
+ }
+@@ -169,10 +171,10 @@ static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq)
+ inet_frag_kill(&fq->q, &nf_frags);
+ }
+
+-static void nf_ct_frag6_evictor(void)
++static void nf_ct_frag6_evictor(struct netns_frags *nf)
+ {
+ local_bh_disable();
+- inet_frag_evictor(&nf_init_frags, &nf_frags);
++ inet_frag_evictor(nf, &nf_frags);
+ local_bh_enable();
+ }
+
+@@ -198,7 +200,7 @@ out:
+ /* Creation primitives. */
+
+ static __inline__ struct nf_ct_frag6_queue *
+-fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst)
++fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst)
+ {
+ struct inet_frag_queue *q;
+ struct ip6_create_arg arg;
+@@ -211,7 +213,7 @@ fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst)
+ read_lock_bh(&nf_frags.lock);
+ hash = ip6qhashfn(id, src, dst);
+
+- q = inet_frag_find(&nf_init_frags, &nf_frags, &arg, hash);
++ q = inet_frag_find(&net->ipv6.ct_frags, &nf_frags, &arg, hash);
+ local_bh_enable();
+ if (q == NULL)
+ goto oom;
+@@ -224,7 +226,8 @@ oom:
+ }
+
+
+-static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
++static int nf_ct_frag6_queue(struct net *net, struct nf_ct_frag6_queue *fq,
++ struct sk_buff *skb,
+ const struct frag_hdr *fhdr, int nhoff)
+ {
+ struct sk_buff *prev, *next;
+@@ -365,7 +368,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
+ fq->q.fragments = next;
+
+ fq->q.meat -= free_it->len;
+- frag_kfree_skb(free_it, NULL);
++ frag_kfree_skb(fq->q.net, free_it, NULL);
+ }
+ }
+
+@@ -381,7 +384,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
+ skb->dev = NULL;
+ fq->q.stamp = skb->tstamp;
+ fq->q.meat += skb->len;
+- atomic_add(skb->truesize, &nf_init_frags.mem);
++ atomic_add(skb->truesize, &net->ipv6.ct_frags.mem);
+
+ /* The first fragment.
+ * nhoffset is obtained from the first fragment, of course.
+@@ -391,7 +394,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
+ fq->q.last_in |= INET_FRAG_FIRST_IN;
+ }
+ write_lock(&nf_frags.lock);
+- list_move_tail(&fq->q.lru_list, &nf_init_frags.lru_list);
++ list_move_tail(&fq->q.lru_list, &net->ipv6.ct_frags.lru_list);
+ write_unlock(&nf_frags.lock);
+ return 0;
+
+@@ -409,7 +412,8 @@ err:
+ * the last and the first frames arrived and all the bits are here.
+ */
+ static struct sk_buff *
+-nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
++nf_ct_frag6_reasm(struct net *net, struct nf_ct_frag6_queue *fq,
++ struct net_device *dev)
+ {
+ struct sk_buff *fp, *op, *head = fq->q.fragments;
+ int payload_len;
+@@ -458,7 +462,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
+ clone->ip_summed = head->ip_summed;
+
+ NFCT_FRAG6_CB(clone)->orig = NULL;
+- atomic_add(clone->truesize, &nf_init_frags.mem);
++ atomic_add(clone->truesize, &net->ipv6.ct_frags.mem);
+ }
+
+ /* We have to remove fragment header from datagram and to relocate
+@@ -472,7 +476,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
+ skb_shinfo(head)->frag_list = head->next;
+ skb_reset_transport_header(head);
+ skb_push(head, head->data - skb_network_header(head));
+- atomic_sub(head->truesize, &nf_init_frags.mem);
++ atomic_sub(head->truesize, &net->ipv6.ct_frags.mem);
+
+ for (fp=head->next; fp; fp = fp->next) {
+ head->data_len += fp->len;
+@@ -482,7 +486,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
+ else if (head->ip_summed == CHECKSUM_COMPLETE)
+ head->csum = csum_add(head->csum, fp->csum);
+ head->truesize += fp->truesize;
+- atomic_sub(fp->truesize, &nf_init_frags.mem);
++ atomic_sub(fp->truesize, &net->ipv6.ct_frags.mem);
+ }
+
+ head->next = NULL;
+@@ -599,6 +603,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+ int fhoff, nhoff;
+ u8 prevhdr;
+ struct sk_buff *ret_skb = NULL;
++ struct net *net = dev_net(dev);
+
+ /* Jumbo payload inhibits frag. header */
+ if (ipv6_hdr(skb)->payload_len == 0) {
+@@ -632,10 +637,11 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+ goto ret_orig;
+ }
+
+- if (atomic_read(&nf_init_frags.mem) > nf_init_frags.high_thresh)
+- nf_ct_frag6_evictor();
++ if (atomic_read(&net->ipv6.ct_frags.mem) >
++ net->ipv6.ct_frags.high_thresh)
++ nf_ct_frag6_evictor(&net->ipv6.ct_frags);
+
+- fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr);
++ fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr);
+ if (fq == NULL) {
+ pr_debug("Can't find and can't create new queue\n");
+ goto ret_orig;
+@@ -643,7 +649,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+
+ spin_lock_bh(&fq->q.lock);
+
+- if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
++ if (nf_ct_frag6_queue(net, fq, clone, fhdr, nhoff) < 0) {
+ spin_unlock_bh(&fq->q.lock);
+ pr_debug("Can't insert skb to queue\n");
+ fq_put(fq);
+@@ -652,7 +658,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
+
+ if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
+ fq->q.meat == fq->q.len) {
+- ret_skb = nf_ct_frag6_reasm(fq, dev);
++ ret_skb = nf_ct_frag6_reasm(net, fq, dev);
+ if (ret_skb == NULL)
+ pr_debug("Can't reassemble fragmented packets\n");
+ }
+@@ -687,8 +693,54 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
+ nf_conntrack_put_reasm(skb);
+ }
+
++static int nf_ct_frag6_init_net(struct net *net)
++{
++ struct netns_frags *frags = &net->ipv6.ct_frags;
++
++#ifdef CONFIG_SYSCTL
++ if (net != &init_net) {
++ struct nf_conntrack_l3proto *ipv6 =
++ net->ipv6.nf_conntrack_l3proto_ipv6;
++
++ ipv6->ctl_table = kmemdup(nf_ct_ipv6_sysctl_table,
++ sizeof(nf_ct_ipv6_sysctl_table),
++ GFP_KERNEL);
++ if (!ipv6->ctl_table)
++ return -ENOMEM;
++
++ ipv6->ctl_table_header = NULL;
++ ipv6->ctl_table_path = nf_net_netfilter_sysctl_path;
++
++ ipv6->ctl_table[0].data = &frags->timeout;
++ ipv6->ctl_table[1].data = &frags->low_thresh;
++ ipv6->ctl_table[2].data = &frags->high_thresh;
++ }
++#endif
++ frags->timeout = IPV6_FRAG_TIMEOUT;
++ frags->high_thresh = 256 * 1024;
++ frags->low_thresh = 192 * 1024;
++ inet_frags_init_net(frags);
++
++ return 0;
++}
++
++static void nf_ct_frag6_exit_net(struct net *net)
++{
++ inet_frags_exit_net(&net->ipv6.ct_frags, &nf_frags);
++ if (net != &init_net)
++ kfree(net->ipv6.nf_conntrack_l3proto_ipv6->ctl_table);
++
++}
++
++static struct pernet_operations nf_ct_frag6_ops = {
++ .init = nf_ct_frag6_init_net,
++ .exit = nf_ct_frag6_exit_net,
++};
++
+ int nf_ct_frag6_init(void)
+ {
++ register_pernet_subsys(&nf_ct_frag6_ops);
++
+ nf_frags.hashfn = nf_hashfn;
+ nf_frags.constructor = ip6_frag_init;
+ nf_frags.destructor = NULL;
+@@ -697,10 +749,6 @@ int nf_ct_frag6_init(void)
+ nf_frags.match = ip6_frag_match;
+ nf_frags.frag_expire = nf_ct_frag6_expire;
+ nf_frags.secret_interval = 10 * 60 * HZ;
+- nf_init_frags.timeout = IPV6_FRAG_TIMEOUT;
+- nf_init_frags.high_thresh = 256 * 1024;
+- nf_init_frags.low_thresh = 192 * 1024;
+- inet_frags_init_net(&nf_init_frags);
+ inet_frags_init(&nf_frags);
+
+ return 0;
+@@ -709,7 +757,5 @@ int nf_ct_frag6_init(void)
+ void nf_ct_frag6_cleanup(void)
+ {
+ inet_frags_fini(&nf_frags);
+-
+- nf_init_frags.low_thresh = 0;
+- nf_ct_frag6_evictor();
++ unregister_pernet_subsys(&nf_ct_frag6_ops);
+ }
+diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
+index 0179b66..652b92b 100644
+--- a/net/ipv6/proc.c
++++ b/net/ipv6/proc.c
+@@ -29,8 +29,6 @@
+ #include <net/transp_v6.h>
+ #include <net/ipv6.h>
+
+-static struct proc_dir_entry *proc_net_devsnmp6;
+-
+ static int sockstat6_seq_show(struct seq_file *seq, void *v)
+ {
+ struct net *net = seq->private;
+@@ -172,11 +170,11 @@ static int snmp6_seq_show(struct seq_file *seq, void *v)
+ snmp6_seq_show_item(seq, (void **)idev->stats.icmpv6, snmp6_icmp6_list);
+ snmp6_seq_show_icmpv6msg(seq, (void **)idev->stats.icmpv6msg);
+ } else {
+- snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipstats_list);
+- snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list);
+- snmp6_seq_show_icmpv6msg(seq, (void **)icmpv6msg_statistics);
+- snmp6_seq_show_item(seq, (void **)udp_stats_in6, snmp6_udp6_list);
+- snmp6_seq_show_item(seq, (void **)udplite_stats_in6, snmp6_udplite6_list);
++ snmp6_seq_show_item(seq, (void **)ve_ipv6_statistics, snmp6_ipstats_list);
++ snmp6_seq_show_item(seq, (void **)ve_icmpv6_statistics, snmp6_icmp6_list);
++ snmp6_seq_show_icmpv6msg(seq, (void **)ve_icmpv6msg_statistics);
++ snmp6_seq_show_item(seq, (void **)ve_udp_stats_in6, snmp6_udp6_list);
++ snmp6_seq_show_item(seq, (void **)ve_udplite_stats_in6, snmp6_udplite6_list);
+ }
+ return 0;
+ }
+@@ -210,18 +208,17 @@ static const struct file_operations snmp6_seq_fops = {
+ int snmp6_register_dev(struct inet6_dev *idev)
+ {
+ struct proc_dir_entry *p;
++ struct net *net;
+
+ if (!idev || !idev->dev)
+ return -EINVAL;
+
+- if (!net_eq(dev_net(idev->dev), &init_net))
+- return 0;
+-
+- if (!proc_net_devsnmp6)
++ net = dev_net(idev->dev);
++ if (!net->ipv6.proc_dev_snmp)
+ return -ENOENT;
+
+ p = proc_create_data(idev->dev->name, S_IRUGO,
+- proc_net_devsnmp6, &snmp6_seq_fops, idev);
++ net->ipv6.proc_dev_snmp, &snmp6_seq_fops, idev);
+ if (!p)
+ return -ENOMEM;
+
+@@ -231,12 +228,14 @@ int snmp6_register_dev(struct inet6_dev *idev)
+
+ int snmp6_unregister_dev(struct inet6_dev *idev)
+ {
+- if (!proc_net_devsnmp6)
++ struct net *net = dev_net(idev->dev);
++
++ if (!net->ipv6.proc_dev_snmp)
+ return -ENOENT;
+ if (!idev || !idev->stats.proc_dir_entry)
+ return -EINVAL;
+ remove_proc_entry(idev->stats.proc_dir_entry->name,
+- proc_net_devsnmp6);
++ net->ipv6.proc_dev_snmp);
+ idev->stats.proc_dir_entry = NULL;
+ return 0;
+ }
+@@ -245,12 +244,24 @@ static int ipv6_proc_init_net(struct net *net)
+ {
+ if (!proc_net_fops_create(net, "sockstat6", S_IRUGO,
+ &sockstat6_seq_fops))
+- return -ENOMEM;
++ goto err_sockstat;
++
++ net->ipv6.proc_dev_snmp = proc_net_mkdir(net,
++ "dev_snmp6", net->proc_net);
++ if (!net->ipv6.proc_dev_snmp)
++ goto err_dev_snmp;
++
+ return 0;
++
++err_dev_snmp:
++ proc_net_remove(net, "sockstat6");
++err_sockstat:
++ return -ENOMEM;
+ }
+
+ static void ipv6_proc_exit_net(struct net *net)
+ {
++ proc_net_remove(net, "dev_snmp6");
+ proc_net_remove(net, "sockstat6");
+ }
+
+@@ -269,14 +280,9 @@ int __init ipv6_misc_proc_init(void)
+ if (!proc_net_fops_create(&init_net, "snmp6", S_IRUGO, &snmp6_seq_fops))
+ goto proc_snmp6_fail;
+
+- proc_net_devsnmp6 = proc_mkdir("dev_snmp6", init_net.proc_net);
+- if (!proc_net_devsnmp6)
+- goto proc_dev_snmp6_fail;
+ out:
+ return rc;
+
+-proc_dev_snmp6_fail:
+- proc_net_remove(&init_net, "snmp6");
+ proc_snmp6_fail:
+ unregister_pernet_subsys(&ipv6_proc_ops);
+ proc_net_fail:
+@@ -286,7 +292,6 @@ proc_net_fail:
+
+ void ipv6_misc_proc_exit(void)
+ {
+- proc_net_remove(&init_net, "dev_snmp6");
+ proc_net_remove(&init_net, "snmp6");
+ unregister_pernet_subsys(&ipv6_proc_ops);
+ }
+diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
+index 89184b5..e01bbb1 100644
+--- a/net/ipv6/reassembly.c
++++ b/net/ipv6/reassembly.c
+@@ -196,8 +196,10 @@ static void ip6_frag_expire(unsigned long data)
+ struct frag_queue *fq;
+ struct net_device *dev = NULL;
+ struct net *net;
++ struct ve_struct *old_ve;
+
+ fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
++ old_ve = set_exec_env(fq->q.owner_ve);
+
+ spin_lock(&fq->q.lock);
+
+@@ -232,6 +234,8 @@ out:
+ dev_put(dev);
+ spin_unlock(&fq->q.lock);
+ fq_put(fq);
++
++ (void)set_exec_env(old_ve);
+ }
+
+ static __inline__ struct frag_queue *
+@@ -508,6 +512,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
+ clone->csum = 0;
+ clone->ip_summed = head->ip_summed;
+ atomic_add(clone->truesize, &fq->q.net->mem);
++ clone->owner_env = head->owner_env;
+ }
+
+ /* We have to remove fragment header from datagram and to relocate
+diff --git a/net/ipv6/route.c b/net/ipv6/route.c
+index 63442a1..803129d 100644
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -1889,10 +1889,12 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
+ rt->rt6i_flags |= RTF_ANYCAST;
+ else
+ rt->rt6i_flags |= RTF_LOCAL;
+- rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+- if (rt->rt6i_nexthop == NULL) {
+- dst_free(&rt->u.dst);
+- return ERR_PTR(-ENOMEM);
++ rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, rt->rt6i_dev);
++ if (IS_ERR(rt->rt6i_nexthop)) {
++ void *err = rt->rt6i_nexthop;
++ rt->rt6i_nexthop = NULL;
++ dst_free((struct dst_entry *) rt);
++ return err;
+ }
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
+index b7a50e9..9a50bc6 100644
+--- a/net/ipv6/sit.c
++++ b/net/ipv6/sit.c
+@@ -32,6 +32,7 @@
+ #include <linux/init.h>
+ #include <linux/netfilter_ipv4.h>
+ #include <linux/if_ether.h>
++#include <linux/vzcalluser.h>
+
+ #include <net/sock.h>
+ #include <net/snmp.h>
+@@ -87,6 +88,9 @@ static struct ip_tunnel * ipip6_tunnel_lookup(struct net *net,
+ struct ip_tunnel *t;
+ struct sit_net *sitn = net_generic(net, sit_net_id);
+
++ if (sitn == NULL)
++ return NULL;
++
+ for (t = sitn->tunnels_r_l[h0^h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr &&
+ remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
+@@ -1005,6 +1009,9 @@ static int sit_init_net(struct net *net)
+ int err;
+ struct sit_net *sitn;
+
++ if (!(get_exec_env()->features & VE_FEATURE_SIT))
++ return 0;
++
+ err = -ENOMEM;
+ sitn = kzalloc(sizeof(struct sit_net), GFP_KERNEL);
+ if (sitn == NULL)
+@@ -1049,6 +1056,9 @@ static void sit_exit_net(struct net *net)
+ struct sit_net *sitn;
+
+ sitn = net_generic(net, sit_net_id);
++ if (sitn == NULL) /* no VE_FEATURE_SIT */
++ return;
++
+ rtnl_lock();
+ sit_destroy_tunnels(sitn);
+ unregister_netdevice(sitn->fb_tunnel_dev);
+diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
+index 10e22fd..f259d80 100644
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -60,6 +60,8 @@
+ #include <net/netdma.h>
+ #include <net/inet_common.h>
+
++#include <bc/tcp.h>
++
+ #include <asm/uaccess.h>
+
+ #include <linux/proc_fs.h>
+@@ -74,7 +76,7 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+
+ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
+
+-static struct inet_connection_sock_af_ops ipv6_mapped;
++struct inet_connection_sock_af_ops ipv6_mapped;
+ static struct inet_connection_sock_af_ops ipv6_specific;
+ #ifdef CONFIG_TCP_MD5SIG
+ static struct tcp_sock_af_ops tcp_sock_ipv6_specific;
+@@ -1521,6 +1523,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct tcp_sock *tp;
+ struct sk_buff *opt_skb = NULL;
++ struct user_beancounter *ub;
+
+ /* Imagine: socket is IPv6. IPv4 packet arrives,
+ goes to IPv4 receive handler and backlogged.
+@@ -1533,6 +1536,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ if (skb->protocol == htons(ETH_P_IP))
+ return tcp_v4_do_rcv(sk, skb);
+
++ ub = set_exec_ub(sock_bc(sk)->ub);
++
+ #ifdef CONFIG_TCP_MD5SIG
+ if (tcp_v6_inbound_md5_hash (sk, skb))
+ goto discard;
+@@ -1569,7 +1574,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ TCP_CHECK_TIMER(sk);
+ if (opt_skb)
+ goto ipv6_pktoptions;
+- return 0;
++ goto restore_context;
+ }
+
+ if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
+@@ -1590,7 +1595,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ goto reset;
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+- return 0;
++ goto restore_context;
+ }
+ }
+
+@@ -1600,6 +1605,9 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ TCP_CHECK_TIMER(sk);
+ if (opt_skb)
+ goto ipv6_pktoptions;
++
++restore_context:
++ (void)set_exec_ub(ub);
+ return 0;
+
+ reset:
+@@ -1608,7 +1616,7 @@ discard:
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+ kfree_skb(skb);
+- return 0;
++ goto restore_context;
+ csum_err:
+ TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
+ goto discard;
+@@ -1640,7 +1648,7 @@ ipv6_pktoptions:
+
+ if (opt_skb)
+ kfree_skb(opt_skb);
+- return 0;
++ goto restore_context;
+ }
+
+ static int tcp_v6_rcv(struct sk_buff *skb)
+@@ -1823,7 +1831,7 @@ static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
+ * TCP over IPv4 via INET6 API
+ */
+
+-static struct inet_connection_sock_af_ops ipv6_mapped = {
++struct inet_connection_sock_af_ops ipv6_mapped = {
+ .queue_xmit = ip_queue_xmit,
+ .send_check = tcp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+@@ -1842,6 +1850,8 @@ static struct inet_connection_sock_af_ops ipv6_mapped = {
+ #endif
+ };
+
++EXPORT_SYMBOL_GPL(ipv6_mapped);
++
+ #ifdef CONFIG_TCP_MD5SIG
+ static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
+ .md5_lookup = tcp_v4_md5_lookup,
+diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
+index 08e4cbb..77e5248 100644
+--- a/net/ipv6/xfrm6_policy.c
++++ b/net/ipv6/xfrm6_policy.c
+@@ -14,6 +14,7 @@
+ #include <linux/err.h>
+ #include <linux/kernel.h>
+ #include <linux/netdevice.h>
++#include <linux/nsproxy.h>
+ #include <net/addrconf.h>
+ #include <net/dst.h>
+ #include <net/xfrm.h>
+@@ -38,7 +39,7 @@ static struct dst_entry *xfrm6_dst_lookup(int tos, xfrm_address_t *saddr,
+ if (saddr)
+ memcpy(&fl.fl6_src, saddr, sizeof(fl.fl6_src));
+
+- dst = ip6_route_output(&init_net, NULL, &fl);
++ dst = ip6_route_output(get_exec_env()->ve_netns, NULL, &fl);
+
+ err = dst->error;
+ if (dst->error) {
+diff --git a/net/netfilter/core.c b/net/netfilter/core.c
+index 292fa28..6bf46b5 100644
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -60,6 +60,8 @@ int nf_register_hook(struct nf_hook_ops *reg)
+ struct nf_hook_ops *elem;
+ int err;
+
++ BUG_ON(!ve_is_super(get_exec_env()));
++
+ err = mutex_lock_interruptible(&nf_hook_mutex);
+ if (err < 0)
+ return err;
+@@ -75,6 +77,8 @@ EXPORT_SYMBOL(nf_register_hook);
+
+ void nf_unregister_hook(struct nf_hook_ops *reg)
+ {
++ BUG_ON(!ve_is_super(get_exec_env()));
++
+ mutex_lock(&nf_hook_mutex);
+ list_del_rcu(&reg->list);
+ mutex_unlock(&nf_hook_mutex);
+@@ -169,8 +173,6 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ struct net *net;
+
+ net = indev == NULL ? dev_net(outdev) : dev_net(indev);
+- if (net != &init_net)
+- return 1;
+ #endif
+
+ /* We may already have this, but read-locks nest anyway */
+diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c
+index 59bd8b9..25aca04 100644
+--- a/net/netfilter/nf_conntrack_acct.c
++++ b/net/netfilter/nf_conntrack_acct.c
+@@ -68,6 +68,9 @@ int nf_conntrack_acct_init(void)
+ {
+ int ret;
+
++ if (!ve_is_super(get_exec_env()))
++ return 0;
++
+ #ifdef CONFIG_NF_CT_ACCT
+ printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Plase use\n");
+ printk(KERN_WARNING "nf_conntrack.acct=1 kernel paramater, acct=1 nf_conntrack module option or\n");
+@@ -97,6 +100,8 @@ int nf_conntrack_acct_init(void)
+
+ void nf_conntrack_acct_fini(void)
+ {
++ if (!ve_is_super(get_exec_env()))
++ return;
+ #ifdef CONFIG_SYSCTL
+ unregister_sysctl_table(acct_sysctl_header);
+ #endif
+diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
+index 9d1830d..ee7a3a4 100644
+--- a/net/netfilter/nf_conntrack_core.c
++++ b/net/netfilter/nf_conntrack_core.c
+@@ -30,6 +30,8 @@
+ #include <linux/socket.h>
+ #include <linux/mm.h>
+
++#include <net/sock.h>
++
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_l3proto.h>
+ #include <net/netfilter/nf_conntrack_l4proto.h>
+@@ -54,8 +56,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
+ int nf_conntrack_max __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_conntrack_max);
+
++#ifndef CONFIG_VE_IPTABLES
+ struct hlist_head *nf_conntrack_hash __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_conntrack_hash);
++#endif
+
+ struct nf_conn nf_conntrack_untracked __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_conntrack_untracked);
+@@ -180,7 +184,14 @@ static void
+ destroy_conntrack(struct nf_conntrack *nfct)
+ {
+ struct nf_conn *ct = (struct nf_conn *)nfct;
++ struct nf_conn_help *help = nfct_help(ct);
++ struct nf_conntrack_l3proto *l3proto;
+ struct nf_conntrack_l4proto *l4proto;
++#ifdef CONFIG_VE_IPTABLES
++ struct ve_struct *old_ve;
++
++ old_ve = set_exec_env(ct->ct_owner_env);
++#endif
+
+ pr_debug("destroy_conntrack(%p)\n", ct);
+ NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
+@@ -189,10 +200,17 @@ destroy_conntrack(struct nf_conntrack *nfct)
+ nf_conntrack_event(IPCT_DESTROY, ct);
+ set_bit(IPS_DYING_BIT, &ct->status);
+
++ if (help && help->helper && help->helper->destroy)
++ help->helper->destroy(ct);
++
+ /* To make sure we don't get any weird locking issues here:
+ * destroy_conntrack() MUST NOT be called with a write lock
+ * to nf_conntrack_lock!!! -HW */
+ rcu_read_lock();
++ l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
++ if (l3proto && l3proto->destroy)
++ l3proto->destroy(ct);
++
+ l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+ if (l4proto && l4proto->destroy)
+ l4proto->destroy(ct);
+@@ -220,6 +238,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
+
+ pr_debug("destroy_conntrack: returning ct=%p to slab\n", ct);
+ nf_conntrack_free(ct);
++#ifdef CONFIG_VE_IPTABLES
++ (void)set_exec_env(old);
++#endif
+ }
+
+ static void death_by_timeout(unsigned long ul_conntrack)
+@@ -256,7 +277,7 @@ __nf_conntrack_find(const struct nf_conntrack_tuple *tuple)
+ * at least once for the stats anyway.
+ */
+ local_bh_disable();
+- hlist_for_each_entry_rcu(h, n, &nf_conntrack_hash[hash], hnode) {
++ hlist_for_each_entry_rcu(h, n, &ve_nf_conntrack_hash[hash], hnode) {
+ if (nf_ct_tuple_equal(tuple, &h->tuple)) {
+ NF_CT_STAT_INC(found);
+ local_bh_enable();
+@@ -295,9 +316,9 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct,
+ unsigned int repl_hash)
+ {
+ hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode,
+- &nf_conntrack_hash[hash]);
++ &ve_nf_conntrack_hash[hash]);
+ hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode,
+- &nf_conntrack_hash[repl_hash]);
++ &ve_nf_conntrack_hash[repl_hash]);
+ }
+
+ void nf_conntrack_hash_insert(struct nf_conn *ct)
+@@ -351,11 +372,11 @@ __nf_conntrack_confirm(struct sk_buff *skb)
+ /* See if there's one in the list already, including reverse:
+ NAT could have grabbed it without realizing, since we're
+ not in the hash. If there is, we lost race. */
+- hlist_for_each_entry(h, n, &nf_conntrack_hash[hash], hnode)
++ hlist_for_each_entry(h, n, &ve_nf_conntrack_hash[hash], hnode)
+ if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ &h->tuple))
+ goto out;
+- hlist_for_each_entry(h, n, &nf_conntrack_hash[repl_hash], hnode)
++ hlist_for_each_entry(h, n, &ve_nf_conntrack_hash[repl_hash], hnode)
+ if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
+ &h->tuple))
+ goto out;
+@@ -406,7 +427,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
+ * least once for the stats anyway.
+ */
+ rcu_read_lock_bh();
+- hlist_for_each_entry_rcu(h, n, &nf_conntrack_hash[hash], hnode) {
++ hlist_for_each_entry_rcu(h, n, &ve_nf_conntrack_hash[hash], hnode) {
+ if (nf_ct_tuplehash_to_ctrack(h) != ignored_conntrack &&
+ nf_ct_tuple_equal(tuple, &h->tuple)) {
+ NF_CT_STAT_INC(found);
+@@ -436,7 +457,7 @@ static noinline int early_drop(unsigned int hash)
+
+ rcu_read_lock();
+ for (i = 0; i < nf_conntrack_htable_size; i++) {
+- hlist_for_each_entry_rcu(h, n, &nf_conntrack_hash[hash],
++ hlist_for_each_entry_rcu(h, n, &ve_nf_conntrack_hash[hash],
+ hnode) {
+ tmp = nf_ct_tuplehash_to_ctrack(h);
+ if (!test_bit(IPS_ASSURED_BIT, &tmp->status))
+@@ -466,9 +487,11 @@ static noinline int early_drop(unsigned int hash)
+
+ struct nf_conn *nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
+ const struct nf_conntrack_tuple *repl,
++ struct user_beancounter *ub,
+ gfp_t gfp)
+ {
+ struct nf_conn *ct = NULL;
++ struct user_beancounter *old_ub;
+
+ if (unlikely(!nf_conntrack_hash_rnd_initted)) {
+ get_random_bytes(&nf_conntrack_hash_rnd, 4);
+@@ -476,25 +499,28 @@ struct nf_conn *nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
+ }
+
+ /* We don't want any race condition at early drop stage */
+- atomic_inc(&nf_conntrack_count);
++ atomic_inc(&ve_nf_conntrack_count);
+
+- if (nf_conntrack_max &&
+- unlikely(atomic_read(&nf_conntrack_count) > nf_conntrack_max)) {
++ if (ve_nf_conntrack_max &&
++ unlikely(atomic_read(&ve_nf_conntrack_count) >
++ ve_nf_conntrack_max)) {
+ unsigned int hash = hash_conntrack(orig);
+ if (!early_drop(hash)) {
+- atomic_dec(&nf_conntrack_count);
++ atomic_dec(&ve_nf_conntrack_count);
+ if (net_ratelimit())
+- printk(KERN_WARNING
+- "nf_conntrack: table full, dropping"
+- " packet.\n");
++ ve_printk(VE_LOG_BOTH, KERN_WARNING
++ "nf_conntrack: CT %d: table full, dropping"
++ " packet.\n", VEID(get_exec_env()));
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
++ old_ub = set_exec_ub(ub);
+ ct = kmem_cache_zalloc(nf_conntrack_cachep, gfp);
++ (void)set_exec_ub(old_ub);
+ if (ct == NULL) {
+ pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");
+- atomic_dec(&nf_conntrack_count);
++ atomic_dec(&ve_nf_conntrack_count);
+ return ERR_PTR(-ENOMEM);
+ }
+
+@@ -504,6 +530,9 @@ struct nf_conn *nf_conntrack_alloc(const struct nf_conntrack_tuple *orig,
+ /* Don't set timer yet: wait for confirmation */
+ setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
+ INIT_RCU_HEAD(&ct->rcu);
++#ifdef CONFIG_VE_IPTABLES
++ ct->ct_owner_env = get_exec_env();
++#endif
+
+ return ct;
+ }
+@@ -512,10 +541,16 @@ EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
+ static void nf_conntrack_free_rcu(struct rcu_head *head)
+ {
+ struct nf_conn *ct = container_of(head, struct nf_conn, rcu);
++#ifdef CONFIG_VE_IPTABLES
++ struct ve_struct *ve = set_exec_env(ct->ct_owner_env);
++#endif
+
+ nf_ct_ext_free(ct);
+ kmem_cache_free(nf_conntrack_cachep, ct);
+- atomic_dec(&nf_conntrack_count);
++ atomic_dec(&ve_nf_conntrack_count);
++#ifdef CONFIG_VE_IPTABLES
++ set_exec_env(ve);
++#endif
+ }
+
+ void nf_conntrack_free(struct nf_conn *ct)
+@@ -538,13 +573,20 @@ init_conntrack(const struct nf_conntrack_tuple *tuple,
+ struct nf_conn_help *help;
+ struct nf_conntrack_tuple repl_tuple;
+ struct nf_conntrack_expect *exp;
++ struct user_beancounter *ub = NULL;
+
+ if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {
+ pr_debug("Can't invert tuple.\n");
+ return NULL;
+ }
+
+- ct = nf_conntrack_alloc(tuple, &repl_tuple, GFP_ATOMIC);
++#ifdef CONFIG_BEANCOUNTERS
++ if (skb->dev != NULL) /* received skb */
++ ub = netdev_bc(skb->dev)->exec_ub;
++ else if (skb->sk != NULL) /* sent skb */
++ ub = sock_bc(skb->sk)->ub;
++#endif
++ ct = nf_conntrack_alloc(tuple, &repl_tuple, ub, GFP_ATOMIC);
+ if (ct == NULL || IS_ERR(ct)) {
+ pr_debug("Can't allocate conntrack.\n");
+ return (struct nf_conntrack_tuple_hash *)ct;
+@@ -593,7 +635,8 @@ init_conntrack(const struct nf_conntrack_tuple *tuple,
+ }
+
+ /* Overload tuple linked list to put us in unconfirmed list. */
+- hlist_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode, &unconfirmed);
++ hlist_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode,
++ &ve_unconfirmed);
+
+ spin_unlock_bh(&nf_conntrack_lock);
+
+@@ -947,13 +990,13 @@ get_next_corpse(int (*iter)(struct nf_conn *i, void *data),
+
+ spin_lock_bh(&nf_conntrack_lock);
+ for (; *bucket < nf_conntrack_htable_size; (*bucket)++) {
+- hlist_for_each_entry(h, n, &nf_conntrack_hash[*bucket], hnode) {
++ hlist_for_each_entry(h, n, &ve_nf_conntrack_hash[*bucket], hnode) {
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ if (iter(ct, data))
+ goto found;
+ }
+ }
+- hlist_for_each_entry(h, n, &unconfirmed, hnode) {
++ hlist_for_each_entry(h, n, &ve_unconfirmed, hnode) {
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ if (iter(ct, data))
+ set_bit(IPS_DYING_BIT, &ct->status);
+@@ -1008,7 +1051,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_flush);
+ supposed to kill the mall. */
+ void nf_conntrack_cleanup(void)
+ {
+- rcu_assign_pointer(ip_ct_attach, NULL);
++ struct ve_struct *ve = get_exec_env();
++
++ if (ve_is_super(ve))
++ rcu_assign_pointer(ip_ct_attach, NULL);
+
+ /* This makes sure all current packets have passed through
+ netfilter framework. Roll on, two-stage module
+@@ -1018,10 +1064,12 @@ void nf_conntrack_cleanup(void)
+ nf_ct_event_cache_flush();
+ i_see_dead_people:
+ nf_conntrack_flush();
+- if (atomic_read(&nf_conntrack_count) != 0) {
++ if (atomic_read(&ve_nf_conntrack_count) != 0) {
+ schedule();
+ goto i_see_dead_people;
+ }
++ if (!ve_is_super(ve))
++ goto skip_ct_cache;
+ /* wait until all references to nf_conntrack_untracked are dropped */
+ while (atomic_read(&nf_conntrack_untracked.ct_general.use) > 1)
+ schedule();
+@@ -1029,13 +1077,19 @@ void nf_conntrack_cleanup(void)
+ rcu_assign_pointer(nf_ct_destroy, NULL);
+
+ kmem_cache_destroy(nf_conntrack_cachep);
+- nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_vmalloc,
+- nf_conntrack_htable_size);
++skip_ct_cache:
++ nf_ct_free_hashtable(ve_nf_conntrack_hash, ve_nf_conntrack_vmalloc,
++ nf_conntrack_htable_size);
+
+ nf_conntrack_acct_fini();
+ nf_conntrack_expect_fini();
+ nf_conntrack_helper_fini();
+ nf_conntrack_proto_fini();
++
++ nf_ct_proto_generic_sysctl_cleanup();
++#ifdef CONFIG_VE_IPTABLES
++ kfree(ve->_nf_conntrack);
++#endif
+ }
+
+ struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced)
+@@ -1046,13 +1100,13 @@ struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced)
+ *vmalloced = 0;
+
+ size = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_head));
+- hash = (void*)__get_free_pages(GFP_KERNEL|__GFP_NOWARN,
++ hash = (void*)__get_free_pages(GFP_KERNEL_UBC|__GFP_NOWARN,
+ get_order(sizeof(struct hlist_head)
+ * size));
+ if (!hash) {
+ *vmalloced = 1;
+ printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n");
+- hash = vmalloc(sizeof(struct hlist_head) * size);
++ hash = ub_vmalloc(sizeof(struct hlist_head) * size);
+ }
+
+ if (hash)
+@@ -1094,8 +1148,8 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
+ */
+ spin_lock_bh(&nf_conntrack_lock);
+ for (i = 0; i < nf_conntrack_htable_size; i++) {
+- while (!hlist_empty(&nf_conntrack_hash[i])) {
+- h = hlist_entry(nf_conntrack_hash[i].first,
++ while (!hlist_empty(&ve_nf_conntrack_hash[i])) {
++ h = hlist_entry(ve_nf_conntrack_hash[i].first,
+ struct nf_conntrack_tuple_hash, hnode);
+ hlist_del_rcu(&h->hnode);
+ bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
+@@ -1103,12 +1157,12 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
+ }
+ }
+ old_size = nf_conntrack_htable_size;
+- old_vmalloced = nf_conntrack_vmalloc;
+- old_hash = nf_conntrack_hash;
++ old_vmalloced = ve_nf_conntrack_vmalloc;
++ old_hash = ve_nf_conntrack_hash;
+
+ nf_conntrack_htable_size = hashsize;
+- nf_conntrack_vmalloc = vmalloced;
+- nf_conntrack_hash = hash;
++ ve_nf_conntrack_vmalloc = vmalloced;
++ ve_nf_conntrack_hash = hash;
+ nf_conntrack_hash_rnd = rnd;
+ spin_unlock_bh(&nf_conntrack_lock);
+
+@@ -1120,53 +1174,82 @@ EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize);
+ module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint,
+ &nf_conntrack_htable_size, 0600);
+
+-int __init nf_conntrack_init(void)
++int nf_conntrack_init(void)
+ {
++ struct ve_struct *ve = get_exec_env();
+ int max_factor = 8;
+- int ret;
++ int ret = 0, i;
++
++ if (ve_is_super(ve)) {
++
++ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
++ * machine has 512 buckets. >= 1GB machines have 16384 buckets. */
++ if (!nf_conntrack_htable_size) {
++ nf_conntrack_htable_size
++ = (((num_physpages << PAGE_SHIFT) / 16384)
++ / sizeof(struct hlist_head));
++ if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
++ nf_conntrack_htable_size = 16384;
++ if (nf_conntrack_htable_size < 32)
++ nf_conntrack_htable_size = 32;
++
++ /* Use a max. factor of four by default to get the same
++ * max as with the old struct list_heads. When a table
++ * size is given we use the old value of 8 to avoid
++ * reducing the max. entries. */
++ max_factor = 4;
++ }
++ nf_conntrack_max = max_factor * nf_conntrack_htable_size;
+
+- /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
+- * machine has 512 buckets. >= 1GB machines have 16384 buckets. */
+- if (!nf_conntrack_htable_size) {
+- nf_conntrack_htable_size
+- = (((num_physpages << PAGE_SHIFT) / 16384)
+- / sizeof(struct hlist_head));
+- if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
+- nf_conntrack_htable_size = 16384;
+- if (nf_conntrack_htable_size < 32)
+- nf_conntrack_htable_size = 32;
+-
+- /* Use a max. factor of four by default to get the same max as
+- * with the old struct list_heads. When a table size is given
+- * we use the old value of 8 to avoid reducing the max.
+- * entries. */
+- max_factor = 4;
++ printk("nf_conntrack version %s (%u buckets, %d max)\n",
++ NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
++ nf_conntrack_max);
+ }
+- nf_conntrack_hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size,
+- &nf_conntrack_vmalloc);
+- if (!nf_conntrack_hash) {
++
++#ifdef CONFIG_VE_IPTABLES
++ ve->_nf_conntrack = kzalloc(sizeof(struct ve_nf_conntrack), GFP_KERNEL);
++ if (!ve->_nf_conntrack) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ve_nf_conntrack_max = nf_conntrack_max;
++ ve_nf_conntrack_checksum = nf_conntrack_checksum;
++ ve_nf_ct_expect_max = nf_ct_expect_max;
++ atomic_set(&ve_nf_conntrack_count, 0);
++ INIT_HLIST_HEAD(&ve_unconfirmed);
++#endif
++ ve_nf_conntrack_hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size,
++ &ve_nf_conntrack_vmalloc);
++ if (!ve_nf_conntrack_hash) {
+ printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
+ goto err_out;
+ }
+
+- nf_conntrack_max = max_factor * nf_conntrack_htable_size;
+-
+- printk("nf_conntrack version %s (%u buckets, %d max)\n",
+- NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
+- nf_conntrack_max);
+-
+- nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
++ if (ve_is_super(ve)) {
++ nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
+ sizeof(struct nf_conn),
+- 0, 0, NULL);
+- if (!nf_conntrack_cachep) {
+- printk(KERN_ERR "Unable to create nf_conn slab cache\n");
+- goto err_free_hash;
++ 0, SLAB_UBC, NULL);
++ if (!nf_conntrack_cachep) {
++ printk(KERN_ERR "Unable to create nf_conn slab cache\n");
++ goto err_free_hash;
++ }
+ }
+
+- ret = nf_conntrack_proto_init();
++ ret = nf_ct_proto_generic_sysctl_init();
+ if (ret < 0)
+ goto err_free_conntrack_slab;
+
++ ret = nf_conntrack_proto_init();
++ if (ret < 0)
++ goto err_generic_proto;
++
++ /* Don't NEED lock here, but good form anyway. */
++ spin_lock_bh(&nf_conntrack_lock);
++ for (i = 0; i < AF_MAX; i++)
++ ve_nf_ct_l3protos[i] = &nf_conntrack_l3proto_generic;
++ spin_unlock_bh(&nf_conntrack_lock);
++
+ ret = nf_conntrack_expect_init();
+ if (ret < 0)
+ goto out_fini_proto;
+@@ -1179,17 +1262,19 @@ int __init nf_conntrack_init(void)
+ if (ret < 0)
+ goto out_fini_helper;
+
+- /* For use by REJECT target */
+- rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach);
+- rcu_assign_pointer(nf_ct_destroy, destroy_conntrack);
++ if (ve_is_super(ve)) {
++ /* For use by REJECT target */
++ rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach);
++ rcu_assign_pointer(nf_ct_destroy, destroy_conntrack);
+
+- /* Set up fake conntrack:
+- - to never be deleted, not in any hashes */
+- atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
+- /* - and look it like as a confirmed connection */
+- set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
++ /* Set up fake conntrack:
++ - to never be deleted, not in any hashes */
++ atomic_set(&nf_conntrack_untracked.ct_general.use, 1);
++ /* - and look it like as a confirmed connection */
++ set_bit(IPS_CONFIRMED_BIT, &nf_conntrack_untracked.status);
++ }
+
+- return ret;
++ return 0;
+
+ out_fini_helper:
+ nf_conntrack_helper_fini();
+@@ -1197,11 +1282,18 @@ out_fini_expect:
+ nf_conntrack_expect_fini();
+ out_fini_proto:
+ nf_conntrack_proto_fini();
++err_generic_proto:
++ nf_ct_proto_generic_sysctl_cleanup();
+ err_free_conntrack_slab:
+- kmem_cache_destroy(nf_conntrack_cachep);
++ if (ve_is_super(ve))
++ kmem_cache_destroy(nf_conntrack_cachep);
+ err_free_hash:
+- nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_vmalloc,
++ nf_ct_free_hashtable(ve_nf_conntrack_hash, nf_conntrack_vmalloc,
+ nf_conntrack_htable_size);
+ err_out:
+- return -ENOMEM;
++#ifdef CONFIG_VE_IPTABLES
++ kfree(ve->_nf_conntrack);
++out:
++#endif
++ return ret;
+ }
+diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
+index 83c41ac..d0ddfb6 100644
+--- a/net/netfilter/nf_conntrack_ecache.c
++++ b/net/netfilter/nf_conntrack_ecache.c
+@@ -53,6 +53,9 @@ void nf_ct_deliver_cached_events(const struct nf_conn *ct)
+ {
+ struct nf_conntrack_ecache *ecache;
+
++ if (!ve_is_super(get_exec_env()))
++ return;
++
+ local_bh_disable();
+ ecache = &__get_cpu_var(nf_conntrack_ecache);
+ if (ecache->ct == ct)
+@@ -66,6 +69,9 @@ void __nf_ct_event_cache_init(struct nf_conn *ct)
+ {
+ struct nf_conntrack_ecache *ecache;
+
++ if (!ve_is_super(get_exec_env()))
++ return;
++
+ /* take care of delivering potentially old events */
+ ecache = &__get_cpu_var(nf_conntrack_ecache);
+ BUG_ON(ecache->ct == ct);
+@@ -84,6 +90,9 @@ void nf_ct_event_cache_flush(void)
+ struct nf_conntrack_ecache *ecache;
+ int cpu;
+
++ if (!ve_is_super(get_exec_env()))
++ return;
++
+ for_each_possible_cpu(cpu) {
+ ecache = &per_cpu(nf_conntrack_ecache, cpu);
+ if (ecache->ct)
+diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
+index e8f0dea..88f3fa8 100644
+--- a/net/netfilter/nf_conntrack_expect.c
++++ b/net/netfilter/nf_conntrack_expect.c
+@@ -28,17 +28,26 @@
+ #include <net/netfilter/nf_conntrack_helper.h>
+ #include <net/netfilter/nf_conntrack_tuple.h>
+
++#ifndef CONFIG_VE_IPTABLES
+ struct hlist_head *nf_ct_expect_hash __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_ct_expect_hash);
++#endif
+
+ unsigned int nf_ct_expect_hsize __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
+
+ static unsigned int nf_ct_expect_hash_rnd __read_mostly;
+-static unsigned int nf_ct_expect_count;
+ unsigned int nf_ct_expect_max __read_mostly;
+ static int nf_ct_expect_hash_rnd_initted __read_mostly;
++#ifdef CONFIG_VE_IPTABLES
++#define ve_nf_ct_expect_count (get_exec_env()->_nf_conntrack->_nf_ct_expect_count)
++#define ve_nf_ct_expect_vmalloc (get_exec_env()->_nf_conntrack->_nf_ct_expect_vmalloc)
++#else
++static unsigned int nf_ct_expect_count;
+ static int nf_ct_expect_vmalloc;
++#define ve_nf_ct_expect_count nf_ct_expect_count
++#define ve_nf_ct_expect_vmalloc nf_ct_expect_vmalloc
++#endif
+
+ static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
+
+@@ -51,7 +60,7 @@ void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
+ NF_CT_ASSERT(!timer_pending(&exp->timeout));
+
+ hlist_del_rcu(&exp->hnode);
+- nf_ct_expect_count--;
++ ve_nf_ct_expect_count--;
+
+ hlist_del(&exp->lnode);
+ master_help->expecting[exp->class]--;
+@@ -93,11 +102,11 @@ __nf_ct_expect_find(const struct nf_conntrack_tuple *tuple)
+ struct hlist_node *n;
+ unsigned int h;
+
+- if (!nf_ct_expect_count)
++ if (!ve_nf_ct_expect_count)
+ return NULL;
+
+ h = nf_ct_expect_dst_hash(tuple);
+- hlist_for_each_entry_rcu(i, n, &nf_ct_expect_hash[h], hnode) {
++ hlist_for_each_entry_rcu(i, n, &ve_nf_ct_expect_hash[h], hnode) {
+ if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
+ return i;
+ }
+@@ -130,11 +139,11 @@ nf_ct_find_expectation(const struct nf_conntrack_tuple *tuple)
+ struct hlist_node *n;
+ unsigned int h;
+
+- if (!nf_ct_expect_count)
++ if (!ve_nf_ct_expect_count)
+ return NULL;
+
+ h = nf_ct_expect_dst_hash(tuple);
+- hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) {
++ hlist_for_each_entry(i, n, &ve_nf_ct_expect_hash[h], hnode) {
+ if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
+ nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {
+ exp = i;
+@@ -308,7 +317,7 @@ void nf_ct_expect_put(struct nf_conntrack_expect *exp)
+ }
+ EXPORT_SYMBOL_GPL(nf_ct_expect_put);
+
+-static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
++void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
+ {
+ struct nf_conn_help *master_help = nfct_help(exp->master);
+ const struct nf_conntrack_expect_policy *p;
+@@ -319,8 +328,8 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
+ hlist_add_head(&exp->lnode, &master_help->expectations);
+ master_help->expecting[exp->class]++;
+
+- hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
+- nf_ct_expect_count++;
++ hlist_add_head_rcu(&exp->hnode, &ve_nf_ct_expect_hash[h]);
++ ve_nf_ct_expect_count++;
+
+ setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
+ (unsigned long)exp);
+@@ -331,6 +340,7 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
+ atomic_inc(&exp->use);
+ NF_CT_STAT_INC(expect_create);
+ }
++EXPORT_SYMBOL_GPL(nf_ct_expect_insert);
+
+ /* Race with expectations being used means we could have none to find; OK. */
+ static void evict_oldest_expect(struct nf_conn *master,
+@@ -383,7 +393,7 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
+ goto out;
+ }
+ h = nf_ct_expect_dst_hash(&expect->tuple);
+- hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) {
++ hlist_for_each_entry(i, n, &ve_nf_ct_expect_hash[h], hnode) {
+ if (expect_matches(i, expect)) {
+ /* Refresh timer: if it's dying, ignore.. */
+ if (refresh_timer(i)) {
+@@ -406,7 +416,7 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
+ }
+ }
+
+- if (nf_ct_expect_count >= nf_ct_expect_max) {
++ if (ve_nf_ct_expect_count >= ve_nf_ct_expect_max) {
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "nf_conntrack: expectation table full\n");
+@@ -434,7 +444,7 @@ static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
+ struct hlist_node *n;
+
+ for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
+- n = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
++ n = rcu_dereference(ve_nf_ct_expect_hash[st->bucket].first);
+ if (n)
+ return n;
+ }
+@@ -450,7 +460,7 @@ static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
+ while (head == NULL) {
+ if (++st->bucket >= nf_ct_expect_hsize)
+ return NULL;
+- head = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
++ head = rcu_dereference(ve_nf_ct_expect_hash[st->bucket].first);
+ }
+ return head;
+ }
+@@ -537,12 +547,13 @@ static const struct file_operations exp_file_ops = {
+ };
+ #endif /* CONFIG_PROC_FS */
+
+-static int __init exp_proc_init(void)
++static int exp_proc_init(void)
+ {
+ #ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc;
+
+- proc = proc_net_fops_create(&init_net, "nf_conntrack_expect", 0440, &exp_file_ops);
++ proc = proc_net_fops_create(get_exec_env()->ve_netns,
++ "nf_conntrack_expect", 0440, &exp_file_ops);
+ if (!proc)
+ return -ENOMEM;
+ #endif /* CONFIG_PROC_FS */
+@@ -552,13 +563,13 @@ static int __init exp_proc_init(void)
+ static void exp_proc_remove(void)
+ {
+ #ifdef CONFIG_PROC_FS
+- proc_net_remove(&init_net, "nf_conntrack_expect");
++ proc_net_remove(get_exec_env()->ve_netns, "nf_conntrack_expect");
+ #endif /* CONFIG_PROC_FS */
+ }
+
+ module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600);
+
+-int __init nf_conntrack_expect_init(void)
++int nf_conntrack_expect_init(void)
+ {
+ int err = -ENOMEM;
+
+@@ -569,16 +580,20 @@ int __init nf_conntrack_expect_init(void)
+ }
+ nf_ct_expect_max = nf_ct_expect_hsize * 4;
+
+- nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
+- &nf_ct_expect_vmalloc);
+- if (nf_ct_expect_hash == NULL)
++ ve_nf_ct_expect_count = 0;
++ ve_nf_ct_expect_max = nf_ct_expect_max;
++ ve_nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
++ &ve_nf_ct_expect_vmalloc);
++ if (ve_nf_ct_expect_hash == NULL)
+ goto err1;
+
+- nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
++ if (ve_is_super(get_exec_env())) {
++ nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
+ sizeof(struct nf_conntrack_expect),
+- 0, 0, NULL);
+- if (!nf_ct_expect_cachep)
+- goto err2;
++ 0, SLAB_UBC, NULL);
++ if (!nf_ct_expect_cachep)
++ goto err2;
++ }
+
+ err = exp_proc_init();
+ if (err < 0)
+@@ -587,9 +602,10 @@ int __init nf_conntrack_expect_init(void)
+ return 0;
+
+ err3:
+- kmem_cache_destroy(nf_ct_expect_cachep);
++ if (ve_is_super(get_exec_env()))
++ kmem_cache_destroy(nf_ct_expect_cachep);
+ err2:
+- nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc,
++ nf_ct_free_hashtable(ve_nf_ct_expect_hash, ve_nf_ct_expect_vmalloc,
+ nf_ct_expect_hsize);
+ err1:
+ return err;
+@@ -598,7 +614,8 @@ err1:
+ void nf_conntrack_expect_fini(void)
+ {
+ exp_proc_remove();
+- kmem_cache_destroy(nf_ct_expect_cachep);
+- nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc,
++ if (ve_is_super(get_exec_env()))
++ kmem_cache_destroy(nf_ct_expect_cachep);
++ nf_ct_free_hashtable(ve_nf_ct_expect_hash, ve_nf_ct_expect_vmalloc,
+ nf_ct_expect_hsize);
+ }
+diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
+index 8e0b4c8..a483342 100644
+--- a/net/netfilter/nf_conntrack_helper.c
++++ b/net/netfilter/nf_conntrack_helper.c
+@@ -30,10 +30,17 @@
+ #include <net/netfilter/nf_conntrack_extend.h>
+
+ static DEFINE_MUTEX(nf_ct_helper_mutex);
+-static struct hlist_head *nf_ct_helper_hash __read_mostly;
+ static unsigned int nf_ct_helper_hsize __read_mostly;
+ static unsigned int nf_ct_helper_count __read_mostly;
++#ifdef CONFIG_VE_IPTABLES
++#define ve_nf_ct_helper_hash (get_exec_env()->_nf_conntrack->_nf_ct_helper_hash)
++#define ve_nf_ct_helper_vmalloc (get_exec_env()->_nf_conntrack->_nf_ct_helper_vmalloc)
++#else
++static struct hlist_head *nf_ct_helper_hash __read_mostly;
+ static int nf_ct_helper_vmalloc;
++#define ve_nf_ct_helper_hash nf_ct_helper_hash
++#define ve_nf_ct_helper_vmalloc nf_ct_helper_vmalloc
++#endif
+
+
+ /* Stupid hash, but collision free for the default registrations of the
+@@ -56,7 +63,7 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
+ return NULL;
+
+ h = helper_hash(tuple);
+- hlist_for_each_entry_rcu(helper, n, &nf_ct_helper_hash[h], hnode) {
++ hlist_for_each_entry_rcu(helper, n, &ve_nf_ct_helper_hash[h], hnode) {
+ if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
+ return helper;
+ }
+@@ -72,7 +79,7 @@ __nf_conntrack_helper_find_byname(const char *name)
+ unsigned int i;
+
+ for (i = 0; i < nf_ct_helper_hsize; i++) {
+- hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
++ hlist_for_each_entry_rcu(h, n, &ve_nf_ct_helper_hash[i], hnode) {
+ if (!strcmp(h->name, name))
+ return h;
+ }
+@@ -115,7 +122,7 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
+ BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
+
+ mutex_lock(&nf_ct_helper_mutex);
+- hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
++ hlist_add_head_rcu(&me->hnode, &ve_nf_ct_helper_hash[h]);
+ nf_ct_helper_count++;
+ mutex_unlock(&nf_ct_helper_mutex);
+
+@@ -145,7 +152,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
+ /* Get rid of expectations */
+ for (i = 0; i < nf_ct_expect_hsize; i++) {
+ hlist_for_each_entry_safe(exp, n, next,
+- &nf_ct_expect_hash[i], hnode) {
++ &ve_nf_ct_expect_hash[i], hnode) {
+ struct nf_conn_help *help = nfct_help(exp->master);
+ if ((help->helper == me || exp->helper == me) &&
+ del_timer(&exp->timeout)) {
+@@ -156,10 +163,10 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
+ }
+
+ /* Get rid of expecteds, set helpers to NULL. */
+- hlist_for_each_entry(h, n, &unconfirmed, hnode)
++ hlist_for_each_entry(h, n, &ve_unconfirmed, hnode)
+ unhelp(h, me);
+ for (i = 0; i < nf_conntrack_htable_size; i++) {
+- hlist_for_each_entry(h, n, &nf_conntrack_hash[i], hnode)
++ hlist_for_each_entry(h, n, &ve_nf_conntrack_hash[i], hnode)
+ unhelp(h, me);
+ }
+ spin_unlock_bh(&nf_conntrack_lock);
+@@ -177,26 +184,29 @@ int nf_conntrack_helper_init(void)
+ int err;
+
+ nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
+- nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize,
+- &nf_ct_helper_vmalloc);
+- if (!nf_ct_helper_hash)
++ ve_nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize,
++ &ve_nf_ct_helper_vmalloc);
++ if (!ve_nf_ct_helper_hash)
+ return -ENOMEM;
+
+- err = nf_ct_extend_register(&helper_extend);
+- if (err < 0)
+- goto err1;
++ if (ve_is_super(get_exec_env())) {
++ err = nf_ct_extend_register(&helper_extend);
++ if (err < 0)
++ goto err1;
++ }
+
+ return 0;
+
+ err1:
+- nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc,
++ nf_ct_free_hashtable(ve_nf_ct_helper_hash, ve_nf_ct_helper_vmalloc,
+ nf_ct_helper_hsize);
+ return err;
+ }
+
+ void nf_conntrack_helper_fini(void)
+ {
+- nf_ct_extend_unregister(&helper_extend);
+- nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_vmalloc,
++ if (ve_is_super(get_exec_env()))
++ nf_ct_extend_unregister(&helper_extend);
++ nf_ct_free_hashtable(ve_nf_ct_helper_hash, ve_nf_ct_helper_vmalloc,
+ nf_ct_helper_hsize);
+ }
+diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
+index a875203..a09d0e1 100644
+--- a/net/netfilter/nf_conntrack_netlink.c
++++ b/net/netfilter/nf_conntrack_netlink.c
+@@ -27,6 +27,7 @@
+ #include <linux/spinlock.h>
+ #include <linux/interrupt.h>
+ #include <linux/notifier.h>
++#include <net/sock.h>
+
+ #include <linux/netfilter.h>
+ #include <net/netlink.h>
+@@ -45,6 +46,8 @@
+
+ #include <linux/netfilter/nfnetlink.h>
+ #include <linux/netfilter/nfnetlink_conntrack.h>
++#include <bc/beancounter.h>
++#include <bc/sock.h>
+
+ MODULE_LICENSE("GPL");
+
+@@ -549,7 +552,8 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+ last = (struct nf_conn *)cb->args[1];
+ for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
+ restart:
+- hlist_for_each_entry_rcu(h, n, &nf_conntrack_hash[cb->args[0]],
++ hlist_for_each_entry_rcu(h, n,
++ &ve_nf_conntrack_hash[cb->args[0]],
+ hnode) {
+ if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
+ continue;
+@@ -1118,14 +1122,15 @@ static int
+ ctnetlink_create_conntrack(struct nlattr *cda[],
+ struct nf_conntrack_tuple *otuple,
+ struct nf_conntrack_tuple *rtuple,
+- struct nf_conn *master_ct)
++ struct nf_conn *master_ct,
++ struct user_beancounter *ub)
+ {
+ struct nf_conn *ct;
+ int err = -EINVAL;
+ struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper;
+
+- ct = nf_conntrack_alloc(otuple, rtuple, GFP_KERNEL);
++ ct = nf_conntrack_alloc(otuple, rtuple, ub, GFP_KERNEL);
+ if (ct == NULL || IS_ERR(ct))
+ return -ENOMEM;
+
+@@ -1241,11 +1246,19 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
+
+ spin_unlock_bh(&nf_conntrack_lock);
+ err = -ENOENT;
+- if (nlh->nlmsg_flags & NLM_F_CREATE)
++ if (nlh->nlmsg_flags & NLM_F_CREATE) {
++ struct user_beancounter *ub = NULL;
++
++#ifdef CONFIG_BEANCOUNTERS
++ if (skb->sk)
++ ub = sock_bc(skb->sk)->ub;
++#endif
+ err = ctnetlink_create_conntrack(cda,
+ &otuple,
+ &rtuple,
+- master_ct);
++ master_ct,
++ ub);
++ }
+ if (err < 0 && master_ct)
+ nf_ct_put(master_ct);
+
+@@ -1467,7 +1480,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
+ last = (struct nf_conntrack_expect *)cb->args[1];
+ for (; cb->args[0] < nf_ct_expect_hsize; cb->args[0]++) {
+ restart:
+- hlist_for_each_entry(exp, n, &nf_ct_expect_hash[cb->args[0]],
++ hlist_for_each_entry(exp, n, &ve_nf_ct_expect_hash[cb->args[0]],
+ hnode) {
+ if (l3proto && exp->tuple.src.l3num != l3proto)
+ continue;
+@@ -1613,7 +1626,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
+ }
+ for (i = 0; i < nf_ct_expect_hsize; i++) {
+ hlist_for_each_entry_safe(exp, n, next,
+- &nf_ct_expect_hash[i],
++ &ve_nf_ct_expect_hash[i],
+ hnode) {
+ m_help = nfct_help(exp->master);
+ if (m_help->helper == h
+@@ -1629,7 +1642,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
+ spin_lock_bh(&nf_conntrack_lock);
+ for (i = 0; i < nf_ct_expect_hsize; i++) {
+ hlist_for_each_entry_safe(exp, n, next,
+- &nf_ct_expect_hash[i],
++ &ve_nf_ct_expect_hash[i],
+ hnode) {
+ if (del_timer(&exp->timeout)) {
+ nf_ct_unlink_expect(exp);
+diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
+index a49fc93..67c53a7 100644
+--- a/net/netfilter/nf_conntrack_proto.c
++++ b/net/netfilter/nf_conntrack_proto.c
+@@ -28,7 +28,7 @@
+ #include <net/netfilter/nf_conntrack_l4proto.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+
+-static struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
++struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
+ struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
+ EXPORT_SYMBOL_GPL(nf_ct_l3protos);
+
+@@ -40,7 +40,8 @@ nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_path *path,
+ struct ctl_table *table, unsigned int *users)
+ {
+ if (*header == NULL) {
+- *header = register_sysctl_paths(path, table);
++ *header = register_net_sysctl_table(get_exec_env()->ve_netns,
++ path, table);
+ if (*header == NULL)
+ return -ENOMEM;
+ }
+@@ -56,7 +57,7 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header,
+ if (users != NULL && --*users > 0)
+ return;
+
+- unregister_sysctl_table(*header);
++ unregister_net_sysctl_table(*header);
+ *header = NULL;
+ }
+ #endif
+@@ -64,10 +65,10 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header,
+ struct nf_conntrack_l4proto *
+ __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
+ {
+- if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
+- return &nf_conntrack_l4proto_generic;
++ if (unlikely(l3proto >= AF_MAX || ve_nf_ct_protos[l3proto] == NULL))
++ return ve_nf_conntrack_l4proto_generic;
+
+- return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
++ return rcu_dereference(ve_nf_ct_protos[l3proto][l4proto]);
+ }
+ EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
+
+@@ -81,7 +82,7 @@ nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
+ rcu_read_lock();
+ p = __nf_ct_l4proto_find(l3proto, l4proto);
+ if (!try_module_get(p->me))
+- p = &nf_conntrack_l4proto_generic;
++ p = ve_nf_conntrack_l4proto_generic;
+ rcu_read_unlock();
+
+ return p;
+@@ -188,7 +189,8 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
+ return -EBUSY;
+
+ mutex_lock(&nf_ct_proto_mutex);
+- if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
++ if (ve_nf_ct_l3protos[proto->l3proto] !=
++ &nf_conntrack_l3proto_generic) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+@@ -197,7 +199,7 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
+ if (ret < 0)
+ goto out_unlock;
+
+- rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
++ rcu_assign_pointer(ve_nf_ct_l3protos[proto->l3proto], proto);
+
+ out_unlock:
+ mutex_unlock(&nf_ct_proto_mutex);
+@@ -210,8 +212,8 @@ void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
+ BUG_ON(proto->l3proto >= AF_MAX);
+
+ mutex_lock(&nf_ct_proto_mutex);
+- BUG_ON(nf_ct_l3protos[proto->l3proto] != proto);
+- rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
++ BUG_ON(ve_nf_ct_l3protos[proto->l3proto] != proto);
++ rcu_assign_pointer(ve_nf_ct_l3protos[proto->l3proto],
+ &nf_conntrack_l3proto_generic);
+ nf_ct_l3proto_unregister_sysctl(proto);
+ mutex_unlock(&nf_ct_proto_mutex);
+@@ -279,7 +281,7 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
+ return -EBUSY;
+
+ mutex_lock(&nf_ct_proto_mutex);
+- if (!nf_ct_protos[l4proto->l3proto]) {
++ if (!ve_nf_ct_protos[l4proto->l3proto]) {
+ /* l3proto may be loaded latter. */
+ struct nf_conntrack_l4proto **proto_array;
+ int i;
+@@ -293,10 +295,10 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
+ }
+
+ for (i = 0; i < MAX_NF_CT_PROTO; i++)
+- proto_array[i] = &nf_conntrack_l4proto_generic;
+- nf_ct_protos[l4proto->l3proto] = proto_array;
+- } else if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto] !=
+- &nf_conntrack_l4proto_generic) {
++ proto_array[i] = ve_nf_conntrack_l4proto_generic;
++ ve_nf_ct_protos[l4proto->l3proto] = proto_array;
++ } else if (ve_nf_ct_protos[l4proto->l3proto][l4proto->l4proto] !=
++ ve_nf_conntrack_l4proto_generic) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+@@ -305,7 +307,7 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
+ if (ret < 0)
+ goto out_unlock;
+
+- rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
++ rcu_assign_pointer(ve_nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
+ l4proto);
+
+ out_unlock:
+@@ -319,9 +321,9 @@ void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
+ BUG_ON(l4proto->l3proto >= PF_MAX);
+
+ mutex_lock(&nf_ct_proto_mutex);
+- BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto);
+- rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
+- &nf_conntrack_l4proto_generic);
++ BUG_ON(ve_nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto);
++ rcu_assign_pointer(ve_nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
++ ve_nf_conntrack_l4proto_generic);
+ nf_ct_l4proto_unregister_sysctl(l4proto);
+ mutex_unlock(&nf_ct_proto_mutex);
+
+@@ -337,12 +339,12 @@ int nf_conntrack_proto_init(void)
+ unsigned int i;
+ int err;
+
+- err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic);
++ err = nf_ct_l4proto_register_sysctl(ve_nf_conntrack_l4proto_generic);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < AF_MAX; i++)
+- rcu_assign_pointer(nf_ct_l3protos[i],
++ rcu_assign_pointer(ve_nf_ct_l3protos[i],
+ &nf_conntrack_l3proto_generic);
+ return 0;
+ }
+@@ -351,9 +353,9 @@ void nf_conntrack_proto_fini(void)
+ {
+ unsigned int i;
+
+- nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic);
++ nf_ct_l4proto_unregister_sysctl(ve_nf_conntrack_l4proto_generic);
+
+ /* free l3proto protocol tables */
+ for (i = 0; i < PF_MAX; i++)
+- kfree(nf_ct_protos[i]);
++ kfree(ve_nf_ct_protos[i]);
+ }
+diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c
+index e31b0e7..24b0e29 100644
+--- a/net/netfilter/nf_conntrack_proto_generic.c
++++ b/net/netfilter/nf_conntrack_proto_generic.c
+@@ -8,6 +8,7 @@
+
+ #include <linux/types.h>
+ #include <linux/jiffies.h>
++#include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/netfilter.h>
+ #include <net/netfilter/nf_conntrack_l4proto.h>
+@@ -48,7 +49,7 @@ static int packet(struct nf_conn *ct,
+ int pf,
+ unsigned int hooknum)
+ {
+- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_generic_timeout);
++ nf_ct_refresh_acct(ct, ctinfo, skb, ve_nf_ct_generic_timeout);
+ return NF_ACCEPT;
+ }
+
+@@ -107,3 +108,64 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly =
+ #endif
+ #endif
+ };
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++int nf_ct_proto_generic_sysctl_init(void)
++{
++ struct nf_conntrack_l4proto *generic;
++
++ if (ve_is_super(get_exec_env())) {
++ generic = &nf_conntrack_l4proto_generic;
++ goto out;
++ }
++
++ generic = kmemdup(&nf_conntrack_l4proto_generic,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (generic == NULL)
++ goto no_mem_ct;
++
++ generic->ctl_table_header = &ve_generic_sysctl_header;
++ generic->ctl_table = kmemdup(generic_sysctl_table,
++ sizeof(generic_sysctl_table), GFP_KERNEL);
++ if (generic->ctl_table == NULL)
++ goto no_mem_sys;
++
++ generic->ctl_table[0].data = &ve_nf_ct_generic_timeout;
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ generic->ctl_compat_table_header = ve_generic_compat_sysctl_header;
++ generic->ctl_compat_table = kmemdup(generic_compat_sysctl_table,
++ sizeof(generic_compat_sysctl_table), GFP_KERNEL);
++ if (generic->ctl_compat_table == NULL)
++ goto no_mem_compat;
++ generic->ctl_compat_table[0].data = &ve_nf_ct_generic_timeout;
++#endif
++out:
++ ve_nf_ct_generic_timeout = nf_ct_generic_timeout;
++
++ ve_nf_conntrack_l4proto_generic = generic;
++ return 0;
++
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++no_mem_compat:
++ kfree(generic->ctl_table);
++#endif
++no_mem_sys:
++ kfree(generic);
++no_mem_ct:
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(nf_ct_proto_generic_sysctl_init);
++
++void nf_ct_proto_generic_sysctl_cleanup(void)
++{
++ if (!ve_is_super(get_exec_env())) {
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(ve_nf_conntrack_l4proto_generic->ctl_compat_table);
++#endif
++ kfree(ve_nf_conntrack_l4proto_generic->ctl_table);
++
++ kfree(ve_nf_conntrack_l4proto_generic);
++ }
++}
++EXPORT_SYMBOL(nf_ct_proto_generic_sysctl_cleanup);
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
+diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
+index 6f61261..844ae68 100644
+--- a/net/netfilter/nf_conntrack_proto_tcp.c
++++ b/net/netfilter/nf_conntrack_proto_tcp.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include <linux/types.h>
++#include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/module.h>
+ #include <linux/in.h>
+@@ -666,7 +667,7 @@ static bool tcp_in_window(const struct nf_conn *ct,
+ } else {
+ res = false;
+ if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL ||
+- nf_ct_tcp_be_liberal)
++ ve_nf_ct_tcp_be_liberal)
+ res = true;
+ if (!res && LOG_INVALID(IPPROTO_TCP))
+ nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+@@ -957,15 +958,15 @@ static int tcp_packet(struct nf_conn *ct,
+ && new_state == TCP_CONNTRACK_FIN_WAIT)
+ ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
+
+- if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans &&
+- tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans)
+- timeout = nf_ct_tcp_timeout_max_retrans;
++ if (ct->proto.tcp.retrans >= ve_nf_ct_tcp_max_retrans &&
++ tcp_timeouts[new_state] > ve_nf_ct_tcp_timeout_max_retrans)
++ timeout = ve_nf_ct_tcp_timeout_max_retrans;
+ else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) &
+ IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
+- tcp_timeouts[new_state] > nf_ct_tcp_timeout_unacknowledged)
+- timeout = nf_ct_tcp_timeout_unacknowledged;
++ tcp_timeouts[new_state] > ve_nf_ct_tcp_timeout_unacknowledged)
++ timeout = ve_nf_ct_tcp_timeout_unacknowledged;
+ else
+- timeout = tcp_timeouts[new_state];
++ timeout = ve_nf_ct_tcp_timeouts[new_state];
+ write_unlock_bh(&tcp_lock);
+
+ nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
+@@ -1033,7 +1034,7 @@ static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb,
+
+ tcp_options(skb, dataoff, th, &ct->proto.tcp.seen[0]);
+ ct->proto.tcp.seen[1].flags = 0;
+- } else if (nf_ct_tcp_loose == 0) {
++ } else if (ve_nf_ct_tcp_loose == 0) {
+ /* Don't try to pick up connections. */
+ return false;
+ } else {
+@@ -1435,3 +1436,117 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly =
+ #endif
+ };
+ EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp6);
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++int nf_ct_proto_tcp_sysctl_init(void)
++{
++ struct nf_conntrack_l4proto *tcp4, *tcp6;
++
++ if (ve_is_super(get_exec_env())) {
++ tcp4 = &nf_conntrack_l4proto_tcp4;
++ tcp6 = &nf_conntrack_l4proto_tcp6;
++ goto out;
++ }
++
++ tcp4 = kmemdup(&nf_conntrack_l4proto_tcp4,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (tcp4 == NULL)
++ goto no_mem_ct4;
++
++ tcp4->ctl_table_users = &ve_tcp_sysctl_table_users;
++ tcp4->ctl_table_header = &ve_tcp_sysctl_header;
++ tcp4->ctl_table = kmemdup(tcp_sysctl_table,
++ sizeof(tcp_sysctl_table), GFP_KERNEL);
++ if (tcp4->ctl_table == NULL)
++ goto no_mem_sys;
++
++ tcp4->ctl_table[0].data = &ve_nf_ct_tcp_timeouts[1];
++ tcp4->ctl_table[1].data = &ve_nf_ct_tcp_timeouts[2];
++ tcp4->ctl_table[2].data = &ve_nf_ct_tcp_timeouts[3];
++ tcp4->ctl_table[3].data = &ve_nf_ct_tcp_timeouts[4];
++ tcp4->ctl_table[4].data = &ve_nf_ct_tcp_timeouts[5];
++ tcp4->ctl_table[5].data = &ve_nf_ct_tcp_timeouts[6];
++ tcp4->ctl_table[6].data = &ve_nf_ct_tcp_timeouts[7];
++ tcp4->ctl_table[7].data = &ve_nf_ct_tcp_timeouts[8];
++ tcp4->ctl_table[8].data = &ve_nf_ct_tcp_timeout_max_retrans;
++ tcp4->ctl_table[9].data = &ve_nf_ct_tcp_timeout_unacknowledged;
++ tcp4->ctl_table[10].data = &ve_nf_ct_tcp_loose;
++ tcp4->ctl_table[11].data = &ve_nf_ct_tcp_be_liberal;
++ tcp4->ctl_table[12].data = &ve_nf_ct_tcp_max_retrans;
++
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ tcp4->ctl_compat_table_header = ve_tcp_compat_sysctl_header;
++ tcp4->ctl_compat_table = kmemdup(tcp_compat_sysctl_table,
++ sizeof(tcp_compat_sysctl_table), GFP_KERNEL);
++ if (tcp4->ctl_compat_table == NULL)
++ goto no_mem_compat;
++
++ tcp4->ctl_compat_table[0].data = &ve_nf_ct_tcp_timeouts[1];
++ tcp4->ctl_compat_table[1].data = &ve_nf_ct_tcp_timeouts[2];
++ tcp4->ctl_compat_table[2].data = &ve_nf_ct_tcp_timeouts[3];
++ tcp4->ctl_compat_table[3].data = &ve_nf_ct_tcp_timeouts[4];
++ tcp4->ctl_compat_table[4].data = &ve_nf_ct_tcp_timeouts[5];
++ tcp4->ctl_compat_table[5].data = &ve_nf_ct_tcp_timeouts[6];
++ tcp4->ctl_compat_table[6].data = &ve_nf_ct_tcp_timeouts[7];
++ tcp4->ctl_compat_table[7].data = &ve_nf_ct_tcp_timeouts[8];
++ tcp4->ctl_compat_table[8].data = &ve_nf_ct_tcp_timeout_max_retrans;
++ tcp4->ctl_compat_table[9].data = &ve_nf_ct_tcp_loose;
++ tcp4->ctl_compat_table[10].data = &ve_nf_ct_tcp_be_liberal;
++ tcp4->ctl_compat_table[11].data = &ve_nf_ct_tcp_max_retrans;
++#endif
++
++ tcp6 = kmemdup(&nf_conntrack_l4proto_tcp6,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (!tcp6)
++ goto no_mem_ct6;
++
++ tcp6->ctl_table_users = &ve_tcp_sysctl_table_users;
++ tcp6->ctl_table_header = &ve_tcp_sysctl_header;
++ tcp6->ctl_table = tcp4->ctl_table;
++out:
++ ve_nf_ct_tcp_timeouts[1] = tcp_timeouts[TCP_CONNTRACK_SYN_SENT];
++ ve_nf_ct_tcp_timeouts[2] = tcp_timeouts[TCP_CONNTRACK_SYN_RECV];
++ ve_nf_ct_tcp_timeouts[3] = tcp_timeouts[TCP_CONNTRACK_ESTABLISHED];
++ ve_nf_ct_tcp_timeouts[4] = tcp_timeouts[TCP_CONNTRACK_FIN_WAIT];
++ ve_nf_ct_tcp_timeouts[5] = tcp_timeouts[TCP_CONNTRACK_CLOSE_WAIT];
++ ve_nf_ct_tcp_timeouts[6] = tcp_timeouts[TCP_CONNTRACK_LAST_ACK];
++ ve_nf_ct_tcp_timeouts[7] = tcp_timeouts[TCP_CONNTRACK_TIME_WAIT];
++ ve_nf_ct_tcp_timeouts[8] = tcp_timeouts[TCP_CONNTRACK_CLOSE];
++ ve_nf_ct_tcp_timeout_max_retrans = nf_ct_tcp_timeout_max_retrans;
++ ve_nf_ct_tcp_timeout_unacknowledged = nf_ct_tcp_timeout_unacknowledged;
++ ve_nf_ct_tcp_loose = nf_ct_tcp_loose;
++ ve_nf_ct_tcp_be_liberal = nf_ct_tcp_be_liberal;
++ ve_nf_ct_tcp_max_retrans = nf_ct_tcp_max_retrans;
++
++ ve_nf_conntrack_l4proto_tcp4 = tcp4;
++ ve_nf_conntrack_l4proto_tcp6 = tcp6;
++ return 0;
++
++no_mem_ct6:
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(tcp4->ctl_compat_table);
++no_mem_compat:
++#endif
++ kfree(tcp4->ctl_table);
++no_mem_sys:
++ kfree(tcp4);
++no_mem_ct4:
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(nf_ct_proto_tcp_sysctl_init);
++
++void nf_ct_proto_tcp_sysctl_cleanup(void)
++{
++ if (!ve_is_super(get_exec_env())) {
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(ve_nf_conntrack_l4proto_tcp4->ctl_compat_table);
++#endif
++ kfree(ve_nf_conntrack_l4proto_tcp4->ctl_table);
++ kfree(ve_nf_conntrack_l4proto_tcp4);
++
++ kfree(ve_nf_conntrack_l4proto_tcp6);
++ }
++}
++EXPORT_SYMBOL(nf_ct_proto_tcp_sysctl_cleanup);
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
++
+diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
+index 8b21762..b01823e 100644
+--- a/net/netfilter/nf_conntrack_proto_udp.c
++++ b/net/netfilter/nf_conntrack_proto_udp.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include <linux/types.h>
++#include <linux/sched.h>
+ #include <linux/timer.h>
+ #include <linux/module.h>
+ #include <linux/udp.h>
+@@ -72,12 +73,13 @@ static int udp_packet(struct nf_conn *ct,
+ /* If we've seen traffic both ways, this is some kind of UDP
+ stream. Extend timeout. */
+ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
+- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream);
++ nf_ct_refresh_acct(ct, ctinfo, skb,
++ ve_nf_ct_udp_timeout_stream);
+ /* Also, more likely to be important, and not a probe */
+ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
+ nf_conntrack_event_cache(IPCT_STATUS, skb);
+ } else
+- nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);
++ nf_ct_refresh_acct(ct, ctinfo, skb, ve_nf_ct_udp_timeout);
+
+ return NF_ACCEPT;
+ }
+@@ -229,3 +231,85 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly =
+ #endif
+ };
+ EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6);
++
++#if defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL)
++int nf_ct_proto_udp_sysctl_init(void)
++{
++ struct nf_conntrack_l4proto *udp4, *udp6;
++
++ if (ve_is_super(get_exec_env())) {
++ udp4 = &nf_conntrack_l4proto_udp4;
++ udp6 = &nf_conntrack_l4proto_udp6;
++ goto out;
++ }
++
++ udp4 = kmemdup(&nf_conntrack_l4proto_udp4,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (udp4 == NULL)
++ goto no_mem_ct4;
++
++ udp4->ctl_table_users = &ve_udp_sysctl_table_users;
++ udp4->ctl_table_header = &ve_udp_sysctl_header;
++ udp4->ctl_table = kmemdup(udp_sysctl_table,
++ sizeof(udp_sysctl_table), GFP_KERNEL);
++ if (udp4->ctl_table == NULL)
++ goto no_mem_sys;
++ udp4->ctl_table[0].data = &ve_nf_ct_udp_timeout;
++ udp4->ctl_table[1].data = &ve_nf_ct_udp_timeout_stream;
++
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ udp4->ctl_compat_table_header = ve_udp_compat_sysctl_header;
++ udp4->ctl_compat_table = kmemdup(udp_compat_sysctl_table,
++ sizeof(udp_compat_sysctl_table), GFP_KERNEL);
++ if (udp4->ctl_compat_table == NULL)
++ goto no_mem_compat;
++ udp4->ctl_compat_table[0].data = &ve_nf_ct_udp_timeout;
++ udp4->ctl_compat_table[1].data = &ve_nf_ct_udp_timeout_stream;
++#endif
++
++ udp6 = kmemdup(&nf_conntrack_l4proto_udp6,
++ sizeof(struct nf_conntrack_l4proto), GFP_KERNEL);
++ if (!udp6)
++ goto no_mem_ct6;
++
++ udp6->ctl_table_users = &ve_udp_sysctl_table_users;
++ udp6->ctl_table_header = &ve_udp_sysctl_header;
++ udp6->ctl_table = udp4->ctl_table;
++
++ udp6->ctl_table[0].data = &ve_nf_ct_udp_timeout;
++ udp6->ctl_table[1].data = &ve_nf_ct_udp_timeout_stream;
++out:
++ ve_nf_ct_udp_timeout = nf_ct_udp_timeout;
++ ve_nf_ct_udp_timeout_stream = nf_ct_udp_timeout_stream;
++
++ ve_nf_conntrack_l4proto_udp4 = udp4;
++ ve_nf_conntrack_l4proto_udp6 = udp6;
++ return 0;
++
++no_mem_ct6:
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(udp4->ctl_compat_table);
++no_mem_compat:
++#endif
++ kfree(udp4->ctl_table);
++no_mem_sys:
++ kfree(udp4);
++no_mem_ct4:
++ return -ENOMEM;
++}
++EXPORT_SYMBOL(nf_ct_proto_udp_sysctl_init);
++
++void nf_ct_proto_udp_sysctl_cleanup(void)
++{
++ if (!ve_is_super(get_exec_env())) {
++#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
++ kfree(ve_nf_conntrack_l4proto_udp4->ctl_compat_table);
++#endif
++ kfree(ve_nf_conntrack_l4proto_udp4->ctl_table);
++ kfree(ve_nf_conntrack_l4proto_udp4);
++
++ kfree(ve_nf_conntrack_l4proto_udp6);
++ }
++}
++EXPORT_SYMBOL(nf_ct_proto_udp_sysctl_cleanup);
++#endif /* CONFIG_VE_IPTABLES && CONFIG_SYSCTL */
+diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
+index 8509db1..8951637 100644
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -9,6 +9,7 @@
+ #include <linux/types.h>
+ #include <linux/netfilter.h>
+ #include <linux/module.h>
++#include <linux/nsproxy.h>
+ #include <linux/skbuff.h>
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
+@@ -18,6 +19,7 @@
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+ #endif
++#include <linux/nfcalls.h>
+
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -29,6 +31,10 @@
+
+ MODULE_LICENSE("GPL");
+
++int ip_conntrack_disable_ve0 = 0;
++module_param(ip_conntrack_disable_ve0, int, 0440);
++EXPORT_SYMBOL(ip_conntrack_disable_ve0);
++
+ #ifdef CONFIG_PROC_FS
+ int
+ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
+@@ -51,7 +57,7 @@ static struct hlist_node *ct_get_first(struct seq_file *seq)
+ for (st->bucket = 0;
+ st->bucket < nf_conntrack_htable_size;
+ st->bucket++) {
+- n = rcu_dereference(nf_conntrack_hash[st->bucket].first);
++ n = rcu_dereference(ve_nf_conntrack_hash[st->bucket].first);
+ if (n)
+ return n;
+ }
+@@ -67,7 +73,7 @@ static struct hlist_node *ct_get_next(struct seq_file *seq,
+ while (head == NULL) {
+ if (++st->bucket >= nf_conntrack_htable_size)
+ return NULL;
+- head = rcu_dereference(nf_conntrack_hash[st->bucket].first);
++ head = rcu_dereference(ve_nf_conntrack_hash[st->bucket].first);
+ }
+ return head;
+ }
+@@ -226,7 +232,7 @@ static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
+
+ static int ct_cpu_seq_show(struct seq_file *seq, void *v)
+ {
+- unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
++ unsigned int nr_conntracks = atomic_read(&ve_nf_conntrack_count);
+ const struct ip_conntrack_stat *st = v;
+
+ if (v == SEQ_START_TOKEN) {
+@@ -280,27 +286,30 @@ static const struct file_operations ct_cpu_seq_fops = {
+ static int nf_conntrack_standalone_init_proc(void)
+ {
+ struct proc_dir_entry *pde;
++ struct net *net = get_exec_env()->ve_netns;
+
+- pde = proc_net_fops_create(&init_net, "nf_conntrack", 0440, &ct_file_ops);
++ pde = proc_net_fops_create(net, "nf_conntrack", 0440, &ct_file_ops);
+ if (!pde)
+ goto out_nf_conntrack;
+
+- pde = proc_create("nf_conntrack", S_IRUGO, init_net.proc_net_stat,
++ pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat,
+ &ct_cpu_seq_fops);
+ if (!pde)
+ goto out_stat_nf_conntrack;
+ return 0;
+
+ out_stat_nf_conntrack:
+- proc_net_remove(&init_net, "nf_conntrack");
++ proc_net_remove(net, "nf_conntrack");
+ out_nf_conntrack:
+ return -ENOMEM;
+ }
+
+ static void nf_conntrack_standalone_fini_proc(void)
+ {
+- remove_proc_entry("nf_conntrack", init_net.proc_net_stat);
+- proc_net_remove(&init_net, "nf_conntrack");
++ struct net *net = get_exec_env()->ve_netns;
++
++ remove_proc_entry("nf_conntrack", net->proc_net_stat);
++ proc_net_remove(net, "nf_conntrack");
+ }
+ #else
+ static int nf_conntrack_standalone_init_proc(void)
+@@ -323,8 +332,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_checksum);
+ static int log_invalid_proto_min = 0;
+ static int log_invalid_proto_max = 255;
+
++#if ! (defined(CONFIG_VE_IPTABLES) && defined(CONFIG_SYSCTL))
+ static struct ctl_table_header *nf_ct_sysctl_header;
+ static struct ctl_table_header *nf_ct_netfilter_header;
++#endif
+
+ static ctl_table nf_ct_sysctl_table[] = {
+ {
+@@ -404,21 +415,54 @@ EXPORT_SYMBOL_GPL(nf_ct_log_invalid);
+
+ static int nf_conntrack_standalone_init_sysctl(void)
+ {
+- nf_ct_netfilter_header =
+- register_sysctl_paths(nf_ct_path, nf_ct_netfilter_table);
+- if (!nf_ct_netfilter_header)
+- goto out;
++ struct ctl_table *nf_table, *ct_table;
++ struct net *net = get_exec_env()->ve_netns;
++
++ nf_table = nf_ct_netfilter_table;
++ ct_table = nf_ct_sysctl_table;
++
++ if (!ve_is_super(get_exec_env())) {
++ nf_table = kmemdup(nf_table, sizeof(nf_ct_netfilter_table),
++ GFP_KERNEL);
++ if (nf_table == NULL)
++ goto out;
++
++ ct_table = kmemdup(ct_table, sizeof(nf_ct_sysctl_table),
++ GFP_KERNEL);
++ if (ct_table == NULL)
++ goto err_ctt;
++ }
+
+- nf_ct_sysctl_header =
+- register_sysctl_paths(nf_net_netfilter_sysctl_path,
+- nf_ct_sysctl_table);
+- if (!nf_ct_sysctl_header)
+- goto out_unregister_netfilter;
++ nf_table[0].data = &ve_nf_conntrack_max;
++ ct_table[0].data = &ve_nf_conntrack_max;
++ ct_table[1].data = &ve_nf_conntrack_count;
++ /* nf_conntrack_htable_size is shared and readonly */
++ ct_table[3].data = &ve_nf_conntrack_checksum;
++ ct_table[4].data = &ve_nf_ct_log_invalid;
++ ct_table[5].data = &ve_nf_ct_expect_max;
++
++ ve_nf_ct_netfilter_header = register_net_sysctl_table(net,
++ nf_ct_path, nf_table);
++ if (!ve_nf_ct_netfilter_header)
++ goto err_reg_nf_table;
++
++ ve_nf_ct_sysctl_header =
++ register_net_sysctl_table(net,
++ nf_net_netfilter_sysctl_path,
++ ct_table);
++ if (!ve_nf_ct_sysctl_header)
++ goto err_reg_ct_table;
+
+ return 0;
+
+-out_unregister_netfilter:
+- unregister_sysctl_table(nf_ct_netfilter_header);
++err_reg_ct_table:
++ unregister_net_sysctl_table(ve_nf_ct_netfilter_header);
++err_reg_nf_table:
++ if (ct_table != nf_ct_sysctl_table)
++ kfree(ct_table);
++err_ctt:
++ if (nf_table != nf_ct_netfilter_table)
++ kfree(nf_table);
+ out:
+ printk("nf_conntrack: can't register to sysctl.\n");
+ return -ENOMEM;
+@@ -426,8 +470,16 @@ out:
+
+ static void nf_conntrack_standalone_fini_sysctl(void)
+ {
+- unregister_sysctl_table(nf_ct_netfilter_header);
+- unregister_sysctl_table(nf_ct_sysctl_header);
++ struct ctl_table *table = ve_nf_ct_sysctl_header->ctl_table_arg;
++ struct ctl_table *table2 = ve_nf_ct_netfilter_header->ctl_table_arg;
++
++ unregister_net_sysctl_table(ve_nf_ct_sysctl_header);
++ unregister_net_sysctl_table(ve_nf_ct_netfilter_header);
++
++ if (!ve_is_super(get_exec_env())) {
++ kfree(table);
++ kfree(table2);
++ }
+ }
+ #else
+ static int nf_conntrack_standalone_init_sysctl(void)
+@@ -440,7 +492,7 @@ static void nf_conntrack_standalone_fini_sysctl(void)
+ }
+ #endif /* CONFIG_SYSCTL */
+
+-static int __init nf_conntrack_standalone_init(void)
++static int nf_conntrack_init_ve(void)
+ {
+ int ret;
+
+@@ -453,8 +505,19 @@ static int __init nf_conntrack_standalone_init(void)
+ ret = nf_conntrack_standalone_init_sysctl();
+ if (ret < 0)
+ goto out_sysctl;
++ ret = nf_ct_proto_tcp_sysctl_init();
++ if (ret < 0)
++ goto out_tcp_sysctl;
++ ret = nf_ct_proto_udp_sysctl_init();
++ if (ret < 0)
++ goto out_udp_sysctl;
++
+ return 0;
+
++out_udp_sysctl:
++ nf_ct_proto_tcp_sysctl_cleanup();
++out_tcp_sysctl:
++ nf_conntrack_standalone_fini_sysctl();
+ out_sysctl:
+ nf_conntrack_standalone_fini_proc();
+ out_proc:
+@@ -463,13 +526,36 @@ out:
+ return ret;
+ }
+
+-static void __exit nf_conntrack_standalone_fini(void)
++static void nf_conntrack_cleanup_ve(void)
+ {
++ nf_ct_proto_udp_sysctl_cleanup();
++ nf_ct_proto_tcp_sysctl_cleanup();
+ nf_conntrack_standalone_fini_sysctl();
+ nf_conntrack_standalone_fini_proc();
+ nf_conntrack_cleanup();
+ }
+
++static int __init nf_conntrack_standalone_init(void)
++{
++#ifdef CONFIG_VE_IPTABLES
++ KSYMRESOLVE(nf_conntrack_init_ve);
++ KSYMRESOLVE(nf_conntrack_cleanup_ve);
++ KSYMMODRESOLVE(nf_conntrack);
++#endif
++
++ return nf_conntrack_init_ve();
++}
++
++static void __exit nf_conntrack_standalone_fini(void)
++{
++#ifdef CONFIG_VE_IPTABLES
++ KSYMMODUNRESOLVE(nf_conntrack);
++ KSYMUNRESOLVE(nf_conntrack_init_ve);
++ KSYMUNRESOLVE(nf_conntrack_cleanup_ve);
++#endif
++ nf_conntrack_cleanup_ve();
++}
++
+ module_init(nf_conntrack_standalone_init);
+ module_exit(nf_conntrack_standalone_fini);
+
+diff --git a/net/netfilter/nf_sockopt.c b/net/netfilter/nf_sockopt.c
+index 0148968..aa01c54 100644
+--- a/net/netfilter/nf_sockopt.c
++++ b/net/netfilter/nf_sockopt.c
+@@ -65,9 +65,6 @@ static struct nf_sockopt_ops *nf_sockopt_find(struct sock *sk, int pf,
+ {
+ struct nf_sockopt_ops *ops;
+
+- if (!net_eq(sock_net(sk), &init_net))
+- return ERR_PTR(-ENOPROTOOPT);
+-
+ if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
+ return ERR_PTR(-EINTR);
+
+diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
+index b75c9c4..04491ab 100644
+--- a/net/netfilter/nfnetlink.c
++++ b/net/netfilter/nfnetlink.c
+@@ -124,7 +124,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ const struct nfnetlink_subsystem *ss;
+ int type, err;
+
+- if (security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ /* All the messages must at least contain nfgenmsg */
+diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
+index 8c86011..d3ad11e 100644
+--- a/net/netfilter/nfnetlink_queue.c
++++ b/net/netfilter/nfnetlink_queue.c
+@@ -555,9 +555,6 @@ nfqnl_rcv_dev_event(struct notifier_block *this,
+ {
+ struct net_device *dev = ptr;
+
+- if (!net_eq(dev_net(dev), &init_net))
+- return NOTIFY_DONE;
+-
+ /* Drop any packets associated with the downed device */
+ if (event == NETDEV_DOWN)
+ nfqnl_dev_drop(dev->ifindex);
+@@ -586,8 +583,7 @@ nfqnl_rcv_nl_event(struct notifier_block *this,
+ struct hlist_head *head = &instance_table[i];
+
+ hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
+- if ((n->net == &init_net) &&
+- (n->pid == inst->peer_pid))
++ if (n->pid == inst->peer_pid)
+ __instance_destroy(inst);
+ }
+ }
+diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
+index 5d75cd8..7ffe66a 100644
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -24,6 +24,8 @@
+ #include <linux/mm.h>
+ #include <net/net_namespace.h>
+
++#include <bc/kmem.h>
++
+ #include <linux/netfilter/x_tables.h>
+ #include <linux/netfilter_arp.h>
+
+@@ -64,6 +66,46 @@ static const char *const xt_prefix[NPROTO] = {
+ [NF_ARP] = "arp",
+ };
+
++#ifdef CONFIG_BEANCOUNTERS
++static inline struct user_beancounter *xt_table_ub(struct xt_table_info *info)
++{
++ struct user_beancounter *ub;
++
++ for (ub = mem_ub(info); ub->parent != NULL; ub = ub->parent);
++ return ub;
++}
++
++static void uncharge_xtables(struct xt_table_info *info, unsigned long size)
++{
++ struct user_beancounter *ub;
++
++ ub = xt_table_ub(info);
++ uncharge_beancounter(ub, UB_NUMXTENT, size);
++}
++
++static int recharge_xtables(int check_ub,
++ struct xt_table_info *new, struct xt_table_info *old)
++{
++ struct user_beancounter *ub;
++ long change;
++
++ ub = xt_table_ub(new);
++ BUG_ON(check_ub && ub != xt_table_ub(old));
++
++ change = (long)new->number - (long)old->number;
++ if (change > 0) {
++ if (charge_beancounter(ub, UB_NUMXTENT, change, UB_SOFT))
++ return -ENOMEM;
++ } else if (change < 0)
++ uncharge_beancounter(ub, UB_NUMXTENT, -change);
++
++ return 0;
++}
++#else
++#define recharge_xtables(c, new, old) (0)
++#define uncharge_xtables(info, s) do { } while (0)
++#endif /* CONFIG_BEANCOUNTERS */
++
+ /* Registration hooks for targets. */
+ int
+ xt_register_target(struct xt_target *target)
+@@ -312,23 +354,23 @@ int xt_check_match(const struct xt_match *match, unsigned short family,
+ unsigned short proto, int inv_proto)
+ {
+ if (XT_ALIGN(match->matchsize) != size) {
+- printk("%s_tables: %s match: invalid size %Zu != %u\n",
++ ve_printk(VE_LOG, "%s_tables: %s match: invalid size %Zu != %u\n",
+ xt_prefix[family], match->name,
+ XT_ALIGN(match->matchsize), size);
+ return -EINVAL;
+ }
+ if (match->table && strcmp(match->table, table)) {
+- printk("%s_tables: %s match: only valid in %s table, not %s\n",
++ ve_printk(VE_LOG, "%s_tables: %s match: only valid in %s table, not %s\n",
+ xt_prefix[family], match->name, match->table, table);
+ return -EINVAL;
+ }
+ if (match->hooks && (hook_mask & ~match->hooks) != 0) {
+- printk("%s_tables: %s match: bad hook_mask %u/%u\n",
++ ve_printk(VE_LOG, "%s_tables: %s match: bad hook_mask %u/%u\n",
+ xt_prefix[family], match->name, hook_mask, match->hooks);
+ return -EINVAL;
+ }
+ if (match->proto && (match->proto != proto || inv_proto)) {
+- printk("%s_tables: %s match: only valid for protocol %u\n",
++ ve_printk(VE_LOG, "%s_tables: %s match: only valid for protocol %u\n",
+ xt_prefix[family], match->name, match->proto);
+ return -EINVAL;
+ }
+@@ -453,24 +495,24 @@ int xt_check_target(const struct xt_target *target, unsigned short family,
+ unsigned short proto, int inv_proto)
+ {
+ if (XT_ALIGN(target->targetsize) != size) {
+- printk("%s_tables: %s target: invalid size %Zu != %u\n",
++ ve_printk(VE_LOG, "%s_tables: %s target: invalid size %Zu != %u\n",
+ xt_prefix[family], target->name,
+ XT_ALIGN(target->targetsize), size);
+ return -EINVAL;
+ }
+ if (target->table && strcmp(target->table, table)) {
+- printk("%s_tables: %s target: only valid in %s table, not %s\n",
++ ve_printk(VE_LOG, "%s_tables: %s target: only valid in %s table, not %s\n",
+ xt_prefix[family], target->name, target->table, table);
+ return -EINVAL;
+ }
+ if (target->hooks && (hook_mask & ~target->hooks) != 0) {
+- printk("%s_tables: %s target: bad hook_mask %u/%u\n",
++ ve_printk(VE_LOG, "%s_tables: %s target: bad hook_mask %u/%u\n",
+ xt_prefix[family], target->name, hook_mask,
+ target->hooks);
+ return -EINVAL;
+ }
+ if (target->proto && (target->proto != proto || inv_proto)) {
+- printk("%s_tables: %s target: only valid for protocol %u\n",
++ ve_printk(VE_LOG, "%s_tables: %s target: only valid for protocol %u\n",
+ xt_prefix[family], target->name, target->proto);
+ return -EINVAL;
+ }
+@@ -550,19 +592,19 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size)
+ if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
+ return NULL;
+
+- newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL);
++ newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL_UBC);
+ if (!newinfo)
+ return NULL;
+
+- newinfo->size = size;
++ newinfo->alloc_size = newinfo->size = size;
+
+ for_each_possible_cpu(cpu) {
+ if (size <= PAGE_SIZE)
+ newinfo->entries[cpu] = kmalloc_node(size,
+- GFP_KERNEL,
++ GFP_KERNEL_UBC,
+ cpu_to_node(cpu));
+ else
+- newinfo->entries[cpu] = vmalloc_node(size,
++ newinfo->entries[cpu] = ub_vmalloc_node(size,
+ cpu_to_node(cpu));
+
+ if (newinfo->entries[cpu] == NULL) {
+@@ -580,7 +622,7 @@ void xt_free_table_info(struct xt_table_info *info)
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+- if (info->size <= PAGE_SIZE)
++ if (info->alloc_size <= PAGE_SIZE)
+ kfree(info->entries[cpu]);
+ else
+ vfree(info->entries[cpu]);
+@@ -645,6 +687,13 @@ xt_replace_table(struct xt_table *table,
+ return NULL;
+ }
+ oldinfo = private;
++
++ if (recharge_xtables(num_counters != 0, newinfo, oldinfo)) {
++ write_unlock_bh(&table->lock);
++ *error = -ENOMEM;
++ return NULL;
++ }
++
+ table->private = newinfo;
+ newinfo->initial_entries = oldinfo->initial_entries;
+ write_unlock_bh(&table->lock);
+@@ -714,6 +763,7 @@ void *xt_unregister_table(struct xt_table *table)
+ list_del(&table->list);
+ mutex_unlock(&xt[table->af].mutex);
+ kfree(table);
++ uncharge_xtables(private, private->number);
+
+ return private;
+ }
+diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c
+index 97efd74..d0453de 100644
+--- a/net/netfilter/xt_DSCP.c
++++ b/net/netfilter/xt_DSCP.c
+@@ -73,7 +73,7 @@ dscp_tg_check(const char *tablename, const void *e_void,
+ const u_int8_t dscp = ((struct xt_DSCP_info *)targinfo)->dscp;
+
+ if (dscp > XT_DSCP_MAX) {
+- printk(KERN_WARNING "DSCP: dscp %x out of range\n", dscp);
++ ve_printk(VE_LOG, KERN_WARNING "DSCP: dscp %x out of range\n", dscp);
+ return false;
+ }
+ return true;
+diff --git a/net/netfilter/xt_MARK.c b/net/netfilter/xt_MARK.c
+index f9ce20b..030ba07 100644
+--- a/net/netfilter/xt_MARK.c
++++ b/net/netfilter/xt_MARK.c
+@@ -80,7 +80,7 @@ mark_tg_check_v0(const char *tablename, const void *entry,
+ const struct xt_mark_target_info *markinfo = targinfo;
+
+ if (markinfo->mark > 0xffffffff) {
+- printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
++ ve_printk(VE_LOG, KERN_WARNING "MARK: Only supports 32bit wide mark\n");
+ return false;
+ }
+ return true;
+@@ -96,12 +96,12 @@ mark_tg_check_v1(const char *tablename, const void *entry,
+ if (markinfo->mode != XT_MARK_SET
+ && markinfo->mode != XT_MARK_AND
+ && markinfo->mode != XT_MARK_OR) {
+- printk(KERN_WARNING "MARK: unknown mode %u\n",
++ ve_printk(VE_LOG, KERN_WARNING "MARK: unknown mode %u\n",
+ markinfo->mode);
+ return false;
+ }
+ if (markinfo->mark > 0xffffffff) {
+- printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
++ ve_printk(VE_LOG, KERN_WARNING "MARK: Only supports 32bit wide mark\n");
+ return false;
+ }
+ return true;
+diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c
+index beb5094..dbd407b 100644
+--- a/net/netfilter/xt_TCPMSS.c
++++ b/net/netfilter/xt_TCPMSS.c
+@@ -67,7 +67,7 @@ tcpmss_mangle_packet(struct sk_buff *skb,
+ badly. --RR */
+ if (tcplen != tcph->doff*4) {
+ if (net_ratelimit())
+- printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n",
++ ve_printk(VE_LOG, KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n",
+ skb->len);
+ return -1;
+ }
+@@ -75,7 +75,7 @@ tcpmss_mangle_packet(struct sk_buff *skb,
+ if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
+ if (dst_mtu(skb->dst) <= minlen) {
+ if (net_ratelimit())
+- printk(KERN_ERR "xt_TCPMSS: "
++ ve_printk(VE_LOG, KERN_ERR "xt_TCPMSS: "
+ "unknown or invalid path-MTU (%u)\n",
+ dst_mtu(skb->dst));
+ return -1;
+@@ -253,13 +253,13 @@ tcpmss_tg4_check(const char *tablename, const void *entry,
+ (hook_mask & ~((1 << NF_INET_FORWARD) |
+ (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING))) != 0) {
+- printk("xt_TCPMSS: path-MTU clamping only supported in "
++ ve_printk(VE_LOG, "xt_TCPMSS: path-MTU clamping only supported in "
+ "FORWARD, OUTPUT and POSTROUTING hooks\n");
+ return false;
+ }
+ if (IPT_MATCH_ITERATE(e, find_syn_match))
+ return true;
+- printk("xt_TCPMSS: Only works on TCP SYN packets\n");
++ ve_printk(VE_LOG, "xt_TCPMSS: Only works on TCP SYN packets\n");
+ return false;
+ }
+
+@@ -276,13 +276,13 @@ tcpmss_tg6_check(const char *tablename, const void *entry,
+ (hook_mask & ~((1 << NF_INET_FORWARD) |
+ (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING))) != 0) {
+- printk("xt_TCPMSS: path-MTU clamping only supported in "
++ ve_printk(VE_LOG, "xt_TCPMSS: path-MTU clamping only supported in "
+ "FORWARD, OUTPUT and POSTROUTING hooks\n");
+ return false;
+ }
+ if (IP6T_MATCH_ITERATE(e, find_syn_match))
+ return true;
+- printk("xt_TCPMSS: Only works on TCP SYN packets\n");
++ ve_printk(VE_LOG, "xt_TCPMSS: Only works on TCP SYN packets\n");
+ return false;
+ }
+ #endif
+diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
+index d9418a2..8a5736a 100644
+--- a/net/netfilter/xt_hashlimit.c
++++ b/net/netfilter/xt_hashlimit.c
+@@ -15,6 +15,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
++#include <linux/nsproxy.h>
+ #include <linux/list.h>
+ #include <linux/skbuff.h>
+ #include <linux/mm.h>
+@@ -41,8 +42,13 @@ MODULE_ALIAS("ipt_hashlimit");
+ MODULE_ALIAS("ip6t_hashlimit");
+
+ /* need to declare this at the top */
++#ifdef CONFIG_VE_IPTABLES
++#define hashlimit_procdir4 (get_exec_env()->_xt_hashlimit->hashlimit_procdir4)
++#define hashlimit_procdir6 (get_exec_env()->_xt_hashlimit->hashlimit_procdir6)
++#else
+ static struct proc_dir_entry *hashlimit_procdir4;
+ static struct proc_dir_entry *hashlimit_procdir6;
++#endif
+ static const struct file_operations dl_file_ops;
+
+ /* hash table crap */
+@@ -99,9 +105,16 @@ struct xt_hashlimit_htable {
+
+ static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */
+ static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */
++#ifdef CONFIG_VE_IPTABLES
++#define hashlimit_htables (get_exec_env()->_xt_hashlimit->hashlimit_htables)
++#else
+ static HLIST_HEAD(hashlimit_htables);
++#endif
+ static struct kmem_cache *hashlimit_cachep __read_mostly;
+
++static int init_xt_hashlimit(void);
++static void fini_xt_hashlimit(void);
++
+ static inline bool dst_cmp(const struct dsthash_ent *ent,
+ const struct dsthash_dst *b)
+ {
+@@ -700,6 +713,9 @@ hashlimit_mt_check_v0(const char *tablename, const void *inf,
+ if (r->name[sizeof(r->name) - 1] != '\0')
+ return false;
+
++ if (init_xt_hashlimit())
++ return 0;
++
+ /* This is the best we've got: We cannot release and re-grab lock,
+ * since checkentry() is called before x_tables.c grabs xt_mutex.
+ * We also cannot grab the hashtable spinlock, since htable_create will
+@@ -746,6 +762,9 @@ hashlimit_mt_check(const char *tablename, const void *inf,
+ return false;
+ }
+
++ if (init_xt_hashlimit())
++ return 0;
++
+ /* This is the best we've got: We cannot release and re-grab lock,
+ * since checkentry() is called before x_tables.c grabs xt_mutex.
+ * We also cannot grab the hashtable spinlock, since htable_create will
+@@ -768,6 +787,8 @@ hashlimit_mt_destroy_v0(const struct xt_match *match, void *matchinfo)
+ const struct xt_hashlimit_info *r = matchinfo;
+
+ htable_put(r->hinfo);
++ if (!ve_is_super(get_exec_env()) && hlist_empty(&hashlimit_htables))
++ fini_xt_hashlimit();
+ }
+
+ static void
+@@ -776,6 +797,8 @@ hashlimit_mt_destroy(const struct xt_match *match, void *matchinfo)
+ const struct xt_hashlimit_mtinfo1 *info = matchinfo;
+
+ htable_put(info->hinfo);
++ if (!ve_is_super(get_exec_env()) && hlist_empty(&hashlimit_htables))
++ fini_xt_hashlimit();
+ }
+
+ #ifdef CONFIG_COMPAT
+@@ -978,6 +1001,78 @@ static const struct file_operations dl_file_ops = {
+ .release = seq_release
+ };
+
++static inline struct proc_dir_entry *proc_from_netns(void)
++{
++#if defined(CONFIG_VE)
++ return get_exec_env()->ve_netns->proc_net;
++#else
++ return init_net.proc_net;
++#endif
++}
++
++static int init_xt_hashlimit(void)
++{
++ struct proc_dir_entry *proc_net = proc_from_netns();
++
++#if defined(CONFIG_VE_IPTABLES)
++ struct ve_struct *ve = get_exec_env();
++
++ if (ve->_xt_hashlimit)
++ return 0;
++
++ ve->_xt_hashlimit = kzalloc(sizeof(struct ve_xt_hashlimit), GFP_KERNEL);
++ if (!ve->_xt_hashlimit)
++ goto err1;
++#endif
++ INIT_HLIST_HEAD(&hashlimit_htables);
++
++ hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", proc_net);
++ if (!hashlimit_procdir4) {
++ printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
++ "entry\n");
++ goto err2;
++ }
++#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
++ hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", proc_net);
++ if (!hashlimit_procdir6) {
++ printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
++ "entry\n");
++ goto err3;
++ }
++#endif
++
++ return 0;
++
++#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
++err3:
++ remove_proc_entry("ipt_hashlimit", proc_net);
++#endif
++err2:
++#if defined(CONFIG_VE_IPTABLES)
++ kfree(ve->_xt_hashlimit);
++ ve->_xt_hashlimit = NULL;
++err1:
++#endif
++ return -ENOMEM;
++}
++
++static void fini_xt_hashlimit(void)
++{
++ struct proc_dir_entry *proc_net = proc_from_netns();
++#ifdef CONFIG_VE_IPTABLES
++ struct ve_struct *ve = get_exec_env();
++#endif
++#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
++ remove_proc_entry("ip6t_hashlimit", proc_net);
++#endif
++ remove_proc_entry("ipt_hashlimit", proc_net);
++
++#if defined(CONFIG_VE_IPTABLES)
++ kfree(ve->_xt_hashlimit);
++ ve->_xt_hashlimit = NULL;
++#endif
++}
++
+ static int __init hashlimit_mt_init(void)
+ {
+ int err;
+@@ -995,24 +1090,11 @@ static int __init hashlimit_mt_init(void)
+ printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n");
+ goto err2;
+ }
+- hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", init_net.proc_net);
+- if (!hashlimit_procdir4) {
+- printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
+- "entry\n");
++ err = init_xt_hashlimit();
++ if (err)
+ goto err3;
+- }
+- err = 0;
+-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+- hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", init_net.proc_net);
+- if (!hashlimit_procdir6) {
+- printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
+- "entry\n");
+- err = -ENOMEM;
+- }
+-#endif
+ if (!err)
+ return 0;
+- remove_proc_entry("ipt_hashlimit", init_net.proc_net);
+ err3:
+ kmem_cache_destroy(hashlimit_cachep);
+ err2:
+@@ -1024,10 +1106,7 @@ err1:
+
+ static void __exit hashlimit_mt_exit(void)
+ {
+- remove_proc_entry("ipt_hashlimit", init_net.proc_net);
+-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+- remove_proc_entry("ip6t_hashlimit", init_net.proc_net);
+-#endif
++ fini_xt_hashlimit();
+ kmem_cache_destroy(hashlimit_cachep);
+ xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
+ }
+diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c
+index aad9ab8..91570c7 100644
+--- a/net/netfilter/xt_limit.c
++++ b/net/netfilter/xt_limit.c
+@@ -105,7 +105,7 @@ limit_mt_check(const char *tablename, const void *inf,
+ /* Check for overflow. */
+ if (r->burst == 0
+ || user2credits(r->avg * r->burst) < user2credits(r->avg)) {
+- printk("Overflow in xt_limit, try lower: %u/%u\n",
++ ve_printk(VE_LOG, "Overflow in xt_limit, try lower: %u/%u\n",
+ r->avg, r->burst);
+ return false;
+ }
+diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
+index b0eacc0..7c9c394 100644
+--- a/net/netlink/af_netlink.c
++++ b/net/netlink/af_netlink.c
+@@ -60,29 +60,14 @@
+ #include <net/sock.h>
+ #include <net/scm.h>
+ #include <net/netlink.h>
++#include <net/netlink_sock.h>
++
++#include <bc/beancounter.h>
++#include <bc/net.h>
+
+ #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
+ #define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
+
+-struct netlink_sock {
+- /* struct sock has to be the first member of netlink_sock */
+- struct sock sk;
+- u32 pid;
+- u32 dst_pid;
+- u32 dst_group;
+- u32 flags;
+- u32 subscriptions;
+- u32 ngroups;
+- unsigned long *groups;
+- unsigned long state;
+- wait_queue_head_t wait;
+- struct netlink_callback *cb;
+- struct mutex *cb_mutex;
+- struct mutex cb_def_mutex;
+- void (*netlink_rcv)(struct sk_buff *skb);
+- struct module *module;
+-};
+-
+ #define NETLINK_KERNEL_SOCKET 0x1
+ #define NETLINK_RECV_PKTINFO 0x2
+
+@@ -402,6 +387,8 @@ static int __netlink_create(struct net *net, struct socket *sock,
+ sk = sk_alloc(net, PF_NETLINK, GFP_KERNEL, &netlink_proto);
+ if (!sk)
+ return -ENOMEM;
++ if (ub_other_sock_charge(sk))
++ goto out_free;
+
+ sock_init_data(sock, sk);
+
+@@ -417,6 +404,10 @@ static int __netlink_create(struct net *net, struct socket *sock,
+ sk->sk_destruct = netlink_sock_destruct;
+ sk->sk_protocol = protocol;
+ return 0;
++
++out_free:
++ sk_free(sk);
++ return -ENOMEM;
+ }
+
+ static int netlink_create(struct net *net, struct socket *sock, int protocol)
+@@ -523,7 +514,7 @@ static int netlink_autobind(struct socket *sock)
+ struct hlist_head *head;
+ struct sock *osk;
+ struct hlist_node *node;
+- s32 pid = current->tgid;
++ s32 pid = task_tgid_vnr(current);
+ int err;
+ static s32 rover = -4097;
+
+@@ -559,7 +550,7 @@ retry:
+ static inline int netlink_capable(struct socket *sock, unsigned int flag)
+ {
+ return (nl_table[sock->sk->sk_protocol].nl_nonroot & flag) ||
+- capable(CAP_NET_ADMIN);
++ capable(CAP_VE_NET_ADMIN);
+ }
+
+ static void
+@@ -764,12 +755,20 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
+ long *timeo, struct sock *ssk)
+ {
+ struct netlink_sock *nlk;
++ unsigned long chargesize;
++ int no_ubc;
+
+ nlk = nlk_sk(sk);
+
+- if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
++ chargesize = skb_charge_fullsize(skb);
++ no_ubc = ub_sock_getwres_other(sk, chargesize);
++ if (no_ubc || atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
+ test_bit(0, &nlk->state)) {
+ DECLARE_WAITQUEUE(wait, current);
++
++ if (!no_ubc)
++ ub_sock_retwres_other(sk, chargesize,
++ SOCK_MIN_UBCSPACE_CH);
+ if (!*timeo) {
+ if (!ssk || netlink_is_kernel(ssk))
+ netlink_overrun(sk);
+@@ -781,13 +780,20 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
+ __set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&nlk->wait, &wait);
+
++ /* this if can't be moved upper because ub_sock_snd_queue_add()
++ * may change task state to TASK_RUNNING */
++ if (no_ubc)
++ ub_sock_sndqueueadd_other(sk, chargesize);
++
+ if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
+- test_bit(0, &nlk->state)) &&
++ test_bit(0, &nlk->state) || no_ubc) &&
+ !sock_flag(sk, SOCK_DEAD))
+ *timeo = schedule_timeout(*timeo);
+
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&nlk->wait, &wait);
++ if (no_ubc)
++ ub_sock_sndqueuedel(sk);
+ sock_put(sk);
+
+ if (signal_pending(current)) {
+@@ -797,6 +803,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
+ return 1;
+ }
+ skb_set_owner_r(skb, sk);
++ ub_skb_set_charge(skb, sk, chargesize, UB_OTHERSOCKBUF);
+ return 0;
+ }
+
+@@ -962,6 +969,9 @@ static inline int do_one_broadcast(struct sock *sk,
+ !test_bit(p->group - 1, nlk->groups))
+ goto out;
+
++ if (!ve_accessible_strict(get_exec_env(), sk->owner_env))
++ goto out;
++
+ if (!net_eq(sock_net(sk), p->net))
+ goto out;
+
+@@ -1531,6 +1541,10 @@ static int netlink_dump(struct sock *sk)
+ skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);
+ if (!skb)
+ goto errout;
++ if (ub_nlrcvbuf_charge(skb, sk) < 0) {
++ kfree_skb(skb);
++ return -EACCES;
++ }
+
+ mutex_lock(nlk->cb_mutex);
+
+diff --git a/net/netlink/attr.c b/net/netlink/attr.c
+index 2d106cf..d9846a4 100644
+--- a/net/netlink/attr.c
++++ b/net/netlink/attr.c
+@@ -164,7 +164,7 @@ int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
+ }
+
+ if (unlikely(rem > 0))
+- printk(KERN_WARNING "netlink: %d bytes leftover after parsing "
++ ve_printk(VE_LOG, KERN_WARNING "netlink: %d bytes leftover after parsing "
+ "attributes.\n", rem);
+
+ err = 0;
+diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
+index 3e1191c..f5c0578 100644
+--- a/net/netlink/genetlink.c
++++ b/net/netlink/genetlink.c
+@@ -437,7 +437,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ return -EOPNOTSUPP;
+
+ if ((ops->flags & GENL_ADMIN_PERM) &&
+- security_netlink_recv(skb, CAP_NET_ADMIN))
++ security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
+index c718e7e..7fcd47b 100644
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -78,6 +78,8 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+
++#include <bc/net.h>
++
+ #ifdef CONFIG_INET
+ #include <net/inet_common.h>
+ #endif
+@@ -493,6 +495,8 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
+ if (dev_net(dev) != sock_net(sk))
+ goto drop;
+
++ skb_orphan(skb);
++
+ skb->dev = dev;
+
+ if (dev->header_ops) {
+@@ -556,6 +560,9 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
+ if (pskb_trim(skb, snaplen))
+ goto drop_n_acct;
+
++ if (ub_sockrcvbuf_charge(sk, skb))
++ goto drop_n_acct;
++
+ skb_set_owner_r(skb, sk);
+ skb->dev = NULL;
+ dst_release(skb->dst);
+@@ -615,6 +622,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
+ if (dev_net(dev) != sock_net(sk))
+ goto drop;
+
++ skb_orphan(skb);
++
+ if (dev->header_ops) {
+ if (sk->sk_type != SOCK_DGRAM)
+ skb_push(skb, skb->data - skb_mac_header(skb));
+@@ -664,6 +673,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
+ snaplen = 0;
+ }
+
++ if (copy_skb &&
++ ub_sockrcvbuf_charge(sk, copy_skb)) {
++ spin_lock(&sk->sk_receive_queue.lock);
++ goto ring_is_full;
++ }
++
+ spin_lock(&sk->sk_receive_queue.lock);
+ h.raw = packet_lookup_frame(po, po->head, TP_STATUS_KERNEL);
+ if (!h.raw)
+@@ -1049,6 +1064,8 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
+ sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);
+ if (sk == NULL)
+ goto out;
++ if (ub_other_sock_charge(sk))
++ goto out_free;
+
+ sock->ops = &packet_ops;
+ if (sock->type == SOCK_PACKET)
+@@ -1086,6 +1103,9 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
+ sk_add_node(sk, &net->packet.sklist);
+ write_unlock_bh(&net->packet.sklist_lock);
+ return(0);
++
++out_free:
++ sk_free(sk);
+ out:
+ return err;
+ }
+diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
+index 8b06fa9..9a20704 100644
+--- a/net/sched/sch_cbq.c
++++ b/net/sched/sch_cbq.c
+@@ -906,8 +906,8 @@ cbq_dequeue_prio(struct Qdisc *sch, int prio)
+
+ if (cl->deficit <= 0) {
+ q->active[prio] = cl;
+- cl = cl->next_alive;
+ cl->deficit += cl->quantum;
++ cl = cl->next_alive;
+ }
+ return skb;
+
+@@ -1080,17 +1080,19 @@ static void cbq_normalize_quanta(struct cbq_sched_data *q, int prio)
+
+ for (h = 0; h < q->clhash.hashsize; h++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[h], common.hnode) {
++ long mtu;
+ /* BUGGGG... Beware! This expression suffer of
+ arithmetic overflows!
+ */
+ if (cl->priority == prio) {
+- cl->quantum = (cl->weight*cl->allot*q->nclasses[prio])/
+- q->quanta[prio];
+- }
+- if (cl->quantum <= 0 || cl->quantum>32*qdisc_dev(cl->qdisc)->mtu) {
+- printk(KERN_WARNING "CBQ: class %08x has bad quantum==%ld, repaired.\n", cl->common.classid, cl->quantum);
+- cl->quantum = qdisc_dev(cl->qdisc)->mtu/2 + 1;
++ cl->quantum = (cl->weight * cl->allot) /
++ (q->quanta[prio] / q->nclasses[prio]);
+ }
++ mtu = qdisc_dev(cl->qdisc)->mtu;
++ if (cl->quantum <= mtu/2)
++ cl->quantum = mtu/2 + 1;
++ else if (cl->quantum > 32*mtu)
++ cl->quantum = 32*mtu;
+ }
+ }
+ }
+diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
+index ec0a083..3777682 100644
+--- a/net/sched/sch_generic.c
++++ b/net/sched/sch_generic.c
+@@ -121,11 +121,13 @@ static inline int qdisc_restart(struct Qdisc *q)
+ struct net_device *dev;
+ spinlock_t *root_lock;
+ struct sk_buff *skb;
++ struct ve_struct *old_ve;
+
+ /* Dequeue packet */
+ if (unlikely((skb = dequeue_skb(q)) == NULL))
+ return 0;
+
++ old_ve = set_exec_env(skb->owner_env);
+ root_lock = qdisc_lock(q);
+
+ /* And release qdisc */
+@@ -167,6 +169,8 @@ static inline int qdisc_restart(struct Qdisc *q)
+ netif_tx_queue_frozen(txq)))
+ ret = 0;
+
++ (void)set_exec_env(old_ve);
++
+ return ret;
+ }
+
+diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
+index d35ef05..01ca7fb 100644
+--- a/net/sched/sch_teql.c
++++ b/net/sched/sch_teql.c
+@@ -182,6 +182,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt)
+ struct teql_master *m = (struct teql_master*)sch->ops;
+ struct teql_sched_data *q = qdisc_priv(sch);
+
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++
+ if (dev->hard_header_len > m->dev->hard_header_len)
+ return -EINVAL;
+
+diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
+index a1f654a..68605a7 100644
+--- a/net/sctp/ulpevent.c
++++ b/net/sctp/ulpevent.c
+@@ -701,7 +701,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
+ if (rx_count >= asoc->base.sk->sk_rcvbuf) {
+
+ if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
+- (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize)))
++ (!sk_rmem_schedule(asoc->base.sk, chunk->skb)))
+ goto fail;
+ }
+
+diff --git a/net/socket.c b/net/socket.c
+index 3e8d4e3..347066e 100644
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -86,6 +86,7 @@
+ #include <linux/kmod.h>
+ #include <linux/audit.h>
+ #include <linux/wireless.h>
++#include <linux/in.h>
+ #include <linux/nsproxy.h>
+
+ #include <asm/uaccess.h>
+@@ -162,15 +163,6 @@ static DEFINE_PER_CPU(int, sockets_in_use) = 0;
+ * divide and look after the messy bits.
+ */
+
+-#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
+- 16 for IP, 16 for IPX,
+- 24 for IPv6,
+- about 80 for AX.25
+- must be at least one bigger than
+- the AF_UNIX size (see net/unix/af_unix.c
+- :unix_mkname()).
+- */
+-
+ /**
+ * move_addr_to_kernel - copy a socket address into kernel space
+ * @uaddr: Address in user space
+@@ -192,6 +184,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr)
+ return -EFAULT;
+ return audit_sockaddr(ulen, kaddr);
+ }
++EXPORT_SYMBOL(move_addr_to_kernel);
+
+ /**
+ * move_addr_to_user - copy an address to user space
+@@ -499,6 +492,8 @@ static struct socket *sock_alloc(void)
+ return sock;
+ }
+
++EXPORT_SYMBOL(sock_alloc);
++
+ /*
+ * In theory you can't get an open on this inode, but /proc provides
+ * a back door. Remember to keep it shut otherwise you'll let the
+@@ -526,6 +521,9 @@ const struct file_operations bad_sock_fops = {
+
+ void sock_release(struct socket *sock)
+ {
++ if (sock->sk)
++ ub_sock_sndqueuedel(sock->sk);
++
+ if (sock->ops) {
+ struct module *owner = sock->ops->owner;
+
+@@ -1093,6 +1091,49 @@ call_kill:
+ return 0;
+ }
+
++int vz_security_family_check(int family)
++{
++#ifdef CONFIG_VE
++ if (ve_is_super(get_exec_env()))
++ return 0;
++
++ switch (family) {
++ case PF_UNSPEC:
++ case PF_PACKET:
++ case PF_NETLINK:
++ case PF_UNIX:
++ case PF_INET:
++ case PF_INET6:
++ break;
++ default:
++ return -EAFNOSUPPORT;
++ }
++#endif
++ return 0;
++}
++EXPORT_SYMBOL_GPL(vz_security_family_check);
++
++int vz_security_protocol_check(int protocol)
++{
++#ifdef CONFIG_VE
++ if (ve_is_super(get_exec_env()))
++ return 0;
++
++ switch (protocol) {
++ case IPPROTO_IP:
++ case IPPROTO_TCP:
++ case IPPROTO_UDP:
++ case IPPROTO_RAW:
++ case IPPROTO_DCCP:
++ break;
++ default:
++ return -EAFNOSUPPORT;
++ }
++#endif
++ return 0;
++}
++EXPORT_SYMBOL_GPL(vz_security_protocol_check);
++
+ static int __sock_create(struct net *net, int family, int type, int protocol,
+ struct socket **res, int kern)
+ {
+@@ -1123,6 +1164,11 @@ static int __sock_create(struct net *net, int family, int type, int protocol,
+ family = PF_PACKET;
+ }
+
++ /* VZ compatibility layer */
++ err = vz_security_family_check(family);
++ if (err < 0)
++ return err;
++
+ err = security_socket_create(family, type, protocol, kern);
+ if (err)
+ return err;
+@@ -2436,9 +2482,12 @@ int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg)
+ {
+ mm_segment_t oldfs = get_fs();
+ int err;
++ struct ve_struct *old_env;
+
+ set_fs(KERNEL_DS);
++ old_env = set_exec_env(sock->sk->owner_env);
+ err = sock->ops->ioctl(sock, cmd, arg);
++ (void)set_exec_env(old_env);
+ set_fs(oldfs);
+
+ return err;
+diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
+index 76739e9..33bb7d7 100644
+--- a/net/sunrpc/clnt.c
++++ b/net/sunrpc/clnt.c
+@@ -32,6 +32,7 @@
+ #include <linux/utsname.h>
+ #include <linux/workqueue.h>
+ #include <linux/in6.h>
++#include <linux/ve_proto.h>
+
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+@@ -89,6 +90,35 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)
+ spin_unlock(&rpc_client_lock);
+ }
+
++/*
++ * Grand abort timeout (stop the client if occures)
++ */
++int xprt_abort_timeout = RPC_MAX_ABORT_TIMEOUT;
++
++static int rpc_abort_hard(struct rpc_task *task)
++{
++ struct rpc_clnt *clnt;
++ clnt = task->tk_client;
++
++ if (clnt->cl_pr_time == 0) {
++ clnt->cl_pr_time = jiffies;
++ return 0;
++ }
++ if (xprt_abort_timeout == RPC_MAX_ABORT_TIMEOUT)
++ return 0;
++ if (time_before(jiffies, clnt->cl_pr_time + xprt_abort_timeout * HZ))
++ return 0;
++
++ clnt->cl_broken = 1;
++ rpc_killall_tasks(clnt);
++ return -ETIMEDOUT;
++}
++
++static void rpc_abort_clear(struct rpc_task *task)
++{
++ task->tk_client->cl_pr_time = 0;
++}
++
+ static int
+ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
+ {
+@@ -178,6 +208,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
+ clnt->cl_vers = version->number;
+ clnt->cl_stats = program->stats;
+ clnt->cl_metrics = rpc_alloc_iostats(clnt);
++ clnt->cl_broken = 0;
+ err = -ENOMEM;
+ if (clnt->cl_metrics == NULL)
+ goto out_no_stats;
+@@ -293,6 +324,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
+ xprt = xprt_create_transport(&xprtargs);
+ if (IS_ERR(xprt))
+ return (struct rpc_clnt *)xprt;
++ xprt->owner_env = get_ve(get_exec_env());
+
+ /*
+ * By default, kernel RPC client connects from a reserved port.
+@@ -305,13 +337,16 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
+ xprt->resvport = 0;
+
+ clnt = rpc_new_client(args, xprt);
+- if (IS_ERR(clnt))
++ if (IS_ERR(clnt)) {
++ put_ve(xprt->owner_env);
+ return clnt;
++ }
+
+ if (!(args->flags & RPC_CLNT_CREATE_NOPING)) {
+ int err = rpc_ping(clnt, RPC_TASK_SOFT);
+ if (err != 0) {
+ rpc_shutdown_client(clnt);
++ put_ve(xprt->owner_env);
+ return ERR_PTR(err);
+ }
+ }
+@@ -519,6 +554,9 @@ struct rpc_task *rpc_run_task(const struct rpc_task_setup *task_setup_data)
+ {
+ struct rpc_task *task, *ret;
+
++ if (task_setup_data->rpc_client->cl_broken)
++ return ERR_PTR(-EIO);
++
+ task = rpc_new_task(task_setup_data);
+ if (task == NULL) {
+ rpc_release_calldata(task_setup_data->callback_ops,
+@@ -936,6 +974,7 @@ call_bind_status(struct rpc_task *task)
+
+ if (task->tk_status >= 0) {
+ dprint_status(task);
++ rpc_abort_clear(task);
+ task->tk_status = 0;
+ task->tk_action = call_connect;
+ return;
+@@ -959,6 +998,10 @@ call_bind_status(struct rpc_task *task)
+ case -ETIMEDOUT:
+ dprintk("RPC: %5u rpcbind request timed out\n",
+ task->tk_pid);
++ if (rpc_abort_hard(task)) {
++ status = -EIO;
++ break;
++ }
+ goto retry_timeout;
+ case -EPFNOSUPPORT:
+ /* server doesn't support any rpcbind version we know of */
+@@ -1024,18 +1067,21 @@ call_connect_status(struct rpc_task *task)
+
+ /* Something failed: remote service port may have changed */
+ rpc_force_rebind(clnt);
++ if (rpc_abort_hard(task))
++ goto exit;
+
+ switch (status) {
+ case -ENOTCONN:
+ case -EAGAIN:
+ task->tk_action = call_bind;
+- if (!RPC_IS_SOFT(task))
++ if (RPC_IS_SOFT(task) || rpc_abort_hard(task))
+ return;
+ /* if soft mounted, test if we've timed out */
+ case -ETIMEDOUT:
+ task->tk_action = call_timeout;
+ return;
+ }
++exit:
+ rpc_exit(task, -EIO);
+ }
+
+@@ -1174,7 +1220,7 @@ call_timeout(struct rpc_task *task)
+ dprintk("RPC: %5u call_timeout (major)\n", task->tk_pid);
+ task->tk_timeouts++;
+
+- if (RPC_IS_SOFT(task)) {
++ if (RPC_IS_SOFT(task) || rpc_abort_hard(task)) {
+ if (clnt->cl_chatty)
+ printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
+ clnt->cl_protname, clnt->cl_server);
+@@ -1222,6 +1268,7 @@ call_decode(struct rpc_task *task)
+ task->tk_flags &= ~RPC_CALL_MAJORSEEN;
+ }
+
++ rpc_abort_clear(task);
+ /*
+ * Ensure that we see all writes made by xprt_complete_rqst()
+ * before it changed req->rq_received.
+@@ -1234,7 +1281,7 @@ call_decode(struct rpc_task *task)
+ sizeof(req->rq_rcv_buf)) != 0);
+
+ if (req->rq_rcv_buf.len < 12) {
+- if (!RPC_IS_SOFT(task)) {
++ if (!RPC_IS_SOFT(task) && !rpc_abort_hard(task)) {
+ task->tk_action = call_bind;
+ clnt->cl_stats->rpcretrans++;
+ goto out_retry;
+@@ -1581,3 +1628,67 @@ void rpc_show_tasks(void)
+ spin_unlock(&rpc_client_lock);
+ }
+ #endif
++
++#ifdef CONFIG_VE
++static int ve_sunrpc_start(void *data)
++{
++ return 0;
++}
++
++void ve_sunrpc_stop(void *data)
++{
++ struct ve_struct *ve = (struct ve_struct *)data;
++ struct rpc_clnt *clnt;
++ struct rpc_task *rovr;
++
++ dprintk("RPC: killing all tasks for VE %d\n", ve->veid);
++
++ spin_lock(&rpc_client_lock);
++ list_for_each_entry(clnt, &all_clients, cl_clients) {
++ if (clnt->cl_xprt->owner_env != ve)
++ continue;
++
++ spin_lock(&clnt->cl_lock);
++ list_for_each_entry(rovr, &clnt->cl_tasks, tk_task) {
++ if (!RPC_IS_ACTIVATED(rovr))
++ continue;
++ printk(KERN_WARNING "RPC: Killing task %d client %p\n",
++ rovr->tk_pid, clnt);
++
++ rovr->tk_flags |= RPC_TASK_KILLED;
++ rpc_exit(rovr, -EIO);
++ rpc_wake_up_queued_task(rovr->tk_waitqueue, rovr);
++ }
++ schedule_work(&clnt->cl_xprt->task_cleanup);
++ spin_unlock(&clnt->cl_lock);
++ }
++ spin_unlock(&rpc_client_lock);
++
++ flush_scheduled_work();
++}
++
++static struct ve_hook sunrpc_hook = {
++ .init = ve_sunrpc_start,
++ .fini = ve_sunrpc_stop,
++ .owner = THIS_MODULE,
++ .priority = HOOK_PRIO_NET_PRE,
++};
++
++void ve_sunrpc_hook_register(void)
++{
++ ve_hook_register(VE_SS_CHAIN, &sunrpc_hook);
++}
++
++void ve_sunrpc_hook_unregister(void)
++{
++ ve_hook_unregister(&sunrpc_hook);
++}
++#else
++void ve_sunrpc_hook_register(void)
++{
++}
++
++void ve_sunrpc_hook_unregister(void)
++{
++}
++#endif
+diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
+index 23a2b8f..469a783 100644
+--- a/net/sunrpc/rpc_pipe.c
++++ b/net/sunrpc/rpc_pipe.c
+@@ -894,6 +894,7 @@ static struct file_system_type rpc_pipe_fs_type = {
+ .name = "rpc_pipefs",
+ .get_sb = rpc_get_sb,
+ .kill_sb = kill_litter_super,
++ .fs_flags = FS_VIRTUALIZED,
+ };
+
+ static void
+diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
+index 385f427..08d0209 100644
+--- a/net/sunrpc/sched.c
++++ b/net/sunrpc/sched.c
+@@ -608,7 +608,9 @@ void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata)
+ static void __rpc_execute(struct rpc_task *task)
+ {
+ int status = 0;
++ struct ve_struct *env;
+
++ env = set_exec_env(task->tk_client->cl_xprt->owner_env);
+ dprintk("RPC: %5u __rpc_execute flags=0x%x\n",
+ task->tk_pid, task->tk_flags);
+
+@@ -650,10 +652,14 @@ static void __rpc_execute(struct rpc_task *task)
+ rpc_clear_running(task);
+ if (RPC_IS_ASYNC(task)) {
+ /* Careful! we may have raced... */
+- if (RPC_IS_QUEUED(task))
++ if (RPC_IS_QUEUED(task)) {
++ (void)set_exec_env(env);
+ return;
+- if (rpc_test_and_set_running(task))
++ }
++ if (rpc_test_and_set_running(task)) {
++ (void)set_exec_env(env);
+ return;
++ }
+ continue;
+ }
+
+@@ -682,6 +688,7 @@ static void __rpc_execute(struct rpc_task *task)
+ task->tk_status);
+ /* Release all resources associated with the task */
+ rpc_release_task(task);
++ (void)set_exec_env(env);
+ }
+
+ /*
+diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
+index 843629f..94c3fb0 100644
+--- a/net/sunrpc/sunrpc_syms.c
++++ b/net/sunrpc/sunrpc_syms.c
+@@ -24,6 +24,9 @@
+
+ extern struct cache_detail ip_map_cache, unix_gid_cache;
+
++extern void ve_sunrpc_hook_register(void);
++extern void ve_sunrpc_hook_unregister(void);
++
+ static int __init
+ init_sunrpc(void)
+ {
+@@ -46,6 +49,7 @@ init_sunrpc(void)
+ svc_init_xprt_sock(); /* svc sock transport */
+ init_socket_xprt(); /* clnt sock transport */
+ rpcauth_init_module();
++ ve_sunrpc_hook_register();
+ out:
+ return err;
+ }
+@@ -53,6 +57,7 @@ out:
+ static void __exit
+ cleanup_sunrpc(void)
+ {
++ ve_sunrpc_hook_unregister();
+ rpcauth_remove_module();
+ cleanup_socket_xprt();
+ svc_cleanup_xprt_sock();
+diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
+index 3e65719..0d49dfc 100644
+--- a/net/sunrpc/svcsock.c
++++ b/net/sunrpc/svcsock.c
+@@ -178,6 +178,9 @@ static int svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr)
+ unsigned int pglen = xdr->page_len;
+ unsigned int flags = MSG_MORE;
+ RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
++ struct ve_struct *old_env;
++
++ old_env = set_exec_env(sock->sk->owner_env);
+
+ slen = xdr->len;
+
+@@ -238,6 +241,8 @@ out:
+ svsk, xdr->head[0].iov_base, xdr->head[0].iov_len,
+ xdr->len, len, svc_print_addr(rqstp, buf, sizeof(buf)));
+
++ (void)set_exec_env(old_env);
++
+ return len;
+ }
+
+@@ -1225,8 +1230,9 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
+
+ error = sock_create_kern(sin->sa_family, type, protocol, &sock);
+ if (error < 0)
+- return ERR_PTR(error);
++ return ERR_PTR(-ENOMEM);
+
++ sk_change_net_get(sock->sk, get_exec_env()->ve_netns);
+ svc_reclassify_socket(sock);
+
+ if (type == SOCK_STREAM)
+@@ -1267,6 +1273,8 @@ static void svc_sock_detach(struct svc_xprt *xprt)
+
+ dprintk("svc: svc_sock_detach(%p)\n", svsk);
+
++ /* XXX: serialization? */
++ sk->sk_user_data = NULL;
+ /* put back the old socket callbacks */
+ sk->sk_state_change = svsk->sk_ostate;
+ sk->sk_data_ready = svsk->sk_odata;
+diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
+index 99a52aa..9880f38 100644
+--- a/net/sunrpc/xprt.c
++++ b/net/sunrpc/xprt.c
+@@ -568,10 +568,13 @@ static void xprt_autoclose(struct work_struct *work)
+ {
+ struct rpc_xprt *xprt =
+ container_of(work, struct rpc_xprt, task_cleanup);
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
+ xprt->ops->close(xprt);
+ clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
+ xprt_release_write(xprt, NULL);
++ (void)set_exec_env(ve);
+ }
+
+ /**
+@@ -638,7 +641,9 @@ static void
+ xprt_init_autodisconnect(unsigned long data)
+ {
+ struct rpc_xprt *xprt = (struct rpc_xprt *)data;
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
+ spin_lock(&xprt->transport_lock);
+ if (!list_empty(&xprt->recv) || xprt->shutdown)
+ goto out_abort;
+@@ -649,9 +654,11 @@ xprt_init_autodisconnect(unsigned long data)
+ xprt_release_write(xprt, NULL);
+ else
+ queue_work(rpciod_workqueue, &xprt->task_cleanup);
++ (void)set_exec_env(ve);
+ return;
+ out_abort:
+ spin_unlock(&xprt->transport_lock);
++ (void)set_exec_env(ve);
+ }
+
+ /**
+@@ -1044,6 +1051,7 @@ found:
+ xprt->last_used = jiffies;
+ xprt->cwnd = RPC_INITCWND;
+ xprt->bind_index = 0;
++ xprt->owner_env = get_exec_env();
+
+ rpc_init_wait_queue(&xprt->binding, "xprt_binding");
+ rpc_init_wait_queue(&xprt->pending, "xprt_pending");
+diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
+index 4486c59..ea790c1 100644
+--- a/net/sunrpc/xprtsock.c
++++ b/net/sunrpc/xprtsock.c
+@@ -64,6 +64,8 @@ static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
+ static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
+ static unsigned int xprt_min_resvport_limit = RPC_MIN_RESVPORT;
+ static unsigned int xprt_max_resvport_limit = RPC_MAX_RESVPORT;
++static int xprt_min_abort_timeout = RPC_MIN_ABORT_TIMEOUT;
++static int xprt_max_abort_timeout = RPC_MAX_ABORT_TIMEOUT;
+
+ static struct ctl_table_header *sunrpc_table_header;
+
+@@ -117,6 +119,16 @@ static ctl_table xs_tunables_table[] = {
+ .extra2 = &xprt_max_resvport_limit
+ },
+ {
++ .procname = "abort_timeout",
++ .data = &xprt_abort_timeout,
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec_minmax,
++ .strategy = &sysctl_intvec,
++ .extra1 = &xprt_min_abort_timeout,
++ .extra2 = &xprt_max_abort_timeout
++ },
++ {
+ .ctl_name = 0,
+ },
+ };
+@@ -752,18 +764,23 @@ out_release:
+ static void xs_close(struct rpc_xprt *xprt)
+ {
+ struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
+- struct socket *sock = transport->sock;
+- struct sock *sk = transport->inet;
+-
+- if (!sk)
+- goto clear_close_wait;
++ struct socket *sock;
++ struct sock *sk;
+
+ dprintk("RPC: xs_close xprt %p\n", xprt);
+
+- write_lock_bh(&sk->sk_callback_lock);
++ spin_lock_bh(&xprt->transport_lock);
++ if (transport->sock == NULL) {
++ spin_unlock_bh(&xprt->transport_lock);
++ goto clear_close_wait;
++ }
++ sock = transport->sock;
++ sk = transport->inet;
+ transport->inet = NULL;
+ transport->sock = NULL;
++ spin_unlock_bh(&xprt->transport_lock);
+
++ write_lock_bh(&sk->sk_callback_lock);
+ sk->sk_user_data = NULL;
+ sk->sk_data_ready = transport->old_data_ready;
+ sk->sk_state_change = transport->old_state_change;
+@@ -1487,7 +1504,12 @@ static void xs_udp_connect_worker4(struct work_struct *work)
+ struct rpc_xprt *xprt = &transport->xprt;
+ struct socket *sock = transport->sock;
+ int err, status = -EIO;
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
++ down_read(&xprt->owner_env->op_sem);
++ if (!xprt->owner_env->is_running)
++ goto out;
+ if (xprt->shutdown || !xprt_bound(xprt))
+ goto out;
+
+@@ -1498,6 +1520,7 @@ static void xs_udp_connect_worker4(struct work_struct *work)
+ dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
+ goto out;
+ }
++ sk_change_net_get(sock->sk, xprt->owner_env->ve_netns);
+ xs_reclassify_socket4(sock);
+
+ if (xs_bind4(transport, sock)) {
+@@ -1513,6 +1536,8 @@ static void xs_udp_connect_worker4(struct work_struct *work)
+ out:
+ xprt_wake_pending_tasks(xprt, status);
+ xprt_clear_connecting(xprt);
++ up_read(&xprt->owner_env->op_sem);
++ (void)set_exec_env(ve);
+ }
+
+ /**
+@@ -1528,7 +1553,12 @@ static void xs_udp_connect_worker6(struct work_struct *work)
+ struct rpc_xprt *xprt = &transport->xprt;
+ struct socket *sock = transport->sock;
+ int err, status = -EIO;
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
++ down_read(&xprt->owner_env->op_sem);
++ if (!xprt->owner_env->is_running)
++ goto out;
+ if (xprt->shutdown || !xprt_bound(xprt))
+ goto out;
+
+@@ -1539,6 +1569,7 @@ static void xs_udp_connect_worker6(struct work_struct *work)
+ dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
+ goto out;
+ }
++ sk_change_net_get(sock->sk, xprt->owner_env->ve_netns);
+ xs_reclassify_socket6(sock);
+
+ if (xs_bind6(transport, sock) < 0) {
+@@ -1554,6 +1585,8 @@ static void xs_udp_connect_worker6(struct work_struct *work)
+ out:
+ xprt_wake_pending_tasks(xprt, status);
+ xprt_clear_connecting(xprt);
++ up_read(&xprt->owner_env->op_sem);
++ (void)set_exec_env(ve);
+ }
+
+ /*
+@@ -1632,7 +1665,12 @@ static void xs_tcp_connect_worker4(struct work_struct *work)
+ struct rpc_xprt *xprt = &transport->xprt;
+ struct socket *sock = transport->sock;
+ int err, status = -EIO;
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
++ down_read(&xprt->owner_env->op_sem);
++ if (!xprt->owner_env->is_running)
++ goto out;
+ if (xprt->shutdown || !xprt_bound(xprt))
+ goto out;
+
+@@ -1642,6 +1680,7 @@ static void xs_tcp_connect_worker4(struct work_struct *work)
+ dprintk("RPC: can't create TCP transport socket (%d).\n", -err);
+ goto out;
+ }
++ sk_change_net_get(sock->sk, xprt->owner_env->ve_netns);
+ xs_reclassify_socket4(sock);
+
+ if (xs_bind4(transport, sock) < 0) {
+@@ -1677,6 +1716,8 @@ out:
+ xprt_wake_pending_tasks(xprt, status);
+ out_clear:
+ xprt_clear_connecting(xprt);
++ up_read(&xprt->owner_env->op_sem);
++ (void)set_exec_env(ve);
+ }
+
+ /**
+@@ -1692,7 +1733,12 @@ static void xs_tcp_connect_worker6(struct work_struct *work)
+ struct rpc_xprt *xprt = &transport->xprt;
+ struct socket *sock = transport->sock;
+ int err, status = -EIO;
++ struct ve_struct *ve;
+
++ ve = set_exec_env(xprt->owner_env);
++ down_read(&xprt->owner_env->op_sem);
++ if (!xprt->owner_env->is_running)
++ goto out;
+ if (xprt->shutdown || !xprt_bound(xprt))
+ goto out;
+
+@@ -1702,6 +1748,7 @@ static void xs_tcp_connect_worker6(struct work_struct *work)
+ dprintk("RPC: can't create TCP transport socket (%d).\n", -err);
+ goto out;
+ }
++ sk_change_net_get(sock->sk, xprt->owner_env->ve_netns);
+ xs_reclassify_socket6(sock);
+
+ if (xs_bind6(transport, sock) < 0) {
+@@ -1736,6 +1783,8 @@ out:
+ xprt_wake_pending_tasks(xprt, status);
+ out_clear:
+ xprt_clear_connecting(xprt);
++ up_read(&xprt->owner_env->op_sem);
++ (void)set_exec_env(ve);
+ }
+
+ /**
+diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
+index 015606b..b3f85a4 100644
+--- a/net/unix/af_unix.c
++++ b/net/unix/af_unix.c
+@@ -115,6 +115,9 @@
+ #include <net/checksum.h>
+ #include <linux/security.h>
+
++#include <bc/net.h>
++#include <bc/beancounter.h>
++
+ static struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
+ static DEFINE_SPINLOCK(unix_table_lock);
+ static atomic_t unix_nr_socks = ATOMIC_INIT(0);
+@@ -591,6 +594,8 @@ static struct sock * unix_create1(struct net *net, struct socket *sock)
+ sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_proto);
+ if (!sk)
+ goto out;
++ if (ub_other_sock_charge(sk))
++ goto out_sk_free;
+
+ sock_init_data(sock,sk);
+ lockdep_set_class(&sk->sk_receive_queue.lock,
+@@ -612,6 +617,9 @@ out:
+ if (sk == NULL)
+ atomic_dec(&unix_nr_socks);
+ return sk;
++out_sk_free:
++ sk_free(sk);
++ return NULL;
+ }
+
+ static int unix_create(struct net *net, struct socket *sock, int protocol)
+@@ -1013,6 +1021,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+ int st;
+ int err;
+ long timeo;
++ unsigned long chargesize;
+
+ err = unix_mkname(sunaddr, addr_len, &hash);
+ if (err < 0)
+@@ -1041,6 +1050,10 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+ skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
++ chargesize = skb_charge_fullsize(skb);
++ if (ub_sock_getwres_other(newsk, chargesize) < 0)
++ goto out;
++ ub_skb_set_charge(skb, newsk, chargesize, UB_OTHERSOCKBUF);
+
+ restart:
+ /* Find listening sock. */
+@@ -1288,7 +1301,7 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ unix_notinflight(scm->fp->fp[i]);
+ }
+
+-static void unix_destruct_fds(struct sk_buff *skb)
++void unix_destruct_fds(struct sk_buff *skb)
+ {
+ struct scm_cookie scm;
+ memset(&scm, 0, sizeof(scm));
+@@ -1299,6 +1312,7 @@ static void unix_destruct_fds(struct sk_buff *skb)
+ scm_destroy(&scm);
+ sock_wfree(skb);
+ }
++EXPORT_SYMBOL_GPL(unix_destruct_fds);
+
+ static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+ {
+@@ -1510,6 +1524,16 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+
+ size = len-sent;
+
++ if (msg->msg_flags & MSG_DONTWAIT)
++ ub_sock_makewres_other(sk, skb_charge_size(size));
++ if (sock_bc(sk) != NULL &&
++ sock_bc(sk)->poll_reserv >=
++ SOCK_MIN_UBCSPACE &&
++ skb_charge_size(size) >
++ sock_bc(sk)->poll_reserv)
++ size = skb_charge_datalen(sock_bc(sk)->poll_reserv);
++
++
+ /* Keep two messages in the pipe so it schedules better */
+ if (size > ((sk->sk_sndbuf >> 1) - 64))
+ size = (sk->sk_sndbuf >> 1) - 64;
+@@ -1521,7 +1545,9 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
+ * Grab a buffer
+ */
+
+- skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err);
++
++ skb = sock_alloc_send_skb2(sk, size, SOCK_MIN_UBCSPACE,
++ msg->msg_flags&MSG_DONTWAIT, &err);
+
+ if (skb==NULL)
+ goto out_err;
+@@ -1961,6 +1987,7 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl
+ {
+ struct sock *sk = sock->sk;
+ unsigned int mask;
++ int no_ub_res;
+
+ poll_wait(file, sk->sk_sleep, wait);
+ mask = 0;
+@@ -1973,6 +2000,10 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl
+ if (sk->sk_shutdown & RCV_SHUTDOWN)
+ mask |= POLLRDHUP;
+
++ no_ub_res = ub_sock_makewres_other(sk, SOCK_MIN_UBCSPACE_CH);
++ if (no_ub_res)
++ ub_sock_sndqueueadd_other(sk, SOCK_MIN_UBCSPACE_CH);
++
+ /* readable? */
+ if (!skb_queue_empty(&sk->sk_receive_queue) ||
+ (sk->sk_shutdown & RCV_SHUTDOWN))
+@@ -1986,7 +2017,7 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl
+ * we set writable also when the other side has shut down the
+ * connection. This prevents stuck sockets.
+ */
+- if (unix_writable(sk))
++ if (!no_ub_res && unix_writable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+diff --git a/net/unix/garbage.c b/net/unix/garbage.c
+index 2a27b84..d4dddb7 100644
+--- a/net/unix/garbage.c
++++ b/net/unix/garbage.c
+@@ -80,6 +80,7 @@
+ #include <linux/file.h>
+ #include <linux/proc_fs.h>
+ #include <linux/mutex.h>
++#include <linux/module.h>
+
+ #include <net/sock.h>
+ #include <net/af_unix.h>
+@@ -151,6 +152,7 @@ void unix_notinflight(struct file *fp)
+ spin_unlock(&unix_gc_lock);
+ }
+ }
++EXPORT_SYMBOL_GPL(unix_notinflight);
+
+ static inline struct sk_buff *sock_queue_head(struct sock *sk)
+ {
+diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
+index 04c4150..aa0bad6 100644
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -1947,7 +1947,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ link = &xfrm_dispatch[type];
+
+ /* All operations require privileges, even GET */
+- if (security_netlink_recv(skb, CAP_NET_ADMIN))
++ if (security_netlink_recv(skb, CAP_VE_NET_ADMIN))
+ return -EPERM;
+
+ if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
+diff --git a/security/Kconfig b/security/Kconfig
+index 5592939..8447040 100644
+--- a/security/Kconfig
++++ b/security/Kconfig
+@@ -41,7 +41,7 @@ config KEYS_DEBUG_PROC_KEYS
+
+ config SECURITY
+ bool "Enable different security models"
+- depends on SYSFS
++ depends on SYSFS && !VE
+ help
+ This allows you to choose different security modules to be
+ configured into your kernel.
+diff --git a/security/commoncap.c b/security/commoncap.c
+index e4c4b3f..3d956c0 100644
+--- a/security/commoncap.c
++++ b/security/commoncap.c
+@@ -35,6 +35,10 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
+
+ int cap_netlink_recv(struct sk_buff *skb, int cap)
+ {
++ if (likely(cap == CAP_VE_NET_ADMIN) &&
++ cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
++ return 0;
++
+ if (!cap_raised(NETLINK_CB(skb).eff_cap, cap))
+ return -EPERM;
+ return 0;
+@@ -420,7 +424,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
+ return 0;
+ } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+- !capable(CAP_SYS_ADMIN))
++ !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_ADMIN))
+ return -EPERM;
+ return 0;
+ }
+@@ -433,7 +437,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
+ return 0;
+ } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+- !capable(CAP_SYS_ADMIN))
++ !capable(CAP_SYS_ADMIN) && !capable(CAP_VE_ADMIN))
+ return -EPERM;
+ return 0;
+ }
+@@ -696,7 +700,7 @@ void cap_task_reparent_to_init (struct task_struct *p)
+
+ int cap_syslog (int type)
+ {
+- if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
++ if ((type != 3 && type != 10) && !capable(CAP_VE_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+ }
+diff --git a/security/device_cgroup.c b/security/device_cgroup.c
+index 46f2397..f782966 100644
+--- a/security/device_cgroup.c
++++ b/security/device_cgroup.c
+@@ -10,11 +10,23 @@
+ #include <linux/list.h>
+ #include <linux/uaccess.h>
+ #include <linux/seq_file.h>
++#include <linux/ve.h>
++#include <linux/vzcalluser.h>
++#include <linux/major.h>
+
+ #define ACC_MKNOD 1
+ #define ACC_READ 2
+ #define ACC_WRITE 4
+-#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
++#define ACC_QUOTA 8
++#define ACC_HIDDEN 16
++#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE | ACC_QUOTA)
++
++static inline int convert_bits(int acc)
++{
++ /* ...10x <-> ...01x trial: guess hwy */
++ return ((((acc & 06) == 00) || ((acc & 06) == 06)) ? acc : acc ^06) &
++ (ACC_READ | ACC_WRITE | ACC_QUOTA);
++}
+
+ #define DEV_BLOCK 1
+ #define DEV_CHAR 2
+@@ -79,6 +91,38 @@ static int devcgroup_can_attach(struct cgroup_subsys *ss,
+ /*
+ * called under cgroup_lock()
+ */
++#ifdef CONFIG_VE
++static struct dev_whitelist_item default_whitelist_items[] = {
++ { ~0, ~0, DEV_ALL, ACC_MKNOD },
++ { UNIX98_PTY_SLAVE_MAJOR, ~0, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { UNIX98_PTY_SLAVE_MAJOR, ~0, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { PTY_MASTER_MAJOR, ~0, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { PTY_SLAVE_MAJOR, ~0, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { MEM_MAJOR, /* null */ 3, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { MEM_MAJOR, /* zero */ 5, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { MEM_MAJOR, /* full */ 7, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { TTYAUX_MAJOR, /* tty */ 0, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { TTYAUX_MAJOR, /* ptmx */ 2, DEV_CHAR, ACC_READ | ACC_WRITE },
++ { MEM_MAJOR, /* random */ 8, DEV_CHAR, ACC_READ },
++ { MEM_MAJOR, /* urandom */ 9, DEV_CHAR, ACC_READ },
++};
++
++static LIST_HEAD(default_perms);
++#define parent_whitelist(p) (&default_perms)
++static void prepare_def_perms(void)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(default_whitelist_items); i++) {
++ default_whitelist_items[i].access |= ACC_HIDDEN;
++ list_add(&default_whitelist_items[i].list, &default_perms);
++ }
++}
++#else
++#define prepare_def_perms() do { } while(0)
++#define parent_whitelist(p) (&parent_dev_cgroup->whitelist)
++#endif
++
+ static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
+ {
+ struct dev_whitelist_item *wh, *tmp, *new;
+@@ -204,10 +248,12 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
+ wh->type = DEV_ALL;
+ wh->access = ACC_MASK;
+ list_add(&wh->list, &dev_cgroup->whitelist);
++
++ prepare_def_perms();
+ } else {
+ parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
+ ret = dev_whitelist_copy(&dev_cgroup->whitelist,
+- &parent_dev_cgroup->whitelist);
++ parent_whitelist(parent_dev_cgroup));
+ if (ret) {
+ kfree(dev_cgroup);
+ return ERR_PTR(ret);
+@@ -282,8 +328,15 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
+ set_access(acc, wh->access);
+ set_majmin(maj, wh->major);
+ set_majmin(min, wh->minor);
+- seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
+- maj, min, acc);
++
++ if (cft != NULL)
++ seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
++ maj, min, acc);
++ else if (!(wh->access & ACC_HIDDEN))
++ seq_printf(m, "%10u %c %03o %s:%s\n",
++ (unsigned)(unsigned long)m->private,
++ type_to_char(wh->type),
++ convert_bits(wh->access), maj, min);
+ }
+ rcu_read_unlock();
+
+@@ -498,37 +551,35 @@ struct cgroup_subsys devices_subsys = {
+ .subsys_id = devices_subsys_id,
+ };
+
+-int devcgroup_inode_permission(struct inode *inode, int mask)
++static int __devcgroup_inode_permission(int blk, dev_t device, int mask)
+ {
+ struct dev_cgroup *dev_cgroup;
+ struct dev_whitelist_item *wh;
+
+- dev_t device = inode->i_rdev;
+ if (!device)
+ return 0;
+- if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
+- return 0;
+
+ rcu_read_lock();
+-
+ dev_cgroup = task_devcgroup(current);
+
+ list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
+ if (wh->type & DEV_ALL)
+ goto acc_check;
+- if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
++ if ((wh->type & DEV_BLOCK) && !blk)
+ continue;
+- if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))
++ if ((wh->type & DEV_CHAR) && blk)
+ continue;
+- if (wh->major != ~0 && wh->major != imajor(inode))
++ if (wh->major != ~0 && wh->major != MAJOR(device))
+ continue;
+- if (wh->minor != ~0 && wh->minor != iminor(inode))
++ if (wh->minor != ~0 && wh->minor != MINOR(device))
+ continue;
+ acc_check:
+ if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))
+ continue;
+ if ((mask & MAY_READ) && !(wh->access & ACC_READ))
+ continue;
++ if ((mask & MAY_QUOTACTL) && !(wh->access & ACC_QUOTA))
++ continue;
+ rcu_read_unlock();
+ return 0;
+ }
+@@ -538,6 +589,15 @@ acc_check:
+ return -EPERM;
+ }
+
++int devcgroup_inode_permission(struct inode *inode, int mask)
++{
++ if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
++ return 0;
++
++ return __devcgroup_inode_permission(S_ISBLK(inode->i_mode),
++ inode->i_rdev, mask);
++}
++
+ int devcgroup_inode_mknod(int mode, dev_t dev)
+ {
+ struct dev_cgroup *dev_cgroup;
+@@ -569,3 +629,75 @@ acc_check:
+
+ return -EPERM;
+ }
++
++#ifdef CONFIG_VE
++int get_device_perms_ve(int dev_type, dev_t dev, int access_mode)
++{
++ int mask = 0;
++
++ mask |= (access_mode & FMODE_READ ? MAY_READ : 0);
++ mask |= (access_mode & FMODE_WRITE ? MAY_WRITE : 0);
++ mask |= (access_mode & FMODE_QUOTACTL ? MAY_QUOTACTL : 0);
++
++ return __devcgroup_inode_permission(dev_type == S_IFBLK, dev, mask);
++}
++EXPORT_SYMBOL(get_device_perms_ve);
++
++int set_device_perms_ve(struct ve_struct *ve,
++ unsigned type, dev_t dev, unsigned mask)
++{
++ int err = -EINVAL;
++ struct dev_whitelist_item *new;
++
++ new = kzalloc(sizeof(*new), GFP_KERNEL);
++ if (new == NULL)
++ return -ENOMEM;
++
++ if ((type & S_IFMT) == S_IFBLK)
++ new->type = DEV_BLOCK;
++ else if ((type & S_IFMT) == S_IFCHR)
++ new->type = DEV_CHAR;
++ else
++ goto out;
++
++ new->access = convert_bits(mask);
++ new->major = new->minor = ~0;
++
++ switch (type & VE_USE_MASK) {
++ default:
++ new->minor = MINOR(dev);
++ case VE_USE_MAJOR:
++ new->major = MAJOR(dev);
++ case 0:
++ ;
++ }
++
++ err = dev_whitelist_add(cgroup_to_devcgroup(ve->ve_cgroup), new);
++out:
++ if (err < 0)
++ kfree(new);
++ return err;
++}
++EXPORT_SYMBOL(set_device_perms_ve);
++
++#ifdef CONFIG_PROC_FS
++int devperms_seq_show(struct seq_file *m, void *v)
++{
++ struct ve_struct *ve = list_entry(v, struct ve_struct, ve_list);
++
++ if (m->private == (void *)0) {
++ seq_printf(m, "Version: 2.7\n");
++ m->private = (void *)-1;
++ }
++
++ if (ve_is_super(ve)) {
++ seq_printf(m, "%10u b 016 *:*\n%10u c 006 *:*\n", 0, 0);
++ return 0;
++ }
++
++ m->private = (void *)(unsigned long)ve->veid;
++ return devcgroup_seq_read(ve->ve_cgroup, NULL, m);
++}
++EXPORT_SYMBOL(devperms_seq_show);
++#endif
++#endif
+diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
+index a436d1c..130a8be 100644
+--- a/security/selinux/Kconfig
++++ b/security/selinux/Kconfig
+@@ -1,6 +1,6 @@
+ config SECURITY_SELINUX
+ bool "NSA SELinux Support"
+- depends on SECURITY_NETWORK && AUDIT && NET && INET
++ depends on SECURITY_NETWORK && AUDIT && NET && INET && !VE
+ select NETWORK_SECMARK
+ default n
+ help
+diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
+index 03fc6a8..ed0f080 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -5225,12 +5225,12 @@ static int selinux_setprocattr(struct task_struct *p,
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+ read_lock(&tasklist_lock);
+- do_each_thread(g, t) {
++ do_each_thread_ve(g, t) {
+ if (t->mm == mm && t != p) {
+ read_unlock(&tasklist_lock);
+ return -EPERM;
+ }
+- } while_each_thread(g, t);
++ } while_each_thread_ve(g, t);
+ read_unlock(&tasklist_lock);
+ }
+
diff --git a/packages/linux/linux-omap_2.6.27.bb b/packages/linux/linux-omap_2.6.27.bb
index cf9c7878e7..7728efa976 100644
--- a/packages/linux/linux-omap_2.6.27.bb
+++ b/packages/linux/linux-omap_2.6.27.bb
@@ -6,7 +6,7 @@ KERNEL_IMAGETYPE = "uImage"
COMPATIBLE_MACHINE = "omap5912osk|omap1710h3|omap2430sdp|omap2420h4|beagleboard|omap3evm"
SRCREV = "2a3408be17f287fdb5809c9b6c68e7ad96d25b74"
-PR = "r0"
+PR = "r1"
SRC_URI = "git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6.git;protocol=git \
file://defconfig"
@@ -32,6 +32,11 @@ SRC_URI_append = " \
file://dvb-fix-dma.diff;patch=1 \
file://0001-Removed-resolution-check-that-prevents-scaling-when.patch;patch=1 \
file://0001-Implement-downsampling-with-debugs.patch;patch=1 \
+ file://openvz/0001-arm-introduce-MAP_EXECPRIO-define.patch;patch=1 \
+ file://openvz/0002-arm-export-arm-version-of-show_mem.patch;patch=1 \
+ file://openvz/0003-arm-wire-OpenVZ-syscalls.patch;patch=1 \
+ file://openvz/0004-arm-add-openvz-and-bc-Kconfigs.patch;patch=1 \
+ file://openvz/openvz-2.6.27.diff;patch=1 \
"