diff --git a/PKGBUILD b/PKGBUILD index 2dd753a..f522bb0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -9,7 +9,7 @@ pkgname=$pkgbase pkgdesc="The Linux Kernel and modules from Linus' git tree" depends=('coreutils' 'linux-firmware-git' 'mkinitcpio') -pkgver=4.5.rc6 +pkgver=4.8.rc8 pkgrel=1 url="http://www.kernel.org/" arch=(i686 x86_64) @@ -17,13 +17,14 @@ license=('GPL2') makedepends=(git bc) options=(!strip) source=($pkgname::git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git) +#source=($pkgname::git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git) md5sums=('SKIP') sha256sums=('SKIP') # set _gitrev to a git revision (man gitrevisions) like a tag, a commit sha1 # hash or a branch name to build from this tree instead of master -_gitrev="v4.4.4" +_gitrev="v4.7.5" #################################################################### # KERNEL CONFIG FILES diff --git a/config.saved.x86_64 b/config.saved.x86_64 index 90c69ed..198fda5 100644 --- a/config.saved.x86_64 +++ b/config.saved.x86_64 @@ -1,21 +1,25 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 4.4.4 Kernel Configuration +# Linux/x86 4.7.5 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y CONFIG_X86=y CONFIG_INSTRUCTION_DECODER=y -CONFIG_PERF_EVENTS_INTEL_UNCORE=y CONFIG_OUTPUT_FORMAT="elf64-x86-64" CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y -CONFIG_HAVE_LATENCYTOP_SUPPORT=y CONFIG_MMU=y +CONFIG_ARCH_MMAP_RND_BITS_MIN=28 +CONFIG_ARCH_MMAP_RND_BITS_MAX=32 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 CONFIG_NEED_DMA_MAP_STATE=y CONFIG_NEED_SG_DMA_LENGTH=y CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y CONFIG_GENERIC_HWEIGHT=y CONFIG_ARCH_MAY_HAVE_PC_FDC=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y @@ -38,6 +42,7 @@ CONFIG_X86_64_SMP=y CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11" CONFIG_ARCH_SUPPORTS_UPROBES=y CONFIG_FIX_EARLYCON_MEM=y +CONFIG_DEBUG_RODATA=y CONFIG_PGTABLE_LEVELS=4 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_IRQ_WORK=y @@ -46,6 +51,7 @@ CONFIG_BUILDTIME_EXTABLE_SORT=y # # General setup # +CONFIG_BLD=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" # CONFIG_COMPILE_TEST is not set @@ -124,47 +130,42 @@ CONFIG_TASK_IO_ACCOUNTING=y # RCU Subsystem # CONFIG_TREE_RCU=y -CONFIG_RCU_EXPERT=y +# CONFIG_RCU_EXPERT is not set CONFIG_SRCU=y # CONFIG_TASKS_RCU is not set CONFIG_RCU_STALL_COMMON=y -CONFIG_RCU_FANOUT=64 -CONFIG_RCU_FANOUT_LEAF=16 -CONFIG_RCU_FAST_NO_HZ=y # CONFIG_TREE_RCU_TRACE is not set -CONFIG_RCU_KTHREAD_PRIO=0 -# CONFIG_RCU_NOCB_CPU is not set # CONFIG_RCU_EXPEDITE_BOOT is not set CONFIG_BUILD_BIN2C=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=19 CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_NMI_LOG_BUF_SHIFT=13 CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y CONFIG_ARCH_SUPPORTS_INT128=y CONFIG_CGROUPS=y -# CONFIG_CGROUP_DEBUG is not set -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_PIDS=y -CONFIG_CGROUP_DEVICE=y -CONFIG_CPUSETS=y -# CONFIG_PROC_PID_CPUSET is not set -CONFIG_CGROUP_CPUACCT=y CONFIG_PAGE_COUNTER=y CONFIG_MEMCG=y # CONFIG_MEMCG_SWAP is not set -# CONFIG_MEMCG_KMEM is not set -# CONFIG_CGROUP_HUGETLB is not set -CONFIG_CGROUP_PERF=y -CONFIG_CGROUP_SCHED=y -CONFIG_FAIR_GROUP_SCHED=y -CONFIG_CFS_BANDWIDTH=y -CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set CONFIG_CGROUP_WRITEBACK=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_HUGETLB is not set +CONFIG_CPUSETS=y +# CONFIG_PROC_PID_CPUSET is not set +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +# CONFIG_CGROUP_PERF is not set +# CONFIG_CGROUP_DEBUG is not set CONFIG_CHECKPOINT_RESTORE=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y @@ -174,7 +175,7 @@ CONFIG_PID_NS=y CONFIG_NET_NS=y CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set -CONFIG_RELAY=y +# CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -183,22 +184,22 @@ CONFIG_RD_GZIP=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set CONFIG_RD_LZ4=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y -CONFIG_HAVE_UID16=y CONFIG_SYSCTL_EXCEPTION_TRACE=y CONFIG_HAVE_PCSPKR_PLATFORM=y CONFIG_BPF=y CONFIG_EXPERT=y -# CONFIG_UID16 is not set CONFIG_MULTIUSER=y # CONFIG_SGETMASK_SYSCALL is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set CONFIG_PRINTK=y -# CONFIG_BUG is not set +CONFIG_PRINTK_NMI=y +CONFIG_BUG=y CONFIG_PCSPKR_PLATFORM=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y @@ -243,8 +244,8 @@ CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_OPTPROBES=y CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_NMI=y CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_ATTRS=y CONFIG_HAVE_DMA_CONTIGUOUS=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y @@ -262,8 +263,6 @@ CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y CONFIG_HAVE_CMPXCHG_LOCAL=y CONFIG_HAVE_CMPXCHG_DOUBLE=y -CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y CONFIG_HAVE_ARCH_SECCOMP_FILTER=y CONFIG_SECCOMP_FILTER=y CONFIG_HAVE_CC_STACKPROTECTOR=y @@ -280,9 +279,14 @@ CONFIG_HAVE_ARCH_SOFT_DIRTY=y CONFIG_MODULES_USE_ELF_RELA=y CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_HAVE_ARCH_MMAP_RND_BITS=y +CONFIG_HAVE_EXIT_THREAD=y +CONFIG_ARCH_MMAP_RND_BITS=28 CONFIG_HAVE_COPY_THREAD_TLS=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_COMPAT_OLD_SIGACTION=y +CONFIG_HAVE_STACK_VALIDATION=y +# CONFIG_HAVE_ARCH_HASH is not set +# CONFIG_ISA_BUS_API is not set +# CONFIG_CPU_NO_EFFICIENT_FFS is not set # # GCOV-based kernel profiling @@ -298,7 +302,7 @@ CONFIG_BLK_DEV_BSG=y CONFIG_BLK_DEV_BSGLIB=y CONFIG_BLK_DEV_INTEGRITY=y CONFIG_BLK_DEV_THROTTLING=y -CONFIG_BLK_CMDLINE_PARSER=y +# CONFIG_BLK_CMDLINE_PARSER is not set # # Partition Types @@ -323,7 +327,6 @@ CONFIG_MSDOS_PARTITION=y CONFIG_EFI_PARTITION=y # CONFIG_SYSV68_PARTITION is not set # CONFIG_CMDLINE_PARTITION is not set -CONFIG_BLOCK_COMPAT=y # # IO Schedulers @@ -362,8 +365,10 @@ CONFIG_FREEZER=y CONFIG_ZONE_DMA=y CONFIG_SMP=y CONFIG_X86_FEATURE_NAMES=y +CONFIG_X86_FAST_FEATURE_TESTS=y CONFIG_X86_X2APIC=y CONFIG_X86_MPPARSE=y +# CONFIG_GOLDFISH is not set # CONFIG_X86_EXTENDED_PLATFORM is not set CONFIG_X86_INTEL_LPSS=y # CONFIG_X86_AMD_PLATFORM_DEVICE is not set @@ -413,8 +418,8 @@ CONFIG_DMI=y # CONFIG_CALGARY_IOMMU is not set CONFIG_SWIOTLB=y CONFIG_IOMMU_HELPER=y -CONFIG_MAXSMP=y -CONFIG_NR_CPUS=8192 +# CONFIG_MAXSMP is not set +CONFIG_NR_CPUS=16 CONFIG_SCHED_SMT=y CONFIG_SCHED_MC=y CONFIG_PREEMPT_NONE=y @@ -429,6 +434,13 @@ CONFIG_X86_MCE_INTEL=y CONFIG_X86_MCE_THRESHOLD=y # CONFIG_X86_MCE_INJECT is not set CONFIG_X86_THERMAL_VECTOR=y + +# +# Performance monitoring +# +CONFIG_PERF_EVENTS_INTEL_UNCORE=y +CONFIG_PERF_EVENTS_INTEL_RAPL=y +CONFIG_PERF_EVENTS_INTEL_CSTATE=y # CONFIG_VM86 is not set # CONFIG_X86_VSYSCALL_EMULATION is not set # CONFIG_I8K is not set @@ -467,11 +479,12 @@ CONFIG_COMPACTION=y CONFIG_MIGRATION=y CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_ZONE_DMA_FLAG=1 CONFIG_BOUNCE=y CONFIG_VIRT_TO_BUS=y CONFIG_MMU_NOTIFIER=y CONFIG_KSM=y +CONFIG_UKSM=y +# CONFIG_KSM_LEGACY is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y CONFIG_MEMORY_FAILURE=y @@ -486,25 +499,29 @@ CONFIG_FRONTSWAP=y CONFIG_ZSWAP=y CONFIG_ZPOOL=y CONFIG_ZBUD=y +CONFIG_Z3FOLD=y CONFIG_ZSMALLOC=y CONFIG_PGTABLE_MAPPING=y # CONFIG_ZSMALLOC_STAT is not set CONFIG_GENERIC_EARLY_IOREMAP=y CONFIG_ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT=y # CONFIG_IDLE_PAGE_TRACKING is not set +CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y +CONFIG_ARCH_HAS_PKEYS=y CONFIG_X86_PMEM_LEGACY_DEVICE=y CONFIG_X86_PMEM_LEGACY=y # CONFIG_X86_CHECK_BIOS_CORRUPTION is not set CONFIG_X86_RESERVE_LOW=64 CONFIG_MTRR=y CONFIG_MTRR_SANITIZER=y -CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1 +CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 CONFIG_X86_PAT=y CONFIG_ARCH_USES_PG_UNCACHED=y CONFIG_ARCH_RANDOM=y CONFIG_X86_SMAP=y CONFIG_X86_INTEL_MPX=y +CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS=y CONFIG_EFI=y CONFIG_EFI_STUB=y # CONFIG_EFI_MIXED is not set @@ -521,13 +538,11 @@ CONFIG_SCHED_HRTICK=y CONFIG_PHYSICAL_START=0x1000000 CONFIG_RELOCATABLE=y CONFIG_RANDOMIZE_BASE=y -CONFIG_RANDOMIZE_BASE_MAX_OFFSET=0x40000000 CONFIG_X86_NEED_RELOCS=y CONFIG_PHYSICAL_ALIGN=0x1000000 CONFIG_HOTPLUG_CPU=y # CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set # CONFIG_DEBUG_HOTPLUG_CPU0 is not set -CONFIG_COMPAT_VDSO=y # CONFIG_LEGACY_VSYSCALL_NATIVE is not set # CONFIG_LEGACY_VSYSCALL_EMULATE is not set CONFIG_LEGACY_VSYSCALL_NONE=y @@ -574,7 +589,7 @@ CONFIG_ACPI_HOTPLUG_CPU=y CONFIG_ACPI_PROCESSOR_AGGREGATOR=y CONFIG_ACPI_THERMAL=y # CONFIG_ACPI_CUSTOM_DSDT is not set -CONFIG_ACPI_INITRD_TABLE_OVERRIDE=y +CONFIG_ACPI_TABLE_UPGRADE=y # CONFIG_ACPI_DEBUG is not set CONFIG_ACPI_PCI_SLOT=y CONFIG_X86_PM_TIMER=y @@ -600,18 +615,21 @@ CONFIG_PMIC_OPREGION=y # CPU Frequency scaling # CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y CONFIG_CPU_FREQ_GOV_COMMON=y # CONFIG_CPU_FREQ_STAT is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y # # CPU frequency scaling drivers @@ -662,6 +680,7 @@ CONFIG_PCIEASPM=y CONFIG_PCIEASPM_POWERSAVE=y # CONFIG_PCIEASPM_PERFORMANCE is not set CONFIG_PCIE_PME=y +# CONFIG_PCIE_DPC is not set CONFIG_PCI_BUS_ADDR_T_64BIT=y CONFIG_PCI_MSI=y CONFIG_PCI_MSI_IRQ_DOMAIN=y @@ -674,17 +693,19 @@ CONFIG_PCI_IOV=y CONFIG_PCI_PRI=y CONFIG_PCI_PASID=y CONFIG_PCI_LABEL=y - -# -# PCI host controller drivers -# -CONFIG_ISA_DMA_API=y -# CONFIG_PCCARD is not set CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_ACPI=y # CONFIG_HOTPLUG_PCI_ACPI_IBM is not set # CONFIG_HOTPLUG_PCI_CPCI is not set # CONFIG_HOTPLUG_PCI_SHPC is not set + +# +# PCI host controller drivers +# +# CONFIG_PCIE_DW_PLAT is not set +# CONFIG_ISA_BUS is not set +CONFIG_ISA_DMA_API=y +# CONFIG_PCCARD is not set # CONFIG_RAPIDIO is not set CONFIG_X86_SYSFB=y @@ -692,22 +713,19 @@ CONFIG_X86_SYSFB=y # Executable file formats / Emulations # CONFIG_BINFMT_ELF=y -CONFIG_COMPAT_BINFMT_ELF=y +CONFIG_ELFCORE=y CONFIG_BINFMT_SCRIPT=y # CONFIG_HAVE_AOUT is not set CONFIG_BINFMT_MISC=y # CONFIG_COREDUMP is not set -CONFIG_IA32_EMULATION=y -CONFIG_IA32_AOUT=y +# CONFIG_IA32_EMULATION is not set # CONFIG_X86_X32 is not set -CONFIG_COMPAT=y -CONFIG_COMPAT_FOR_U64_ALIGNMENT=y -CONFIG_SYSVIPC_COMPAT=y -CONFIG_KEYS_COMPAT=y CONFIG_X86_DEV_DMA_OPS=y CONFIG_PMC_ATOM=y +# CONFIG_VMD is not set CONFIG_NET=y CONFIG_NET_INGRESS=y +CONFIG_NET_EGRESS=y # # Networking options @@ -754,7 +772,6 @@ CONFIG_NET_FOU=y # CONFIG_INET_XFRM_MODE_TRANSPORT is not set # CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set -CONFIG_INET_LRO=y # CONFIG_INET_DIAG is not set CONFIG_TCP_CONG_ADVANCED=y # CONFIG_TCP_CONG_BIC is not set @@ -793,7 +810,8 @@ CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y # CONFIG_IPV6_VTI is not set # CONFIG_IPV6_SIT is not set # CONFIG_IPV6_TUNNEL is not set -# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_FOU=y +# CONFIG_IPV6_FOU_TUNNEL is not set CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_IPV6_SUBTREES=y CONFIG_IPV6_MROUTE=y @@ -805,6 +823,7 @@ CONFIG_NET_PTP_CLASSIFY=y CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y # # Core Netfilter Configuration @@ -871,6 +890,7 @@ CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set CONFIG_NETFILTER_XT_MATCH_OWNER=y # CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y # CONFIG_NETFILTER_XT_MATCH_QUOTA is not set CONFIG_NETFILTER_XT_MATCH_RATEEST=y @@ -945,6 +965,7 @@ CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y # CONFIG_IP6_NF_RAW is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set CONFIG_IP_SCTP=y # CONFIG_SCTP_DBG_OBJCNT is not set @@ -960,7 +981,9 @@ CONFIG_SCTP_COOKIE_HMAC_MD5=y CONFIG_STP=y CONFIG_GARP=y CONFIG_MRP=y -# CONFIG_BRIDGE is not set +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_HAVE_NET_DSA=y # CONFIG_NET_DSA is not set CONFIG_VLAN_8021Q=y @@ -1044,6 +1067,7 @@ CONFIG_NET_ACT_SKBEDIT=y CONFIG_NET_ACT_CSUM=y CONFIG_NET_ACT_VLAN=y CONFIG_NET_ACT_BPF=y +# CONFIG_NET_ACT_IFE is not set CONFIG_NET_CLS_IND=y CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set @@ -1051,7 +1075,6 @@ CONFIG_DNS_RESOLVER=y # CONFIG_BATMAN_ADV is not set # CONFIG_OPENVSWITCH is not set CONFIG_VSOCKETS=y -CONFIG_NETLINK_MMAP=y CONFIG_NETLINK_DIAG=y # CONFIG_MPLS is not set # CONFIG_HSR is not set @@ -1060,6 +1083,7 @@ CONFIG_NET_SWITCHDEV=y CONFIG_RPS=y CONFIG_RFS_ACCEL=y CONFIG_XPS=y +CONFIG_SOCK_CGROUP_DATA=y CONFIG_CGROUP_NET_PRIO=y CONFIG_CGROUP_NET_CLASSID=y CONFIG_NET_RX_BUSY_POLL=y @@ -1075,6 +1099,7 @@ CONFIG_NET_FLOW_LIMIT=y # CONFIG_IRDA is not set # CONFIG_BT is not set # CONFIG_AF_RXRPC is not set +CONFIG_AF_KCM=y CONFIG_FIB_RULES=y # CONFIG_WIRELESS is not set # CONFIG_WIMAX is not set @@ -1083,8 +1108,11 @@ CONFIG_FIB_RULES=y # CONFIG_CAIF is not set # CONFIG_CEPH_LIB is not set # CONFIG_NFC is not set -# CONFIG_LWTUNNEL is not set -CONFIG_HAVE_BPF_JIT=y +CONFIG_LWTUNNEL=y +CONFIG_DST_CACHE=y +CONFIG_NET_DEVLINK=y +CONFIG_MAY_USE_DEVLINK=y +CONFIG_HAVE_EBPF_JIT=y # # Device Drivers @@ -1108,6 +1136,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set # CONFIG_GENERIC_CPU_DEVICES is not set CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y CONFIG_DMA_SHARED_BUFFER=y # CONFIG_FENCE_TRACE is not set @@ -1212,6 +1242,11 @@ CONFIG_INTEL_MEI_TXE=y # # CONFIG_SCIF_BUS is not set +# +# VOP Bus Driver +# +# CONFIG_VOP_BUS is not set + # # Intel MIC Host Driver # @@ -1227,6 +1262,10 @@ CONFIG_INTEL_MEI_TXE=y # # Intel MIC Coprocessor State Management (COSM) Drivers # + +# +# VOP Driver +# # CONFIG_GENWQE is not set # CONFIG_ECHO is not set # CONFIG_CXL_BASE is not set @@ -1306,14 +1345,13 @@ CONFIG_BLK_DEV_DM=y CONFIG_DM_MQ_DEFAULT=y # CONFIG_DM_DEBUG is not set CONFIG_DM_BUFIO=y +# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set CONFIG_DM_BIO_PRISON=y CONFIG_DM_PERSISTENT_DATA=y -# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set CONFIG_DM_CRYPT=y CONFIG_DM_SNAPSHOT=y CONFIG_DM_THIN_PROVISIONING=y CONFIG_DM_CACHE=y -CONFIG_DM_CACHE_MQ=y CONFIG_DM_CACHE_SMQ=y CONFIG_DM_CACHE_CLEANER=y CONFIG_DM_ERA=y @@ -1326,6 +1364,7 @@ CONFIG_DM_ZERO=y CONFIG_DM_UEVENT=y # CONFIG_DM_FLAKEY is not set CONFIG_DM_VERITY=y +# CONFIG_DM_VERITY_FEC is not set CONFIG_DM_SWITCH=y CONFIG_DM_LOG_WRITES=y # CONFIG_TARGET_CORE is not set @@ -1351,6 +1390,8 @@ CONFIG_MACVTAP=y CONFIG_IPVLAN=y # CONFIG_VXLAN is not set # CONFIG_GENEVE is not set +# CONFIG_GTP is not set +CONFIG_MACSEC=y # CONFIG_NETCONSOLE is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set @@ -1363,14 +1404,10 @@ CONFIG_IPVLAN=y # # CAIF transport drivers # -# CONFIG_VHOST_NET is not set -# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set # # Distributed Switch Architecture drivers # -# CONFIG_NET_DSA_MV88E6XXX is not set -# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set CONFIG_ETHERNET=y # CONFIG_NET_VENDOR_3COM is not set # CONFIG_NET_VENDOR_ADAPTEC is not set @@ -1399,6 +1436,7 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set CONFIG_E1000E=y +CONFIG_E1000E_HWTS=y CONFIG_IGB=y CONFIG_IGB_HWMON=y # CONFIG_IGBVF is not set @@ -1416,6 +1454,7 @@ CONFIG_IGB_HWMON=y # CONFIG_NET_VENDOR_MYRI is not set # CONFIG_FEALNX is not set # CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set # CONFIG_NET_VENDOR_NVIDIA is not set # CONFIG_NET_VENDOR_OKI is not set # CONFIG_ETHOC is not set @@ -1508,6 +1547,7 @@ CONFIG_KEYBOARD_ATKBD=y CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y # CONFIG_MOUSE_PS2_ALPS is not set +# CONFIG_MOUSE_PS2_BYD is not set # CONFIG_MOUSE_PS2_LOGIPS2PP is not set # CONFIG_MOUSE_PS2_SYNAPTICS is not set # CONFIG_MOUSE_PS2_CYPRESS is not set @@ -1555,6 +1595,7 @@ CONFIG_INPUT_UINPUT=y # CONFIG_INPUT_DRV260X_HAPTICS is not set # CONFIG_INPUT_DRV2665_HAPTICS is not set # CONFIG_INPUT_DRV2667_HAPTICS is not set +# CONFIG_RMI4_CORE is not set # # Hardware I/O ports @@ -1584,7 +1625,6 @@ CONFIG_VT_CONSOLE_SLEEP=y CONFIG_HW_CONSOLE=y CONFIG_VT_HW_CONSOLE_BINDING=y CONFIG_UNIX98_PTYS=y -CONFIG_DEVPTS_MULTIPLE_INSTANCES=y # CONFIG_LEGACY_PTYS is not set # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_NOZOMI is not set @@ -1600,6 +1640,7 @@ CONFIG_SERIAL_EARLYCON=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y CONFIG_SERIAL_8250_PNP=y +# CONFIG_SERIAL_8250_FINTEK is not set CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_PCI=y CONFIG_SERIAL_8250_NR_UARTS=4 @@ -1608,8 +1649,8 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=4 # CONFIG_SERIAL_8250_FSL is not set # CONFIG_SERIAL_8250_DW is not set # CONFIG_SERIAL_8250_RT288X is not set -# CONFIG_SERIAL_8250_FINTEK is not set # CONFIG_SERIAL_8250_MID is not set +# CONFIG_SERIAL_8250_MOXA is not set # # Non-8250 serial port support @@ -1784,7 +1825,6 @@ CONFIG_GPIO_LYNXPOINT=y # # Port-mapped I/O GPIO drivers # -# CONFIG_GPIO_104_IDIO_16 is not set # CONFIG_GPIO_F7188X is not set # CONFIG_GPIO_IT87 is not set # CONFIG_GPIO_SCH is not set @@ -1799,6 +1839,7 @@ CONFIG_GPIO_LYNXPOINT=y # CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_TPIC2810 is not set # # MFD GPIO expanders @@ -1838,6 +1879,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_CHARGER_GPIO is not set # CONFIG_CHARGER_BQ2415X is not set # CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set # CONFIG_CHARGER_BQ24735 is not set # CONFIG_CHARGER_BQ25890 is not set # CONFIG_CHARGER_SMB347 is not set @@ -1871,7 +1913,6 @@ CONFIG_HWMON=y # CONFIG_SENSORS_ASC7621 is not set # CONFIG_SENSORS_K8TEMP is not set # CONFIG_SENSORS_K10TEMP is not set -# CONFIG_SENSORS_FAM15H_POWER is not set # CONFIG_SENSORS_APPLESMC is not set # CONFIG_SENSORS_ASB100 is not set # CONFIG_SENSORS_ATXP1 is not set @@ -1898,6 +1939,7 @@ CONFIG_SENSORS_JC42=y # CONFIG_SENSORS_POWR1220 is not set # CONFIG_SENSORS_LINEAGE is not set # CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC2990 is not set # CONFIG_SENSORS_LTC4151 is not set # CONFIG_SENSORS_LTC4215 is not set # CONFIG_SENSORS_LTC4222 is not set @@ -1913,7 +1955,6 @@ CONFIG_SENSORS_JC42=y # CONFIG_SENSORS_MAX6650 is not set # CONFIG_SENSORS_MAX6697 is not set # CONFIG_SENSORS_MAX31790 is not set -# CONFIG_SENSORS_HTU21 is not set # CONFIG_SENSORS_MCP3021 is not set # CONFIG_SENSORS_LM63 is not set # CONFIG_SENSORS_LM73 is not set @@ -2002,18 +2043,24 @@ CONFIG_INTEL_POWERCLAMP=y CONFIG_X86_PKG_TEMP_THERMAL=y CONFIG_INTEL_SOC_DTS_IOSF_CORE=y # CONFIG_INTEL_SOC_DTS_THERMAL is not set + +# +# ACPI INT340X thermal drivers +# CONFIG_INT340X_THERMAL=y CONFIG_ACPI_THERMAL_REL=y CONFIG_INTEL_PCH_THERMAL=y CONFIG_WATCHDOG=y CONFIG_WATCHDOG_CORE=y # CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_SYSFS=y # # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set # CONFIG_XILINX_WATCHDOG is not set +# CONFIG_ZIIRAVE_WATCHDOG is not set # CONFIG_CADENCE_WATCHDOG is not set # CONFIG_DW_WATCHDOG is not set # CONFIG_MAX63XX_WATCHDOG is not set @@ -2048,7 +2095,8 @@ CONFIG_ITCO_VENDOR_SUPPORT=y # CONFIG_W83977F_WDT is not set # CONFIG_MACHZ_WDT is not set # CONFIG_SBC_EPX_C3_WATCHDOG is not set -# CONFIG_BCM7038_WDT is not set +CONFIG_INTEL_MEI_WDT=y +# CONFIG_NI903X_WDT is not set # CONFIG_MEN_A21_WDT is not set # @@ -2082,7 +2130,7 @@ CONFIG_MFD_CORE=y # CONFIG_PMIC_ADP5520 is not set # CONFIG_MFD_AAT2870_CORE is not set # CONFIG_MFD_BCM590XX is not set -# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_AXP20X_I2C is not set # CONFIG_MFD_CROS_EC is not set # CONFIG_PMIC_DA903X is not set # CONFIG_MFD_DA9052_I2C is not set @@ -2137,12 +2185,12 @@ CONFIG_LPC_ICH=y # CONFIG_TPS6105X is not set # CONFIG_TPS65010 is not set # CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65086 is not set # CONFIG_MFD_TPS65090 is not set # CONFIG_MFD_TPS65217 is not set # CONFIG_MFD_TPS65218 is not set # CONFIG_MFD_TPS6586X is not set # CONFIG_MFD_TPS65910 is not set -# CONFIG_MFD_TPS65912 is not set # CONFIG_MFD_TPS65912_I2C is not set # CONFIG_MFD_TPS80031 is not set # CONFIG_TWL4030_CORE is not set @@ -2166,6 +2214,7 @@ CONFIG_LPC_ICH=y # CONFIG_VGA_ARB is not set # CONFIG_VGA_SWITCHEROO is not set CONFIG_DRM=y +CONFIG_DRM_DP_AUX_CHARDEV=y CONFIG_DRM_KMS_HELPER=y CONFIG_DRM_KMS_FB_HELPER=y CONFIG_DRM_FBDEV_EMULATION=y @@ -2183,6 +2232,10 @@ CONFIG_DRM_TTM=y # CONFIG_DRM_R128 is not set # CONFIG_DRM_RADEON is not set # CONFIG_DRM_AMDGPU is not set + +# +# ACP (Audio CoProcessor) Configuration +# # CONFIG_DRM_NOUVEAU is not set # CONFIG_DRM_I915 is not set # CONFIG_DRM_MGA is not set @@ -2202,6 +2255,7 @@ CONFIG_DRM_BRIDGE=y # # Display Interface Bridges # +# CONFIG_DRM_ANALOGIX_ANX78XX is not set # # Frame buffer Devices @@ -2209,6 +2263,7 @@ CONFIG_DRM_BRIDGE=y CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_CMDLINE=y +CONFIG_FB_NOTIFY=y # CONFIG_FB_DDC is not set # CONFIG_FB_BOOT_VESA_SUPPORT is not set CONFIG_FB_CFB_FILLRECT=y @@ -2220,6 +2275,7 @@ CONFIG_FB_SYS_COPYAREA=y CONFIG_FB_SYS_IMAGEBLIT=y # CONFIG_FB_FOREIGN_ENDIAN is not set CONFIG_FB_SYS_FOPS=y +CONFIG_FB_DEFERRED_IO=y # CONFIG_FB_SVGALIB is not set # CONFIG_FB_MACMODES is not set # CONFIG_FB_BACKLIGHT is not set @@ -2314,6 +2370,7 @@ CONFIG_HID_GENERIC=y # CONFIG_HID_BETOP_FF is not set # CONFIG_HID_CHERRY is not set # CONFIG_HID_CHICONY is not set +# CONFIG_HID_CMEDIA is not set # CONFIG_HID_CP2112 is not set # CONFIG_HID_CYPRESS is not set # CONFIG_HID_DRAGONRISE is not set @@ -2494,6 +2551,7 @@ CONFIG_USB_STORAGE=y # CONFIG_USB_EZUSB_FX2 is not set # CONFIG_USB_HSIC_USB3503 is not set # CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_UCSI is not set # # USB Physical Layer drivers @@ -2553,31 +2611,37 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_DS1307 is not set # CONFIG_RTC_DRV_DS1374 is not set # CONFIG_RTC_DRV_DS1672 is not set -# CONFIG_RTC_DRV_DS3232 is not set # CONFIG_RTC_DRV_MAX6900 is not set # CONFIG_RTC_DRV_RS5C372 is not set # CONFIG_RTC_DRV_ISL1208 is not set # CONFIG_RTC_DRV_ISL12022 is not set # CONFIG_RTC_DRV_ISL12057 is not set # CONFIG_RTC_DRV_X1205 is not set -# CONFIG_RTC_DRV_PCF2127 is not set # CONFIG_RTC_DRV_PCF8523 is not set -# CONFIG_RTC_DRV_PCF8563 is not set # CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8563 is not set # CONFIG_RTC_DRV_PCF8583 is not set # CONFIG_RTC_DRV_M41T80 is not set # CONFIG_RTC_DRV_BQ32K is not set # CONFIG_RTC_DRV_S35390A is not set # CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8010 is not set # CONFIG_RTC_DRV_RX8581 is not set # CONFIG_RTC_DRV_RX8025 is not set # CONFIG_RTC_DRV_EM3027 is not set -# CONFIG_RTC_DRV_RV3029C2 is not set # CONFIG_RTC_DRV_RV8803 is not set # # SPI RTC drivers # +CONFIG_RTC_I2C_AND_SPI=y + +# +# SPI and I2C RTC drivers +# +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set # # Platform RTC drivers @@ -2607,6 +2671,11 @@ CONFIG_RTC_DRV_CMOS=y # # CONFIG_RTC_DRV_HID_SENSOR_TIME is not set # CONFIG_DMADEVICES is not set + +# +# DMABUF options +# +CONFIG_SYNC_FILE=y # CONFIG_AUXDISPLAY is not set # CONFIG_UIO is not set # CONFIG_VFIO is not set @@ -2631,22 +2700,18 @@ CONFIG_STAGING=y # Speakup console speech # # CONFIG_SPEAKUP is not set -# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set # CONFIG_STAGING_MEDIA is not set # # Android # -# CONFIG_WIMAX_GDM72XX is not set # CONFIG_DGNC is not set -# CONFIG_DGAP is not set # CONFIG_GS_FPGABOOT is not set # CONFIG_CRYPTO_SKEIN is not set # CONFIG_UNISYSSPAR is not set # CONFIG_MOST is not set CONFIG_X86_PLATFORM_DEVICES=y # CONFIG_ACERHDF is not set -# CONFIG_DELL_WMI is not set # CONFIG_DELL_WMI_AIO is not set # CONFIG_DELL_SMO8800 is not set # CONFIG_FUJITSU_TABLET is not set @@ -2655,13 +2720,16 @@ CONFIG_X86_PLATFORM_DEVICES=y # CONFIG_HP_WMI is not set # CONFIG_SENSORS_HDAPS is not set # CONFIG_INTEL_MENLOW is not set +# CONFIG_ASUS_WIRELESS is not set CONFIG_ACPI_WMI=y # CONFIG_TOPSTAR_LAPTOP is not set # CONFIG_TOSHIBA_BT_RFKILL is not set # CONFIG_TOSHIBA_HAPS is not set # CONFIG_TOSHIBA_WMI is not set # CONFIG_ACPI_CMPC is not set +# CONFIG_INTEL_HID_EVENT is not set CONFIG_INTEL_IPS=y +CONFIG_INTEL_PMC_CORE=y # CONFIG_IBM_RTL is not set # CONFIG_MXM_WMI is not set # CONFIG_SAMSUNG_Q10 is not set @@ -2670,6 +2738,7 @@ CONFIG_INTEL_SMARTCONNECT=y # CONFIG_PVPANIC is not set # CONFIG_INTEL_PMC_IPC is not set # CONFIG_SURFACE_PRO3_BUTTON is not set +CONFIG_INTEL_PUNIT_IPC=y # CONFIG_CHROME_PLATFORMS is not set CONFIG_CLKDEV_LOOKUP=y CONFIG_HAVE_CLK_PREPARE=y @@ -2679,8 +2748,12 @@ CONFIG_COMMON_CLK=y # Common Clock Framework # # CONFIG_COMMON_CLK_SI5351 is not set -# CONFIG_COMMON_CLK_PXA is not set # CONFIG_COMMON_CLK_CDCE706 is not set +# CONFIG_COMMON_CLK_CS2000_CP is not set +# CONFIG_COMMON_CLK_NXP is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_PIC32 is not set +# CONFIG_COMMON_CLK_OXNAS is not set # # Hardware Spinlock drivers @@ -2734,6 +2807,7 @@ CONFIG_IRQ_REMAP=y # CONFIG_NTB is not set # CONFIG_VME_BUS is not set # CONFIG_PWM is not set +CONFIG_ARM_GIC_MAX_NR=1 # CONFIG_IPACK_BUS is not set # CONFIG_RESET_CONTROLLER is not set # CONFIG_FMC is not set @@ -2766,10 +2840,9 @@ CONFIG_ND_BLK=y CONFIG_ND_CLAIM=y CONFIG_ND_BTT=y CONFIG_BTT=y +CONFIG_DEV_DAX=y # CONFIG_NVMEM is not set # CONFIG_STM is not set -# CONFIG_STM_DUMMY is not set -# CONFIG_STM_SOURCE_CONSOLE is not set # CONFIG_INTEL_TH is not set # @@ -2788,6 +2861,7 @@ CONFIG_DMIID=y CONFIG_DMI_SYSFS=y CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y # CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_FW_CFG_SYSFS is not set # CONFIG_GOOGLE_FIRMWARE is not set # @@ -2799,6 +2873,8 @@ CONFIG_EFI_VARS_PSTORE=y # CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set # CONFIG_EFI_FAKE_MEMMAP is not set CONFIG_EFI_RUNTIME_WRAPPERS=y +CONFIG_EFI_BOOTLOADER_CONTROL=y +CONFIG_EFI_CAPSULE_LOADER=y CONFIG_UEFI_CPER=y # @@ -2832,13 +2908,16 @@ CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_F2FS_FS=y CONFIG_F2FS_FS_XATTR=y CONFIG_F2FS_FS_POSIX_ACL=y -CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_FS_SECURITY is not set # CONFIG_F2FS_CHECK_FS is not set CONFIG_F2FS_FS_ENCRYPTION=y +# CONFIG_F2FS_FAULT_INJECTION is not set # CONFIG_FS_DAX is not set CONFIG_FS_POSIX_ACL=y CONFIG_EXPORTFS=y CONFIG_FILE_LOCKING=y +# CONFIG_MANDATORY_FILE_LOCKING is not set +CONFIG_FS_ENCRYPTION=y CONFIG_FSNOTIFY=y CONFIG_DNOTIFY=y CONFIG_INOTIFY_USER=y @@ -2878,6 +2957,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=949 CONFIG_FAT_DEFAULT_IOCHARSET="utf8" +CONFIG_FAT_DEFAULT_UTF8=y CONFIG_NTFS_FS=y # CONFIG_NTFS_DEBUG is not set CONFIG_NTFS_RW=y @@ -2900,11 +2980,13 @@ CONFIG_HUGETLB_PAGE=y CONFIG_CONFIGFS_FS=y CONFIG_EFIVAR_FS=y CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ORANGEFS_FS is not set # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ECRYPT_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set +CONFIG_HFS_FS=y +CONFIG_HFSPLUS_FS=y +CONFIG_HFSPLUS_FS_POSIX_ACL=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set @@ -3003,7 +3085,7 @@ CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 # # CONFIG_DEBUG_INFO is not set CONFIG_ENABLE_WARN_DEPRECATED=y -CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_ENABLE_MUST_CHECK is not set CONFIG_FRAME_WARN=2048 CONFIG_STRIP_ASM_SYMS=y # CONFIG_READABLE_ASM is not set @@ -3015,6 +3097,7 @@ CONFIG_STRIP_ASM_SYMS=y CONFIG_SECTION_MISMATCH_WARN_ONLY=y CONFIG_ARCH_WANT_FRAME_POINTERS=y # CONFIG_FRAME_POINTER is not set +CONFIG_STACK_VALIDATION=y # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 @@ -3025,6 +3108,7 @@ CONFIG_DEBUG_KERNEL=y # # CONFIG_PAGE_EXTENSION is not set # CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_PAGE_POISONING is not set # CONFIG_DEBUG_OBJECTS is not set # CONFIG_SLUB_STATS is not set CONFIG_HAVE_DEBUG_KMEMLEAK=y @@ -3039,6 +3123,8 @@ CONFIG_HAVE_DEBUG_STACKOVERFLOW=y CONFIG_HAVE_ARCH_KMEMCHECK=y # CONFIG_KMEMCHECK is not set CONFIG_HAVE_ARCH_KASAN=y +CONFIG_ARCH_HAS_KCOV=y +# CONFIG_KCOV is not set # CONFIG_DEBUG_SHIRQ is not set # @@ -3051,6 +3137,7 @@ CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=1 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 # CONFIG_DETECT_HUNG_TASK is not set +CONFIG_WQ_WATCHDOG=y # CONFIG_PANIC_ON_OOPS is not set CONFIG_PANIC_ON_OOPS_VALUE=0 CONFIG_PANIC_TIMEOUT=15 @@ -3076,6 +3163,7 @@ CONFIG_TIMER_STATS=y # CONFIG_LOCK_TORTURE_TEST is not set # CONFIG_STACKTRACE is not set # CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set # CONFIG_DEBUG_LIST is not set # CONFIG_DEBUG_PI_LIST is not set # CONFIG_DEBUG_SG is not set @@ -3088,11 +3176,14 @@ CONFIG_TIMER_STATS=y # CONFIG_PROVE_RCU is not set # CONFIG_SPARSE_RCU_POINTER is not set # CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_PERF_TEST is not set # CONFIG_RCU_TORTURE_TEST is not set CONFIG_RCU_CPU_STALL_TIMEOUT=21 # CONFIG_RCU_TRACE is not set # CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set # CONFIG_NOTIFIER_ERROR_INJECTION is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set @@ -3123,7 +3214,10 @@ CONFIG_TRACING_SUPPORT=y # CONFIG_TEST_STRING_HELPERS is not set # CONFIG_TEST_KSTRTOX is not set # CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_BITMAP is not set +# CONFIG_TEST_UUID is not set # CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_HASH is not set # CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_TEST_FIRMWARE is not set @@ -3132,13 +3226,16 @@ CONFIG_TRACING_SUPPORT=y # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y +# CONFIG_UBSAN is not set +CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y CONFIG_STRICT_DEVMEM=y +CONFIG_IO_STRICT_DEVMEM=y # CONFIG_X86_VERBOSE_BOOTUP is not set # CONFIG_EARLY_PRINTK is not set # CONFIG_X86_PTDUMP_CORE is not set # CONFIG_X86_PTDUMP is not set # CONFIG_EFI_PGT_DUMP is not set -CONFIG_DEBUG_RODATA=y # CONFIG_DEBUG_RODATA_TEST is not set # CONFIG_DEBUG_WX is not set # CONFIG_DOUBLEFAULT is not set @@ -3158,7 +3255,6 @@ CONFIG_DEFAULT_IO_DELAY_TYPE=3 CONFIG_OPTIMIZE_INLINING=y # CONFIG_DEBUG_ENTRY is not set # CONFIG_DEBUG_NMI_SELFTEST is not set -# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set # CONFIG_X86_DEBUG_FPU is not set # CONFIG_PUNIT_ATOM_DEBUG is not set @@ -3167,8 +3263,9 @@ CONFIG_OPTIMIZE_INLINING=y # CONFIG_KEYS=y CONFIG_PERSISTENT_KEYRINGS=y -# CONFIG_BIG_KEYS is not set +CONFIG_BIG_KEYS=y CONFIG_ENCRYPTED_KEYS=y +# CONFIG_KEY_DH_OPERATIONS is not set CONFIG_SECURITY_DMESG_RESTRICT=y # CONFIG_SECURITY is not set CONFIG_SECURITYFS=y @@ -3197,8 +3294,6 @@ CONFIG_CRYPTO_HASH2=y CONFIG_CRYPTO_RNG=y CONFIG_CRYPTO_RNG2=y CONFIG_CRYPTO_RNG_DEFAULT=y -CONFIG_CRYPTO_PCOMP=y -CONFIG_CRYPTO_PCOMP2=y CONFIG_CRYPTO_AKCIPHER2=y CONFIG_CRYPTO_AKCIPHER=y # CONFIG_CRYPTO_RSA is not set @@ -3320,7 +3415,6 @@ CONFIG_CRYPTO_TWOFISH_AVX_X86_64=y # Compression # CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_ZLIB=y CONFIG_CRYPTO_LZO=y CONFIG_CRYPTO_842=y CONFIG_CRYPTO_LZ4=y @@ -3345,17 +3439,18 @@ CONFIG_CRYPTO_HW=y # CONFIG_CRYPTO_DEV_CCP is not set CONFIG_CRYPTO_DEV_QAT=y CONFIG_CRYPTO_DEV_QAT_DH895xCC=y +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=y +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set # CONFIG_ASYMMETRIC_KEY_TYPE is not set # # Certificates for signature checking # -CONFIG_SYSTEM_TRUSTED_KEYRING=y -CONFIG_SYSTEM_TRUSTED_KEYS="" CONFIG_HAVE_KVM=y -CONFIG_VIRTUALIZATION=y -# CONFIG_KVM is not set +# CONFIG_VIRTUALIZATION is not set # CONFIG_BINARY_PRINTF is not set # @@ -3414,12 +3509,12 @@ CONFIG_TEXTSEARCH=y CONFIG_TEXTSEARCH_KMP=y CONFIG_TEXTSEARCH_BM=y CONFIG_TEXTSEARCH_FSM=y +CONFIG_RADIX_TREE_MULTIORDER=y CONFIG_ASSOCIATIVE_ARRAY=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y CONFIG_HAS_DMA=y CONFIG_CHECK_SIGNATURE=y -CONFIG_CPUMASK_OFFSTACK=y CONFIG_CPU_RMAP=y CONFIG_DQL=y CONFIG_GLOB=y @@ -3428,12 +3523,14 @@ CONFIG_NLATTR=y CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y CONFIG_CORDIC=y CONFIG_DDR=y +# CONFIG_IRQ_POLL is not set CONFIG_UCS2_STRING=y CONFIG_FONT_SUPPORT=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y # CONFIG_SG_SPLIT is not set +CONFIG_SG_POOL=y CONFIG_ARCH_HAS_SG_CHAIN=y CONFIG_ARCH_HAS_PMEM_API=y CONFIG_ARCH_HAS_MMIO_FLUSH=y diff --git a/linux-spica.install b/linux-spica.install index f1d0f6b..60e8431 100644 --- a/linux-spica.install +++ b/linux-spica.install @@ -1,5 +1,5 @@ pkgname=linux-spica -kernver=4.4.4-1spica-dirty +kernver=4.7.5-1spica-dirty #bootdevice="BOOT_IMAGE=/boot/vmlinuz-$pkgname root=UUID=d670564f-2cb3-4981-9d51-6ed9c1327d47" #option="rw quiet clocksource=hpet initrd=EFI/spi-ca/initrd intel_iommu=on pci-stub.ids=1002:683f,1002:aab0 vfio_iommu_type1.allow_unsafe_interrupts=1,kvm.ignore_msrs=1" #option="rw quiet clocksource=hpet initrd=EFI/spi-ca/initrd quiet intremap=no_x2apic_optout zswap.enabled=1 zswap.max_pool_percent=25 zswap.compressor=lz4" diff --git a/patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r10-4.4.0.patch b/patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r11-4.7.0.patch similarity index 93% rename from patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r10-4.4.0.patch rename to patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r11-4.7.0.patch index 69a5662..ff75a8b 100644 --- a/patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r10-4.4.0.patch +++ b/patches/0001-block-cgroups-kconfig-build-bits-for-BFQ-v7r11-4.7.0.patch @@ -1,7 +1,7 @@ -From d54aac68a9655574a91e6d224300aba239ea58cc Mon Sep 17 00:00:00 2001 +From 22ee35ec82fa543b65c1b6d516a086a21f723846 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 7 Apr 2015 13:39:12 +0200 -Subject: [PATCH 1/3] block: cgroups, kconfig, build bits for BFQ-v7r10-4.4.0 +Subject: [PATCH 1/4] block: cgroups, kconfig, build bits for BFQ-v7r11-4.7.0 Update Kconfig.iosched and do the related Makefile changes to include kernel configuration options for BFQ. Also increase the number of @@ -74,7 +74,7 @@ index 421bef9..0ee5f0f 100644 endmenu diff --git a/block/Makefile b/block/Makefile -index 00ecc97..1ed86d5 100644 +index 9eda232..4a36683 100644 --- a/block/Makefile +++ b/block/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o @@ -86,10 +86,10 @@ index 00ecc97..1ed86d5 100644 obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h -index c70e358..ae43492 100644 +index 3d9cf32..8d862a0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h -@@ -44,7 +44,7 @@ struct pr_ops; +@@ -45,7 +45,7 @@ struct pr_ops; * Maximum number of blkcg policies allowed to be registered concurrently. * Defined here to simplify include dependency. */ diff --git a/patches/0002-block-introduce-the-BFQ-v7r10-I-O-sched-for-4.4.0.patch b/patches/0002-block-introduce-the-BFQ-v7r11-I-O-sched-for-4.7.0.patch similarity index 98% rename from patches/0002-block-introduce-the-BFQ-v7r10-I-O-sched-for-4.4.0.patch rename to patches/0002-block-introduce-the-BFQ-v7r11-I-O-sched-for-4.7.0.patch index fa63968..368a4ff 100644 --- a/patches/0002-block-introduce-the-BFQ-v7r10-I-O-sched-for-4.4.0.patch +++ b/patches/0002-block-introduce-the-BFQ-v7r11-I-O-sched-for-4.7.0.patch @@ -1,9 +1,8 @@ -From e1db9f07f51ef6a8b1dfd2750cd45cb8f890ec7f Mon Sep 17 00:00:00 2001 +From 2aae32be2a18a7d0da104ae42c08cb9bce9d9c7c Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Thu, 9 May 2013 19:10:02 +0200 -Subject: [PATCH 2/3] block: introduce the BFQ-v7r10 I/O sched for 4.4.0 +Subject: [PATCH 2/4] block: introduce the BFQ-v7r11 I/O sched for 4.7.0 -Add the BFQ-v7r10 I/O scheduler to 4.4.0. The general structure is borrowed from CFQ, as much of the code for handling I/O contexts. Over time, several useful features have been ported from CFQ as well (details in the changelog in README.BFQ). A @@ -57,12 +56,12 @@ Signed-off-by: Paolo Valente Signed-off-by: Arianna Avanzini --- block/Kconfig.iosched | 6 +- - block/bfq-cgroup.c | 1203 ++++++++++++++++ + block/bfq-cgroup.c | 1182 ++++++++++++++++ block/bfq-ioc.c | 36 + - block/bfq-iosched.c | 3753 +++++++++++++++++++++++++++++++++++++++++++++++++ - block/bfq-sched.c | 1197 ++++++++++++++++ - block/bfq.h | 807 +++++++++++ - 6 files changed, 6998 insertions(+), 4 deletions(-) + block/bfq-iosched.c | 3754 +++++++++++++++++++++++++++++++++++++++++++++++++ + block/bfq-sched.c | 1200 ++++++++++++++++ + block/bfq.h | 801 +++++++++++ + 6 files changed, 6975 insertions(+), 4 deletions(-) create mode 100644 block/bfq-cgroup.c create mode 100644 block/bfq-ioc.c create mode 100644 block/bfq-iosched.c @@ -92,10 +91,10 @@ index 0ee5f0f..f78cd1a 100644 prompt "Default I/O scheduler" diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c new file mode 100644 -index 0000000..707364a +index 0000000..8610cd6 --- /dev/null +++ b/block/bfq-cgroup.c -@@ -0,0 +1,1203 @@ +@@ -0,0 +1,1182 @@ +/* + * BFQ: CGROUPS support. + * @@ -259,7 +258,9 @@ index 0000000..707364a + +static struct bfq_group *blkg_to_bfqg(struct blkcg_gq *blkg) +{ -+ return pd_to_bfqg(blkg_to_pd(blkg, &blkcg_policy_bfq)); ++ struct blkg_policy_data *pd = blkg_to_pd(blkg, &blkcg_policy_bfq); ++ BUG_ON(!pd); ++ return pd_to_bfqg(pd); +} + +/* @@ -494,7 +495,8 @@ index 0000000..707364a + if (!bfqg) + return NULL; + -+ if (bfqg_stats_init(&bfqg->stats, gfp)) { ++ if (bfqg_stats_init(&bfqg->stats, gfp) || ++ bfqg_stats_init(&bfqg->dead_stats, gfp)) { + kfree(bfqg); + return NULL; + } @@ -502,6 +504,20 @@ index 0000000..707364a + return &bfqg->pd; +} + ++static void bfq_group_set_parent(struct bfq_group *bfqg, ++ struct bfq_group *parent) ++{ ++ struct bfq_entity *entity; ++ ++ BUG_ON(!parent); ++ BUG_ON(!bfqg); ++ BUG_ON(bfqg == parent); ++ ++ entity = &bfqg->entity; ++ entity->parent = parent->my_entity; ++ entity->sched_data = &parent->sched_data; ++} ++ +static void bfq_pd_init(struct blkg_policy_data *pd) +{ + struct blkcg_gq *blkg = pd_to_blkg(pd); @@ -518,15 +534,16 @@ index 0000000..707364a + */ + bfqg->bfqd = bfqd; + bfqg->active_entities = 0; -+ -+ /* if the root_group does not exist, we are handling it right now */ -+ if (bfqd->root_group && bfqg != bfqd->root_group) -+ hlist_add_head(&bfqg->bfqd_node, &bfqd->group_list); +} + +static void bfq_pd_free(struct blkg_policy_data *pd) +{ -+ return kfree(pd_to_bfqg(pd)); ++ struct bfq_group *bfqg = pd_to_bfqg(pd); ++ ++ bfqg_stats_exit(&bfqg->stats); ++ bfqg_stats_exit(&bfqg->dead_stats); ++ ++ return kfree(bfqg); +} + +/* offset delta from bfqg->stats to bfqg->dead_stats */ @@ -565,20 +582,6 @@ index 0000000..707364a + bfqg_stats_reset(&bfqg->dead_stats); +} + -+static void bfq_group_set_parent(struct bfq_group *bfqg, -+ struct bfq_group *parent) -+{ -+ struct bfq_entity *entity; -+ -+ BUG_ON(!parent); -+ BUG_ON(!bfqg); -+ BUG_ON(bfqg == parent); -+ -+ entity = &bfqg->entity; -+ entity->parent = parent->my_entity; -+ entity->sched_data = &parent->sched_data; -+} -+ +static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + struct blkcg *blkcg) +{ @@ -815,11 +818,19 @@ index 0000000..707364a +static void bfq_pd_offline(struct blkg_policy_data *pd) +{ + struct bfq_service_tree *st; -+ struct bfq_group *bfqg = pd_to_bfqg(pd); -+ struct bfq_data *bfqd = bfqg->bfqd; -+ struct bfq_entity *entity = bfqg->my_entity; ++ struct bfq_group *bfqg; ++ struct bfq_data *bfqd; ++ struct bfq_entity *entity; + int i; + ++ BUG_ON(!pd); ++ bfqg = pd_to_bfqg(pd); ++ BUG_ON(!bfqg); ++ bfqd = bfqg->bfqd; ++ BUG_ON(bfqd && !bfqd->root_group); ++ ++ entity = bfqg->my_entity; ++ + if (!entity) /* root group */ + return; + @@ -828,8 +839,8 @@ index 0000000..707364a + * deactivating the group itself. + */ + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) { ++ BUG_ON(!bfqg->sched_data.service_tree); + st = bfqg->sched_data.service_tree + i; -+ + /* + * The idle tree may still contain bfq_queues belonging + * to exited task because they never migrated to a different @@ -857,7 +868,6 @@ index 0000000..707364a + BUG_ON(bfqg->sched_data.next_in_service); + BUG_ON(bfqg->sched_data.in_service_entity); + -+ hlist_del(&bfqg->bfqd_node); + __bfq_deactivate_entity(entity, 0); + bfq_put_async_queues(bfqd, bfqg); + BUG_ON(entity->tree); @@ -867,46 +877,14 @@ index 0000000..707364a + +static void bfq_end_wr_async(struct bfq_data *bfqd) +{ -+ struct hlist_node *tmp; -+ struct bfq_group *bfqg; ++ struct blkcg_gq *blkg; ++ ++ list_for_each_entry(blkg, &bfqd->queue->blkg_list, q_node) { ++ struct bfq_group *bfqg = blkg_to_bfqg(blkg); + -+ hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) + bfq_end_wr_async_queues(bfqd, bfqg); -+ bfq_end_wr_async_queues(bfqd, bfqd->root_group); -+} -+ -+/** -+ * bfq_disconnect_groups - disconnect @bfqd from all its groups. -+ * @bfqd: the device descriptor being exited. -+ * -+ * When the device exits we just make sure that no lookup can return -+ * the now unused group structures. They will be deallocated on cgroup -+ * destruction. -+ */ -+static void bfq_disconnect_groups(struct bfq_data *bfqd) -+{ -+ struct hlist_node *tmp; -+ struct bfq_group *bfqg; -+ -+ bfq_log(bfqd, "disconnect_groups beginning"); -+ hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) { -+ hlist_del(&bfqg->bfqd_node); -+ -+ __bfq_deactivate_entity(bfqg->my_entity, 0); -+ -+ /* -+ * Don't remove from the group hash, just set an -+ * invalid key. No lookups can race with the -+ * assignment as bfqd is being destroyed; this -+ * implies also that new elements cannot be added -+ * to the list. -+ */ -+ rcu_assign_pointer(bfqg->bfqd, NULL); -+ -+ bfq_log(bfqd, "disconnect_groups: put async for group %p", -+ bfqg); -+ bfq_put_async_queues(bfqd, bfqg); + } ++ bfq_end_wr_async_queues(bfqd, bfqd->root_group); +} + +static u64 bfqio_cgroup_weight_read(struct cgroup_subsys_state *css, @@ -1343,10 +1321,10 @@ index 0000000..fb7bb8f +} diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c new file mode 100644 -index 0000000..d61e402 +index 0000000..f9787a6 --- /dev/null +++ b/block/bfq-iosched.c -@@ -0,0 +1,3753 @@ +@@ -0,0 +1,3754 @@ +/* + * Budget Fair Queueing (BFQ) disk scheduler. + * @@ -4612,7 +4590,6 @@ index 0000000..d61e402 + list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list) + bfq_deactivate_bfqq(bfqd, bfqq, 0); + -+ bfq_disconnect_groups(bfqd); + spin_unlock_irq(q->queue_lock); + + bfq_shutdown_timer_wq(bfqd); @@ -4623,6 +4600,8 @@ index 0000000..d61e402 + +#ifdef CONFIG_BFQ_GROUP_IOSCHED + blkcg_deactivate_policy(q, &blkcg_policy_bfq); ++#else ++ kfree(bfqd->root_group); +#endif + + kfree(bfqd); @@ -5075,7 +5054,7 @@ index 0000000..d61e402 + if (ret) + goto err_pol_unreg; + -+ pr_info("BFQ I/O-scheduler: v7r10"); ++ pr_info("BFQ I/O-scheduler: v7r11"); + + return 0; + @@ -5102,10 +5081,10 @@ index 0000000..d61e402 +MODULE_LICENSE("GPL"); diff --git a/block/bfq-sched.c b/block/bfq-sched.c new file mode 100644 -index 0000000..9328a1f +index 0000000..a64fec1 --- /dev/null +++ b/block/bfq-sched.c -@@ -0,0 +1,1197 @@ +@@ -0,0 +1,1200 @@ +/* + * BFQ: Hierarchical B-WF2Q+ scheduler. + * @@ -5947,13 +5926,16 @@ index 0000000..9328a1f +static int __bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd = entity->sched_data; -+ struct bfq_service_tree *st = bfq_entity_service_tree(entity); -+ int was_in_service = entity == sd->in_service_entity; ++ struct bfq_service_tree *st; ++ int was_in_service; + int ret = 0; + -+ if (!entity->on_st) ++ if (sd == NULL || !entity->on_st) /* never activated, or inactive */ + return 0; + ++ st = bfq_entity_service_tree(entity); ++ was_in_service = entity == sd->in_service_entity; ++ + BUG_ON(was_in_service && entity->tree); + + if (was_in_service) { @@ -6305,12 +6287,12 @@ index 0000000..9328a1f +} diff --git a/block/bfq.h b/block/bfq.h new file mode 100644 -index 0000000..9b04d19 +index 0000000..485d0c9 --- /dev/null +++ b/block/bfq.h -@@ -0,0 +1,807 @@ +@@ -0,0 +1,801 @@ +/* -+ * BFQ-v7r10 for 4.4.0: data structures and common functions prototypes. ++ * BFQ-v7r11 for 4.5.0: data structures and common functions prototypes. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe @@ -6700,7 +6682,6 @@ index 0000000..9b04d19 + * @peak_rate_samples: number of samples used to calculate @peak_rate. + * @bfq_max_budget: maximum budget allotted to a bfq_queue before + * rescheduling. -+ * @group_list: list of all the bfq_groups active on the device. + * @active_list: list of all the bfq_queues active on the device. + * @idle_list: list of all the bfq_queues idle on the device. + * @bfq_fifo_expire: timeout for async/sync requests; when it expires @@ -6805,7 +6786,6 @@ index 0000000..9b04d19 + u64 peak_rate; + int bfq_max_budget; + -+ struct hlist_head group_list; + struct list_head active_list; + struct list_head idle_list; + @@ -6975,8 +6955,6 @@ index 0000000..9b04d19 + * @entity: schedulable entity to insert into the parent group sched_data. + * @sched_data: own sched_data, to contain child entities (they may be + * both bfq_queues and bfq_groups). -+ * @bfqd_node: node to be inserted into the @bfqd->group_list list -+ * of the groups active on the same device; used for cleanup. + * @bfqd: the bfq_data for the device this group acts upon. + * @async_bfqq: array of async queues for all the tasks belonging to + * the group, one queue per ioprio value per ioprio_class, @@ -7007,8 +6985,6 @@ index 0000000..9b04d19 + struct bfq_entity entity; + struct bfq_sched_data sched_data; + -+ struct hlist_node bfqd_node; -+ + void *bfqd; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; diff --git a/patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r10-for.patch b/patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r11-for.patch similarity index 97% rename from patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r10-for.patch rename to patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r11-for.patch index 1b57872..a9876aa 100644 --- a/patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r10-for.patch +++ b/patches/0003-block-bfq-add-Early-Queue-Merge-EQM-to-BFQ-v7r11-for.patch @@ -1,8 +1,8 @@ -From 8a3e03e4d2c06f9eacf26eb786ccc312dcc2aa8e Mon Sep 17 00:00:00 2001 +From 47de1e46ef5f462e9694e5b0607aec6ad658f1e0 Mon Sep 17 00:00:00 2001 From: Mauro Andreolini Date: Sun, 6 Sep 2015 16:09:05 +0200 -Subject: [PATCH 3/3] block, bfq: add Early Queue Merge (EQM) to BFQ-v7r10 for - 4.4.0 +Subject: [PATCH 3/4] block, bfq: add Early Queue Merge (EQM) to BFQ-v7r11 for + 4.7.0 A set of processes may happen to perform interleaved reads, i.e.,requests whose union would give rise to a sequential read pattern. There are two @@ -41,18 +41,18 @@ Signed-off-by: Linus Walleij 3 files changed, 743 insertions(+), 14 deletions(-) diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c -index 707364a..7a61920 100644 +index 8610cd6..5ee99ec 100644 --- a/block/bfq-cgroup.c +++ b/block/bfq-cgroup.c -@@ -420,6 +420,7 @@ static void bfq_pd_init(struct blkg_policy_data *pd) +@@ -437,6 +437,7 @@ static void bfq_pd_init(struct blkg_policy_data *pd) */ bfqg->bfqd = bfqd; bfqg->active_entities = 0; + bfqg->rq_pos_tree = RB_ROOT; + } - /* if the root_group does not exist, we are handling it right now */ - if (bfqd->root_group && bfqg != bfqd->root_group) -@@ -526,6 +527,8 @@ static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + static void bfq_pd_free(struct blkg_policy_data *pd) +@@ -530,6 +531,8 @@ static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, return bfqg; } @@ -61,7 +61,7 @@ index 707364a..7a61920 100644 /** * bfq_bfqq_move - migrate @bfqq to @bfqg. * @bfqd: queue descriptor. -@@ -573,6 +576,7 @@ static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, +@@ -577,6 +580,7 @@ static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, bfqg_get(bfqg); if (busy) { @@ -70,7 +70,7 @@ index 707364a..7a61920 100644 bfq_activate_bfqq(bfqd, bfqq); } diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c -index d61e402..6f8e52a 100644 +index f9787a6..d1f648d 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -296,6 +296,72 @@ static struct request *bfq_choose_req(struct bfq_data *bfqd, @@ -946,7 +946,7 @@ index d61e402..6f8e52a 100644 spin_unlock_irqrestore(q->queue_lock, flags); return 0; -@@ -3289,6 +3945,7 @@ static void bfq_init_root_group(struct bfq_group *root_group, +@@ -3290,6 +3946,7 @@ static void bfq_init_root_group(struct bfq_group *root_group, root_group->my_entity = NULL; root_group->bfqd = bfqd; #endif @@ -954,7 +954,7 @@ index d61e402..6f8e52a 100644 for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) root_group->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; } -@@ -3369,6 +4026,8 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) +@@ -3370,6 +4027,8 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; @@ -964,7 +964,7 @@ index d61e402..6f8e52a 100644 bfqd->bfq_large_burst_thresh = 11; diff --git a/block/bfq.h b/block/bfq.h -index 9b04d19..97a677f 100644 +index 485d0c9..f73c942 100644 --- a/block/bfq.h +++ b/block/bfq.h @@ -183,6 +183,8 @@ struct bfq_group; @@ -1020,7 +1020,7 @@ index 9b04d19..97a677f 100644 }; enum bfq_device_speed { -@@ -559,6 +591,9 @@ enum bfqq_state_flags { +@@ -557,6 +589,9 @@ enum bfqq_state_flags { * may need softrt-next-start * update */ @@ -1030,7 +1030,7 @@ index 9b04d19..97a677f 100644 }; #define BFQ_BFQQ_FNS(name) \ -@@ -585,6 +620,9 @@ BFQ_BFQQ_FNS(budget_new); +@@ -583,6 +618,9 @@ BFQ_BFQQ_FNS(budget_new); BFQ_BFQQ_FNS(IO_bound); BFQ_BFQQ_FNS(in_large_burst); BFQ_BFQQ_FNS(constantly_seeky); @@ -1040,7 +1040,7 @@ index 9b04d19..97a677f 100644 BFQ_BFQQ_FNS(softrt_update); #undef BFQ_BFQQ_FNS -@@ -679,6 +717,9 @@ struct bfq_group_data { +@@ -675,6 +713,9 @@ struct bfq_group_data { * are groups with more than one active @bfq_entity * (see the comments to the function * bfq_bfqq_must_not_expire()). @@ -1050,7 +1050,7 @@ index 9b04d19..97a677f 100644 * * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup * there is a set of bfq_groups, each one collecting the lower-level -@@ -707,6 +748,8 @@ struct bfq_group { +@@ -701,6 +742,8 @@ struct bfq_group { int active_entities; @@ -1059,7 +1059,7 @@ index 9b04d19..97a677f 100644 struct bfqg_stats stats; struct bfqg_stats dead_stats; /* stats pushed from dead children */ }; -@@ -717,6 +760,8 @@ struct bfq_group { +@@ -711,6 +754,8 @@ struct bfq_group { struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; struct bfq_queue *async_idle_bfqq; @@ -1068,7 +1068,7 @@ index 9b04d19..97a677f 100644 }; #endif -@@ -793,6 +838,27 @@ static void bfq_put_bfqd_unlock(struct bfq_data *bfqd, unsigned long *flags) +@@ -787,6 +832,27 @@ static void bfq_put_bfqd_unlock(struct bfq_data *bfqd, unsigned long *flags) spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); } diff --git a/patches/0004-block-bfq-turn-BFQ-v7r11-for-4.7.0-into-BFQ-v8r3-for.patch b/patches/0004-block-bfq-turn-BFQ-v7r11-for-4.7.0-into-BFQ-v8r3-for.patch new file mode 100644 index 0000000..bf56ac7 --- /dev/null +++ b/patches/0004-block-bfq-turn-BFQ-v7r11-for-4.7.0-into-BFQ-v8r3-for.patch @@ -0,0 +1,6492 @@ +From d384ccf796a992e27691b7359ce54534db57e74c Mon Sep 17 00:00:00 2001 +From: Paolo Valente +Date: Tue, 17 May 2016 08:28:04 +0200 +Subject: [PATCH 4/4] block, bfq: turn BFQ-v7r11 for 4.7.0 into BFQ-v8r3 for + 4.7.0 + +--- + block/Kconfig.iosched | 2 +- + block/bfq-cgroup.c | 480 +++++---- + block/bfq-iosched.c | 2602 +++++++++++++++++++++++++++++-------------------- + block/bfq-sched.c | 441 +++++++-- + block/bfq.h | 708 +++++++------- + 5 files changed, 2484 insertions(+), 1749 deletions(-) + +diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched +index f78cd1a..6d92579 100644 +--- a/block/Kconfig.iosched ++++ b/block/Kconfig.iosched +@@ -53,7 +53,7 @@ config IOSCHED_BFQ + + config BFQ_GROUP_IOSCHED + bool "BFQ hierarchical scheduling support" +- depends on CGROUPS && IOSCHED_BFQ=y ++ depends on IOSCHED_BFQ && BLK_CGROUP + default n + ---help--- + Enable hierarchical scheduling in BFQ, using the blkio controller. +diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c +index 5ee99ec..c83d90c 100644 +--- a/block/bfq-cgroup.c ++++ b/block/bfq-cgroup.c +@@ -162,7 +162,6 @@ static struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg) + static struct bfq_group *blkg_to_bfqg(struct blkcg_gq *blkg) + { + struct blkg_policy_data *pd = blkg_to_pd(blkg, &blkcg_policy_bfq); +- BUG_ON(!pd); + return pd_to_bfqg(pd); + } + +@@ -224,14 +223,6 @@ static void bfqg_stats_update_io_merged(struct bfq_group *bfqg, int rw) + blkg_rwstat_add(&bfqg->stats.merged, rw, 1); + } + +-static void bfqg_stats_update_dispatch(struct bfq_group *bfqg, +- uint64_t bytes, int rw) +-{ +- blkg_stat_add(&bfqg->stats.sectors, bytes >> 9); +- blkg_rwstat_add(&bfqg->stats.serviced, rw, 1); +- blkg_rwstat_add(&bfqg->stats.service_bytes, rw, bytes); +-} +- + static void bfqg_stats_update_completion(struct bfq_group *bfqg, + uint64_t start_time, uint64_t io_start_time, int rw) + { +@@ -248,17 +239,11 @@ static void bfqg_stats_update_completion(struct bfq_group *bfqg, + /* @stats = 0 */ + static void bfqg_stats_reset(struct bfqg_stats *stats) + { +- if (!stats) +- return; +- + /* queued stats shouldn't be cleared */ +- blkg_rwstat_reset(&stats->service_bytes); +- blkg_rwstat_reset(&stats->serviced); + blkg_rwstat_reset(&stats->merged); + blkg_rwstat_reset(&stats->service_time); + blkg_rwstat_reset(&stats->wait_time); + blkg_stat_reset(&stats->time); +- blkg_stat_reset(&stats->unaccounted_time); + blkg_stat_reset(&stats->avg_queue_size_sum); + blkg_stat_reset(&stats->avg_queue_size_samples); + blkg_stat_reset(&stats->dequeue); +@@ -268,21 +253,19 @@ static void bfqg_stats_reset(struct bfqg_stats *stats) + } + + /* @to += @from */ +-static void bfqg_stats_merge(struct bfqg_stats *to, struct bfqg_stats *from) ++static void bfqg_stats_add_aux(struct bfqg_stats *to, struct bfqg_stats *from) + { + if (!to || !from) + return; + + /* queued stats shouldn't be cleared */ +- blkg_rwstat_add_aux(&to->service_bytes, &from->service_bytes); +- blkg_rwstat_add_aux(&to->serviced, &from->serviced); + blkg_rwstat_add_aux(&to->merged, &from->merged); + blkg_rwstat_add_aux(&to->service_time, &from->service_time); + blkg_rwstat_add_aux(&to->wait_time, &from->wait_time); + blkg_stat_add_aux(&from->time, &from->time); +- blkg_stat_add_aux(&to->unaccounted_time, &from->unaccounted_time); + blkg_stat_add_aux(&to->avg_queue_size_sum, &from->avg_queue_size_sum); +- blkg_stat_add_aux(&to->avg_queue_size_samples, &from->avg_queue_size_samples); ++ blkg_stat_add_aux(&to->avg_queue_size_samples, ++ &from->avg_queue_size_samples); + blkg_stat_add_aux(&to->dequeue, &from->dequeue); + blkg_stat_add_aux(&to->group_wait_time, &from->group_wait_time); + blkg_stat_add_aux(&to->idle_time, &from->idle_time); +@@ -308,10 +291,8 @@ static void bfqg_stats_xfer_dead(struct bfq_group *bfqg) + if (unlikely(!parent)) + return; + +- bfqg_stats_merge(&parent->dead_stats, &bfqg->stats); +- bfqg_stats_merge(&parent->dead_stats, &bfqg->dead_stats); ++ bfqg_stats_add_aux(&parent->stats, &bfqg->stats); + bfqg_stats_reset(&bfqg->stats); +- bfqg_stats_reset(&bfqg->dead_stats); + } + + static void bfq_init_entity(struct bfq_entity *entity, +@@ -332,15 +313,11 @@ static void bfq_init_entity(struct bfq_entity *entity, + + static void bfqg_stats_exit(struct bfqg_stats *stats) + { +- blkg_rwstat_exit(&stats->service_bytes); +- blkg_rwstat_exit(&stats->serviced); + blkg_rwstat_exit(&stats->merged); + blkg_rwstat_exit(&stats->service_time); + blkg_rwstat_exit(&stats->wait_time); + blkg_rwstat_exit(&stats->queued); +- blkg_stat_exit(&stats->sectors); + blkg_stat_exit(&stats->time); +- blkg_stat_exit(&stats->unaccounted_time); + blkg_stat_exit(&stats->avg_queue_size_sum); + blkg_stat_exit(&stats->avg_queue_size_samples); + blkg_stat_exit(&stats->dequeue); +@@ -351,15 +328,11 @@ static void bfqg_stats_exit(struct bfqg_stats *stats) + + static int bfqg_stats_init(struct bfqg_stats *stats, gfp_t gfp) + { +- if (blkg_rwstat_init(&stats->service_bytes, gfp) || +- blkg_rwstat_init(&stats->serviced, gfp) || +- blkg_rwstat_init(&stats->merged, gfp) || ++ if (blkg_rwstat_init(&stats->merged, gfp) || + blkg_rwstat_init(&stats->service_time, gfp) || + blkg_rwstat_init(&stats->wait_time, gfp) || + blkg_rwstat_init(&stats->queued, gfp) || +- blkg_stat_init(&stats->sectors, gfp) || + blkg_stat_init(&stats->time, gfp) || +- blkg_stat_init(&stats->unaccounted_time, gfp) || + blkg_stat_init(&stats->avg_queue_size_sum, gfp) || + blkg_stat_init(&stats->avg_queue_size_samples, gfp) || + blkg_stat_init(&stats->dequeue, gfp) || +@@ -374,20 +347,36 @@ static int bfqg_stats_init(struct bfqg_stats *stats, gfp_t gfp) + } + + static struct bfq_group_data *cpd_to_bfqgd(struct blkcg_policy_data *cpd) +- { ++{ + return cpd ? container_of(cpd, struct bfq_group_data, pd) : NULL; +- } ++} + + static struct bfq_group_data *blkcg_to_bfqgd(struct blkcg *blkcg) + { + return cpd_to_bfqgd(blkcg_to_cpd(blkcg, &blkcg_policy_bfq)); + } + ++static struct blkcg_policy_data *bfq_cpd_alloc(gfp_t gfp) ++{ ++ struct bfq_group_data *bgd; ++ ++ bgd = kzalloc(sizeof(*bgd), GFP_KERNEL); ++ if (!bgd) ++ return NULL; ++ return &bgd->pd; ++} ++ + static void bfq_cpd_init(struct blkcg_policy_data *cpd) + { + struct bfq_group_data *d = cpd_to_bfqgd(cpd); + +- d->weight = BFQ_DEFAULT_GRP_WEIGHT; ++ d->weight = cgroup_subsys_on_dfl(io_cgrp_subsys) ? ++ CGROUP_WEIGHT_DFL : BFQ_WEIGHT_LEGACY_DFL; ++} ++ ++static void bfq_cpd_free(struct blkcg_policy_data *cpd) ++{ ++ kfree(cpd_to_bfqgd(cpd)); + } + + static struct blkg_policy_data *bfq_pd_alloc(gfp_t gfp, int node) +@@ -398,8 +387,7 @@ static struct blkg_policy_data *bfq_pd_alloc(gfp_t gfp, int node) + if (!bfqg) + return NULL; + +- if (bfqg_stats_init(&bfqg->stats, gfp) || +- bfqg_stats_init(&bfqg->dead_stats, gfp)) { ++ if (bfqg_stats_init(&bfqg->stats, gfp)) { + kfree(bfqg); + return NULL; + } +@@ -407,27 +395,20 @@ static struct blkg_policy_data *bfq_pd_alloc(gfp_t gfp, int node) + return &bfqg->pd; + } + +-static void bfq_group_set_parent(struct bfq_group *bfqg, +- struct bfq_group *parent) ++static void bfq_pd_init(struct blkg_policy_data *pd) + { ++ struct blkcg_gq *blkg; ++ struct bfq_group *bfqg; ++ struct bfq_data *bfqd; + struct bfq_entity *entity; ++ struct bfq_group_data *d; + +- BUG_ON(!parent); +- BUG_ON(!bfqg); +- BUG_ON(bfqg == parent); +- ++ blkg = pd_to_blkg(pd); ++ BUG_ON(!blkg); ++ bfqg = blkg_to_bfqg(blkg); ++ bfqd = blkg->q->elevator->elevator_data; + entity = &bfqg->entity; +- entity->parent = parent->my_entity; +- entity->sched_data = &parent->sched_data; +-} +- +-static void bfq_pd_init(struct blkg_policy_data *pd) +-{ +- struct blkcg_gq *blkg = pd_to_blkg(pd); +- struct bfq_group *bfqg = blkg_to_bfqg(blkg); +- struct bfq_data *bfqd = blkg->q->elevator->elevator_data; +- struct bfq_entity *entity = &bfqg->entity; +- struct bfq_group_data *d = blkcg_to_bfqgd(blkg->blkcg); ++ d = blkcg_to_bfqgd(blkg->blkcg); + + entity->orig_weight = entity->weight = entity->new_weight = d->weight; + entity->my_sched_data = &bfqg->sched_data; +@@ -445,70 +426,53 @@ static void bfq_pd_free(struct blkg_policy_data *pd) + struct bfq_group *bfqg = pd_to_bfqg(pd); + + bfqg_stats_exit(&bfqg->stats); +- bfqg_stats_exit(&bfqg->dead_stats); +- + return kfree(bfqg); + } + +-/* offset delta from bfqg->stats to bfqg->dead_stats */ +-static const int dead_stats_off_delta = offsetof(struct bfq_group, dead_stats) - +- offsetof(struct bfq_group, stats); +- +-/* to be used by recursive prfill, sums live and dead stats recursively */ +-static u64 bfqg_stat_pd_recursive_sum(struct blkg_policy_data *pd, int off) ++static void bfq_pd_reset_stats(struct blkg_policy_data *pd) + { +- u64 sum = 0; ++ struct bfq_group *bfqg = pd_to_bfqg(pd); + +- sum += blkg_stat_recursive_sum(pd_to_blkg(pd), &blkcg_policy_bfq, off); +- sum += blkg_stat_recursive_sum(pd_to_blkg(pd), &blkcg_policy_bfq, +- off + dead_stats_off_delta); +- return sum; ++ bfqg_stats_reset(&bfqg->stats); + } + +-/* to be used by recursive prfill, sums live and dead rwstats recursively */ +-static struct blkg_rwstat bfqg_rwstat_pd_recursive_sum(struct blkg_policy_data *pd, +- int off) ++static void bfq_group_set_parent(struct bfq_group *bfqg, ++ struct bfq_group *parent) + { +- struct blkg_rwstat a, b; ++ struct bfq_entity *entity; ++ ++ BUG_ON(!parent); ++ BUG_ON(!bfqg); ++ BUG_ON(bfqg == parent); + +- a = blkg_rwstat_recursive_sum(pd_to_blkg(pd), &blkcg_policy_bfq, off); +- b = blkg_rwstat_recursive_sum(pd_to_blkg(pd), &blkcg_policy_bfq, +- off + dead_stats_off_delta); +- blkg_rwstat_add_aux(&a, &b); +- return a; ++ entity = &bfqg->entity; ++ entity->parent = parent->my_entity; ++ entity->sched_data = &parent->sched_data; + } + +-static void bfq_pd_reset_stats(struct blkg_policy_data *pd) ++static struct bfq_group *bfq_lookup_bfqg(struct bfq_data *bfqd, ++ struct blkcg *blkcg) + { +- struct bfq_group *bfqg = pd_to_bfqg(pd); ++ struct blkcg_gq *blkg; + +- bfqg_stats_reset(&bfqg->stats); +- bfqg_stats_reset(&bfqg->dead_stats); ++ blkg = blkg_lookup(blkcg, bfqd->queue); ++ if (likely(blkg)) ++ return blkg_to_bfqg(blkg); ++ return NULL; + } + +-static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, +- struct blkcg *blkcg) ++static struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd, ++ struct blkcg *blkcg) + { +- struct request_queue *q = bfqd->queue; +- struct bfq_group *bfqg = NULL, *parent; +- struct bfq_entity *entity = NULL; ++ struct bfq_group *bfqg, *parent; ++ struct bfq_entity *entity; + + assert_spin_locked(bfqd->queue->queue_lock); + +- /* avoid lookup for the common case where there's no blkcg */ +- if (blkcg == &blkcg_root) { +- bfqg = bfqd->root_group; +- } else { +- struct blkcg_gq *blkg; +- +- blkg = blkg_lookup_create(blkcg, q); +- if (!IS_ERR(blkg)) +- bfqg = blkg_to_bfqg(blkg); +- else /* fallback to root_group */ +- bfqg = bfqd->root_group; +- } ++ bfqg = bfq_lookup_bfqg(bfqd, blkcg); + +- BUG_ON(!bfqg); ++ if (unlikely(!bfqg)) ++ return NULL; + + /* + * Update chain of bfq_groups as we might be handling a leaf group +@@ -531,13 +495,18 @@ static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + return bfqg; + } + +-static void bfq_pos_tree_add_move(struct bfq_data *bfqd, struct bfq_queue *bfqq); ++static void bfq_pos_tree_add_move(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq); ++ ++static void bfq_bfqq_expire(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ bool compensate, ++ enum bfqq_expiration reason); + + /** + * bfq_bfqq_move - migrate @bfqq to @bfqg. + * @bfqd: queue descriptor. + * @bfqq: the queue to move. +- * @entity: @bfqq's entity. + * @bfqg: the group to move to. + * + * Move @bfqq to @bfqg, deactivating it from its old group and reactivating +@@ -548,26 +517,40 @@ static void bfq_pos_tree_add_move(struct bfq_data *bfqd, struct bfq_queue *bfqq) + * rcu_read_lock()). + */ + static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, +- struct bfq_entity *entity, struct bfq_group *bfqg) ++ struct bfq_group *bfqg) + { +- int busy, resume; +- +- busy = bfq_bfqq_busy(bfqq); +- resume = !RB_EMPTY_ROOT(&bfqq->sort_list); ++ struct bfq_entity *entity = &bfqq->entity; + +- BUG_ON(resume && !entity->on_st); +- BUG_ON(busy && !resume && entity->on_st && ++ BUG_ON(!bfq_bfqq_busy(bfqq) && !RB_EMPTY_ROOT(&bfqq->sort_list)); ++ BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list) && !entity->on_st); ++ BUG_ON(bfq_bfqq_busy(bfqq) && RB_EMPTY_ROOT(&bfqq->sort_list) ++ && entity->on_st && + bfqq != bfqd->in_service_queue); ++ BUG_ON(!bfq_bfqq_busy(bfqq) && bfqq == bfqd->in_service_queue); ++ ++ /* If bfqq is empty, then bfq_bfqq_expire also invokes ++ * bfq_del_bfqq_busy, thereby removing bfqq and its entity ++ * from data structures related to current group. Otherwise we ++ * need to remove bfqq explicitly with bfq_deactivate_bfqq, as ++ * we do below. ++ */ ++ if (bfqq == bfqd->in_service_queue) ++ bfq_bfqq_expire(bfqd, bfqd->in_service_queue, ++ false, BFQ_BFQQ_PREEMPTED); ++ ++ BUG_ON(entity->on_st && !bfq_bfqq_busy(bfqq) ++ && &bfq_entity_service_tree(entity)->idle != ++ entity->tree); + +- if (busy) { +- BUG_ON(atomic_read(&bfqq->ref) < 2); ++ BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_bfqq_busy(bfqq)); + +- if (!resume) +- bfq_del_bfqq_busy(bfqd, bfqq, 0); +- else +- bfq_deactivate_bfqq(bfqd, bfqq, 0); +- } else if (entity->on_st) ++ if (bfq_bfqq_busy(bfqq)) ++ bfq_deactivate_bfqq(bfqd, bfqq, 0); ++ else if (entity->on_st) { ++ BUG_ON(&bfq_entity_service_tree(entity)->idle != ++ entity->tree); + bfq_put_idle_entity(bfq_entity_service_tree(entity), entity); ++ } + bfqg_put(bfqq_group(bfqq)); + + /* +@@ -579,14 +562,17 @@ static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, + entity->sched_data = &bfqg->sched_data; + bfqg_get(bfqg); + +- if (busy) { ++ BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list) && bfq_bfqq_busy(bfqq)); ++ if (bfq_bfqq_busy(bfqq)) { + bfq_pos_tree_add_move(bfqd, bfqq); +- if (resume) +- bfq_activate_bfqq(bfqd, bfqq); ++ bfq_activate_bfqq(bfqd, bfqq); + } + + if (!bfqd->in_service_queue && !bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); ++ BUG_ON(entity->on_st && !bfq_bfqq_busy(bfqq) ++ && &bfq_entity_service_tree(entity)->idle != ++ entity->tree); + } + + /** +@@ -613,7 +599,7 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + + lockdep_assert_held(bfqd->queue->queue_lock); + +- bfqg = bfq_find_alloc_group(bfqd, blkcg); ++ bfqg = bfq_find_set_group(bfqd, blkcg); + if (async_bfqq) { + entity = &async_bfqq->entity; + +@@ -621,7 +607,8 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + bic_set_bfqq(bic, NULL, 0); + bfq_log_bfqq(bfqd, async_bfqq, + "bic_change_group: %p %d", +- async_bfqq, atomic_read(&async_bfqq->ref)); ++ async_bfqq, ++ async_bfqq->ref); + bfq_put_queue(async_bfqq); + } + } +@@ -629,7 +616,7 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + if (sync_bfqq) { + entity = &sync_bfqq->entity; + if (entity->sched_data != &bfqg->sched_data) +- bfq_bfqq_move(bfqd, sync_bfqq, entity, bfqg); ++ bfq_bfqq_move(bfqd, sync_bfqq, bfqg); + } + + return bfqg; +@@ -638,25 +625,23 @@ static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + static void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio) + { + struct bfq_data *bfqd = bic_to_bfqd(bic); +- struct blkcg *blkcg; + struct bfq_group *bfqg = NULL; +- uint64_t id; ++ uint64_t serial_nr; + + rcu_read_lock(); +- blkcg = bio_blkcg(bio); +- id = blkcg->css.serial_nr; +- rcu_read_unlock(); ++ serial_nr = bio_blkcg(bio)->css.serial_nr; + + /* + * Check whether blkcg has changed. The condition may trigger + * spuriously on a newly created cic but there's no harm. + */ +- if (unlikely(!bfqd) || likely(bic->blkcg_id == id)) +- return; ++ if (unlikely(!bfqd) || likely(bic->blkcg_serial_nr == serial_nr)) ++ goto out; + +- bfqg = __bfq_bic_change_cgroup(bfqd, bic, blkcg); +- BUG_ON(!bfqg); +- bic->blkcg_id = id; ++ bfqg = __bfq_bic_change_cgroup(bfqd, bic, bio_blkcg(bio)); ++ bic->blkcg_serial_nr = serial_nr; ++out: ++ rcu_read_unlock(); + } + + /** +@@ -682,8 +667,7 @@ static void bfq_reparent_leaf_entity(struct bfq_data *bfqd, + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(!bfqq); +- bfq_bfqq_move(bfqd, bfqq, entity, bfqd->root_group); +- return; ++ bfq_bfqq_move(bfqd, bfqq, bfqd->root_group); + } + + /** +@@ -711,16 +695,15 @@ static void bfq_reparent_active_entities(struct bfq_data *bfqd, + if (bfqg->sched_data.in_service_entity) + bfq_reparent_leaf_entity(bfqd, + bfqg->sched_data.in_service_entity); +- +- return; + } + + /** +- * bfq_destroy_group - destroy @bfqg. +- * @bfqg: the group being destroyed. ++ * bfq_pd_offline - deactivate the entity associated with @pd, ++ * and reparent its children entities. ++ * @pd: descriptor of the policy going offline. + * +- * Destroy @bfqg, making sure that it is not referenced from its parent. +- * blkio already grabs the queue_lock for us, so no need to use RCU-based magic ++ * blkio already grabs the queue_lock for us, so no need to use ++ * RCU-based magic + */ + static void bfq_pd_offline(struct blkg_policy_data *pd) + { +@@ -779,6 +762,12 @@ static void bfq_pd_offline(struct blkg_policy_data *pd) + bfq_put_async_queues(bfqd, bfqg); + BUG_ON(entity->tree); + ++ /* ++ * @blkg is going offline and will be ignored by ++ * blkg_[rw]stat_recursive_sum(). Transfer stats to the parent so ++ * that they don't get lost. If IOs complete after this point, the ++ * stats for them will be lost. Oh well... ++ */ + bfqg_stats_xfer_dead(bfqg); + } + +@@ -788,46 +777,35 @@ static void bfq_end_wr_async(struct bfq_data *bfqd) + + list_for_each_entry(blkg, &bfqd->queue->blkg_list, q_node) { + struct bfq_group *bfqg = blkg_to_bfqg(blkg); ++ BUG_ON(!bfqg); + + bfq_end_wr_async_queues(bfqd, bfqg); + } + bfq_end_wr_async_queues(bfqd, bfqd->root_group); + } + +-static u64 bfqio_cgroup_weight_read(struct cgroup_subsys_state *css, +- struct cftype *cftype) +-{ +- struct blkcg *blkcg = css_to_blkcg(css); +- struct bfq_group_data *bfqgd = blkcg_to_bfqgd(blkcg); +- int ret = -EINVAL; +- +- spin_lock_irq(&blkcg->lock); +- ret = bfqgd->weight; +- spin_unlock_irq(&blkcg->lock); +- +- return ret; +-} +- +-static int bfqio_cgroup_weight_read_dfl(struct seq_file *sf, void *v) ++static int bfq_io_show_weight(struct seq_file *sf, void *v) + { + struct blkcg *blkcg = css_to_blkcg(seq_css(sf)); + struct bfq_group_data *bfqgd = blkcg_to_bfqgd(blkcg); ++ unsigned int val = 0; + +- spin_lock_irq(&blkcg->lock); +- seq_printf(sf, "%u\n", bfqgd->weight); +- spin_unlock_irq(&blkcg->lock); ++ if (bfqgd) ++ val = bfqgd->weight; ++ ++ seq_printf(sf, "%u\n", val); + + return 0; + } + +-static int bfqio_cgroup_weight_write(struct cgroup_subsys_state *css, +- struct cftype *cftype, +- u64 val) ++static int bfq_io_set_weight_legacy(struct cgroup_subsys_state *css, ++ struct cftype *cftype, ++ u64 val) + { + struct blkcg *blkcg = css_to_blkcg(css); + struct bfq_group_data *bfqgd = blkcg_to_bfqgd(blkcg); + struct blkcg_gq *blkg; +- int ret = -EINVAL; ++ int ret = -ERANGE; + + if (val < BFQ_MIN_WEIGHT || val > BFQ_MAX_WEIGHT) + return ret; +@@ -837,6 +815,7 @@ static int bfqio_cgroup_weight_write(struct cgroup_subsys_state *css, + bfqgd->weight = (unsigned short)val; + hlist_for_each_entry(blkg, &blkcg->blkg_list, blkcg_node) { + struct bfq_group *bfqg = blkg_to_bfqg(blkg); ++ + if (!bfqg) + continue; + /* +@@ -871,13 +850,18 @@ static int bfqio_cgroup_weight_write(struct cgroup_subsys_state *css, + return ret; + } + +-static ssize_t bfqio_cgroup_weight_write_dfl(struct kernfs_open_file *of, +- char *buf, size_t nbytes, +- loff_t off) ++static ssize_t bfq_io_set_weight(struct kernfs_open_file *of, ++ char *buf, size_t nbytes, ++ loff_t off) + { ++ u64 weight; + /* First unsigned long found in the file is used */ +- return bfqio_cgroup_weight_write(of_css(of), NULL, +- simple_strtoull(strim(buf), NULL, 0)); ++ int ret = kstrtoull(strim(buf), 0, &weight); ++ ++ if (ret) ++ return ret; ++ ++ return bfq_io_set_weight_legacy(of_css(of), NULL, weight); + } + + static int bfqg_print_stat(struct seq_file *sf, void *v) +@@ -897,16 +881,17 @@ static int bfqg_print_rwstat(struct seq_file *sf, void *v) + static u64 bfqg_prfill_stat_recursive(struct seq_file *sf, + struct blkg_policy_data *pd, int off) + { +- u64 sum = bfqg_stat_pd_recursive_sum(pd, off); +- ++ u64 sum = blkg_stat_recursive_sum(pd_to_blkg(pd), ++ &blkcg_policy_bfq, off); + return __blkg_prfill_u64(sf, pd, sum); + } + + static u64 bfqg_prfill_rwstat_recursive(struct seq_file *sf, + struct blkg_policy_data *pd, int off) + { +- struct blkg_rwstat sum = bfqg_rwstat_pd_recursive_sum(pd, off); +- ++ struct blkg_rwstat sum = blkg_rwstat_recursive_sum(pd_to_blkg(pd), ++ &blkcg_policy_bfq, ++ off); + return __blkg_prfill_rwstat(sf, pd, &sum); + } + +@@ -926,6 +911,41 @@ static int bfqg_print_rwstat_recursive(struct seq_file *sf, void *v) + return 0; + } + ++static u64 bfqg_prfill_sectors(struct seq_file *sf, struct blkg_policy_data *pd, ++ int off) ++{ ++ u64 sum = blkg_rwstat_total(&pd->blkg->stat_bytes); ++ ++ return __blkg_prfill_u64(sf, pd, sum >> 9); ++} ++ ++static int bfqg_print_stat_sectors(struct seq_file *sf, void *v) ++{ ++ blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), ++ bfqg_prfill_sectors, &blkcg_policy_bfq, 0, false); ++ return 0; ++} ++ ++static u64 bfqg_prfill_sectors_recursive(struct seq_file *sf, ++ struct blkg_policy_data *pd, int off) ++{ ++ struct blkg_rwstat tmp = blkg_rwstat_recursive_sum(pd->blkg, NULL, ++ offsetof(struct blkcg_gq, stat_bytes)); ++ u64 sum = atomic64_read(&tmp.aux_cnt[BLKG_RWSTAT_READ]) + ++ atomic64_read(&tmp.aux_cnt[BLKG_RWSTAT_WRITE]); ++ ++ return __blkg_prfill_u64(sf, pd, sum >> 9); ++} ++ ++static int bfqg_print_stat_sectors_recursive(struct seq_file *sf, void *v) ++{ ++ blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), ++ bfqg_prfill_sectors_recursive, &blkcg_policy_bfq, 0, ++ false); ++ return 0; ++} ++ ++ + static u64 bfqg_prfill_avg_queue_size(struct seq_file *sf, + struct blkg_policy_data *pd, int off) + { +@@ -950,7 +970,8 @@ static int bfqg_print_avg_queue_size(struct seq_file *sf, void *v) + return 0; + } + +-static struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int node) ++static struct bfq_group * ++bfq_create_group_hierarchy(struct bfq_data *bfqd, int node) + { + int ret; + +@@ -958,41 +979,18 @@ static struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int n + if (ret) + return NULL; + +- return blkg_to_bfqg(bfqd->queue->root_blkg); ++ return blkg_to_bfqg(bfqd->queue->root_blkg); + } + +-static struct blkcg_policy_data *bfq_cpd_alloc(gfp_t gfp) +-{ +- struct bfq_group_data *bgd; +- +- bgd = kzalloc(sizeof(*bgd), GFP_KERNEL); +- if (!bgd) +- return NULL; +- return &bgd->pd; +-} +- +-static void bfq_cpd_free(struct blkcg_policy_data *cpd) +-{ +- kfree(cpd_to_bfqgd(cpd)); +-} +- +-static struct cftype bfqio_files_dfl[] = { ++static struct cftype bfq_blkcg_legacy_files[] = { + { +- .name = "weight", ++ .name = "bfq.weight", + .flags = CFTYPE_NOT_ON_ROOT, +- .seq_show = bfqio_cgroup_weight_read_dfl, +- .write = bfqio_cgroup_weight_write_dfl, ++ .seq_show = bfq_io_show_weight, ++ .write_u64 = bfq_io_set_weight_legacy, + }, +- {} /* terminate */ +-}; + +-static struct cftype bfqio_files[] = { +- { +- .name = "bfq.weight", +- .read_u64 = bfqio_cgroup_weight_read, +- .write_u64 = bfqio_cgroup_weight_write, +- }, +- /* statistics, cover only the tasks in the bfqg */ ++ /* statistics, covers only the tasks in the bfqg */ + { + .name = "bfq.time", + .private = offsetof(struct bfq_group, stats.time), +@@ -1000,18 +998,17 @@ static struct cftype bfqio_files[] = { + }, + { + .name = "bfq.sectors", +- .private = offsetof(struct bfq_group, stats.sectors), +- .seq_show = bfqg_print_stat, ++ .seq_show = bfqg_print_stat_sectors, + }, + { + .name = "bfq.io_service_bytes", +- .private = offsetof(struct bfq_group, stats.service_bytes), +- .seq_show = bfqg_print_rwstat, ++ .private = (unsigned long)&blkcg_policy_bfq, ++ .seq_show = blkg_print_stat_bytes, + }, + { + .name = "bfq.io_serviced", +- .private = offsetof(struct bfq_group, stats.serviced), +- .seq_show = bfqg_print_rwstat, ++ .private = (unsigned long)&blkcg_policy_bfq, ++ .seq_show = blkg_print_stat_ios, + }, + { + .name = "bfq.io_service_time", +@@ -1042,18 +1039,17 @@ static struct cftype bfqio_files[] = { + }, + { + .name = "bfq.sectors_recursive", +- .private = offsetof(struct bfq_group, stats.sectors), +- .seq_show = bfqg_print_stat_recursive, ++ .seq_show = bfqg_print_stat_sectors_recursive, + }, + { + .name = "bfq.io_service_bytes_recursive", +- .private = offsetof(struct bfq_group, stats.service_bytes), +- .seq_show = bfqg_print_rwstat_recursive, ++ .private = (unsigned long)&blkcg_policy_bfq, ++ .seq_show = blkg_print_stat_bytes_recursive, + }, + { + .name = "bfq.io_serviced_recursive", +- .private = offsetof(struct bfq_group, stats.serviced), +- .seq_show = bfqg_print_rwstat_recursive, ++ .private = (unsigned long)&blkcg_policy_bfq, ++ .seq_show = blkg_print_stat_ios_recursive, + }, + { + .name = "bfq.io_service_time_recursive", +@@ -1099,32 +1095,35 @@ static struct cftype bfqio_files[] = { + .private = offsetof(struct bfq_group, stats.dequeue), + .seq_show = bfqg_print_stat, + }, +- { +- .name = "bfq.unaccounted_time", +- .private = offsetof(struct bfq_group, stats.unaccounted_time), +- .seq_show = bfqg_print_stat, +- }, + { } /* terminate */ + }; + +-static struct blkcg_policy blkcg_policy_bfq = { +- .dfl_cftypes = bfqio_files_dfl, +- .legacy_cftypes = bfqio_files, +- +- .pd_alloc_fn = bfq_pd_alloc, +- .pd_init_fn = bfq_pd_init, +- .pd_offline_fn = bfq_pd_offline, +- .pd_free_fn = bfq_pd_free, +- .pd_reset_stats_fn = bfq_pd_reset_stats, +- +- .cpd_alloc_fn = bfq_cpd_alloc, +- .cpd_init_fn = bfq_cpd_init, +- .cpd_bind_fn = bfq_cpd_init, +- .cpd_free_fn = bfq_cpd_free, +- ++static struct cftype bfq_blkg_files[] = { ++ { ++ .name = "bfq.weight", ++ .flags = CFTYPE_NOT_ON_ROOT, ++ .seq_show = bfq_io_show_weight, ++ .write = bfq_io_set_weight, ++ }, ++ {} /* terminate */ + }; + +-#else ++#else /* CONFIG_BFQ_GROUP_IOSCHED */ ++ ++static inline void bfqg_stats_update_io_add(struct bfq_group *bfqg, ++ struct bfq_queue *bfqq, int rw) { } ++static inline void bfqg_stats_update_io_remove(struct bfq_group *bfqg, int rw) { } ++static inline void bfqg_stats_update_io_merged(struct bfq_group *bfqg, int rw) { } ++static inline void bfqg_stats_update_completion(struct bfq_group *bfqg, ++ uint64_t start_time, uint64_t io_start_time, int rw) { } ++static inline void bfqg_stats_set_start_group_wait_time(struct bfq_group *bfqg, ++struct bfq_group *curr_bfqg) { } ++static inline void bfqg_stats_end_empty_time(struct bfqg_stats *stats) { } ++static inline void bfqg_stats_update_dequeue(struct bfq_group *bfqg) { } ++static inline void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg) { } ++static inline void bfqg_stats_update_idle_time(struct bfq_group *bfqg) { } ++static inline void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg) { } ++static inline void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg) { } + + static void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +@@ -1146,27 +1145,20 @@ bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio) + return bfqd->root_group; + } + +-static void bfq_bfqq_move(struct bfq_data *bfqd, +- struct bfq_queue *bfqq, +- struct bfq_entity *entity, +- struct bfq_group *bfqg) +-{ +-} +- + static void bfq_end_wr_async(struct bfq_data *bfqd) + { + bfq_end_wr_async_queues(bfqd, bfqd->root_group); + } + +-static void bfq_disconnect_groups(struct bfq_data *bfqd) ++static struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd, ++ struct blkcg *blkcg) + { +- bfq_put_async_queues(bfqd, bfqd->root_group); ++ return bfqd->root_group; + } + +-static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, +- struct blkcg *blkcg) ++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq) + { +- return bfqd->root_group; ++ return bfqq->bfqd->root_group; + } + + static struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int node) +diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c +index d1f648d..3bc1f8b 100644 +--- a/block/bfq-iosched.c ++++ b/block/bfq-iosched.c +@@ -7,25 +7,26 @@ + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * +- * Copyright (C) 2010 Paolo Valente ++ * Copyright (C) 2016 Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ + * file. + * +- * BFQ is a proportional-share storage-I/O scheduling algorithm based on +- * the slice-by-slice service scheme of CFQ. But BFQ assigns budgets, +- * measured in number of sectors, to processes instead of time slices. The +- * device is not granted to the in-service process for a given time slice, +- * but until it has exhausted its assigned budget. This change from the time +- * to the service domain allows BFQ to distribute the device throughput +- * among processes as desired, without any distortion due to ZBR, workload +- * fluctuations or other factors. BFQ uses an ad hoc internal scheduler, +- * called B-WF2Q+, to schedule processes according to their budgets. More +- * precisely, BFQ schedules queues associated to processes. Thanks to the +- * accurate policy of B-WF2Q+, BFQ can afford to assign high budgets to +- * I/O-bound processes issuing sequential requests (to boost the +- * throughput), and yet guarantee a low latency to interactive and soft +- * real-time applications. ++ * BFQ is a proportional-share storage-I/O scheduling algorithm based ++ * on the slice-by-slice service scheme of CFQ. But BFQ assigns ++ * budgets, measured in number of sectors, to processes instead of ++ * time slices. The device is not granted to the in-service process ++ * for a given time slice, but until it has exhausted its assigned ++ * budget. This change from the time to the service domain enables BFQ ++ * to distribute the device throughput among processes as desired, ++ * without any distortion due to throughput fluctuations, or to device ++ * internal queueing. BFQ uses an ad hoc internal scheduler, called ++ * B-WF2Q+, to schedule processes according to their budgets. More ++ * precisely, BFQ schedules queues associated with processes. Thanks to ++ * the accurate policy of B-WF2Q+, BFQ can afford to assign high ++ * budgets to I/O-bound processes issuing sequential requests (to ++ * boost the throughput), and yet guarantee a low latency to ++ * interactive and soft real-time applications. + * + * BFQ is described in [1], where also a reference to the initial, more + * theoretical paper on BFQ can be found. The interested reader can find +@@ -87,7 +88,6 @@ static const int bfq_stats_min_budgets = 194; + + /* Default maximum budget values, in sectors and number of requests. */ + static const int bfq_default_max_budget = 16 * 1024; +-static const int bfq_max_budget_async_rq = 4; + + /* + * Async to sync throughput distribution is controlled as follows: +@@ -97,8 +97,7 @@ static const int bfq_max_budget_async_rq = 4; + static const int bfq_async_charge_factor = 10; + + /* Default timeout values, in jiffies, approximating CFQ defaults. */ +-static const int bfq_timeout_sync = HZ / 8; +-static int bfq_timeout_async = HZ / 25; ++static const int bfq_timeout = HZ / 8; + + struct kmem_cache *bfq_pool; + +@@ -109,8 +108,9 @@ struct kmem_cache *bfq_pool; + #define BFQ_HW_QUEUE_THRESHOLD 4 + #define BFQ_HW_QUEUE_SAMPLES 32 + +-#define BFQQ_SEEK_THR (sector_t)(8 * 1024) +-#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > BFQQ_SEEK_THR) ++#define BFQQ_SEEK_THR (sector_t)(8 * 100) ++#define BFQQ_CLOSE_THR (sector_t)(8 * 1024) ++#define BFQQ_SEEKY(bfqq) (hweight32(bfqq->seek_history) > 32/8) + + /* Min samples used for peak rate estimation (for autotuning). */ + #define BFQ_PEAK_RATE_SAMPLES 32 +@@ -141,16 +141,24 @@ struct kmem_cache *bfq_pool; + * The device's speed class is dynamically (re)detected in + * bfq_update_peak_rate() every time the estimated peak rate is updated. + * +- * In the following definitions, R_slow[0]/R_fast[0] and T_slow[0]/T_fast[0] +- * are the reference values for a slow/fast rotational device, whereas +- * R_slow[1]/R_fast[1] and T_slow[1]/T_fast[1] are the reference values for +- * a slow/fast non-rotational device. Finally, device_speed_thresh are the +- * thresholds used to switch between speed classes. ++ * In the following definitions, R_slow[0]/R_fast[0] and ++ * T_slow[0]/T_fast[0] are the reference values for a slow/fast ++ * rotational device, whereas R_slow[1]/R_fast[1] and ++ * T_slow[1]/T_fast[1] are the reference values for a slow/fast ++ * non-rotational device. Finally, device_speed_thresh are the ++ * thresholds used to switch between speed classes. The reference ++ * rates are not the actual peak rates of the devices used as a ++ * reference, but slightly lower values. The reason for using these ++ * slightly lower values is that the peak-rate estimator tends to ++ * yield slightly lower values than the actual peak rate (it can yield ++ * the actual peak rate only if there is only one process doing I/O, ++ * and the process does sequential I/O). ++ * + * Both the reference peak rates and the thresholds are measured in + * sectors/usec, left-shifted by BFQ_RATE_SHIFT. + */ +-static int R_slow[2] = {1536, 10752}; +-static int R_fast[2] = {17415, 34791}; ++static int R_slow[2] = {1000, 10700}; ++static int R_fast[2] = {14000, 33000}; + /* + * To improve readability, a conversion function is used to initialize the + * following arrays, which entails that they can be initialized only in a +@@ -410,11 +418,7 @@ static bool bfq_differentiated_weights(struct bfq_data *bfqd) + */ + static bool bfq_symmetric_scenario(struct bfq_data *bfqd) + { +- return +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- !bfqd->active_numerous_groups && +-#endif +- !bfq_differentiated_weights(bfqd); ++ return !bfq_differentiated_weights(bfqd); + } + + /* +@@ -534,9 +538,19 @@ static struct request *bfq_find_next_rq(struct bfq_data *bfqd, + static unsigned long bfq_serv_to_charge(struct request *rq, + struct bfq_queue *bfqq) + { +- return blk_rq_sectors(rq) * +- (1 + ((!bfq_bfqq_sync(bfqq)) * (bfqq->wr_coeff == 1) * +- bfq_async_charge_factor)); ++ if (bfq_bfqq_sync(bfqq) || bfqq->wr_coeff > 1) ++ return blk_rq_sectors(rq); ++ ++ /* ++ * If there are no weight-raised queues, then amplify service ++ * by just the async charge factor; otherwise amplify service ++ * by twice the async charge factor, to further reduce latency ++ * for weight-raised queues. ++ */ ++ if (bfqq->bfqd->wr_busy_queues == 0) ++ return blk_rq_sectors(rq) * bfq_async_charge_factor; ++ ++ return blk_rq_sectors(rq) * 2 * bfq_async_charge_factor; + } + + /** +@@ -591,12 +605,23 @@ static unsigned int bfq_wr_duration(struct bfq_data *bfqd) + dur = bfqd->RT_prod; + do_div(dur, bfqd->peak_rate); + +- return dur; +-} ++ /* ++ * Limit duration between 3 and 13 seconds. Tests show that ++ * higher values than 13 seconds often yield the opposite of ++ * the desired result, i.e., worsen responsiveness by letting ++ * non-interactive and non-soft-real-time applications ++ * preserve weight raising for a too long time interval. ++ * ++ * On the other end, lower values than 3 seconds make it ++ * difficult for most interactive tasks to complete their jobs ++ * before weight-raising finishes. ++ */ ++ if (dur > msecs_to_jiffies(13000)) ++ dur = msecs_to_jiffies(13000); ++ else if (dur < msecs_to_jiffies(3000)) ++ dur = msecs_to_jiffies(3000); + +-static unsigned bfq_bfqq_cooperations(struct bfq_queue *bfqq) +-{ +- return bfqq->bic ? bfqq->bic->cooperations : 0; ++ return dur; + } + + static void +@@ -606,31 +631,11 @@ bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_io_cq *bic) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); ++ + if (bic->saved_IO_bound) + bfq_mark_bfqq_IO_bound(bfqq); + else + bfq_clear_bfqq_IO_bound(bfqq); +- /* Assuming that the flag in_large_burst is already correctly set */ +- if (bic->wr_time_left && bfqq->bfqd->low_latency && +- !bfq_bfqq_in_large_burst(bfqq) && +- bic->cooperations < bfqq->bfqd->bfq_coop_thresh) { +- /* +- * Start a weight raising period with the duration given by +- * the raising_time_left snapshot. +- */ +- if (bfq_bfqq_busy(bfqq)) +- bfqq->bfqd->wr_busy_queues++; +- bfqq->wr_coeff = bfqq->bfqd->bfq_wr_coeff; +- bfqq->wr_cur_max_time = bic->wr_time_left; +- bfqq->last_wr_start_finish = jiffies; +- bfqq->entity.prio_changed = 1; +- } +- /* +- * Clear wr_time_left to prevent bfq_bfqq_save_state() from +- * getting confused about the queue's need of a weight-raising +- * period. +- */ +- bic->wr_time_left = 0; + } + + static int bfqq_process_refs(struct bfq_queue *bfqq) +@@ -640,7 +645,7 @@ static int bfqq_process_refs(struct bfq_queue *bfqq) + lockdep_assert_held(bfqq->bfqd->queue->queue_lock); + + io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; +- process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; ++ process_refs = bfqq->ref - io_refs - bfqq->entity.on_st; + BUG_ON(process_refs < 0); + return process_refs; + } +@@ -655,6 +660,7 @@ static void bfq_reset_burst_list(struct bfq_data *bfqd, struct bfq_queue *bfqq) + hlist_del_init(&item->burst_list_node); + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); + bfqd->burst_size = 1; ++ bfqd->burst_parent_entity = bfqq->entity.parent; + } + + /* Add bfqq to the list of queues in current burst (see bfq_handle_burst) */ +@@ -663,6 +669,10 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + /* Increment burst size to take into account also bfqq */ + bfqd->burst_size++; + ++ bfq_log_bfqq(bfqd, bfqq, "add_to_burst %d", bfqd->burst_size); ++ ++ BUG_ON(bfqd->burst_size > bfqd->bfq_large_burst_thresh); ++ + if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) { + struct bfq_queue *pos, *bfqq_item; + struct hlist_node *n; +@@ -672,15 +682,19 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + * other to consider this burst as large. + */ + bfqd->large_burst = true; ++ bfq_log_bfqq(bfqd, bfqq, "add_to_burst: large burst started"); + + /* + * We can now mark all queues in the burst list as + * belonging to a large burst. + */ + hlist_for_each_entry(bfqq_item, &bfqd->burst_list, +- burst_list_node) ++ burst_list_node) { + bfq_mark_bfqq_in_large_burst(bfqq_item); ++ bfq_log_bfqq(bfqd, bfqq_item, "marked in large burst"); ++ } + bfq_mark_bfqq_in_large_burst(bfqq); ++ bfq_log_bfqq(bfqd, bfqq, "marked in large burst"); + + /* + * From now on, and until the current burst finishes, any +@@ -692,67 +706,79 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + hlist_for_each_entry_safe(pos, n, &bfqd->burst_list, + burst_list_node) + hlist_del_init(&pos->burst_list_node); +- } else /* burst not yet large: add bfqq to the burst list */ ++ } else /* ++ * Burst not yet large: add bfqq to the burst list. Do ++ * not increment the ref counter for bfqq, because bfqq ++ * is removed from the burst list before freeing bfqq ++ * in put_queue. ++ */ + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); + } + + /* +- * If many queues happen to become active shortly after each other, then, +- * to help the processes associated to these queues get their job done as +- * soon as possible, it is usually better to not grant either weight-raising +- * or device idling to these queues. In this comment we describe, firstly, +- * the reasons why this fact holds, and, secondly, the next function, which +- * implements the main steps needed to properly mark these queues so that +- * they can then be treated in a different way. ++ * If many queues belonging to the same group happen to be created ++ * shortly after each other, then the processes associated with these ++ * queues have typically a common goal. In particular, bursts of queue ++ * creations are usually caused by services or applications that spawn ++ * many parallel threads/processes. Examples are systemd during boot, ++ * or git grep. To help these processes get their job done as soon as ++ * possible, it is usually better to not grant either weight-raising ++ * or device idling to their queues. + * +- * As for the terminology, we say that a queue becomes active, i.e., +- * switches from idle to backlogged, either when it is created (as a +- * consequence of the arrival of an I/O request), or, if already existing, +- * when a new request for the queue arrives while the queue is idle. +- * Bursts of activations, i.e., activations of different queues occurring +- * shortly after each other, are typically caused by services or applications +- * that spawn or reactivate many parallel threads/processes. Examples are +- * systemd during boot or git grep. ++ * In this comment we describe, firstly, the reasons why this fact ++ * holds, and, secondly, the next function, which implements the main ++ * steps needed to properly mark these queues so that they can then be ++ * treated in a different way. + * +- * These services or applications benefit mostly from a high throughput: +- * the quicker the requests of the activated queues are cumulatively served, +- * the sooner the target job of these queues gets completed. As a consequence, +- * weight-raising any of these queues, which also implies idling the device +- * for it, is almost always counterproductive: in most cases it just lowers +- * throughput. ++ * The above services or applications benefit mostly from a high ++ * throughput: the quicker the requests of the activated queues are ++ * cumulatively served, the sooner the target job of these queues gets ++ * completed. As a consequence, weight-raising any of these queues, ++ * which also implies idling the device for it, is almost always ++ * counterproductive. In most cases it just lowers throughput. + * +- * On the other hand, a burst of activations may be also caused by the start +- * of an application that does not consist in a lot of parallel I/O-bound +- * threads. In fact, with a complex application, the burst may be just a +- * consequence of the fact that several processes need to be executed to +- * start-up the application. To start an application as quickly as possible, +- * the best thing to do is to privilege the I/O related to the application +- * with respect to all other I/O. Therefore, the best strategy to start as +- * quickly as possible an application that causes a burst of activations is +- * to weight-raise all the queues activated during the burst. This is the ++ * On the other hand, a burst of queue creations may be caused also by ++ * the start of an application that does not consist of a lot of ++ * parallel I/O-bound threads. In fact, with a complex application, ++ * several short processes may need to be executed to start-up the ++ * application. In this respect, to start an application as quickly as ++ * possible, the best thing to do is in any case to privilege the I/O ++ * related to the application with respect to all other ++ * I/O. Therefore, the best strategy to start as quickly as possible ++ * an application that causes a burst of queue creations is to ++ * weight-raise all the queues created during the burst. This is the + * exact opposite of the best strategy for the other type of bursts. + * +- * In the end, to take the best action for each of the two cases, the two +- * types of bursts need to be distinguished. Fortunately, this seems +- * relatively easy to do, by looking at the sizes of the bursts. In +- * particular, we found a threshold such that bursts with a larger size +- * than that threshold are apparently caused only by services or commands +- * such as systemd or git grep. For brevity, hereafter we call just 'large' +- * these bursts. BFQ *does not* weight-raise queues whose activations occur +- * in a large burst. In addition, for each of these queues BFQ performs or +- * does not perform idling depending on which choice boosts the throughput +- * most. The exact choice depends on the device and request pattern at ++ * In the end, to take the best action for each of the two cases, the ++ * two types of bursts need to be distinguished. Fortunately, this ++ * seems relatively easy, by looking at the sizes of the bursts. In ++ * particular, we found a threshold such that only bursts with a ++ * larger size than that threshold are apparently caused by ++ * services or commands such as systemd or git grep. For brevity, ++ * hereafter we call just 'large' these bursts. BFQ *does not* ++ * weight-raise queues whose creation occurs in a large burst. In ++ * addition, for each of these queues BFQ performs or does not perform ++ * idling depending on which choice boosts the throughput more. The ++ * exact choice depends on the device and request pattern at + * hand. + * +- * Turning back to the next function, it implements all the steps needed +- * to detect the occurrence of a large burst and to properly mark all the +- * queues belonging to it (so that they can then be treated in a different +- * way). This goal is achieved by maintaining a special "burst list" that +- * holds, temporarily, the queues that belong to the burst in progress. The +- * list is then used to mark these queues as belonging to a large burst if +- * the burst does become large. The main steps are the following. ++ * Unfortunately, false positives may occur while an interactive task ++ * is starting (e.g., an application is being started). The ++ * consequence is that the queues associated with the task do not ++ * enjoy weight raising as expected. Fortunately these false positives ++ * are very rare. They typically occur if some service happens to ++ * start doing I/O exactly when the interactive task starts. ++ * ++ * Turning back to the next function, it implements all the steps ++ * needed to detect the occurrence of a large burst and to properly ++ * mark all the queues belonging to it (so that they can then be ++ * treated in a different way). This goal is achieved by maintaining a ++ * "burst list" that holds, temporarily, the queues that belong to the ++ * burst in progress. The list is then used to mark these queues as ++ * belonging to a large burst if the burst does become large. The main ++ * steps are the following. + * +- * . when the very first queue is activated, the queue is inserted into the ++ * . when the very first queue is created, the queue is inserted into the + * list (as it could be the first queue in a possible burst) + * + * . if the current burst has not yet become large, and a queue Q that does +@@ -773,13 +799,13 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + * + * . the device enters a large-burst mode + * +- * . if a queue Q that does not belong to the burst is activated while ++ * . if a queue Q that does not belong to the burst is created while + * the device is in large-burst mode and shortly after the last time + * at which a queue either entered the burst list or was marked as + * belonging to the current large burst, then Q is immediately marked + * as belonging to a large burst. + * +- * . if a queue Q that does not belong to the burst is activated a while ++ * . if a queue Q that does not belong to the burst is created a while + * later, i.e., not shortly after, than the last time at which a queue + * either entered the burst list or was marked as belonging to the + * current large burst, then the current burst is deemed as finished and: +@@ -792,52 +818,44 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + * in a possible new burst (then the burst list contains just Q + * after this step). + */ +-static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq, +- bool idle_for_long_time) ++static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) + { + /* +- * If bfqq happened to be activated in a burst, but has been idle +- * for at least as long as an interactive queue, then we assume +- * that, in the overall I/O initiated in the burst, the I/O +- * associated to bfqq is finished. So bfqq does not need to be +- * treated as a queue belonging to a burst anymore. Accordingly, +- * we reset bfqq's in_large_burst flag if set, and remove bfqq +- * from the burst list if it's there. We do not decrement instead +- * burst_size, because the fact that bfqq does not need to belong +- * to the burst list any more does not invalidate the fact that +- * bfqq may have been activated during the current burst. +- */ +- if (idle_for_long_time) { +- hlist_del_init(&bfqq->burst_list_node); +- bfq_clear_bfqq_in_large_burst(bfqq); +- } +- +- /* + * If bfqq is already in the burst list or is part of a large +- * burst, then there is nothing else to do. ++ * burst, or finally has just been split, then there is ++ * nothing else to do. + */ + if (!hlist_unhashed(&bfqq->burst_list_node) || +- bfq_bfqq_in_large_burst(bfqq)) ++ bfq_bfqq_in_large_burst(bfqq) || ++ time_is_after_eq_jiffies(bfqq->split_time + ++ msecs_to_jiffies(10))) + return; + + /* +- * If bfqq's activation happens late enough, then the current +- * burst is finished, and related data structures must be reset. ++ * If bfqq's creation happens late enough, or bfqq belongs to ++ * a different group than the burst group, then the current ++ * burst is finished, and related data structures must be ++ * reset. + * +- * In this respect, consider the special case where bfqq is the very +- * first queue being activated. In this case, last_ins_in_burst is +- * not yet significant when we get here. But it is easy to verify +- * that, whether or not the following condition is true, bfqq will +- * end up being inserted into the burst list. In particular the +- * list will happen to contain only bfqq. And this is exactly what +- * has to happen, as bfqq may be the first queue in a possible ++ * In this respect, consider the special case where bfqq is ++ * the very first queue created after BFQ is selected for this ++ * device. In this case, last_ins_in_burst and ++ * burst_parent_entity are not yet significant when we get ++ * here. But it is easy to verify that, whether or not the ++ * following condition is true, bfqq will end up being ++ * inserted into the burst list. In particular the list will ++ * happen to contain only bfqq. And this is exactly what has ++ * to happen, as bfqq may be the first queue of the first + * burst. + */ + if (time_is_before_jiffies(bfqd->last_ins_in_burst + +- bfqd->bfq_burst_interval)) { ++ bfqd->bfq_burst_interval) || ++ bfqq->entity.parent != bfqd->burst_parent_entity) { + bfqd->large_burst = false; + bfq_reset_burst_list(bfqd, bfqq); +- return; ++ bfq_log_bfqq(bfqd, bfqq, ++ "handle_burst: late activation or different group"); ++ goto end; + } + + /* +@@ -846,8 +864,9 @@ static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq, + * bfqq as belonging to this large burst immediately. + */ + if (bfqd->large_burst) { ++ bfq_log_bfqq(bfqd, bfqq, "handle_burst: marked in burst"); + bfq_mark_bfqq_in_large_burst(bfqq); +- return; ++ goto end; + } + + /* +@@ -856,25 +875,498 @@ static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq, + * queue. Then we add bfqq to the burst. + */ + bfq_add_to_burst(bfqd, bfqq); ++end: ++ /* ++ * At this point, bfqq either has been added to the current ++ * burst or has caused the current burst to terminate and a ++ * possible new burst to start. In particular, in the second ++ * case, bfqq has become the first queue in the possible new ++ * burst. In both cases last_ins_in_burst needs to be moved ++ * forward. ++ */ ++ bfqd->last_ins_in_burst = jiffies; ++ ++} ++ ++static int bfq_bfqq_budget_left(struct bfq_queue *bfqq) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ return entity->budget - entity->service; ++} ++ ++/* ++ * If enough samples have been computed, return the current max budget ++ * stored in bfqd, which is dynamically updated according to the ++ * estimated disk peak rate; otherwise return the default max budget ++ */ ++static int bfq_max_budget(struct bfq_data *bfqd) ++{ ++ if (bfqd->budgets_assigned < bfq_stats_min_budgets) ++ return bfq_default_max_budget; ++ else ++ return bfqd->bfq_max_budget; ++} ++ ++/* ++ * Return min budget, which is a fraction of the current or default ++ * max budget (trying with 1/32) ++ */ ++static int bfq_min_budget(struct bfq_data *bfqd) ++{ ++ if (bfqd->budgets_assigned < bfq_stats_min_budgets) ++ return bfq_default_max_budget / 32; ++ else ++ return bfqd->bfq_max_budget / 32; ++} ++ ++static void bfq_bfqq_expire(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ bool compensate, ++ enum bfqq_expiration reason); ++ ++/* ++ * The next function, invoked after the input queue bfqq switches from ++ * idle to busy, updates the budget of bfqq. The function also tells ++ * whether the in-service queue should be expired, by returning ++ * true. The purpose of expiring the in-service queue is to give bfqq ++ * the chance to possibly preempt the in-service queue, and the reason ++ * for preempting the in-service queue is to achieve one of the two ++ * goals below. ++ * ++ * 1. Guarantee to bfqq its reserved bandwidth even if bfqq has ++ * expired because it has remained idle. In particular, bfqq may have ++ * expired for one of the following two reasons: ++ * ++ * - BFQ_BFQQ_NO_MORE_REQUEST bfqq did not enjoy any device idling and ++ * did not make it to issue a new request before its last request ++ * was served; ++ * ++ * - BFQ_BFQQ_TOO_IDLE bfqq did enjoy device idling, but did not issue ++ * a new request before the expiration of the idling-time. ++ * ++ * Even if bfqq has expired for one of the above reasons, the process ++ * associated with the queue may be however issuing requests greedily, ++ * and thus be sensitive to the bandwidth it receives (bfqq may have ++ * remained idle for other reasons: CPU high load, bfqq not enjoying ++ * idling, I/O throttling somewhere in the path from the process to ++ * the I/O scheduler, ...). But if, after every expiration for one of ++ * the above two reasons, bfqq has to wait for the service of at least ++ * one full budget of another queue before being served again, then ++ * bfqq is likely to get a much lower bandwidth or resource time than ++ * its reserved ones. To address this issue, two countermeasures need ++ * to be taken. ++ * ++ * First, the budget and the timestamps of bfqq need to be updated in ++ * a special way on bfqq reactivation: they need to be updated as if ++ * bfqq did not remain idle and did not expire. In fact, if they are ++ * computed as if bfqq expired and remained idle until reactivation, ++ * then the process associated with bfqq is treated as if, instead of ++ * being greedy, it stopped issuing requests when bfqq remained idle, ++ * and restarts issuing requests only on this reactivation. In other ++ * words, the scheduler does not help the process recover the "service ++ * hole" between bfqq expiration and reactivation. As a consequence, ++ * the process receives a lower bandwidth than its reserved one. In ++ * contrast, to recover this hole, the budget must be updated as if ++ * bfqq was not expired at all before this reactivation, i.e., it must ++ * be set to the value of the remaining budget when bfqq was ++ * expired. Along the same line, timestamps need to be assigned the ++ * value they had the last time bfqq was selected for service, i.e., ++ * before last expiration. Thus timestamps need to be back-shifted ++ * with respect to their normal computation (see [1] for more details ++ * on this tricky aspect). ++ * ++ * Secondly, to allow the process to recover the hole, the in-service ++ * queue must be expired too, to give bfqq the chance to preempt it ++ * immediately. In fact, if bfqq has to wait for a full budget of the ++ * in-service queue to be completed, then it may become impossible to ++ * let the process recover the hole, even if the back-shifted ++ * timestamps of bfqq are lower than those of the in-service queue. If ++ * this happens for most or all of the holes, then the process may not ++ * receive its reserved bandwidth. In this respect, it is worth noting ++ * that, being the service of outstanding requests unpreemptible, a ++ * little fraction of the holes may however be unrecoverable, thereby ++ * causing a little loss of bandwidth. ++ * ++ * The last important point is detecting whether bfqq does need this ++ * bandwidth recovery. In this respect, the next function deems the ++ * process associated with bfqq greedy, and thus allows it to recover ++ * the hole, if: 1) the process is waiting for the arrival of a new ++ * request (which implies that bfqq expired for one of the above two ++ * reasons), and 2) such a request has arrived soon. The first ++ * condition is controlled through the flag non_blocking_wait_rq, ++ * while the second through the flag arrived_in_time. If both ++ * conditions hold, then the function computes the budget in the ++ * above-described special way, and signals that the in-service queue ++ * should be expired. Timestamp back-shifting is done later in ++ * __bfq_activate_entity. ++ * ++ * 2. Reduce latency. Even if timestamps are not backshifted to let ++ * the process associated with bfqq recover a service hole, bfqq may ++ * however happen to have, after being (re)activated, a lower finish ++ * timestamp than the in-service queue. That is, the next budget of ++ * bfqq may have to be completed before the one of the in-service ++ * queue. If this is the case, then preempting the in-service queue ++ * allows this goal to be achieved, apart from the unpreemptible, ++ * outstanding requests mentioned above. ++ * ++ * Unfortunately, regardless of which of the above two goals one wants ++ * to achieve, service trees need first to be updated to know whether ++ * the in-service queue must be preempted. To have service trees ++ * correctly updated, the in-service queue must be expired and ++ * rescheduled, and bfqq must be scheduled too. This is one of the ++ * most costly operations (in future versions, the scheduling ++ * mechanism may be re-designed in such a way to make it possible to ++ * know whether preemption is needed without needing to update service ++ * trees). In addition, queue preemptions almost always cause random ++ * I/O, and thus loss of throughput. Because of these facts, the next ++ * function adopts the following simple scheme to avoid both costly ++ * operations and too frequent preemptions: it requests the expiration ++ * of the in-service queue (unconditionally) only for queues that need ++ * to recover a hole, or that either are weight-raised or deserve to ++ * be weight-raised. ++ */ ++static bool bfq_bfqq_update_budg_for_activation(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ bool arrived_in_time, ++ bool wr_or_deserves_wr) ++{ ++ struct bfq_entity *entity = &bfqq->entity; ++ ++ if (bfq_bfqq_non_blocking_wait_rq(bfqq) && arrived_in_time) { ++ /* ++ * We do not clear the flag non_blocking_wait_rq here, as ++ * the latter is used in bfq_activate_bfqq to signal ++ * that timestamps need to be back-shifted (and is ++ * cleared right after). ++ */ ++ ++ /* ++ * In next assignment we rely on that either ++ * entity->service or entity->budget are not updated ++ * on expiration if bfqq is empty (see ++ * __bfq_bfqq_recalc_budget). Thus both quantities ++ * remain unchanged after such an expiration, and the ++ * following statement therefore assigns to ++ * entity->budget the remaining budget on such an ++ * expiration. For clarity, entity->service is not ++ * updated on expiration in any case, and, in normal ++ * operation, is reset only when bfqq is selected for ++ * service (see bfq_get_next_queue). ++ */ ++ entity->budget = min_t(unsigned long, ++ bfq_bfqq_budget_left(bfqq), ++ bfqq->max_budget); ++ ++ BUG_ON(entity->budget < 0); ++ return true; ++ } ++ ++ entity->budget = max_t(unsigned long, bfqq->max_budget, ++ bfq_serv_to_charge(bfqq->next_rq,bfqq)); ++ BUG_ON(entity->budget < 0); ++ ++ bfq_clear_bfqq_non_blocking_wait_rq(bfqq); ++ return wr_or_deserves_wr; ++} ++ ++static void bfq_update_bfqq_wr_on_rq_arrival(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ unsigned int old_wr_coeff, ++ bool wr_or_deserves_wr, ++ bool interactive, ++ bool in_burst, ++ bool soft_rt) ++{ ++ if (old_wr_coeff == 1 && wr_or_deserves_wr) { ++ /* start a weight-raising period */ ++ if (interactive) { ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff; ++ bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); ++ } else { ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff * ++ BFQ_SOFTRT_WEIGHT_FACTOR; ++ bfqq->wr_cur_max_time = ++ bfqd->bfq_wr_rt_max_time; ++ } ++ /* ++ * If needed, further reduce budget to make sure it is ++ * close to bfqq's backlog, so as to reduce the ++ * scheduling-error component due to a too large ++ * budget. Do not care about throughput consequences, ++ * but only about latency. Finally, do not assign a ++ * too small budget either, to avoid increasing ++ * latency by causing too frequent expirations. ++ */ ++ bfqq->entity.budget = min_t(unsigned long, ++ bfqq->entity.budget, ++ 2 * bfq_min_budget(bfqd)); ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "wrais starting at %lu, rais_max_time %u", ++ jiffies, ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } else if (old_wr_coeff > 1) { ++ if (interactive) { /* update wr coeff and duration */ ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff; ++ bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); ++ } else if (in_burst) { ++ bfqq->wr_coeff = 1; ++ bfq_log_bfqq(bfqd, bfqq, ++ "wrais ending at %lu, rais_max_time %u", ++ jiffies, ++ jiffies_to_msecs(bfqq-> ++ wr_cur_max_time)); ++ } else if (time_before( ++ bfqq->last_wr_start_finish + ++ bfqq->wr_cur_max_time, ++ jiffies + ++ bfqd->bfq_wr_rt_max_time) && ++ soft_rt) { ++ /* ++ * The remaining weight-raising time is lower ++ * than bfqd->bfq_wr_rt_max_time, which means ++ * that the application is enjoying weight ++ * raising either because deemed soft-rt in ++ * the near past, or because deemed interactive ++ * a long ago. ++ * In both cases, resetting now the current ++ * remaining weight-raising time for the ++ * application to the weight-raising duration ++ * for soft rt applications would not cause any ++ * latency increase for the application (as the ++ * new duration would be higher than the ++ * remaining time). ++ * ++ * In addition, the application is now meeting ++ * the requirements for being deemed soft rt. ++ * In the end we can correctly and safely ++ * (re)charge the weight-raising duration for ++ * the application with the weight-raising ++ * duration for soft rt applications. ++ * ++ * In particular, doing this recharge now, i.e., ++ * before the weight-raising period for the ++ * application finishes, reduces the probability ++ * of the following negative scenario: ++ * 1) the weight of a soft rt application is ++ * raised at startup (as for any newly ++ * created application), ++ * 2) since the application is not interactive, ++ * at a certain time weight-raising is ++ * stopped for the application, ++ * 3) at that time the application happens to ++ * still have pending requests, and hence ++ * is destined to not have a chance to be ++ * deemed soft rt before these requests are ++ * completed (see the comments to the ++ * function bfq_bfqq_softrt_next_start() ++ * for details on soft rt detection), ++ * 4) these pending requests experience a high ++ * latency because the application is not ++ * weight-raised while they are pending. ++ */ ++ bfqq->last_wr_start_finish = jiffies; ++ bfqq->wr_cur_max_time = ++ bfqd->bfq_wr_rt_max_time; ++ bfqq->wr_coeff = bfqd->bfq_wr_coeff * ++ BFQ_SOFTRT_WEIGHT_FACTOR; ++ bfq_log_bfqq(bfqd, bfqq, ++ "switching to soft_rt wr, or " ++ " just moving forward duration"); ++ } ++ } ++} ++ ++static bool bfq_bfqq_idle_for_long_time(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ return bfqq->dispatched == 0 && ++ time_is_before_jiffies( ++ bfqq->budget_timeout + ++ bfqd->bfq_wr_min_idle_time); ++} ++ ++static void bfq_bfqq_handle_idle_busy_switch(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq, ++ int old_wr_coeff, ++ struct request *rq, ++ bool *interactive) ++{ ++ bool soft_rt, in_burst, wr_or_deserves_wr, ++ bfqq_wants_to_preempt, ++ idle_for_long_time = bfq_bfqq_idle_for_long_time(bfqd, bfqq), ++ /* ++ * See the comments on ++ * bfq_bfqq_update_budg_for_activation for ++ * details on the usage of the next variable. ++ */ ++ arrived_in_time = time_is_after_jiffies( ++ RQ_BIC(rq)->ttime.last_end_request + ++ bfqd->bfq_slice_idle * 3); ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "bfq_add_request non-busy: " ++ "jiffies %lu, in_time %d, idle_long %d busyw %d " ++ "wr_coeff %u", ++ jiffies, arrived_in_time, ++ idle_for_long_time, ++ bfq_bfqq_non_blocking_wait_rq(bfqq), ++ old_wr_coeff); ++ ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); ++ ++ BUG_ON(bfqq == bfqd->in_service_queue); ++ bfqg_stats_update_io_add(bfqq_group(RQ_BFQQ(rq)), bfqq, ++ rq->cmd_flags); ++ ++ /* ++ * bfqq deserves to be weight-raised if: ++ * - it is sync, ++ * - it does not belong to a large burst, ++ * - it has been idle for enough time or is soft real-time, ++ * - is linked to a bfq_io_cq (it is not shared in any sense) ++ */ ++ in_burst = bfq_bfqq_in_large_burst(bfqq); ++ soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && ++ !in_burst && ++ time_is_before_jiffies(bfqq->soft_rt_next_start); ++ *interactive = ++ !in_burst && ++ idle_for_long_time; ++ wr_or_deserves_wr = bfqd->low_latency && ++ (bfqq->wr_coeff > 1 || ++ (bfq_bfqq_sync(bfqq) && ++ bfqq->bic && (*interactive || soft_rt))); ++ ++ bfq_log_bfqq(bfqd, bfqq, ++ "bfq_add_request: " ++ "in_burst %d, " ++ "soft_rt %d (next %lu), inter %d, bic %p", ++ bfq_bfqq_in_large_burst(bfqq), soft_rt, ++ bfqq->soft_rt_next_start, ++ *interactive, ++ bfqq->bic); ++ ++ /* ++ * Using the last flag, update budget and check whether bfqq ++ * may want to preempt the in-service queue. ++ */ ++ bfqq_wants_to_preempt = ++ bfq_bfqq_update_budg_for_activation(bfqd, bfqq, ++ arrived_in_time, ++ wr_or_deserves_wr); ++ ++ /* ++ * If bfqq happened to be activated in a burst, but has been ++ * idle for much more than an interactive queue, then we ++ * assume that, in the overall I/O initiated in the burst, the ++ * I/O associated with bfqq is finished. So bfqq does not need ++ * to be treated as a queue belonging to a burst ++ * anymore. Accordingly, we reset bfqq's in_large_burst flag ++ * if set, and remove bfqq from the burst list if it's ++ * there. We do not decrement burst_size, because the fact ++ * that bfqq does not need to belong to the burst list any ++ * more does not invalidate the fact that bfqq was created in ++ * a burst. ++ */ ++ if (likely(!bfq_bfqq_just_created(bfqq)) && ++ idle_for_long_time && ++ time_is_before_jiffies( ++ bfqq->budget_timeout + ++ msecs_to_jiffies(10000))) { ++ hlist_del_init(&bfqq->burst_list_node); ++ bfq_clear_bfqq_in_large_burst(bfqq); ++ } ++ ++ bfq_clear_bfqq_just_created(bfqq); ++ ++ if (!bfq_bfqq_IO_bound(bfqq)) { ++ if (arrived_in_time) { ++ bfqq->requests_within_timer++; ++ if (bfqq->requests_within_timer >= ++ bfqd->bfq_requests_within_timer) ++ bfq_mark_bfqq_IO_bound(bfqq); ++ } else ++ bfqq->requests_within_timer = 0; ++ bfq_log_bfqq(bfqd, bfqq, "requests in time %d", ++ bfqq->requests_within_timer); ++ } ++ ++ if (bfqd->low_latency) { ++ if (unlikely(time_is_after_jiffies(bfqq->split_time))) ++ /* wraparound */ ++ bfqq->split_time = ++ jiffies - bfqd->bfq_wr_min_idle_time - 1; ++ ++ if (time_is_before_jiffies(bfqq->split_time + ++ bfqd->bfq_wr_min_idle_time)) { ++ bfq_update_bfqq_wr_on_rq_arrival(bfqd, bfqq, ++ old_wr_coeff, ++ wr_or_deserves_wr, ++ *interactive, ++ in_burst, ++ soft_rt); ++ ++ if (old_wr_coeff != bfqq->wr_coeff) ++ bfqq->entity.prio_changed = 1; ++ } ++ } ++ ++ bfqq->last_idle_bklogged = jiffies; ++ bfqq->service_from_backlogged = 0; ++ bfq_clear_bfqq_softrt_update(bfqq); ++ ++ bfq_add_bfqq_busy(bfqd, bfqq); ++ ++ /* ++ * Expire in-service queue only if preemption may be needed ++ * for guarantees. In this respect, the function ++ * next_queue_may_preempt just checks a simple, necessary ++ * condition, and not a sufficient condition based on ++ * timestamps. In fact, for the latter condition to be ++ * evaluated, timestamps would need first to be updated, and ++ * this operation is quite costly (see the comments on the ++ * function bfq_bfqq_update_budg_for_activation). ++ */ ++ if (bfqd->in_service_queue && bfqq_wants_to_preempt && ++ bfqd->in_service_queue->wr_coeff < bfqq->wr_coeff && ++ next_queue_may_preempt(bfqd)) { ++ struct bfq_queue *in_serv = ++ bfqd->in_service_queue; ++ BUG_ON(in_serv == bfqq); ++ ++ bfq_bfqq_expire(bfqd, bfqd->in_service_queue, ++ false, BFQ_BFQQ_PREEMPTED); ++ BUG_ON(in_serv->entity.budget < 0); ++ } + } + + static void bfq_add_request(struct request *rq) + { + struct bfq_queue *bfqq = RQ_BFQQ(rq); +- struct bfq_entity *entity = &bfqq->entity; + struct bfq_data *bfqd = bfqq->bfqd; + struct request *next_rq, *prev; +- unsigned long old_wr_coeff = bfqq->wr_coeff; ++ unsigned int old_wr_coeff = bfqq->wr_coeff; + bool interactive = false; + +- bfq_log_bfqq(bfqd, bfqq, "add_request %d", rq_is_sync(rq)); ++ bfq_log_bfqq(bfqd, bfqq, "add_request: size %u %s", ++ blk_rq_sectors(rq), rq_is_sync(rq) ? "S" : "A"); ++ ++ if (bfqq->wr_coeff > 1) /* queue is being weight-raised */ ++ bfq_log_bfqq(bfqd, bfqq, ++ "raising period dur %u/%u msec, old coeff %u, w %d(%d)", ++ jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), ++ jiffies_to_msecs(bfqq->wr_cur_max_time), ++ bfqq->wr_coeff, ++ bfqq->entity.weight, bfqq->entity.orig_weight); ++ + bfqq->queued[rq_is_sync(rq)]++; + bfqd->queued++; + + elv_rb_add(&bfqq->sort_list, rq); + + /* +- * Check if this request is a better next-serve candidate. ++ * Check if this request is a better next-to-serve candidate. + */ + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position); +@@ -887,160 +1379,10 @@ static void bfq_add_request(struct request *rq) + if (prev != bfqq->next_rq) + bfq_pos_tree_add_move(bfqd, bfqq); + +- if (!bfq_bfqq_busy(bfqq)) { +- bool soft_rt, coop_or_in_burst, +- idle_for_long_time = time_is_before_jiffies( +- bfqq->budget_timeout + +- bfqd->bfq_wr_min_idle_time); +- +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- bfqg_stats_update_io_add(bfqq_group(RQ_BFQQ(rq)), bfqq, +- rq->cmd_flags); +-#endif +- if (bfq_bfqq_sync(bfqq)) { +- bool already_in_burst = +- !hlist_unhashed(&bfqq->burst_list_node) || +- bfq_bfqq_in_large_burst(bfqq); +- bfq_handle_burst(bfqd, bfqq, idle_for_long_time); +- /* +- * If bfqq was not already in the current burst, +- * then, at this point, bfqq either has been +- * added to the current burst or has caused the +- * current burst to terminate. In particular, in +- * the second case, bfqq has become the first +- * queue in a possible new burst. +- * In both cases last_ins_in_burst needs to be +- * moved forward. +- */ +- if (!already_in_burst) +- bfqd->last_ins_in_burst = jiffies; +- } +- +- coop_or_in_burst = bfq_bfqq_in_large_burst(bfqq) || +- bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh; +- soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && +- !coop_or_in_burst && +- time_is_before_jiffies(bfqq->soft_rt_next_start); +- interactive = !coop_or_in_burst && idle_for_long_time; +- entity->budget = max_t(unsigned long, bfqq->max_budget, +- bfq_serv_to_charge(next_rq, bfqq)); +- +- if (!bfq_bfqq_IO_bound(bfqq)) { +- if (time_before(jiffies, +- RQ_BIC(rq)->ttime.last_end_request + +- bfqd->bfq_slice_idle)) { +- bfqq->requests_within_timer++; +- if (bfqq->requests_within_timer >= +- bfqd->bfq_requests_within_timer) +- bfq_mark_bfqq_IO_bound(bfqq); +- } else +- bfqq->requests_within_timer = 0; +- } +- +- if (!bfqd->low_latency) +- goto add_bfqq_busy; +- +- if (bfq_bfqq_just_split(bfqq)) +- goto set_prio_changed; +- +- /* +- * If the queue: +- * - is not being boosted, +- * - has been idle for enough time, +- * - is not a sync queue or is linked to a bfq_io_cq (it is +- * shared "for its nature" or it is not shared and its +- * requests have not been redirected to a shared queue) +- * start a weight-raising period. +- */ +- if (old_wr_coeff == 1 && (interactive || soft_rt) && +- (!bfq_bfqq_sync(bfqq) || bfqq->bic)) { +- bfqq->wr_coeff = bfqd->bfq_wr_coeff; +- if (interactive) +- bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); +- else +- bfqq->wr_cur_max_time = +- bfqd->bfq_wr_rt_max_time; +- bfq_log_bfqq(bfqd, bfqq, +- "wrais starting at %lu, rais_max_time %u", +- jiffies, +- jiffies_to_msecs(bfqq->wr_cur_max_time)); +- } else if (old_wr_coeff > 1) { +- if (interactive) +- bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); +- else if (coop_or_in_burst || +- (bfqq->wr_cur_max_time == +- bfqd->bfq_wr_rt_max_time && +- !soft_rt)) { +- bfqq->wr_coeff = 1; +- bfq_log_bfqq(bfqd, bfqq, +- "wrais ending at %lu, rais_max_time %u", +- jiffies, +- jiffies_to_msecs(bfqq-> +- wr_cur_max_time)); +- } else if (time_before( +- bfqq->last_wr_start_finish + +- bfqq->wr_cur_max_time, +- jiffies + +- bfqd->bfq_wr_rt_max_time) && +- soft_rt) { +- /* +- * +- * The remaining weight-raising time is lower +- * than bfqd->bfq_wr_rt_max_time, which means +- * that the application is enjoying weight +- * raising either because deemed soft-rt in +- * the near past, or because deemed interactive +- * a long ago. +- * In both cases, resetting now the current +- * remaining weight-raising time for the +- * application to the weight-raising duration +- * for soft rt applications would not cause any +- * latency increase for the application (as the +- * new duration would be higher than the +- * remaining time). +- * +- * In addition, the application is now meeting +- * the requirements for being deemed soft rt. +- * In the end we can correctly and safely +- * (re)charge the weight-raising duration for +- * the application with the weight-raising +- * duration for soft rt applications. +- * +- * In particular, doing this recharge now, i.e., +- * before the weight-raising period for the +- * application finishes, reduces the probability +- * of the following negative scenario: +- * 1) the weight of a soft rt application is +- * raised at startup (as for any newly +- * created application), +- * 2) since the application is not interactive, +- * at a certain time weight-raising is +- * stopped for the application, +- * 3) at that time the application happens to +- * still have pending requests, and hence +- * is destined to not have a chance to be +- * deemed soft rt before these requests are +- * completed (see the comments to the +- * function bfq_bfqq_softrt_next_start() +- * for details on soft rt detection), +- * 4) these pending requests experience a high +- * latency because the application is not +- * weight-raised while they are pending. +- */ +- bfqq->last_wr_start_finish = jiffies; +- bfqq->wr_cur_max_time = +- bfqd->bfq_wr_rt_max_time; +- } +- } +-set_prio_changed: +- if (old_wr_coeff != bfqq->wr_coeff) +- entity->prio_changed = 1; +-add_bfqq_busy: +- bfqq->last_idle_bklogged = jiffies; +- bfqq->service_from_backlogged = 0; +- bfq_clear_bfqq_softrt_update(bfqq); +- bfq_add_bfqq_busy(bfqd, bfqq); +- } else { ++ if (!bfq_bfqq_busy(bfqq)) /* switching to busy ... */ ++ bfq_bfqq_handle_idle_busy_switch(bfqd, bfqq, old_wr_coeff, ++ rq, &interactive); ++ else { + if (bfqd->low_latency && old_wr_coeff == 1 && !rq_is_sync(rq) && + time_is_before_jiffies( + bfqq->last_wr_start_finish + +@@ -1049,16 +1391,43 @@ add_bfqq_busy: + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + + bfqd->wr_busy_queues++; +- entity->prio_changed = 1; ++ bfqq->entity.prio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, +- "non-idle wrais starting at %lu, rais_max_time %u", +- jiffies, +- jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ "non-idle wrais starting, " ++ "wr_max_time %u wr_busy %d", ++ jiffies_to_msecs(bfqq->wr_cur_max_time), ++ bfqd->wr_busy_queues); + } + if (prev != bfqq->next_rq) + bfq_updated_next_req(bfqd, bfqq); + } + ++ /* ++ * Assign jiffies to last_wr_start_finish in the following ++ * cases: ++ * ++ * . if bfqq is not going to be weight-raised, because, for ++ * non weight-raised queues, last_wr_start_finish stores the ++ * arrival time of the last request; as of now, this piece ++ * of information is used only for deciding whether to ++ * weight-raise async queues ++ * ++ * . if bfqq is not weight-raised, because, if bfqq is now ++ * switching to weight-raised, then last_wr_start_finish ++ * stores the time when weight-raising starts ++ * ++ * . if bfqq is interactive, because, regardless of whether ++ * bfqq is currently weight-raised, the weight-raising ++ * period must start or restart (this case is considered ++ * separately because it is not detected by the above ++ * conditions, if bfqq is already weight-raised) ++ * ++ * last_wr_start_finish has to be updated also if bfqq is soft ++ * real-time, because the weight-raising period is constantly ++ * restarted on idle-to-busy transitions for these queues, but ++ * this is already done in bfq_bfqq_handle_idle_busy_switch if ++ * needed. ++ */ + if (bfqd->low_latency && + (old_wr_coeff == 1 || bfqq->wr_coeff == 1 || interactive)) + bfqq->last_wr_start_finish = jiffies; +@@ -1106,6 +1475,9 @@ static void bfq_remove_request(struct request *rq) + struct bfq_data *bfqd = bfqq->bfqd; + const int sync = rq_is_sync(rq); + ++ BUG_ON(bfqq->entity.service > bfqq->entity.budget && ++ bfqq == bfqd->in_service_queue); ++ + if (bfqq->next_rq == rq) { + bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq); + bfq_updated_next_req(bfqd, bfqq); +@@ -1119,8 +1491,25 @@ static void bfq_remove_request(struct request *rq) + elv_rb_del(&bfqq->sort_list, rq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { +- if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) ++ BUG_ON(bfqq->entity.budget < 0); ++ ++ if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) { + bfq_del_bfqq_busy(bfqd, bfqq, 1); ++ ++ /* bfqq emptied. In normal operation, when ++ * bfqq is empty, bfqq->entity.service and ++ * bfqq->entity.budget must contain, ++ * respectively, the service received and the ++ * budget used last time bfqq emptied. These ++ * facts do not hold in this case, as at least ++ * this last removal occurred while bfqq is ++ * not in service. To avoid inconsistencies, ++ * reset both bfqq->entity.service and ++ * bfqq->entity.budget. ++ */ ++ bfqq->entity.budget = bfqq->entity.service = 0; ++ } ++ + /* + * Remove queue from request-position tree as it is empty. + */ +@@ -1134,9 +1523,7 @@ static void bfq_remove_request(struct request *rq) + BUG_ON(bfqq->meta_pending == 0); + bfqq->meta_pending--; + } +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_io_remove(bfqq_group(bfqq), rq->cmd_flags); +-#endif + } + + static int bfq_merge(struct request_queue *q, struct request **req, +@@ -1221,21 +1608,25 @@ static void bfq_merged_requests(struct request_queue *q, struct request *rq, + bfqq->next_rq = rq; + + bfq_remove_request(next); +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_io_merged(bfqq_group(bfqq), next->cmd_flags); +-#endif + } + + /* Must be called with bfqq != NULL */ + static void bfq_bfqq_end_wr(struct bfq_queue *bfqq) + { + BUG_ON(!bfqq); ++ + if (bfq_bfqq_busy(bfqq)) + bfqq->bfqd->wr_busy_queues--; + bfqq->wr_coeff = 1; + bfqq->wr_cur_max_time = 0; +- /* Trigger a weight change on the next activation of the queue */ ++ /* ++ * Trigger a weight change on the next invocation of ++ * __bfq_entity_update_weight_prio. ++ */ + bfqq->entity.prio_changed = 1; ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "end_wr: wr_busy %d", ++ bfqq->bfqd->wr_busy_queues); + } + + static void bfq_end_wr_async_queues(struct bfq_data *bfqd, +@@ -1278,7 +1669,7 @@ static int bfq_rq_close_to_sector(void *io_struct, bool request, + sector_t sector) + { + return abs(bfq_io_struct_pos(io_struct, request) - sector) <= +- BFQQ_SEEK_THR; ++ BFQQ_CLOSE_THR; + } + + static struct bfq_queue *bfqq_find_close(struct bfq_data *bfqd, +@@ -1400,7 +1791,7 @@ bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) + * throughput. + */ + bfqq->new_bfqq = new_bfqq; +- atomic_add(process_refs, &new_bfqq->ref); ++ new_bfqq->ref += process_refs; + return new_bfqq; + } + +@@ -1431,9 +1822,23 @@ static bool bfq_may_be_close_cooperator(struct bfq_queue *bfqq, + } + + /* +- * Attempt to schedule a merge of bfqq with the currently in-service queue +- * or with a close queue among the scheduled queues. +- * Return NULL if no merge was scheduled, a pointer to the shared bfq_queue ++ * If this function returns true, then bfqq cannot be merged. The idea ++ * is that true cooperation happens very early after processes start ++ * to do I/O. Usually, late cooperations are just accidental false ++ * positives. In case bfqq is weight-raised, such false positives ++ * would evidently degrade latency guarantees for bfqq. ++ */ ++bool wr_from_too_long(struct bfq_queue *bfqq) ++{ ++ return bfqq->wr_coeff > 1 && ++ time_is_before_jiffies(bfqq->last_wr_start_finish + ++ msecs_to_jiffies(100)); ++} ++ ++/* ++ * Attempt to schedule a merge of bfqq with the currently in-service ++ * queue or with a close queue among the scheduled queues. Return ++ * NULL if no merge was scheduled, a pointer to the shared bfq_queue + * structure otherwise. + * + * The OOM queue is not allowed to participate to cooperation: in fact, since +@@ -1442,6 +1847,18 @@ static bool bfq_may_be_close_cooperator(struct bfq_queue *bfqq, + * handle merging with the OOM queue would be quite complex and expensive + * to maintain. Besides, in such a critical condition as an out of memory, + * the benefits of queue merging may be little relevant, or even negligible. ++ * ++ * Weight-raised queues can be merged only if their weight-raising ++ * period has just started. In fact cooperating processes are usually ++ * started together. Thus, with this filter we avoid false positives ++ * that would jeopardize low-latency guarantees. ++ * ++ * WARNING: queue merging may impair fairness among non-weight raised ++ * queues, for at least two reasons: 1) the original weight of a ++ * merged queue may change during the merged state, 2) even being the ++ * weight the same, a merged queue may be bloated with many more ++ * requests than the ones produced by its originally-associated ++ * process. + */ + static struct bfq_queue * + bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, +@@ -1451,16 +1868,32 @@ bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, + + if (bfqq->new_bfqq) + return bfqq->new_bfqq; +- if (!io_struct || unlikely(bfqq == &bfqd->oom_bfqq)) ++ ++ if (io_struct && wr_from_too_long(bfqq) && ++ likely(bfqq != &bfqd->oom_bfqq)) ++ bfq_log_bfqq(bfqd, bfqq, ++ "would have looked for coop, but bfq%d wr", ++ bfqq->pid); ++ ++ if (!io_struct || ++ wr_from_too_long(bfqq) || ++ unlikely(bfqq == &bfqd->oom_bfqq)) + return NULL; +- /* If device has only one backlogged bfq_queue, don't search. */ ++ ++ /* If there is only one backlogged queue, don't search. */ + if (bfqd->busy_queues == 1) + return NULL; + + in_service_bfqq = bfqd->in_service_queue; + ++ if (in_service_bfqq && in_service_bfqq != bfqq && ++ bfqd->in_service_bic && wr_from_too_long(in_service_bfqq) ++ && likely(in_service_bfqq == &bfqd->oom_bfqq)) ++ bfq_log_bfqq(bfqd, bfqq, ++ "would have tried merge with in-service-queue, but wr"); ++ + if (!in_service_bfqq || in_service_bfqq == bfqq || +- !bfqd->in_service_bic || ++ !bfqd->in_service_bic || wr_from_too_long(in_service_bfqq) || + unlikely(in_service_bfqq == &bfqd->oom_bfqq)) + goto check_scheduled; + +@@ -1482,7 +1915,15 @@ check_scheduled: + + BUG_ON(new_bfqq && bfqq->entity.parent != new_bfqq->entity.parent); + +- if (new_bfqq && likely(new_bfqq != &bfqd->oom_bfqq) && ++ if (new_bfqq && wr_from_too_long(new_bfqq) && ++ likely(new_bfqq != &bfqd->oom_bfqq) && ++ bfq_may_be_close_cooperator(bfqq, new_bfqq)) ++ bfq_log_bfqq(bfqd, bfqq, ++ "would have merged with bfq%d, but wr", ++ new_bfqq->pid); ++ ++ if (new_bfqq && !wr_from_too_long(new_bfqq) && ++ likely(new_bfqq != &bfqd->oom_bfqq) && + bfq_may_be_close_cooperator(bfqq, new_bfqq)) + return bfq_setup_merge(bfqq, new_bfqq); + +@@ -1498,46 +1939,11 @@ static void bfq_bfqq_save_state(struct bfq_queue *bfqq) + */ + if (!bfqq->bic) + return; +- if (bfqq->bic->wr_time_left) +- /* +- * This is the queue of a just-started process, and would +- * deserve weight raising: we set wr_time_left to the full +- * weight-raising duration to trigger weight-raising when +- * and if the queue is split and the first request of the +- * queue is enqueued. +- */ +- bfqq->bic->wr_time_left = bfq_wr_duration(bfqq->bfqd); +- else if (bfqq->wr_coeff > 1) { +- unsigned long wr_duration = +- jiffies - bfqq->last_wr_start_finish; +- /* +- * It may happen that a queue's weight raising period lasts +- * longer than its wr_cur_max_time, as weight raising is +- * handled only when a request is enqueued or dispatched (it +- * does not use any timer). If the weight raising period is +- * about to end, don't save it. +- */ +- if (bfqq->wr_cur_max_time <= wr_duration) +- bfqq->bic->wr_time_left = 0; +- else +- bfqq->bic->wr_time_left = +- bfqq->wr_cur_max_time - wr_duration; +- /* +- * The bfq_queue is becoming shared or the requests of the +- * process owning the queue are being redirected to a shared +- * queue. Stop the weight raising period of the queue, as in +- * both cases it should not be owned by an interactive or +- * soft real-time application. +- */ +- bfq_bfqq_end_wr(bfqq); +- } else +- bfqq->bic->wr_time_left = 0; ++ + bfqq->bic->saved_idle_window = bfq_bfqq_idle_window(bfqq); + bfqq->bic->saved_IO_bound = bfq_bfqq_IO_bound(bfqq); + bfqq->bic->saved_in_large_burst = bfq_bfqq_in_large_burst(bfqq); + bfqq->bic->was_in_burst_list = !hlist_unhashed(&bfqq->burst_list_node); +- bfqq->bic->cooperations++; +- bfqq->bic->failed_cooperations = 0; + } + + static void bfq_get_bic_reference(struct bfq_queue *bfqq) +@@ -1562,6 +1968,40 @@ bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, + if (bfq_bfqq_IO_bound(bfqq)) + bfq_mark_bfqq_IO_bound(new_bfqq); + bfq_clear_bfqq_IO_bound(bfqq); ++ ++ /* ++ * If bfqq is weight-raised, then let new_bfqq inherit ++ * weight-raising. To reduce false positives, neglect the case ++ * where bfqq has just been created, but has not yet made it ++ * to be weight-raised (which may happen because EQM may merge ++ * bfqq even before bfq_add_request is executed for the first ++ * time for bfqq). Handling this case would however be very ++ * easy, thanks to the flag just_created. ++ */ ++ if (new_bfqq->wr_coeff == 1 && bfqq->wr_coeff > 1) { ++ new_bfqq->wr_coeff = bfqq->wr_coeff; ++ new_bfqq->wr_cur_max_time = bfqq->wr_cur_max_time; ++ new_bfqq->last_wr_start_finish = bfqq->last_wr_start_finish; ++ if (bfq_bfqq_busy(new_bfqq)) ++ bfqd->wr_busy_queues++; ++ new_bfqq->entity.prio_changed = 1; ++ bfq_log_bfqq(bfqd, new_bfqq, ++ "wr starting after merge with %d, " ++ "rais_max_time %u", ++ bfqq->pid, ++ jiffies_to_msecs(bfqq->wr_cur_max_time)); ++ } ++ ++ if (bfqq->wr_coeff > 1) { /* bfqq has given its wr to new_bfqq */ ++ bfqq->wr_coeff = 1; ++ bfqq->entity.prio_changed = 1; ++ if (bfq_bfqq_busy(bfqq)) ++ bfqd->wr_busy_queues--; ++ } ++ ++ bfq_log_bfqq(bfqd, new_bfqq, "merge_bfqqs: wr_busy %d", ++ bfqd->wr_busy_queues); ++ + /* + * Grab a reference to the bic, to prevent it from being destroyed + * before being possibly touched by a bfq_split_bfqq(). +@@ -1588,18 +2028,6 @@ bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, + bfq_put_queue(bfqq); + } + +-static void bfq_bfqq_increase_failed_cooperations(struct bfq_queue *bfqq) +-{ +- struct bfq_io_cq *bic = bfqq->bic; +- struct bfq_data *bfqd = bfqq->bfqd; +- +- if (bic && bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh) { +- bic->failed_cooperations++; +- if (bic->failed_cooperations >= bfqd->bfq_failed_cooperations) +- bic->cooperations = 0; +- } +-} +- + static int bfq_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) + { +@@ -1637,30 +2065,86 @@ static int bfq_allow_merge(struct request_queue *q, struct request *rq, + * to decide whether bio and rq can be merged. + */ + bfqq = new_bfqq; +- } else +- bfq_bfqq_increase_failed_cooperations(bfqq); ++ } + } + + return bfqq == RQ_BFQQ(rq); + } + ++/* ++ * Set the maximum time for the in-service queue to consume its ++ * budget. This prevents seeky processes from lowering the throughput. ++ * In practice, a time-slice service scheme is used with seeky ++ * processes. ++ */ ++static void bfq_set_budget_timeout(struct bfq_data *bfqd, ++ struct bfq_queue *bfqq) ++{ ++ unsigned int timeout_coeff; ++ if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time) ++ timeout_coeff = 1; ++ else ++ timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight; ++ ++ bfqd->last_budget_start = ktime_get(); ++ ++ bfqq->budget_timeout = jiffies + ++ bfqd->bfq_timeout * timeout_coeff; ++ ++ bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", ++ jiffies_to_msecs(bfqd->bfq_timeout * timeout_coeff)); ++} ++ + static void __bfq_set_in_service_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) + { + if (bfqq) { +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_avg_queue_size(bfqq_group(bfqq)); +-#endif + bfq_mark_bfqq_must_alloc(bfqq); +- bfq_mark_bfqq_budget_new(bfqq); + bfq_clear_bfqq_fifo_expire(bfqq); + + bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; + ++ BUG_ON(bfqq == bfqd->in_service_queue); ++ BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); ++ ++ if (bfqq->wr_coeff > 1 && ++ bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time && ++ time_is_before_jiffies(bfqq->budget_timeout)) { ++ /* ++ * For soft real-time queues, move the start ++ * of the weight-raising period forward by the ++ * time the queue has not received any ++ * service. Otherwise, a relatively long ++ * service delay is likely to cause the ++ * weight-raising period of the queue to end, ++ * because of the short duration of the ++ * weight-raising period of a soft real-time ++ * queue. It is worth noting that this move ++ * is not so dangerous for the other queues, ++ * because soft real-time queues are not ++ * greedy. ++ * ++ * To not add a further variable, we use the ++ * overloaded field budget_timeout to ++ * determine for how long the queue has not ++ * received service, i.e., how much time has ++ * elapsed since the queue expired. However, ++ * this is a little imprecise, because ++ * budget_timeout is set to jiffies if bfqq ++ * not only expires, but also remains with no ++ * request. ++ */ ++ bfqq->last_wr_start_finish += jiffies - ++ bfqq->budget_timeout; ++ } ++ ++ bfq_set_budget_timeout(bfqd, bfqq); + bfq_log_bfqq(bfqd, bfqq, + "set_in_service_queue, cur-budget = %d", + bfqq->entity.budget); +- } ++ } else ++ bfq_log(bfqd, "set_in_service_queue: NULL"); + + bfqd->in_service_queue = bfqq; + } +@@ -1676,31 +2160,6 @@ static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd) + return bfqq; + } + +-/* +- * If enough samples have been computed, return the current max budget +- * stored in bfqd, which is dynamically updated according to the +- * estimated disk peak rate; otherwise return the default max budget +- */ +-static int bfq_max_budget(struct bfq_data *bfqd) +-{ +- if (bfqd->budgets_assigned < bfq_stats_min_budgets) +- return bfq_default_max_budget; +- else +- return bfqd->bfq_max_budget; +-} +- +-/* +- * Return min budget, which is a fraction of the current or default +- * max budget (trying with 1/32) +- */ +-static int bfq_min_budget(struct bfq_data *bfqd) +-{ +- if (bfqd->budgets_assigned < bfq_stats_min_budgets) +- return bfq_default_max_budget / 32; +- else +- return bfqd->bfq_max_budget / 32; +-} +- + static void bfq_arm_slice_timer(struct bfq_data *bfqd) + { + struct bfq_queue *bfqq = bfqd->in_service_queue; +@@ -1725,62 +2184,34 @@ static void bfq_arm_slice_timer(struct bfq_data *bfqd) + * being too ill-treated, grant them a small fraction of the + * assigned budget before reducing the waiting time to + * BFQ_MIN_TT. This happened to help reduce latency. +- */ +- sl = bfqd->bfq_slice_idle; +- /* +- * Unless the queue is being weight-raised or the scenario is +- * asymmetric, grant only minimum idle time if the queue either +- * has been seeky for long enough or has already proved to be +- * constantly seeky. +- */ +- if (bfq_sample_valid(bfqq->seek_samples) && +- ((BFQQ_SEEKY(bfqq) && bfqq->entity.service > +- bfq_max_budget(bfqq->bfqd) / 8) || +- bfq_bfqq_constantly_seeky(bfqq)) && bfqq->wr_coeff == 1 && +- bfq_symmetric_scenario(bfqd)) +- sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); +- else if (bfqq->wr_coeff > 1) +- sl = sl * 3; +- bfqd->last_idling_start = ktime_get(); +- mod_timer(&bfqd->idle_slice_timer, jiffies + sl); +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- bfqg_stats_set_start_idle_time(bfqq_group(bfqq)); +-#endif +- bfq_log(bfqd, "arm idle: %u/%u ms", +- jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); +-} +- +-/* +- * Set the maximum time for the in-service queue to consume its +- * budget. This prevents seeky processes from lowering the disk +- * throughput (always guaranteed with a time slice scheme as in CFQ). +- */ +-static void bfq_set_budget_timeout(struct bfq_data *bfqd) +-{ +- struct bfq_queue *bfqq = bfqd->in_service_queue; +- unsigned int timeout_coeff; +- if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time) +- timeout_coeff = 1; +- else +- timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight; +- +- bfqd->last_budget_start = ktime_get(); +- +- bfq_clear_bfqq_budget_new(bfqq); +- bfqq->budget_timeout = jiffies + +- bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * timeout_coeff; ++ */ ++ sl = bfqd->bfq_slice_idle; ++ /* ++ * Unless the queue is being weight-raised or the scenario is ++ * asymmetric, grant only minimum idle time if the queue ++ * is seeky. A long idling is preserved for a weight-raised ++ * queue, or, more in general, in an asymemtric scenario, ++ * because a long idling is needed for guaranteeing to a queue ++ * its reserved share of the throughput (in particular, it is ++ * needed if the queue has a higher weight than some other ++ * queue). ++ */ ++ if (BFQQ_SEEKY(bfqq) && bfqq->wr_coeff == 1 && ++ bfq_symmetric_scenario(bfqd)) ++ sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); + +- bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", +- jiffies_to_msecs(bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * +- timeout_coeff)); ++ bfqd->last_idling_start = ktime_get(); ++ mod_timer(&bfqd->idle_slice_timer, jiffies + sl); ++ bfqg_stats_set_start_idle_time(bfqq_group(bfqq)); ++ bfq_log(bfqd, "arm idle: %u/%u ms", ++ jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); + } + + /* +- * Move request from internal lists to the request queue dispatch list. ++ * Move request from internal lists to the dispatch list of the request queue + */ + static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) + { +- struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + /* +@@ -1794,15 +2225,9 @@ static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) + * incrementing bfqq->dispatched. + */ + bfqq->dispatched++; ++ + bfq_remove_request(rq); + elv_dispatch_sort(q, rq); +- +- if (bfq_bfqq_sync(bfqq)) +- bfqd->sync_flight++; +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- bfqg_stats_update_dispatch(bfqq_group(bfqq), blk_rq_bytes(rq), +- rq->cmd_flags); +-#endif + } + + /* +@@ -1822,18 +2247,12 @@ static struct request *bfq_check_fifo(struct bfq_queue *bfqq) + + rq = rq_entry_fifo(bfqq->fifo.next); + +- if (time_before(jiffies, rq->fifo_time)) ++ if (time_is_after_jiffies(rq->fifo_time)) + return NULL; + + return rq; + } + +-static int bfq_bfqq_budget_left(struct bfq_queue *bfqq) +-{ +- struct bfq_entity *entity = &bfqq->entity; +- return entity->budget - entity->service; +-} +- + static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) + { + BUG_ON(bfqq != bfqd->in_service_queue); +@@ -1850,12 +2269,15 @@ static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) + bfq_mark_bfqq_split_coop(bfqq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { +- /* +- * Overloading budget_timeout field to store the time +- * at which the queue remains with no backlog; used by +- * the weight-raising mechanism. +- */ +- bfqq->budget_timeout = jiffies; ++ if (bfqq->dispatched == 0) ++ /* ++ * Overloading budget_timeout field to store ++ * the time at which the queue remains with no ++ * backlog and no outstanding request; used by ++ * the weight-raising mechanism. ++ */ ++ bfqq->budget_timeout = jiffies; ++ + bfq_del_bfqq_busy(bfqd, bfqq, 1); + } else { + bfq_activate_bfqq(bfqd, bfqq); +@@ -1882,10 +2304,19 @@ static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + struct request *next_rq; + int budget, min_budget; + +- budget = bfqq->max_budget; ++ BUG_ON(bfqq != bfqd->in_service_queue); ++ + min_budget = bfq_min_budget(bfqd); + +- BUG_ON(bfqq != bfqd->in_service_queue); ++ if (bfqq->wr_coeff == 1) ++ budget = bfqq->max_budget; ++ else /* ++ * Use a constant, low budget for weight-raised queues, ++ * to help achieve a low latency. Keep it slightly higher ++ * than the minimum possible budget, to cause a little ++ * bit fewer expirations. ++ */ ++ budget = 2 * min_budget; + + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %d, budg left %d", + bfqq->entity.budget, bfq_bfqq_budget_left(bfqq)); +@@ -1894,7 +2325,7 @@ static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d", + bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->in_service_queue)); + +- if (bfq_bfqq_sync(bfqq)) { ++ if (bfq_bfqq_sync(bfqq) && bfqq->wr_coeff == 1) { + switch (reason) { + /* + * Caveat: in all the following cases we trade latency +@@ -1936,14 +2367,10 @@ static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + break; + case BFQ_BFQQ_BUDGET_TIMEOUT: + /* +- * We double the budget here because: 1) it +- * gives the chance to boost the throughput if +- * this is not a seeky process (which may have +- * bumped into this timeout because of, e.g., +- * ZBR), 2) together with charge_full_budget +- * it helps give seeky processes higher +- * timestamps, and hence be served less +- * frequently. ++ * We double the budget here because it gives ++ * the chance to boost the throughput if this ++ * is not a seeky process (and has bumped into ++ * this timeout because of, e.g., ZBR). + */ + budget = min(budget * 2, bfqd->bfq_max_budget); + break; +@@ -1960,17 +2387,49 @@ static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + budget = min(budget * 4, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_NO_MORE_REQUESTS: +- /* +- * Leave the budget unchanged. +- */ ++ /* ++ * For queues that expire for this reason, it ++ * is particularly important to keep the ++ * budget close to the actual service they ++ * need. Doing so reduces the timestamp ++ * misalignment problem described in the ++ * comments in the body of ++ * __bfq_activate_entity. In fact, suppose ++ * that a queue systematically expires for ++ * BFQ_BFQQ_NO_MORE_REQUESTS and presents a ++ * new request in time to enjoy timestamp ++ * back-shifting. The larger the budget of the ++ * queue is with respect to the service the ++ * queue actually requests in each service ++ * slot, the more times the queue can be ++ * reactivated with the same virtual finish ++ * time. It follows that, even if this finish ++ * time is pushed to the system virtual time ++ * to reduce the consequent timestamp ++ * misalignment, the queue unjustly enjoys for ++ * many re-activations a lower finish time ++ * than all newly activated queues. ++ * ++ * The service needed by bfqq is measured ++ * quite precisely by bfqq->entity.service. ++ * Since bfqq does not enjoy device idling, ++ * bfqq->entity.service is equal to the number ++ * of sectors that the process associated with ++ * bfqq requested to read/write before waiting ++ * for request completions, or blocking for ++ * other reasons. ++ */ ++ budget = max_t(int, bfqq->entity.service, min_budget); ++ break; + default: + return; + } +- } else ++ } else if (!bfq_bfqq_sync(bfqq)) + /* +- * Async queues get always the maximum possible budget +- * (their ability to dispatch is limited by +- * @bfqd->bfq_max_budget_async_rq). ++ * Async queues get always the maximum possible ++ * budget, as for them we do not care about latency ++ * (in addition, their ability to dispatch is limited ++ * by the charging factor). + */ + budget = bfqd->bfq_max_budget; + +@@ -1981,65 +2440,105 @@ static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + bfqq->max_budget = min(bfqq->max_budget, bfqd->bfq_max_budget); + + /* +- * Make sure that we have enough budget for the next request. +- * Since the finish time of the bfqq must be kept in sync with +- * the budget, be sure to call __bfq_bfqq_expire() after the ++ * If there is still backlog, then assign a new budget, making ++ * sure that it is large enough for the next request. Since ++ * the finish time of bfqq must be kept in sync with the ++ * budget, be sure to call __bfq_bfqq_expire() *after* this + * update. ++ * ++ * If there is no backlog, then no need to update the budget; ++ * it will be updated on the arrival of a new request. + */ + next_rq = bfqq->next_rq; +- if (next_rq) ++ if (next_rq) { ++ BUG_ON(reason == BFQ_BFQQ_TOO_IDLE || ++ reason == BFQ_BFQQ_NO_MORE_REQUESTS); + bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); +- else +- bfqq->entity.budget = bfqq->max_budget; ++ BUG_ON(!bfq_bfqq_busy(bfqq)); ++ BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); ++ } + + bfq_log_bfqq(bfqd, bfqq, "head sect: %u, new budget %d", + next_rq ? blk_rq_sectors(next_rq) : 0, + bfqq->entity.budget); + } + +-static unsigned long bfq_calc_max_budget(u64 peak_rate, u64 timeout) ++static unsigned long bfq_calc_max_budget(struct bfq_data *bfqd) + { +- unsigned long max_budget; +- + /* + * The max_budget calculated when autotuning is equal to the +- * amount of sectors transfered in timeout_sync at the ++ * amount of sectors transfered in timeout at the + * estimated peak rate. + */ +- max_budget = (unsigned long)(peak_rate * 1000 * +- timeout >> BFQ_RATE_SHIFT); +- +- return max_budget; ++ return bfqd->peak_rate * 1000 * jiffies_to_msecs(bfqd->bfq_timeout) >> ++ BFQ_RATE_SHIFT; + } + + /* +- * In addition to updating the peak rate, checks whether the process +- * is "slow", and returns 1 if so. This slow flag is used, in addition +- * to the budget timeout, to reduce the amount of service provided to +- * seeky processes, and hence reduce their chances to lower the +- * throughput. See the code for more details. ++ * Update the read peak rate (quantity used for auto-tuning) as a ++ * function of the rate at which bfqq has been served, and check ++ * whether the process associated with bfqq is "slow". Return true if ++ * the process is slow. The slow flag is used, in addition to the ++ * budget timeout, to reduce the amount of service provided to seeky ++ * processes, and hence reduce their chances to lower the ++ * throughput. More details in the body of the function. ++ * ++ * An important observation is in order: with devices with internal ++ * queues, it is hard if ever possible to know when and for how long ++ * an I/O request is processed by the device (apart from the trivial ++ * I/O pattern where a new request is dispatched only after the ++ * previous one has been completed). This makes it hard to evaluate ++ * the real rate at which the I/O requests of each bfq_queue are ++ * served. In fact, for an I/O scheduler like BFQ, serving a ++ * bfq_queue means just dispatching its requests during its service ++ * slot, i.e., until the budget of the queue is exhausted, or the ++ * queue remains idle, or, finally, a timeout fires. But, during the ++ * service slot of a bfq_queue, the device may be still processing ++ * requests of bfq_queues served in previous service slots. On the ++ * opposite end, the requests of the in-service bfq_queue may be ++ * completed after the service slot of the queue finishes. Anyway, ++ * unless more sophisticated solutions are used (where possible), the ++ * sum of the sizes of the requests dispatched during the service slot ++ * of a bfq_queue is probably the only approximation available for ++ * the service received by the bfq_queue during its service slot. And, ++ * as written above, this sum is the quantity used in this function to ++ * evaluate the peak rate. + */ + static bool bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, +- bool compensate, enum bfqq_expiration reason) ++ bool compensate, enum bfqq_expiration reason, ++ unsigned long *delta_ms) + { +- u64 bw, usecs, expected, timeout; +- ktime_t delta; ++ u64 bw, bwdiv10, delta_usecs, delta_ms_tmp; ++ ktime_t delta_ktime; + int update = 0; ++ bool slow = BFQQ_SEEKY(bfqq); /* if delta too short, use seekyness */ + +- if (!bfq_bfqq_sync(bfqq) || bfq_bfqq_budget_new(bfqq)) ++ if (!bfq_bfqq_sync(bfqq)) + return false; + + if (compensate) +- delta = bfqd->last_idling_start; ++ delta_ktime = bfqd->last_idling_start; + else +- delta = ktime_get(); +- delta = ktime_sub(delta, bfqd->last_budget_start); +- usecs = ktime_to_us(delta); ++ delta_ktime = ktime_get(); ++ delta_ktime = ktime_sub(delta_ktime, bfqd->last_budget_start); ++ delta_usecs = ktime_to_us(delta_ktime); + + /* Don't trust short/unrealistic values. */ +- if (usecs < 100 || usecs >= LONG_MAX) +- return false; ++ if (delta_usecs < 1000 || delta_usecs >= LONG_MAX) { ++ if (blk_queue_nonrot(bfqd->queue)) ++ *delta_ms = BFQ_MIN_TT; /* give same worst-case ++ guarantees as ++ idling for seeky ++ */ ++ else /* Charge at least one seek */ ++ *delta_ms = jiffies_to_msecs(bfq_slice_idle); ++ return slow; ++ } ++ ++ delta_ms_tmp = delta_usecs; ++ do_div(delta_ms_tmp, 1000); ++ *delta_ms = delta_ms_tmp; + + /* + * Calculate the bandwidth for the last slice. We use a 64 bit +@@ -2048,32 +2547,51 @@ static bool bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + * and to avoid overflows. + */ + bw = (u64)bfqq->entity.service << BFQ_RATE_SHIFT; +- do_div(bw, (unsigned long)usecs); +- +- timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); ++ do_div(bw, (unsigned long)delta_usecs); + ++ bfq_log(bfqd, "measured bw = %llu sects/sec", ++ (1000000*bw)>>BFQ_RATE_SHIFT); + /* + * Use only long (> 20ms) intervals to filter out spikes for + * the peak rate estimation. + */ +- if (usecs > 20000) { ++ if (delta_usecs > 20000) { ++ bool fully_sequential = bfqq->seek_history == 0; ++ /* ++ * Soft real-time queues are not good candidates for ++ * evaluating bw, as they are likely to be slow even ++ * if sequential. ++ */ ++ bool non_soft_rt = bfqq->wr_coeff == 1 || ++ bfqq->wr_cur_max_time != bfqd->bfq_wr_rt_max_time; ++ bool consumed_large_budget = ++ reason == BFQ_BFQQ_BUDGET_EXHAUSTED && ++ bfqq->entity.budget >= bfqd->bfq_max_budget * 2 / 3; ++ bool served_for_long_time = ++ reason == BFQ_BFQQ_BUDGET_TIMEOUT || ++ consumed_large_budget; ++ ++ BUG_ON(bfqq->seek_history == 0 && ++ hweight32(bfqq->seek_history) != 0); ++ + if (bw > bfqd->peak_rate || +- (!BFQQ_SEEKY(bfqq) && +- reason == BFQ_BFQQ_BUDGET_TIMEOUT)) { +- bfq_log(bfqd, "measured bw =%llu", bw); ++ (bfq_bfqq_sync(bfqq) && fully_sequential && non_soft_rt && ++ served_for_long_time)) { + /* + * To smooth oscillations use a low-pass filter with +- * alpha=7/8, i.e., +- * new_rate = (7/8) * old_rate + (1/8) * bw ++ * alpha=9/10, i.e., ++ * new_rate = (9/10) * old_rate + (1/10) * bw + */ +- do_div(bw, 8); +- if (bw == 0) +- return 0; +- bfqd->peak_rate *= 7; +- do_div(bfqd->peak_rate, 8); +- bfqd->peak_rate += bw; ++ bwdiv10 = bw; ++ do_div(bwdiv10, 10); ++ if (bwdiv10 == 0) ++ return false; /* bw too low to be used */ ++ bfqd->peak_rate *= 9; ++ do_div(bfqd->peak_rate, 10); ++ bfqd->peak_rate += bwdiv10; + update = 1; +- bfq_log(bfqd, "new peak_rate=%llu", bfqd->peak_rate); ++ bfq_log(bfqd, "new peak_rate = %llu sects/sec", ++ (1000000*bfqd->peak_rate)>>BFQ_RATE_SHIFT); + } + + update |= bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES - 1; +@@ -2086,9 +2604,8 @@ static bool bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int dev_type = blk_queue_nonrot(bfqd->queue); + if (bfqd->bfq_user_max_budget == 0) { + bfqd->bfq_max_budget = +- bfq_calc_max_budget(bfqd->peak_rate, +- timeout); +- bfq_log(bfqd, "new max_budget=%d", ++ bfq_calc_max_budget(bfqd); ++ bfq_log(bfqd, "new max_budget = %d", + bfqd->bfq_max_budget); + } + if (bfqd->device_speed == BFQ_BFQD_FAST && +@@ -2102,38 +2619,35 @@ static bool bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + bfqd->RT_prod = R_fast[dev_type] * + T_fast[dev_type]; + } ++ bfq_log(bfqd, "dev_speed_class = %d (%d sects/sec), " ++ "thresh %d setcs/sec", ++ bfqd->device_speed, ++ bfqd->device_speed == BFQ_BFQD_FAST ? ++ (1000000*R_fast[dev_type])>>BFQ_RATE_SHIFT : ++ (1000000*R_slow[dev_type])>>BFQ_RATE_SHIFT, ++ (1000000*device_speed_thresh[dev_type])>> ++ BFQ_RATE_SHIFT); + } ++ /* ++ * Caveat: processes doing IO in the slower disk zones ++ * tend to be slow(er) even if not seeky. In this ++ * respect, the estimated peak rate is likely to be an ++ * average over the disk surface. Accordingly, to not ++ * be too harsh with unlucky processes, a process is ++ * deemed slow only if its bw has been lower than half ++ * of the estimated peak rate. ++ */ ++ slow = bw < bfqd->peak_rate / 2; + } + +- /* +- * If the process has been served for a too short time +- * interval to let its possible sequential accesses prevail on +- * the initial seek time needed to move the disk head on the +- * first sector it requested, then give the process a chance +- * and for the moment return false. +- */ +- if (bfqq->entity.budget <= bfq_max_budget(bfqd) / 8) +- return false; +- +- /* +- * A process is considered ``slow'' (i.e., seeky, so that we +- * cannot treat it fairly in the service domain, as it would +- * slow down too much the other processes) if, when a slice +- * ends for whatever reason, it has received service at a +- * rate that would not be high enough to complete the budget +- * before the budget timeout expiration. +- */ +- expected = bw * 1000 * timeout >> BFQ_RATE_SHIFT; ++ bfq_log_bfqq(bfqd, bfqq, ++ "update_peak_rate: bw %llu sect/s, peak rate %llu, " ++ "slow %d", ++ (1000000*bw)>>BFQ_RATE_SHIFT, ++ (1000000*bfqd->peak_rate)>>BFQ_RATE_SHIFT, ++ bw < bfqd->peak_rate / 2); + +- /* +- * Caveat: processes doing IO in the slower disk zones will +- * tend to be slow(er) even if not seeky. And the estimated +- * peak rate will actually be an average over the disk +- * surface. Hence, to not be too harsh with unlucky processes, +- * we keep a budget/3 margin of safety before declaring a +- * process slow. +- */ +- return expected > (4 * bfqq->entity.budget) / 3; ++ return slow; + } + + /* +@@ -2191,6 +2705,15 @@ static bool bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd, + struct bfq_queue *bfqq) + { ++ bfq_log_bfqq(bfqd, bfqq, ++ "softrt_next_start: service_blkg %lu " ++ "soft_rate %u sects/sec" ++ "interval %u", ++ bfqq->service_from_backlogged, ++ bfqd->bfq_wr_max_softrt_rate, ++ jiffies_to_msecs(HZ * bfqq->service_from_backlogged / ++ bfqd->bfq_wr_max_softrt_rate)); ++ + return max(bfqq->last_idle_bklogged + + HZ * bfqq->service_from_backlogged / + bfqd->bfq_wr_max_softrt_rate, +@@ -2198,13 +2721,21 @@ static unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd, + } + + /* +- * Return the largest-possible time instant such that, for as long as possible, +- * the current time will be lower than this time instant according to the macro +- * time_is_before_jiffies(). ++ * Return the farthest future time instant according to jiffies ++ * macros. ++ */ ++static unsigned long bfq_greatest_from_now(void) ++{ ++ return jiffies + MAX_JIFFY_OFFSET; ++} ++ ++/* ++ * Return the farthest past time instant according to jiffies ++ * macros. + */ +-static unsigned long bfq_infinity_from_now(unsigned long now) ++static unsigned long bfq_smallest_from_now(void) + { +- return now + ULONG_MAX / 2; ++ return jiffies - MAX_JIFFY_OFFSET; + } + + /** +@@ -2214,28 +2745,24 @@ static unsigned long bfq_infinity_from_now(unsigned long now) + * @compensate: if true, compensate for the time spent idling. + * @reason: the reason causing the expiration. + * ++ * If the process associated with bfqq does slow I/O (e.g., because it ++ * issues random requests), we charge bfqq with the time it has been ++ * in service instead of the service it has received (see ++ * bfq_bfqq_charge_time for details on how this goal is achieved). As ++ * a consequence, bfqq will typically get higher timestamps upon ++ * reactivation, and hence it will be rescheduled as if it had ++ * received more service than what it has actually received. In the ++ * end, bfqq receives less service in proportion to how slowly its ++ * associated process consumes its budgets (and hence how seriously it ++ * tends to lower the throughput). In addition, this time-charging ++ * strategy guarantees time fairness among slow processes. In ++ * contrast, if the process associated with bfqq is not slow, we ++ * charge bfqq exactly with the service it has received. + * +- * If the process associated to the queue is slow (i.e., seeky), or in +- * case of budget timeout, or, finally, if it is async, we +- * artificially charge it an entire budget (independently of the +- * actual service it received). As a consequence, the queue will get +- * higher timestamps than the correct ones upon reactivation, and +- * hence it will be rescheduled as if it had received more service +- * than what it actually received. In the end, this class of processes +- * will receive less service in proportion to how slowly they consume +- * their budgets (and hence how seriously they tend to lower the +- * throughput). +- * +- * In contrast, when a queue expires because it has been idling for +- * too much or because it exhausted its budget, we do not touch the +- * amount of service it has received. Hence when the queue will be +- * reactivated and its timestamps updated, the latter will be in sync +- * with the actual service received by the queue until expiration. +- * +- * Charging a full budget to the first type of queues and the exact +- * service to the others has the effect of using the WF2Q+ policy to +- * schedule the former on a timeslice basis, without violating the +- * service domain guarantees of the latter. ++ * Charging time to the first type of queues and the exact service to ++ * the other has the effect of using the WF2Q+ policy to schedule the ++ * former on a timeslice basis, without violating service domain ++ * guarantees among the latter. + */ + static void bfq_bfqq_expire(struct bfq_data *bfqd, + struct bfq_queue *bfqq, +@@ -2243,40 +2770,53 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, + enum bfqq_expiration reason) + { + bool slow; ++ unsigned long delta = 0; ++ struct bfq_entity *entity = &bfqq->entity; ++ + BUG_ON(bfqq != bfqd->in_service_queue); + + /* +- * Update disk peak rate for autotuning and check whether the ++ * Update device peak rate for autotuning and check whether the + * process is slow (see bfq_update_peak_rate). + */ +- slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason); ++ slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason, &delta); + + /* +- * As above explained, 'punish' slow (i.e., seeky), timed-out +- * and async queues, to favor sequential sync workloads. +- * +- * Processes doing I/O in the slower disk zones will tend to be +- * slow(er) even if not seeky. Hence, since the estimated peak +- * rate is actually an average over the disk surface, these +- * processes may timeout just for bad luck. To avoid punishing +- * them we do not charge a full budget to a process that +- * succeeded in consuming at least 2/3 of its budget. ++ * Increase service_from_backlogged before next statement, ++ * because the possible next invocation of ++ * bfq_bfqq_charge_time would likely inflate ++ * entity->service. In contrast, service_from_backlogged must ++ * contain real service, to enable the soft real-time ++ * heuristic to correctly compute the bandwidth consumed by ++ * bfqq. + */ +- if (slow || (reason == BFQ_BFQQ_BUDGET_TIMEOUT && +- bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) +- bfq_bfqq_charge_full_budget(bfqq); ++ bfqq->service_from_backlogged += entity->service; + +- bfqq->service_from_backlogged += bfqq->entity.service; ++ /* ++ * As above explained, charge slow (typically seeky) and ++ * timed-out queues with the time and not the service ++ * received, to favor sequential workloads. ++ * ++ * Processes doing I/O in the slower disk zones will tend to ++ * be slow(er) even if not seeky. Therefore, since the ++ * estimated peak rate is actually an average over the disk ++ * surface, these processes may timeout just for bad luck. To ++ * avoid punishing them, do not charge time to processes that ++ * succeeded in consuming at least 2/3 of their budget. This ++ * allows BFQ to preserve enough elasticity to still perform ++ * bandwidth, and not time, distribution with little unlucky ++ * or quasi-sequential processes. ++ */ ++ if (bfqq->wr_coeff == 1 && ++ (slow || ++ (reason == BFQ_BFQQ_BUDGET_TIMEOUT && ++ bfq_bfqq_budget_left(bfqq) >= entity->budget / 3))) ++ bfq_bfqq_charge_time(bfqd, bfqq, delta); + +- if (BFQQ_SEEKY(bfqq) && reason == BFQ_BFQQ_BUDGET_TIMEOUT && +- !bfq_bfqq_constantly_seeky(bfqq)) { +- bfq_mark_bfqq_constantly_seeky(bfqq); +- if (!blk_queue_nonrot(bfqd->queue)) +- bfqd->const_seeky_busy_in_flight_queues++; +- } ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); + + if (reason == BFQ_BFQQ_TOO_IDLE && +- bfqq->entity.service <= 2 * bfqq->entity.budget / 10 ) ++ entity->service <= 2 * entity->budget / 10 ) + bfq_clear_bfqq_IO_bound(bfqq); + + if (bfqd->low_latency && bfqq->wr_coeff == 1) +@@ -2285,19 +2825,23 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, + if (bfqd->low_latency && bfqd->bfq_wr_max_softrt_rate > 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) { + /* +- * If we get here, and there are no outstanding requests, +- * then the request pattern is isochronous (see the comments +- * to the function bfq_bfqq_softrt_next_start()). Hence we +- * can compute soft_rt_next_start. If, instead, the queue +- * still has outstanding requests, then we have to wait +- * for the completion of all the outstanding requests to ++ * If we get here, and there are no outstanding ++ * requests, then the request pattern is isochronous ++ * (see the comments on the function ++ * bfq_bfqq_softrt_next_start()). Thus we can compute ++ * soft_rt_next_start. If, instead, the queue still ++ * has outstanding requests, then we have to wait for ++ * the completion of all the outstanding requests to + * discover whether the request pattern is actually + * isochronous. + */ +- if (bfqq->dispatched == 0) ++ BUG_ON(bfqd->busy_queues < 1); ++ if (bfqq->dispatched == 0) { + bfqq->soft_rt_next_start = + bfq_bfqq_softrt_next_start(bfqd, bfqq); +- else { ++ bfq_log_bfqq(bfqd, bfqq, "new soft_rt_next %lu", ++ bfqq->soft_rt_next_start); ++ } else { + /* + * The application is still waiting for the + * completion of one or more requests: +@@ -2314,7 +2858,7 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, + * happened to be in the past. + */ + bfqq->soft_rt_next_start = +- bfq_infinity_from_now(jiffies); ++ bfq_greatest_from_now(); + /* + * Schedule an update of soft_rt_next_start to when + * the task may be discovered to be isochronous. +@@ -2324,15 +2868,27 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, + } + + bfq_log_bfqq(bfqd, bfqq, +- "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, +- slow, bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); ++ "expire (%d, slow %d, num_disp %d, idle_win %d, weight %d)", ++ reason, slow, bfqq->dispatched, ++ bfq_bfqq_idle_window(bfqq), entity->weight); + + /* + * Increase, decrease or leave budget unchanged according to + * reason. + */ ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); + __bfq_bfqq_recalc_budget(bfqd, bfqq, reason); ++ BUG_ON(bfqq->next_rq == NULL && ++ bfqq->entity.budget < bfqq->entity.service); + __bfq_bfqq_expire(bfqd, bfqq); ++ ++ BUG_ON(!bfq_bfqq_busy(bfqq) && reason == BFQ_BFQQ_BUDGET_EXHAUSTED && ++ !bfq_class_idle(bfqq)); ++ ++ if (!bfq_bfqq_busy(bfqq) && ++ reason != BFQ_BFQQ_BUDGET_TIMEOUT && ++ reason != BFQ_BFQQ_BUDGET_EXHAUSTED) ++ bfq_mark_bfqq_non_blocking_wait_rq(bfqq); + } + + /* +@@ -2342,20 +2898,17 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, + */ + static bool bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) + { +- if (bfq_bfqq_budget_new(bfqq) || +- time_before(jiffies, bfqq->budget_timeout)) +- return false; +- return true; ++ return time_is_before_eq_jiffies(bfqq->budget_timeout); + } + + /* +- * If we expire a queue that is waiting for the arrival of a new +- * request, we may prevent the fictitious timestamp back-shifting that +- * allows the guarantees of the queue to be preserved (see [1] for +- * this tricky aspect). Hence we return true only if this condition +- * does not hold, or if the queue is slow enough to deserve only to be +- * kicked off for preserving a high throughput. +-*/ ++ * If we expire a queue that is actively waiting (i.e., with the ++ * device idled) for the arrival of a new request, then we may incur ++ * the timestamp misalignment problem described in the body of the ++ * function __bfq_activate_entity. Hence we return true only if this ++ * condition does not hold, or if the queue is slow enough to deserve ++ * only to be kicked off for preserving a high throughput. ++ */ + static bool bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) + { + bfq_log_bfqq(bfqq->bfqd, bfqq, +@@ -2397,10 +2950,12 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + { + struct bfq_data *bfqd = bfqq->bfqd; + bool idling_boosts_thr, idling_boosts_thr_without_issues, +- all_queues_seeky, on_hdd_and_not_all_queues_seeky, + idling_needed_for_service_guarantees, + asymmetric_scenario; + ++ if (bfqd->strict_guarantees) ++ return true; ++ + /* + * The next variable takes into account the cases where idling + * boosts the throughput. +@@ -2422,7 +2977,7 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + */ + idling_boosts_thr = !bfqd->hw_tag || + (!blk_queue_nonrot(bfqd->queue) && bfq_bfqq_IO_bound(bfqq) && +- bfq_bfqq_idle_window(bfqq)) ; ++ bfq_bfqq_idle_window(bfqq)); + + /* + * The value of the next variable, +@@ -2463,74 +3018,27 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + bfqd->wr_busy_queues == 0; + + /* +- * There are then two cases where idling must be performed not ++ * There is then a case where idling must be performed not + * for throughput concerns, but to preserve service +- * guarantees. In the description of these cases, we say, for +- * short, that a queue is sequential/random if the process +- * associated to the queue issues sequential/random requests +- * (in the second case the queue may be tagged as seeky or +- * even constantly_seeky). +- * +- * To introduce the first case, we note that, since +- * bfq_bfqq_idle_window(bfqq) is false if the device is +- * NCQ-capable and bfqq is random (see +- * bfq_update_idle_window()), then, from the above two +- * assignments it follows that +- * idling_boosts_thr_without_issues is false if the device is +- * NCQ-capable and bfqq is random. Therefore, for this case, +- * device idling would never be allowed if we used just +- * idling_boosts_thr_without_issues to decide whether to allow +- * it. And, beneficially, this would imply that throughput +- * would always be boosted also with random I/O on NCQ-capable +- * HDDs. ++ * guarantees. + * +- * But we must be careful on this point, to avoid an unfair +- * treatment for bfqq. In fact, because of the same above +- * assignments, idling_boosts_thr_without_issues is, on the +- * other hand, true if 1) the device is an HDD and bfqq is +- * sequential, and 2) there are no busy weight-raised +- * queues. As a consequence, if we used just +- * idling_boosts_thr_without_issues to decide whether to idle +- * the device, then with an HDD we might easily bump into a +- * scenario where queues that are sequential and I/O-bound +- * would enjoy idling, whereas random queues would not. The +- * latter might then get a low share of the device throughput, +- * simply because the former would get many requests served +- * after being set as in service, while the latter would not. +- * +- * To address this issue, we start by setting to true a +- * sentinel variable, on_hdd_and_not_all_queues_seeky, if the +- * device is rotational and not all queues with pending or +- * in-flight requests are constantly seeky (i.e., there are +- * active sequential queues, and bfqq might then be mistreated +- * if it does not enjoy idling because it is random). +- */ +- all_queues_seeky = bfq_bfqq_constantly_seeky(bfqq) && +- bfqd->busy_in_flight_queues == +- bfqd->const_seeky_busy_in_flight_queues; +- +- on_hdd_and_not_all_queues_seeky = +- !blk_queue_nonrot(bfqd->queue) && !all_queues_seeky; +- +- /* +- * To introduce the second case where idling needs to be +- * performed to preserve service guarantees, we can note that +- * allowing the drive to enqueue more than one request at a +- * time, and hence delegating de facto final scheduling +- * decisions to the drive's internal scheduler, causes loss of +- * control on the actual request service order. In particular, +- * the critical situation is when requests from different +- * processes happens to be present, at the same time, in the +- * internal queue(s) of the drive. In such a situation, the +- * drive, by deciding the service order of the +- * internally-queued requests, does determine also the actual +- * throughput distribution among these processes. But the +- * drive typically has no notion or concern about per-process +- * throughput distribution, and makes its decisions only on a +- * per-request basis. Therefore, the service distribution +- * enforced by the drive's internal scheduler is likely to +- * coincide with the desired device-throughput distribution +- * only in a completely symmetric scenario where: ++ * To introduce this case, we can note that allowing the drive ++ * to enqueue more than one request at a time, and hence ++ * delegating de facto final scheduling decisions to the ++ * drive's internal scheduler, entails loss of control on the ++ * actual request service order. In particular, the critical ++ * situation is when requests from different processes happen ++ * to be present, at the same time, in the internal queue(s) ++ * of the drive. In such a situation, the drive, by deciding ++ * the service order of the internally-queued requests, does ++ * determine also the actual throughput distribution among ++ * these processes. But the drive typically has no notion or ++ * concern about per-process throughput distribution, and ++ * makes its decisions only on a per-request basis. Therefore, ++ * the service distribution enforced by the drive's internal ++ * scheduler is likely to coincide with the desired ++ * device-throughput distribution only in a completely ++ * symmetric scenario where: + * (i) each of these processes must get the same throughput as + * the others; + * (ii) all these processes have the same I/O pattern +@@ -2552,26 +3060,53 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + * words, only if sub-condition (i) holds, then idling is + * allowed, and the device tends to be prevented from queueing + * many requests, possibly of several processes. The reason +- * for not controlling also sub-condition (ii) is that, first, +- * in the case of an HDD, the asymmetry in terms of types of +- * I/O patterns is already taken in to account in the above +- * sentinel variable +- * on_hdd_and_not_all_queues_seeky. Secondly, in the case of a +- * flash-based device, we prefer however to privilege +- * throughput (and idling lowers throughput for this type of +- * devices), for the following reasons: +- * 1) differently from HDDs, the service time of random +- * requests is not orders of magnitudes lower than the service +- * time of sequential requests; thus, even if processes doing +- * sequential I/O get a preferential treatment with respect to +- * others doing random I/O, the consequences are not as +- * dramatic as with HDDs; +- * 2) if a process doing random I/O does need strong +- * throughput guarantees, it is hopefully already being +- * weight-raised, or the user is likely to have assigned it a +- * higher weight than the other processes (and thus +- * sub-condition (i) is likely to be false, which triggers +- * idling). ++ * for not controlling also sub-condition (ii) is that we ++ * exploit preemption to preserve guarantees in case of ++ * symmetric scenarios, even if (ii) does not hold, as ++ * explained in the next two paragraphs. ++ * ++ * Even if a queue, say Q, is expired when it remains idle, Q ++ * can still preempt the new in-service queue if the next ++ * request of Q arrives soon (see the comments on ++ * bfq_bfqq_update_budg_for_activation). If all queues and ++ * groups have the same weight, this form of preemption, ++ * combined with the hole-recovery heuristic described in the ++ * comments on function bfq_bfqq_update_budg_for_activation, ++ * are enough to preserve a correct bandwidth distribution in ++ * the mid term, even without idling. In fact, even if not ++ * idling allows the internal queues of the device to contain ++ * many requests, and thus to reorder requests, we can rather ++ * safely assume that the internal scheduler still preserves a ++ * minimum of mid-term fairness. The motivation for using ++ * preemption instead of idling is that, by not idling, ++ * service guarantees are preserved without minimally ++ * sacrificing throughput. In other words, both a high ++ * throughput and its desired distribution are obtained. ++ * ++ * More precisely, this preemption-based, idleless approach ++ * provides fairness in terms of IOPS, and not sectors per ++ * second. This can be seen with a simple example. Suppose ++ * that there are two queues with the same weight, but that ++ * the first queue receives requests of 8 sectors, while the ++ * second queue receives requests of 1024 sectors. In ++ * addition, suppose that each of the two queues contains at ++ * most one request at a time, which implies that each queue ++ * always remains idle after it is served. Finally, after ++ * remaining idle, each queue receives very quickly a new ++ * request. It follows that the two queues are served ++ * alternatively, preempting each other if needed. This ++ * implies that, although both queues have the same weight, ++ * the queue with large requests receives a service that is ++ * 1024/8 times as high as the service received by the other ++ * queue. ++ * ++ * On the other hand, device idling is performed, and thus ++ * pure sector-domain guarantees are provided, for the ++ * following queues, which are likely to need stronger ++ * throughput guarantees: weight-raised queues, and queues ++ * with a higher weight than other queues. When such queues ++ * are active, sub-condition (i) is false, which triggers ++ * device idling. + * + * According to the above considerations, the next variable is + * true (only) if sub-condition (i) holds. To compute the +@@ -2579,7 +3114,7 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + * the function bfq_symmetric_scenario(), but also check + * whether bfqq is being weight-raised, because + * bfq_symmetric_scenario() does not take into account also +- * weight-raised queues (see comments to ++ * weight-raised queues (see comments on + * bfq_weights_tree_add()). + * + * As a side note, it is worth considering that the above +@@ -2601,17 +3136,16 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + * bfqq. Such a case is when bfqq became active in a burst of + * queue activations. Queues that became active during a large + * burst benefit only from throughput, as discussed in the +- * comments to bfq_handle_burst. Thus, if bfqq became active ++ * comments on bfq_handle_burst. Thus, if bfqq became active + * in a burst and not idling the device maximizes throughput, + * then the device must no be idled, because not idling the + * device provides bfqq and all other queues in the burst with +- * maximum benefit. Combining this and the two cases above, we +- * can now establish when idling is actually needed to +- * preserve service guarantees. ++ * maximum benefit. Combining this and the above case, we can ++ * now establish when idling is actually needed to preserve ++ * service guarantees. + */ + idling_needed_for_service_guarantees = +- (on_hdd_and_not_all_queues_seeky || asymmetric_scenario) && +- !bfq_bfqq_in_large_burst(bfqq); ++ asymmetric_scenario && !bfq_bfqq_in_large_burst(bfqq); + + /* + * We have now all the components we need to compute the return +@@ -2621,6 +3155,14 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + * 2) idling either boosts the throughput (without issues), or + * is necessary to preserve service guarantees. + */ ++ bfq_log_bfqq(bfqd, bfqq, "may_idle: sync %d idling_boosts_thr %d " ++ "wr_busy %d boosts %d IO-bound %d guar %d", ++ bfq_bfqq_sync(bfqq), idling_boosts_thr, ++ bfqd->wr_busy_queues, ++ idling_boosts_thr_without_issues, ++ bfq_bfqq_IO_bound(bfqq), ++ idling_needed_for_service_guarantees); ++ + return bfq_bfqq_sync(bfqq) && + (idling_boosts_thr_without_issues || + idling_needed_for_service_guarantees); +@@ -2632,7 +3174,7 @@ static bool bfq_bfqq_may_idle(struct bfq_queue *bfqq) + * 1) the queue must remain in service and cannot be expired, and + * 2) the device must be idled to wait for the possible arrival of a new + * request for the queue. +- * See the comments to the function bfq_bfqq_may_idle for the reasons ++ * See the comments on the function bfq_bfqq_may_idle for the reasons + * why performing device idling is the best choice to boost the throughput + * and preserve service guarantees when bfq_bfqq_may_idle itself + * returns true. +@@ -2698,9 +3240,7 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_idle_time(bfqq_group(bfqq)); +-#endif + } + goto keep_queue; + } +@@ -2745,14 +3285,11 @@ static void bfq_update_wr_data(struct bfq_data *bfqd, struct bfq_queue *bfqq) + bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change"); + + /* +- * If the queue was activated in a burst, or +- * too much time has elapsed from the beginning +- * of this weight-raising period, or the queue has +- * exceeded the acceptable number of cooperations, +- * then end weight raising. ++ * If the queue was activated in a burst, or too much ++ * time has elapsed from the beginning of this ++ * weight-raising period, then end weight raising. + */ + if (bfq_bfqq_in_large_burst(bfqq) || +- bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh || + time_is_before_jiffies(bfqq->last_wr_start_finish + + bfqq->wr_cur_max_time)) { + bfqq->last_wr_start_finish = jiffies; +@@ -2811,13 +3348,29 @@ static int bfq_dispatch_request(struct bfq_data *bfqd, + */ + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); + goto expire; + } + ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); + /* Finally, insert request into driver dispatch list. */ + bfq_bfqq_served(bfqq, service_to_charge); ++ ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); ++ + bfq_dispatch_insert(bfqd->queue, rq); + ++ /* ++ * If weight raising has to terminate for bfqq, then next ++ * function causes an immediate update of bfqq's weight, ++ * without waiting for next activation. As a consequence, on ++ * expiration, bfqq will be timestamped as if has never been ++ * weight-raised during this service slot, even if it has ++ * received part or even most of the service as a ++ * weight-raised queue. This inflates bfqq's timestamps, which ++ * is beneficial, as bfqq is then more willing to leave the ++ * device immediately to possible other weight-raised queues. ++ */ + bfq_update_wr_data(bfqd, bfqq); + + bfq_log_bfqq(bfqd, bfqq, +@@ -2833,9 +3386,7 @@ static int bfq_dispatch_request(struct bfq_data *bfqd, + bfqd->in_service_bic = RQ_BIC(rq); + } + +- if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && +- dispatched >= bfqd->bfq_max_budget_async_rq) || +- bfq_class_idle(bfqq))) ++ if (bfqd->busy_queues > 1 && bfq_class_idle(bfqq)) + goto expire; + + return dispatched; +@@ -2881,8 +3432,8 @@ static int bfq_forced_dispatch(struct bfq_data *bfqd) + st = bfq_entity_service_tree(&bfqq->entity); + + dispatched += __bfq_forced_dispatch_bfqq(bfqq); +- bfqq->max_budget = bfq_max_budget(bfqd); + ++ bfqq->max_budget = bfq_max_budget(bfqd); + bfq_forget_idle(st); + } + +@@ -2895,9 +3446,9 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) + { + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq; +- int max_dispatch; + + bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); ++ + if (bfqd->busy_queues == 0) + return 0; + +@@ -2908,21 +3459,7 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) + if (!bfqq) + return 0; + +- if (bfq_class_idle(bfqq)) +- max_dispatch = 1; +- +- if (!bfq_bfqq_sync(bfqq)) +- max_dispatch = bfqd->bfq_max_budget_async_rq; +- +- if (!bfq_bfqq_sync(bfqq) && bfqq->dispatched >= max_dispatch) { +- if (bfqd->busy_queues > 1) +- return 0; +- if (bfqq->dispatched >= 4 * max_dispatch) +- return 0; +- } +- +- if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) +- return 0; ++ BUG_ON(bfqq->entity.budget < bfqq->entity.service); + + bfq_clear_bfqq_wait_request(bfqq); + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); +@@ -2933,6 +3470,8 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) + bfq_log_bfqq(bfqd, bfqq, "dispatched %s request", + bfq_bfqq_sync(bfqq) ? "sync" : "async"); + ++ BUG_ON(bfqq->next_rq == NULL && ++ bfqq->entity.budget < bfqq->entity.service); + return 1; + } + +@@ -2944,23 +3483,22 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) + */ + static void bfq_put_queue(struct bfq_queue *bfqq) + { +- struct bfq_data *bfqd = bfqq->bfqd; + #ifdef CONFIG_BFQ_GROUP_IOSCHED + struct bfq_group *bfqg = bfqq_group(bfqq); + #endif + +- BUG_ON(atomic_read(&bfqq->ref) <= 0); ++ BUG_ON(bfqq->ref <= 0); + +- bfq_log_bfqq(bfqd, bfqq, "put_queue: %p %d", bfqq, +- atomic_read(&bfqq->ref)); +- if (!atomic_dec_and_test(&bfqq->ref)) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p %d", bfqq, bfqq->ref); ++ bfqq->ref--; ++ if (bfqq->ref) + return; + + BUG_ON(rb_first(&bfqq->sort_list)); + BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0); + BUG_ON(bfqq->entity.tree); + BUG_ON(bfq_bfqq_busy(bfqq)); +- BUG_ON(bfqd->in_service_queue == bfqq); ++ BUG_ON(bfqq->bfqd->in_service_queue == bfqq); + + if (bfq_bfqq_sync(bfqq)) + /* +@@ -2973,7 +3511,7 @@ static void bfq_put_queue(struct bfq_queue *bfqq) + */ + hlist_del_init(&bfqq->burst_list_node); + +- bfq_log_bfqq(bfqd, bfqq, "put_queue: %p freed", bfqq); ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p freed", bfqq); + + kmem_cache_free(bfq_pool, bfqq); + #ifdef CONFIG_BFQ_GROUP_IOSCHED +@@ -3007,8 +3545,7 @@ static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) + bfq_schedule_dispatch(bfqd); + } + +- bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, +- atomic_read(&bfqq->ref)); ++ bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, bfqq->ref); + + bfq_put_cooperator(bfqq); + +@@ -3019,26 +3556,7 @@ static void bfq_init_icq(struct io_cq *icq) + { + struct bfq_io_cq *bic = icq_to_bic(icq); + +- bic->ttime.last_end_request = jiffies; +- /* +- * A newly created bic indicates that the process has just +- * started doing I/O, and is probably mapping into memory its +- * executable and libraries: it definitely needs weight raising. +- * There is however the possibility that the process performs, +- * for a while, I/O close to some other process. EQM intercepts +- * this behavior and may merge the queue corresponding to the +- * process with some other queue, BEFORE the weight of the queue +- * is raised. Merged queues are not weight-raised (they are assumed +- * to belong to processes that benefit only from high throughput). +- * If the merge is basically the consequence of an accident, then +- * the queue will be split soon and will get back its old weight. +- * It is then important to write down somewhere that this queue +- * does need weight raising, even if it did not make it to get its +- * weight raised before being merged. To this purpose, we overload +- * the field raising_time_left and assign 1 to it, to mark the queue +- * as needing weight raising. +- */ +- bic->wr_time_left = 1; ++ bic->ttime.last_end_request = bfq_smallest_from_now(); + } + + static void bfq_exit_icq(struct io_cq *icq) +@@ -3046,21 +3564,21 @@ static void bfq_exit_icq(struct io_cq *icq) + struct bfq_io_cq *bic = icq_to_bic(icq); + struct bfq_data *bfqd = bic_to_bfqd(bic); + +- if (bic->bfqq[BLK_RW_ASYNC]) { +- bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_ASYNC]); +- bic->bfqq[BLK_RW_ASYNC] = NULL; ++ if (bic_to_bfqq(bic, false)) { ++ bfq_exit_bfqq(bfqd, bic_to_bfqq(bic, false)); ++ bic_set_bfqq(bic, NULL, false); + } + +- if (bic->bfqq[BLK_RW_SYNC]) { ++ if (bic_to_bfqq(bic, true)) { + /* + * If the bic is using a shared queue, put the reference + * taken on the io_context when the bic started using a + * shared bfq_queue. + */ +- if (bfq_bfqq_coop(bic->bfqq[BLK_RW_SYNC])) ++ if (bfq_bfqq_coop(bic_to_bfqq(bic, true))) + put_io_context(icq->ioc); +- bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_SYNC]); +- bic->bfqq[BLK_RW_SYNC] = NULL; ++ bfq_exit_bfqq(bfqd, bic_to_bfqq(bic, true)); ++ bic_set_bfqq(bic, NULL, true); + } + } + +@@ -3068,7 +3586,8 @@ static void bfq_exit_icq(struct io_cq *icq) + * Update the entity prio values; note that the new values will not + * be used until the next (re)activation. + */ +-static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) ++static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, ++ struct bfq_io_cq *bic) + { + struct task_struct *tsk = current; + int ioprio_class; +@@ -3100,7 +3619,7 @@ static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *b + break; + } + +- if (bfqq->new_ioprio < 0 || bfqq->new_ioprio >= IOPRIO_BE_NR) { ++ if (bfqq->new_ioprio >= IOPRIO_BE_NR) { + printk(KERN_CRIT "bfq_set_next_ioprio_data: new_ioprio %d\n", + bfqq->new_ioprio); + BUG(); +@@ -3108,45 +3627,40 @@ static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *b + + bfqq->entity.new_weight = bfq_ioprio_to_weight(bfqq->new_ioprio); + bfqq->entity.prio_changed = 1; ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "set_next_ioprio_data: bic_class %d prio %d class %d", ++ ioprio_class, bfqq->new_ioprio, bfqq->new_ioprio_class); + } + + static void bfq_check_ioprio_change(struct bfq_io_cq *bic, struct bio *bio) + { +- struct bfq_data *bfqd; +- struct bfq_queue *bfqq, *new_bfqq; ++ struct bfq_data *bfqd = bic_to_bfqd(bic); ++ struct bfq_queue *bfqq; + unsigned long uninitialized_var(flags); + int ioprio = bic->icq.ioc->ioprio; + +- bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), +- &flags); + /* + * This condition may trigger on a newly created bic, be sure to + * drop the lock before returning. + */ + if (unlikely(!bfqd) || likely(bic->ioprio == ioprio)) +- goto out; ++ return; + + bic->ioprio = ioprio; + +- bfqq = bic->bfqq[BLK_RW_ASYNC]; ++ bfqq = bic_to_bfqq(bic, false); + if (bfqq) { +- new_bfqq = bfq_get_queue(bfqd, bio, BLK_RW_ASYNC, bic, +- GFP_ATOMIC); +- if (new_bfqq) { +- bic->bfqq[BLK_RW_ASYNC] = new_bfqq; +- bfq_log_bfqq(bfqd, bfqq, +- "check_ioprio_change: bfqq %p %d", +- bfqq, atomic_read(&bfqq->ref)); +- bfq_put_queue(bfqq); +- } ++ bfq_put_queue(bfqq); ++ bfqq = bfq_get_queue(bfqd, bio, BLK_RW_ASYNC, bic); ++ bic_set_bfqq(bic, bfqq, false); ++ bfq_log_bfqq(bfqd, bfqq, ++ "check_ioprio_change: bfqq %p %d", ++ bfqq, bfqq->ref); + } + +- bfqq = bic->bfqq[BLK_RW_SYNC]; ++ bfqq = bic_to_bfqq(bic, true); + if (bfqq) + bfq_set_next_ioprio_data(bfqq, bic); +- +-out: +- bfq_put_bfqd_unlock(bfqd, &flags); + } + + static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, +@@ -3155,8 +3669,9 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + RB_CLEAR_NODE(&bfqq->entity.rb_node); + INIT_LIST_HEAD(&bfqq->fifo); + INIT_HLIST_NODE(&bfqq->burst_list_node); ++ BUG_ON(!hlist_unhashed(&bfqq->burst_list_node)); + +- atomic_set(&bfqq->ref, 0); ++ bfqq->ref = 0; + bfqq->bfqd = bfqd; + + if (bic) +@@ -3166,6 +3681,7 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + if (!bfq_class_idle(bfqq)) + bfq_mark_bfqq_idle_window(bfqq); + bfq_mark_bfqq_sync(bfqq); ++ bfq_mark_bfqq_just_created(bfqq); + } else + bfq_clear_bfqq_sync(bfqq); + bfq_mark_bfqq_IO_bound(bfqq); +@@ -3175,72 +3691,17 @@ static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + bfqq->pid = pid; + + bfqq->wr_coeff = 1; +- bfqq->last_wr_start_finish = 0; ++ bfqq->last_wr_start_finish = bfq_smallest_from_now(); ++ bfqq->budget_timeout = bfq_smallest_from_now(); ++ bfqq->split_time = bfq_smallest_from_now(); + /* + * Set to the value for which bfqq will not be deemed as + * soft rt when it becomes backlogged. + */ +- bfqq->soft_rt_next_start = bfq_infinity_from_now(jiffies); +-} +- +-static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, +- struct bio *bio, int is_sync, +- struct bfq_io_cq *bic, +- gfp_t gfp_mask) +-{ +- struct bfq_group *bfqg; +- struct bfq_queue *bfqq, *new_bfqq = NULL; +- struct blkcg *blkcg; +- +-retry: +- rcu_read_lock(); +- +- blkcg = bio_blkcg(bio); +- bfqg = bfq_find_alloc_group(bfqd, blkcg); +- /* bic always exists here */ +- bfqq = bic_to_bfqq(bic, is_sync); +- +- /* +- * Always try a new alloc if we fall back to the OOM bfqq +- * originally, since it should just be a temporary situation. +- */ +- if (!bfqq || bfqq == &bfqd->oom_bfqq) { +- bfqq = NULL; +- if (new_bfqq) { +- bfqq = new_bfqq; +- new_bfqq = NULL; +- } else if (gfpflags_allow_blocking(gfp_mask)) { +- rcu_read_unlock(); +- spin_unlock_irq(bfqd->queue->queue_lock); +- new_bfqq = kmem_cache_alloc_node(bfq_pool, +- gfp_mask | __GFP_ZERO, +- bfqd->queue->node); +- spin_lock_irq(bfqd->queue->queue_lock); +- if (new_bfqq) +- goto retry; +- } else { +- bfqq = kmem_cache_alloc_node(bfq_pool, +- gfp_mask | __GFP_ZERO, +- bfqd->queue->node); +- } +- +- if (bfqq) { +- bfq_init_bfqq(bfqd, bfqq, bic, current->pid, +- is_sync); +- bfq_init_entity(&bfqq->entity, bfqg); +- bfq_log_bfqq(bfqd, bfqq, "allocated"); +- } else { +- bfqq = &bfqd->oom_bfqq; +- bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); +- } +- } +- +- if (new_bfqq) +- kmem_cache_free(bfq_pool, new_bfqq); +- +- rcu_read_unlock(); ++ bfqq->soft_rt_next_start = bfq_greatest_from_now(); + +- return bfqq; ++ /* first request is almost certainly seeky */ ++ bfqq->seek_history = 1; + } + + static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, +@@ -3263,44 +3724,60 @@ static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, + } + + static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, +- struct bio *bio, int is_sync, +- struct bfq_io_cq *bic, gfp_t gfp_mask) ++ struct bio *bio, bool is_sync, ++ struct bfq_io_cq *bic) + { + const int ioprio = IOPRIO_PRIO_DATA(bic->ioprio); + const int ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); + struct bfq_queue **async_bfqq = NULL; +- struct bfq_queue *bfqq = NULL; ++ struct bfq_queue *bfqq; ++ struct bfq_group *bfqg; + +- if (!is_sync) { +- struct blkcg *blkcg; +- struct bfq_group *bfqg; ++ rcu_read_lock(); ++ ++ bfqg = bfq_find_set_group(bfqd,bio_blkcg(bio)); ++ if (!bfqg) { ++ bfqq = &bfqd->oom_bfqq; ++ goto out; ++ } + +- rcu_read_lock(); +- blkcg = bio_blkcg(bio); +- rcu_read_unlock(); +- bfqg = bfq_find_alloc_group(bfqd, blkcg); ++ if (!is_sync) { + async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class, + ioprio); + bfqq = *async_bfqq; ++ if (bfqq) ++ goto out; + } + +- if (!bfqq) +- bfqq = bfq_find_alloc_queue(bfqd, bio, is_sync, bic, gfp_mask); ++ bfqq = kmem_cache_alloc_node(bfq_pool, GFP_NOWAIT | __GFP_ZERO, ++ bfqd->queue->node); ++ ++ if (bfqq) { ++ bfq_init_bfqq(bfqd, bfqq, bic, current->pid, ++ is_sync); ++ bfq_init_entity(&bfqq->entity, bfqg); ++ bfq_log_bfqq(bfqd, bfqq, "allocated"); ++ } else { ++ bfqq = &bfqd->oom_bfqq; ++ bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); ++ goto out; ++ } + + /* + * Pin the queue now that it's allocated, scheduler exit will + * prune it. + */ +- if (!is_sync && !(*async_bfqq)) { +- atomic_inc(&bfqq->ref); ++ if (async_bfqq) { ++ bfqq->ref++; + bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d", +- bfqq, atomic_read(&bfqq->ref)); ++ bfqq, bfqq->ref); + *async_bfqq = bfqq; + } + +- atomic_inc(&bfqq->ref); +- bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, +- atomic_read(&bfqq->ref)); ++out: ++ bfqq->ref++; ++ bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, bfqq->ref); ++ rcu_read_unlock(); + return bfqq; + } + +@@ -3316,37 +3793,21 @@ static void bfq_update_io_thinktime(struct bfq_data *bfqd, + bic->ttime.ttime_samples; + } + +-static void bfq_update_io_seektime(struct bfq_data *bfqd, +- struct bfq_queue *bfqq, +- struct request *rq) +-{ +- sector_t sdist; +- u64 total; +- +- if (bfqq->last_request_pos < blk_rq_pos(rq)) +- sdist = blk_rq_pos(rq) - bfqq->last_request_pos; +- else +- sdist = bfqq->last_request_pos - blk_rq_pos(rq); +- +- /* +- * Don't allow the seek distance to get too large from the +- * odd fragment, pagein, etc. +- */ +- if (bfqq->seek_samples == 0) /* first request, not really a seek */ +- sdist = 0; +- else if (bfqq->seek_samples <= 60) /* second & third seek */ +- sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*1024); +- else +- sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*64); + +- bfqq->seek_samples = (7*bfqq->seek_samples + 256) / 8; +- bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; +- total = bfqq->seek_total + (bfqq->seek_samples/2); +- do_div(total, bfqq->seek_samples); +- bfqq->seek_mean = (sector_t)total; ++static void ++bfq_update_io_seektime(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ struct request *rq) ++{ ++ sector_t sdist = 0; ++ if (bfqq->last_request_pos) { ++ if (bfqq->last_request_pos < blk_rq_pos(rq)) ++ sdist = blk_rq_pos(rq) - bfqq->last_request_pos; ++ else ++ sdist = bfqq->last_request_pos - blk_rq_pos(rq); ++ } + +- bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, +- (u64)bfqq->seek_mean); ++ bfqq->seek_history <<= 1; ++ bfqq->seek_history |= (sdist > BFQQ_SEEK_THR); + } + + /* +@@ -3364,7 +3825,8 @@ static void bfq_update_idle_window(struct bfq_data *bfqd, + return; + + /* Idle window just restored, statistics are meaningless. */ +- if (bfq_bfqq_just_split(bfqq)) ++ if (time_is_after_eq_jiffies(bfqq->split_time + ++ bfqd->bfq_wr_min_idle_time)) + return; + + enable_idle = bfq_bfqq_idle_window(bfqq); +@@ -3404,22 +3866,13 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + + bfq_update_io_thinktime(bfqd, bic); + bfq_update_io_seektime(bfqd, bfqq, rq); +- if (!BFQQ_SEEKY(bfqq) && bfq_bfqq_constantly_seeky(bfqq)) { +- bfq_clear_bfqq_constantly_seeky(bfqq); +- if (!blk_queue_nonrot(bfqd->queue)) { +- BUG_ON(!bfqd->const_seeky_busy_in_flight_queues); +- bfqd->const_seeky_busy_in_flight_queues--; +- } +- } + if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || + !BFQQ_SEEKY(bfqq)) + bfq_update_idle_window(bfqd, bfqq, bic); +- bfq_clear_bfqq_just_split(bfqq); + + bfq_log_bfqq(bfqd, bfqq, +- "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", +- bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq), +- (long long unsigned)bfqq->seek_mean); ++ "rq_enqueued: idle_window=%d (seeky %d)", ++ bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq)); + + bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); + +@@ -3433,14 +3886,15 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + * is small and the queue is not to be expired, then + * just exit. + * +- * In this way, if the disk is being idled to wait for +- * a new request from the in-service queue, we avoid +- * unplugging the device and committing the disk to serve +- * just a small request. On the contrary, we wait for +- * the block layer to decide when to unplug the device: +- * hopefully, new requests will be merged to this one +- * quickly, then the device will be unplugged and +- * larger requests will be dispatched. ++ * In this way, if the device is being idled to wait ++ * for a new request from the in-service queue, we ++ * avoid unplugging the device and committing the ++ * device to serve just a small request. On the ++ * contrary, we wait for the block layer to decide ++ * when to unplug the device: hopefully, new requests ++ * will be merged to this one quickly, then the device ++ * will be unplugged and larger requests will be ++ * dispatched. + */ + if (small_req && !budget_timeout) + return; +@@ -3453,9 +3907,7 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_idle_time(bfqq_group(bfqq)); +-#endif + + /* + * The queue is not empty, because a new request just +@@ -3499,27 +3951,19 @@ static void bfq_insert_request(struct request_queue *q, struct request *rq) + */ + new_bfqq->allocated[rq_data_dir(rq)]++; + bfqq->allocated[rq_data_dir(rq)]--; +- atomic_inc(&new_bfqq->ref); ++ new_bfqq->ref++; ++ bfq_clear_bfqq_just_created(bfqq); + bfq_put_queue(bfqq); + if (bic_to_bfqq(RQ_BIC(rq), 1) == bfqq) + bfq_merge_bfqqs(bfqd, RQ_BIC(rq), + bfqq, new_bfqq); + rq->elv.priv[1] = new_bfqq; + bfqq = new_bfqq; +- } else +- bfq_bfqq_increase_failed_cooperations(bfqq); ++ } + } + + bfq_add_request(rq); + +- /* +- * Here a newly-created bfq_queue has already started a weight-raising +- * period: clear raising_time_left to prevent bfq_bfqq_save_state() +- * from assigning it a full weight-raising period. See the detailed +- * comments about this field in bfq_init_icq(). +- */ +- if (bfqq->bic) +- bfqq->bic->wr_time_left = 0; + rq->fifo_time = jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]; + list_add_tail(&rq->queuelist, &bfqq->fifo); + +@@ -3528,8 +3972,8 @@ static void bfq_insert_request(struct request_queue *q, struct request *rq) + + static void bfq_update_hw_tag(struct bfq_data *bfqd) + { +- bfqd->max_rq_in_driver = max(bfqd->max_rq_in_driver, +- bfqd->rq_in_driver); ++ bfqd->max_rq_in_driver = max_t(int, bfqd->max_rq_in_driver, ++ bfqd->rq_in_driver); + + if (bfqd->hw_tag == 1) + return; +@@ -3555,48 +3999,45 @@ static void bfq_completed_request(struct request_queue *q, struct request *rq) + { + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; +- bool sync = bfq_bfqq_sync(bfqq); + +- bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left (%d)", +- blk_rq_sectors(rq), sync); ++ bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left", ++ blk_rq_sectors(rq)); + ++ assert_spin_locked(bfqd->queue->queue_lock); + bfq_update_hw_tag(bfqd); + + BUG_ON(!bfqd->rq_in_driver); + BUG_ON(!bfqq->dispatched); + bfqd->rq_in_driver--; + bfqq->dispatched--; +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_completion(bfqq_group(bfqq), + rq_start_time_ns(rq), + rq_io_start_time_ns(rq), rq->cmd_flags); +-#endif + + if (!bfqq->dispatched && !bfq_bfqq_busy(bfqq)) { ++ BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); ++ /* ++ * Set budget_timeout (which we overload to store the ++ * time at which the queue remains with no backlog and ++ * no outstanding request; used by the weight-raising ++ * mechanism). ++ */ ++ bfqq->budget_timeout = jiffies; ++ + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); +- if (!blk_queue_nonrot(bfqd->queue)) { +- BUG_ON(!bfqd->busy_in_flight_queues); +- bfqd->busy_in_flight_queues--; +- if (bfq_bfqq_constantly_seeky(bfqq)) { +- BUG_ON(!bfqd-> +- const_seeky_busy_in_flight_queues); +- bfqd->const_seeky_busy_in_flight_queues--; +- } +- } + } + +- if (sync) { +- bfqd->sync_flight--; +- RQ_BIC(rq)->ttime.last_end_request = jiffies; +- } ++ RQ_BIC(rq)->ttime.last_end_request = jiffies; + + /* +- * If we are waiting to discover whether the request pattern of the +- * task associated with the queue is actually isochronous, and +- * both requisites for this condition to hold are satisfied, then +- * compute soft_rt_next_start (see the comments to the function +- * bfq_bfqq_softrt_next_start()). ++ * If we are waiting to discover whether the request pattern ++ * of the task associated with the queue is actually ++ * isochronous, and both requisites for this condition to hold ++ * are now satisfied, then compute soft_rt_next_start (see the ++ * comments on the function bfq_bfqq_softrt_next_start()). We ++ * schedule this delayed check when bfqq expires, if it still ++ * has in-flight requests. + */ + if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) +@@ -3608,10 +4049,7 @@ static void bfq_completed_request(struct request_queue *q, struct request *rq) + * or if we want to idle in case it has no pending requests. + */ + if (bfqd->in_service_queue == bfqq) { +- if (bfq_bfqq_budget_new(bfqq)) +- bfq_set_budget_timeout(bfqd); +- +- if (bfq_bfqq_must_idle(bfqq)) { ++ if (bfqq->dispatched == 0 && bfq_bfqq_must_idle(bfqq)) { + bfq_arm_slice_timer(bfqd); + goto out; + } else if (bfq_may_expire_for_budg_timeout(bfqq)) +@@ -3682,14 +4120,14 @@ static void bfq_put_request(struct request *rq) + rq->elv.priv[1] = NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d", +- bfqq, atomic_read(&bfqq->ref)); ++ bfqq, bfqq->ref); + bfq_put_queue(bfqq); + } + } + + /* + * Returns NULL if a new bfqq should be allocated, or the old bfqq if this +- * was the last process referring to said bfqq. ++ * was the last process referring to that bfqq. + */ + static struct bfq_queue * + bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) +@@ -3727,11 +4165,8 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, + unsigned long flags; + bool split = false; + +- might_sleep_if(gfpflags_allow_blocking(gfp_mask)); +- +- bfq_check_ioprio_change(bic, bio); +- + spin_lock_irqsave(q->queue_lock, flags); ++ bfq_check_ioprio_change(bic, bio); + + if (!bic) + goto queue_fail; +@@ -3741,23 +4176,47 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, + new_queue: + bfqq = bic_to_bfqq(bic, is_sync); + if (!bfqq || bfqq == &bfqd->oom_bfqq) { +- bfqq = bfq_get_queue(bfqd, bio, is_sync, bic, gfp_mask); ++ if (bfqq) ++ bfq_put_queue(bfqq); ++ bfqq = bfq_get_queue(bfqd, bio, is_sync, bic); ++ BUG_ON(!hlist_unhashed(&bfqq->burst_list_node)); ++ + bic_set_bfqq(bic, bfqq, is_sync); + if (split && is_sync) { ++ bfq_log_bfqq(bfqd, bfqq, ++ "set_request: was_in_list %d " ++ "was_in_large_burst %d " ++ "large burst in progress %d", ++ bic->was_in_burst_list, ++ bic->saved_in_large_burst, ++ bfqd->large_burst); ++ + if ((bic->was_in_burst_list && bfqd->large_burst) || +- bic->saved_in_large_burst) ++ bic->saved_in_large_burst) { ++ bfq_log_bfqq(bfqd, bfqq, ++ "set_request: marking in " ++ "large burst"); + bfq_mark_bfqq_in_large_burst(bfqq); +- else { +- bfq_clear_bfqq_in_large_burst(bfqq); +- if (bic->was_in_burst_list) +- hlist_add_head(&bfqq->burst_list_node, +- &bfqd->burst_list); ++ } else { ++ bfq_log_bfqq(bfqd, bfqq, ++ "set_request: clearing in " ++ "large burst"); ++ bfq_clear_bfqq_in_large_burst(bfqq); ++ if (bic->was_in_burst_list) ++ hlist_add_head(&bfqq->burst_list_node, ++ &bfqd->burst_list); + } ++ bfqq->split_time = jiffies; + } + } else { + /* If the queue was seeky for too long, break it apart. */ + if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { + bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); ++ ++ /* Update bic before losing reference to bfqq */ ++ if (bfq_bfqq_in_large_burst(bfqq)) ++ bic->saved_in_large_burst = true; ++ + bfqq = bfq_split_bfqq(bic, bfqq); + split = true; + if (!bfqq) +@@ -3766,9 +4225,8 @@ new_queue: + } + + bfqq->allocated[rw]++; +- atomic_inc(&bfqq->ref); +- bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, +- atomic_read(&bfqq->ref)); ++ bfqq->ref++; ++ bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, bfqq->ref); + + rq->elv.priv[0] = bic; + rq->elv.priv[1] = bfqq; +@@ -3783,7 +4241,6 @@ new_queue: + if (likely(bfqq != &bfqd->oom_bfqq) && bfqq_process_refs(bfqq) == 1) { + bfqq->bic = bic; + if (split) { +- bfq_mark_bfqq_just_split(bfqq); + /* + * If the queue has just been split from a shared + * queue, restore the idle window and the possible +@@ -3793,6 +4250,9 @@ new_queue: + } + } + ++ if (unlikely(bfq_bfqq_just_created(bfqq))) ++ bfq_handle_burst(bfqd, bfqq); ++ + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +@@ -3872,6 +4332,7 @@ static void bfq_shutdown_timer_wq(struct bfq_data *bfqd) + cancel_work_sync(&bfqd->unplug_work); + } + ++#ifdef CONFIG_BFQ_GROUP_IOSCHED + static void __bfq_put_async_bfqq(struct bfq_data *bfqd, + struct bfq_queue **bfqq_ptr) + { +@@ -3880,9 +4341,9 @@ static void __bfq_put_async_bfqq(struct bfq_data *bfqd, + + bfq_log(bfqd, "put_async_bfqq: %p", bfqq); + if (bfqq) { +- bfq_bfqq_move(bfqd, bfqq, &bfqq->entity, root_group); ++ bfq_bfqq_move(bfqd, bfqq, root_group); + bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d", +- bfqq, atomic_read(&bfqq->ref)); ++ bfqq, bfqq->ref); + bfq_put_queue(bfqq); + *bfqq_ptr = NULL; + } +@@ -3904,6 +4365,7 @@ static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) + + __bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq); + } ++#endif + + static void bfq_exit_queue(struct elevator_queue *e) + { +@@ -3923,8 +4385,6 @@ static void bfq_exit_queue(struct elevator_queue *e) + + bfq_shutdown_timer_wq(bfqd); + +- synchronize_rcu(); +- + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + #ifdef CONFIG_BFQ_GROUP_IOSCHED +@@ -3973,11 +4433,14 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) + * will not attempt to free it. + */ + bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, NULL, 1, 0); +- atomic_inc(&bfqd->oom_bfqq.ref); ++ bfqd->oom_bfqq.ref++; + bfqd->oom_bfqq.new_ioprio = BFQ_DEFAULT_QUEUE_IOPRIO; + bfqd->oom_bfqq.new_ioprio_class = IOPRIO_CLASS_BE; + bfqd->oom_bfqq.entity.new_weight = + bfq_ioprio_to_weight(bfqd->oom_bfqq.new_ioprio); ++ ++ /* oom_bfqq does not participate to bursts */ ++ bfq_clear_bfqq_just_created(&bfqd->oom_bfqq); + /* + * Trigger weight initialization, according to ioprio, at the + * oom_bfqq's first activation. The oom_bfqq's ioprio and ioprio +@@ -3996,9 +4459,6 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) + goto out_free; + bfq_init_root_group(bfqd->root_group, bfqd); + bfq_init_entity(&bfqd->oom_bfqq.entity, bfqd->root_group); +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- bfqd->active_numerous_groups = 0; +-#endif + + init_timer(&bfqd->idle_slice_timer); + bfqd->idle_slice_timer.function = bfq_idle_slice_timer; +@@ -4023,20 +4483,19 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) + bfqd->bfq_back_penalty = bfq_back_penalty; + bfqd->bfq_slice_idle = bfq_slice_idle; + bfqd->bfq_class_idle_last_service = 0; +- bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; +- bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; +- bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; ++ bfqd->bfq_timeout = bfq_timeout; + +- bfqd->bfq_coop_thresh = 2; +- bfqd->bfq_failed_cooperations = 7000; + bfqd->bfq_requests_within_timer = 120; + +- bfqd->bfq_large_burst_thresh = 11; +- bfqd->bfq_burst_interval = msecs_to_jiffies(500); ++ bfqd->bfq_large_burst_thresh = 8; ++ bfqd->bfq_burst_interval = msecs_to_jiffies(180); + + bfqd->low_latency = true; + +- bfqd->bfq_wr_coeff = 20; ++ /* ++ * Trade-off between responsiveness and fairness. ++ */ ++ bfqd->bfq_wr_coeff = 30; + bfqd->bfq_wr_rt_max_time = msecs_to_jiffies(300); + bfqd->bfq_wr_max_time = 0; + bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000); +@@ -4048,16 +4507,15 @@ static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) + * video. + */ + bfqd->wr_busy_queues = 0; +- bfqd->busy_in_flight_queues = 0; +- bfqd->const_seeky_busy_in_flight_queues = 0; + + /* +- * Begin by assuming, optimistically, that the device peak rate is +- * equal to the highest reference rate. ++ * Begin by assuming, optimistically, that the device is a ++ * high-speed one, and that its peak rate is equal to 2/3 of ++ * the highest reference rate. + */ + bfqd->RT_prod = R_fast[blk_queue_nonrot(bfqd->queue)] * + T_fast[blk_queue_nonrot(bfqd->queue)]; +- bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)]; ++ bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)] * 2 / 3; + bfqd->device_speed = BFQ_BFQD_FAST; + + return 0; +@@ -4161,10 +4619,8 @@ SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0); + SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0); + SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 1); + SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0); +-SHOW_FUNCTION(bfq_max_budget_async_rq_show, +- bfqd->bfq_max_budget_async_rq, 0); +-SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); +-SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); ++SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout, 1); ++SHOW_FUNCTION(bfq_strict_guarantees_show, bfqd->strict_guarantees, 0); + SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); + SHOW_FUNCTION(bfq_wr_coeff_show, bfqd->bfq_wr_coeff, 0); + SHOW_FUNCTION(bfq_wr_rt_max_time_show, bfqd->bfq_wr_rt_max_time, 1); +@@ -4199,10 +4655,6 @@ STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0); + STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1, + INT_MAX, 0); + STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 1); +-STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, +- 1, INT_MAX, 0); +-STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, +- INT_MAX, 1); + STORE_FUNCTION(bfq_wr_coeff_store, &bfqd->bfq_wr_coeff, 1, INT_MAX, 0); + STORE_FUNCTION(bfq_wr_max_time_store, &bfqd->bfq_wr_max_time, 0, INT_MAX, 1); + STORE_FUNCTION(bfq_wr_rt_max_time_store, &bfqd->bfq_wr_rt_max_time, 0, INT_MAX, +@@ -4224,10 +4676,8 @@ static ssize_t bfq_weights_store(struct elevator_queue *e, + + static unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) + { +- u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); +- + if (bfqd->peak_rate_samples >= BFQ_PEAK_RATE_SAMPLES) +- return bfq_calc_max_budget(bfqd->peak_rate, timeout); ++ return bfq_calc_max_budget(bfqd); + else + return bfq_default_max_budget; + } +@@ -4252,6 +4702,10 @@ static ssize_t bfq_max_budget_store(struct elevator_queue *e, + return ret; + } + ++/* ++ * Leaving this name to preserve name compatibility with cfq ++ * parameters, but this timeout is used for both sync and async. ++ */ + static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, + const char *page, size_t count) + { +@@ -4264,13 +4718,31 @@ static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, + else if (__data > INT_MAX) + __data = INT_MAX; + +- bfqd->bfq_timeout[BLK_RW_SYNC] = msecs_to_jiffies(__data); ++ bfqd->bfq_timeout = msecs_to_jiffies(__data); + if (bfqd->bfq_user_max_budget == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + + return ret; + } + ++static ssize_t bfq_strict_guarantees_store(struct elevator_queue *e, ++ const char *page, size_t count) ++{ ++ struct bfq_data *bfqd = e->elevator_data; ++ unsigned long uninitialized_var(__data); ++ int ret = bfq_var_store(&__data, (page), count); ++ ++ if (__data > 1) ++ __data = 1; ++ if (!bfqd->strict_guarantees && __data == 1 ++ && bfqd->bfq_slice_idle < msecs_to_jiffies(8)) ++ bfqd->bfq_slice_idle = msecs_to_jiffies(8); ++ ++ bfqd->strict_guarantees = __data; ++ ++ return ret; ++} ++ + static ssize_t bfq_low_latency_store(struct elevator_queue *e, + const char *page, size_t count) + { +@@ -4297,9 +4769,8 @@ static struct elv_fs_entry bfq_attrs[] = { + BFQ_ATTR(back_seek_penalty), + BFQ_ATTR(slice_idle), + BFQ_ATTR(max_budget), +- BFQ_ATTR(max_budget_async_rq), + BFQ_ATTR(timeout_sync), +- BFQ_ATTR(timeout_async), ++ BFQ_ATTR(strict_guarantees), + BFQ_ATTR(low_latency), + BFQ_ATTR(wr_coeff), + BFQ_ATTR(wr_max_time), +@@ -4342,9 +4813,28 @@ static struct elevator_type iosched_bfq = { + .elevator_owner = THIS_MODULE, + }; + ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++static struct blkcg_policy blkcg_policy_bfq = { ++ .dfl_cftypes = bfq_blkg_files, ++ .legacy_cftypes = bfq_blkcg_legacy_files, ++ ++ .cpd_alloc_fn = bfq_cpd_alloc, ++ .cpd_init_fn = bfq_cpd_init, ++ .cpd_bind_fn = bfq_cpd_init, ++ .cpd_free_fn = bfq_cpd_free, ++ ++ .pd_alloc_fn = bfq_pd_alloc, ++ .pd_init_fn = bfq_pd_init, ++ .pd_offline_fn = bfq_pd_offline, ++ .pd_free_fn = bfq_pd_free, ++ .pd_reset_stats_fn = bfq_pd_reset_stats, ++}; ++#endif ++ + static int __init bfq_init(void) + { + int ret; ++ char msg[50] = "BFQ I/O-scheduler: v8r3"; + + /* + * Can be 0 on HZ < 1000 setups. +@@ -4352,9 +4842,6 @@ static int __init bfq_init(void) + if (bfq_slice_idle == 0) + bfq_slice_idle = 1; + +- if (bfq_timeout_async == 0) +- bfq_timeout_async = 1; +- + #ifdef CONFIG_BFQ_GROUP_IOSCHED + ret = blkcg_policy_register(&blkcg_policy_bfq); + if (ret) +@@ -4370,23 +4857,34 @@ static int __init bfq_init(void) + * installed on the reference devices (see the comments before the + * definitions of the two arrays). + */ +- T_slow[0] = msecs_to_jiffies(2600); +- T_slow[1] = msecs_to_jiffies(1000); +- T_fast[0] = msecs_to_jiffies(5500); +- T_fast[1] = msecs_to_jiffies(2000); ++ T_slow[0] = msecs_to_jiffies(3500); ++ T_slow[1] = msecs_to_jiffies(1500); ++ T_fast[0] = msecs_to_jiffies(8000); ++ T_fast[1] = msecs_to_jiffies(3000); + + /* +- * Thresholds that determine the switch between speed classes (see +- * the comments before the definition of the array). ++ * Thresholds that determine the switch between speed classes ++ * (see the comments before the definition of the array ++ * device_speed_thresh). These thresholds are biased towards ++ * transitions to the fast class. This is safer than the ++ * opposite bias. In fact, a wrong transition to the slow ++ * class results in short weight-raising periods, because the ++ * speed of the device then tends to be higher that the ++ * reference peak rate. On the opposite end, a wrong ++ * transition to the fast class tends to increase ++ * weight-raising periods, because of the opposite reason. + */ +- device_speed_thresh[0] = (R_fast[0] + R_slow[0]) / 2; +- device_speed_thresh[1] = (R_fast[1] + R_slow[1]) / 2; ++ device_speed_thresh[0] = (4 * R_slow[0]) / 3; ++ device_speed_thresh[1] = (4 * R_slow[1]) / 3; + + ret = elv_register(&iosched_bfq); + if (ret) + goto err_pol_unreg; + +- pr_info("BFQ I/O-scheduler: v7r11"); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ strcat(msg, " (with cgroups support)"); ++#endif ++ pr_info("%s", msg); + + return 0; + +diff --git a/block/bfq-sched.c b/block/bfq-sched.c +index a64fec1..7d73b9d 100644 +--- a/block/bfq-sched.c ++++ b/block/bfq-sched.c +@@ -7,9 +7,11 @@ + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * +- * Copyright (C) 2010 Paolo Valente ++ * Copyright (C) 2016 Paolo Valente + */ + ++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq); ++ + #ifdef CONFIG_BFQ_GROUP_IOSCHED + #define for_each_entity(entity) \ + for (; entity ; entity = entity->parent) +@@ -22,8 +24,6 @@ static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract, + struct bfq_data *bfqd); + +-static struct bfq_group *bfqq_group(struct bfq_queue *bfqq); +- + static void bfq_update_budget(struct bfq_entity *next_in_service) + { + struct bfq_entity *bfqg_entity; +@@ -48,6 +48,7 @@ static void bfq_update_budget(struct bfq_entity *next_in_service) + static int bfq_update_next_in_service(struct bfq_sched_data *sd) + { + struct bfq_entity *next_in_service; ++ struct bfq_queue *bfqq; + + if (sd->in_service_entity) + /* will update/requeue at the end of service */ +@@ -65,14 +66,29 @@ static int bfq_update_next_in_service(struct bfq_sched_data *sd) + + if (next_in_service) + bfq_update_budget(next_in_service); ++ else ++ goto exit; + ++ bfqq = bfq_entity_to_bfqq(next_in_service); ++ if (bfqq) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "update_next_in_service: chosen this queue"); ++ else { ++ struct bfq_group *bfqg = ++ container_of(next_in_service, ++ struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "update_next_in_service: chosen this entity"); ++ } ++exit: + return 1; + } + + static void bfq_check_next_in_service(struct bfq_sched_data *sd, + struct bfq_entity *entity) + { +- BUG_ON(sd->next_in_service != entity); ++ WARN_ON(sd->next_in_service != entity); + } + #else + #define for_each_entity(entity) \ +@@ -151,20 +167,35 @@ static u64 bfq_delta(unsigned long service, unsigned long weight) + static void bfq_calc_finish(struct bfq_entity *entity, unsigned long service) + { + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); +- ++ unsigned long long start, finish, delta ; + BUG_ON(entity->weight == 0); + + entity->finish = entity->start + + bfq_delta(service, entity->weight); + ++ start = ((entity->start>>10)*1000)>>12; ++ finish = ((entity->finish>>10)*1000)>>12; ++ delta = ((bfq_delta(service, entity->weight)>>10)*1000)>>12; ++ + if (bfqq) { + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: serv %lu, w %d", + service, entity->weight); + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: start %llu, finish %llu, delta %llu", +- entity->start, entity->finish, +- bfq_delta(service, entity->weight)); ++ start, finish, delta); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ } else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "calc_finish group: serv %lu, w %d", ++ service, entity->weight); ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "calc_finish group: start %llu, finish %llu, delta %llu", ++ start, finish, delta); ++#endif + } + } + +@@ -386,8 +417,6 @@ static void bfq_active_insert(struct bfq_service_tree *st, + BUG_ON(!bfqg); + BUG_ON(!bfqd); + bfqg->active_entities++; +- if (bfqg->active_entities == 2) +- bfqd->active_numerous_groups++; + } + #endif + } +@@ -399,7 +428,7 @@ static void bfq_active_insert(struct bfq_service_tree *st, + static unsigned short bfq_ioprio_to_weight(int ioprio) + { + BUG_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR); +- return IOPRIO_BE_NR * BFQ_WEIGHT_CONVERSION_COEFF - ioprio; ++ return (IOPRIO_BE_NR - ioprio) * BFQ_WEIGHT_CONVERSION_COEFF ; + } + + /** +@@ -422,9 +451,9 @@ static void bfq_get_entity(struct bfq_entity *entity) + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + if (bfqq) { +- atomic_inc(&bfqq->ref); ++ bfqq->ref++; + bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d", +- bfqq, atomic_read(&bfqq->ref)); ++ bfqq, bfqq->ref); + } + } + +@@ -499,10 +528,6 @@ static void bfq_active_extract(struct bfq_service_tree *st, + BUG_ON(!bfqd); + BUG_ON(!bfqg->active_entities); + bfqg->active_entities--; +- if (bfqg->active_entities == 1) { +- BUG_ON(!bfqd->active_numerous_groups); +- bfqd->active_numerous_groups--; +- } + } + #endif + } +@@ -552,7 +577,7 @@ static void bfq_forget_entity(struct bfq_service_tree *st, + if (bfqq) { + sd = entity->sched_data; + bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity: %p %d", +- bfqq, atomic_read(&bfqq->ref)); ++ bfqq, bfqq->ref); + bfq_put_queue(bfqq); + } + } +@@ -602,7 +627,7 @@ __bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + + if (entity->prio_changed) { + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); +- unsigned short prev_weight, new_weight; ++ unsigned int prev_weight, new_weight; + struct bfq_data *bfqd = NULL; + struct rb_root *root; + #ifdef CONFIG_BFQ_GROUP_IOSCHED +@@ -628,12 +653,14 @@ __bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + if (entity->new_weight != entity->orig_weight) { + if (entity->new_weight < BFQ_MIN_WEIGHT || + entity->new_weight > BFQ_MAX_WEIGHT) { +- printk(KERN_CRIT "update_weight_prio: " +- "new_weight %d\n", ++ pr_crit("update_weight_prio: new_weight %d\n", + entity->new_weight); +- BUG(); ++ if (entity->new_weight < BFQ_MIN_WEIGHT) ++ entity->new_weight = BFQ_MIN_WEIGHT; ++ else ++ entity->new_weight = BFQ_MAX_WEIGHT; + } +- entity->orig_weight = entity->new_weight; ++ entity->orig_weight = entity->new_weight; + if (bfqq) + bfqq->ioprio = + bfq_weight_to_ioprio(entity->orig_weight); +@@ -662,6 +689,13 @@ __bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + * associated with its new weight. + */ + if (prev_weight != new_weight) { ++ if (bfqq) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "weight changed %d %d(%d %d)", ++ prev_weight, new_weight, ++ entity->orig_weight, ++ bfqq->wr_coeff); ++ + root = bfqq ? &bfqd->queue_weights_tree : + &bfqd->group_weights_tree; + bfq_weights_tree_remove(bfqd, entity, root); +@@ -708,7 +742,7 @@ static void bfq_bfqq_served(struct bfq_queue *bfqq, int served) + st = bfq_entity_service_tree(entity); + + entity->service += served; +- BUG_ON(entity->service > entity->budget); ++ + BUG_ON(st->wsum == 0); + + st->vtime += bfq_delta(served, st->wsum); +@@ -717,31 +751,69 @@ static void bfq_bfqq_served(struct bfq_queue *bfqq, int served) + #ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_set_start_empty_time(bfqq_group(bfqq)); + #endif +- bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %d secs", served); ++ st = bfq_entity_service_tree(&bfqq->entity); ++ bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %d secs, vtime %llu on %p", ++ served, ((st->vtime>>10)*1000)>>12, st); + } + + /** +- * bfq_bfqq_charge_full_budget - set the service to the entity budget. ++ * bfq_bfqq_charge_time - charge an amount of service equivalent to the length ++ * of the time interval during which bfqq has been in ++ * service. ++ * @bfqd: the device + * @bfqq: the queue that needs a service update. ++ * @time_ms: the amount of time during which the queue has received service ++ * ++ * If a queue does not consume its budget fast enough, then providing ++ * the queue with service fairness may impair throughput, more or less ++ * severely. For this reason, queues that consume their budget slowly ++ * are provided with time fairness instead of service fairness. This ++ * goal is achieved through the BFQ scheduling engine, even if such an ++ * engine works in the service, and not in the time domain. The trick ++ * is charging these queues with an inflated amount of service, equal ++ * to the amount of service that they would have received during their ++ * service slot if they had been fast, i.e., if their requests had ++ * been dispatched at a rate equal to the estimated peak rate. + * +- * When it's not possible to be fair in the service domain, because +- * a queue is not consuming its budget fast enough (the meaning of +- * fast depends on the timeout parameter), we charge it a full +- * budget. In this way we should obtain a sort of time-domain +- * fairness among all the seeky/slow queues. ++ * It is worth noting that time fairness can cause important ++ * distortions in terms of bandwidth distribution, on devices with ++ * internal queueing. The reason is that I/O requests dispatched ++ * during the service slot of a queue may be served after that service ++ * slot is finished, and may have a total processing time loosely ++ * correlated with the duration of the service slot. This is ++ * especially true for short service slots. + */ +-static void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) ++static void bfq_bfqq_charge_time(struct bfq_data *bfqd, struct bfq_queue *bfqq, ++ unsigned long time_ms) + { + struct bfq_entity *entity = &bfqq->entity; ++ int tot_serv_to_charge = entity->service; ++ unsigned int timeout_ms = jiffies_to_msecs(bfq_timeout); ++ ++ if (time_ms > 0 && time_ms < timeout_ms) ++ tot_serv_to_charge = ++ (bfqd->bfq_max_budget * time_ms) / timeout_ms; ++ ++ if (tot_serv_to_charge < entity->service) ++ tot_serv_to_charge = entity->service; + +- bfq_log_bfqq(bfqq->bfqd, bfqq, "charge_full_budget"); ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "charge_time: %lu/%u ms, %d/%d/%d sectors", ++ time_ms, timeout_ms, entity->service, ++ tot_serv_to_charge, entity->budget); + +- bfq_bfqq_served(bfqq, entity->budget - entity->service); ++ /* Increase budget to avoid inconsistencies */ ++ if (tot_serv_to_charge > entity->budget) ++ entity->budget = tot_serv_to_charge; ++ ++ bfq_bfqq_served(bfqq, ++ max_t(int, 0, tot_serv_to_charge - entity->service)); + } + + /** + * __bfq_activate_entity - activate an entity. + * @entity: the entity being activated. ++ * @non_blocking_wait_rq: true if this entity was waiting for a request + * + * Called whenever an entity is activated, i.e., it is not active and one + * of its children receives a new request, or has to be reactivated due to +@@ -749,11 +821,16 @@ static void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) + * service received if @entity is active) of the queue to calculate its + * timestamps. + */ +-static void __bfq_activate_entity(struct bfq_entity *entity) ++static void __bfq_activate_entity(struct bfq_entity *entity, ++ bool non_blocking_wait_rq) + { + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ bool backshifted = false; + ++ BUG_ON(!sd); ++ BUG_ON(!st); + if (entity == sd->in_service_entity) { + BUG_ON(entity->tree); + /* +@@ -771,45 +848,133 @@ static void __bfq_activate_entity(struct bfq_entity *entity) + * old start time. + */ + bfq_active_extract(st, entity); +- } else if (entity->tree == &st->idle) { +- /* +- * Must be on the idle tree, bfq_idle_extract() will +- * check for that. +- */ +- bfq_idle_extract(st, entity); +- entity->start = bfq_gt(st->vtime, entity->finish) ? +- st->vtime : entity->finish; + } else { +- /* +- * The finish time of the entity may be invalid, and +- * it is in the past for sure, otherwise the queue +- * would have been on the idle tree. +- */ +- entity->start = st->vtime; +- st->wsum += entity->weight; +- bfq_get_entity(entity); ++ unsigned long long min_vstart; + +- BUG_ON(entity->on_st); +- entity->on_st = 1; ++ /* See comments on bfq_fqq_update_budg_for_activation */ ++ if (non_blocking_wait_rq && bfq_gt(st->vtime, entity->finish)) { ++ backshifted = true; ++ min_vstart = entity->finish; ++ } else ++ min_vstart = st->vtime; ++ ++ if (entity->tree == &st->idle) { ++ /* ++ * Must be on the idle tree, bfq_idle_extract() will ++ * check for that. ++ */ ++ bfq_idle_extract(st, entity); ++ entity->start = bfq_gt(min_vstart, entity->finish) ? ++ min_vstart : entity->finish; ++ } else { ++ /* ++ * The finish time of the entity may be invalid, and ++ * it is in the past for sure, otherwise the queue ++ * would have been on the idle tree. ++ */ ++ entity->start = min_vstart; ++ st->wsum += entity->weight; ++ bfq_get_entity(entity); ++ ++ BUG_ON(entity->on_st); ++ entity->on_st = 1; ++ } + } + + st = __bfq_entity_update_weight_prio(st, entity); + bfq_calc_finish(entity, entity->budget); ++ ++ /* ++ * If some queues enjoy backshifting for a while, then their ++ * (virtual) finish timestamps may happen to become lower and ++ * lower than the system virtual time. In particular, if ++ * these queues often happen to be idle for short time ++ * periods, and during such time periods other queues with ++ * higher timestamps happen to be busy, then the backshifted ++ * timestamps of the former queues can become much lower than ++ * the system virtual time. In fact, to serve the queues with ++ * higher timestamps while the ones with lower timestamps are ++ * idle, the system virtual time may be pushed-up to much ++ * higher values than the finish timestamps of the idle ++ * queues. As a consequence, the finish timestamps of all new ++ * or newly activated queues may end up being much larger than ++ * those of lucky queues with backshifted timestamps. The ++ * latter queues may then monopolize the device for a lot of ++ * time. This would simply break service guarantees. ++ * ++ * To reduce this problem, push up a little bit the ++ * backshifted timestamps of the queue associated with this ++ * entity (only a queue can happen to have the backshifted ++ * flag set): just enough to let the finish timestamp of the ++ * queue be equal to the current value of the system virtual ++ * time. This may introduce a little unfairness among queues ++ * with backshifted timestamps, but it does not break ++ * worst-case fairness guarantees. ++ * ++ * As a special case, if bfqq is weight-raised, push up ++ * timestamps much less, to keep very low the probability that ++ * this push up causes the backshifted finish timestamps of ++ * weight-raised queues to become higher than the backshifted ++ * finish timestamps of non weight-raised queues. ++ */ ++ if (backshifted && bfq_gt(st->vtime, entity->finish)) { ++ unsigned long delta = st->vtime - entity->finish; ++ ++ if (bfqq) ++ delta /= bfqq->wr_coeff; ++ ++ entity->start += delta; ++ entity->finish += delta; ++ ++ if (bfqq) { ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "__activate_entity: new queue finish %llu", ++ ((entity->finish>>10)*1000)>>12); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ } else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "__activate_entity: new group finish %llu", ++ ((entity->finish>>10)*1000)>>12); ++#endif ++ } ++ } ++ + bfq_active_insert(st, entity); ++ ++ if (bfqq) { ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "__activate_entity: queue %seligible in st %p", ++ entity->start <= st->vtime ? "" : "non ", st); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ } else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "__activate_entity: group %seligible in st %p", ++ entity->start <= st->vtime ? "" : "non ", st); ++#endif ++ } + } + + /** + * bfq_activate_entity - activate an entity and its ancestors if necessary. + * @entity: the entity to activate. ++ * @non_blocking_wait_rq: true if this entity was waiting for a request + * + * Activate @entity and all the entities on the path from it to the root. + */ +-static void bfq_activate_entity(struct bfq_entity *entity) ++static void bfq_activate_entity(struct bfq_entity *entity, ++ bool non_blocking_wait_rq) + { + struct bfq_sched_data *sd; + + for_each_entity(entity) { +- __bfq_activate_entity(entity); ++ BUG_ON(!entity); ++ __bfq_activate_entity(entity, non_blocking_wait_rq); + + sd = entity->sched_data; + if (!bfq_update_next_in_service(sd)) +@@ -890,23 +1055,24 @@ static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) + + if (!__bfq_deactivate_entity(entity, requeue)) + /* +- * The parent entity is still backlogged, and +- * we don't need to update it as it is still +- * in service. ++ * next_in_service has not been changed, so ++ * no upwards update is needed + */ + break; + + if (sd->next_in_service) + /* +- * The parent entity is still backlogged and +- * the budgets on the path towards the root +- * need to be updated. ++ * The parent entity is still backlogged, ++ * because next_in_service is not NULL, and ++ * next_in_service has been updated (see ++ * comment on the body of the above if): ++ * upwards update of the schedule is needed. + */ + goto update; + + /* +- * If we reach there the parent is no more backlogged and +- * we want to propagate the dequeue upwards. ++ * If we get here, then the parent is no more backlogged and ++ * we want to propagate the deactivation upwards. + */ + requeue = 1; + } +@@ -916,9 +1082,23 @@ static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) + update: + entity = parent; + for_each_entity(entity) { +- __bfq_activate_entity(entity); ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ __bfq_activate_entity(entity, false); + + sd = entity->sched_data; ++ if (bfqq) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "invoking udpdate_next for this queue"); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, ++ struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "invoking udpdate_next for this entity"); ++ } ++#endif + if (!bfq_update_next_in_service(sd)) + break; + } +@@ -997,10 +1177,11 @@ left: + * Update the virtual time in @st and return the first eligible entity + * it contains. + */ +-static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st, +- bool force) ++static struct bfq_entity * ++__bfq_lookup_next_entity(struct bfq_service_tree *st, bool force) + { + struct bfq_entity *entity, *new_next_in_service = NULL; ++ struct bfq_queue *bfqq; + + if (RB_EMPTY_ROOT(&st->active)) + return NULL; +@@ -1009,6 +1190,24 @@ static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st, + entity = bfq_first_active_entity(st); + BUG_ON(bfq_gt(entity->start, st->vtime)); + ++ bfqq = bfq_entity_to_bfqq(entity); ++ if (bfqq) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "__lookup_next: start %llu vtime %llu st %p", ++ ((entity->start>>10)*1000)>>12, ++ ((st->vtime>>10)*1000)>>12, st); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "__lookup_next: start %llu vtime %llu st %p", ++ ((entity->start>>10)*1000)>>12, ++ ((st->vtime>>10)*1000)>>12, st); ++ } ++#endif ++ + /* + * If the chosen entity does not match with the sched_data's + * next_in_service and we are forcedly serving the IDLE priority +@@ -1045,10 +1244,28 @@ static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + BUG_ON(sd->in_service_entity); + + if (bfqd && +- jiffies - bfqd->bfq_class_idle_last_service > BFQ_CL_IDLE_TIMEOUT) { ++ jiffies - bfqd->bfq_class_idle_last_service > ++ BFQ_CL_IDLE_TIMEOUT) { + entity = __bfq_lookup_next_entity(st + BFQ_IOPRIO_CLASSES - 1, + true); + if (entity) { ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ if (bfqq) ++ bfq_log_bfqq(bfqd, bfqq, ++ "idle chosen from st %p %d", ++ st + BFQ_IOPRIO_CLASSES - 1, ++ BFQ_IOPRIO_CLASSES - 1) ; ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg(bfqd, bfqg, ++ "idle chosen from st %p %d", ++ st + BFQ_IOPRIO_CLASSES - 1, ++ BFQ_IOPRIO_CLASSES - 1) ; ++ } ++#endif + i = BFQ_IOPRIO_CLASSES - 1; + bfqd->bfq_class_idle_last_service = jiffies; + sd->next_in_service = entity; +@@ -1057,6 +1274,24 @@ static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + for (; i < BFQ_IOPRIO_CLASSES; i++) { + entity = __bfq_lookup_next_entity(st + i, false); + if (entity) { ++ if (bfqd != NULL) { ++ struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); ++ if (bfqq) ++ bfq_log_bfqq(bfqd, bfqq, ++ "chosen from st %p %d", ++ st + i, i) ; ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg(bfqd, bfqg, ++ "chosen from st %p %d", ++ st + i, i) ; ++ } ++#endif ++ } ++ + if (extract) { + bfq_check_next_in_service(sd, entity); + bfq_active_extract(st + i, entity); +@@ -1070,6 +1305,13 @@ static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + return entity; + } + ++static bool next_queue_may_preempt(struct bfq_data *bfqd) ++{ ++ struct bfq_sched_data *sd = &bfqd->root_group->sched_data; ++ ++ return sd->next_in_service != sd->in_service_entity; ++} ++ + /* + * Get next queue for service. + */ +@@ -1086,7 +1328,36 @@ static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) + + sd = &bfqd->root_group->sched_data; + for (; sd ; sd = entity->my_sched_data) { ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ if (entity) { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg(bfqd, bfqg, ++ "get_next_queue: lookup in this group"); ++ } else ++ bfq_log_bfqg(bfqd, bfqd->root_group, ++ "get_next_queue: lookup in root group"); ++#endif ++ + entity = bfq_lookup_next_entity(sd, 1, bfqd); ++ ++ bfqq = bfq_entity_to_bfqq(entity); ++ if (bfqq) ++ bfq_log_bfqq(bfqd, bfqq, ++ "get_next_queue: this queue, finish %llu", ++ (((entity->finish>>10)*1000)>>10)>>2); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg(bfqd, bfqg, ++ "get_next_queue: this entity, finish %llu", ++ (((entity->finish>>10)*1000)>>10)>>2); ++ } ++#endif ++ + BUG_ON(!entity); + entity->service = 0; + } +@@ -1113,9 +1384,7 @@ static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + { + struct bfq_entity *entity = &bfqq->entity; + +- if (bfqq == bfqd->in_service_queue) +- __bfq_bfqd_reset_in_service(bfqd); +- ++ BUG_ON(bfqq == bfqd->in_service_queue); + bfq_deactivate_entity(entity, requeue); + } + +@@ -1123,12 +1392,11 @@ static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) + { + struct bfq_entity *entity = &bfqq->entity; + +- bfq_activate_entity(entity); ++ bfq_activate_entity(entity, bfq_bfqq_non_blocking_wait_rq(bfqq)); ++ bfq_clear_bfqq_non_blocking_wait_rq(bfqq); + } + +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + static void bfqg_stats_update_dequeue(struct bfq_group *bfqg); +-#endif + + /* + * Called when the bfqq no longer has requests pending, remove it from +@@ -1139,6 +1407,7 @@ static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, + { + BUG_ON(!bfq_bfqq_busy(bfqq)); + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); ++ BUG_ON(bfqq == bfqd->in_service_queue); + + bfq_log_bfqq(bfqd, bfqq, "del from busy"); + +@@ -1147,27 +1416,20 @@ static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, + BUG_ON(bfqd->busy_queues == 0); + bfqd->busy_queues--; + +- if (!bfqq->dispatched) { ++ if (!bfqq->dispatched) + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); +- if (!blk_queue_nonrot(bfqd->queue)) { +- BUG_ON(!bfqd->busy_in_flight_queues); +- bfqd->busy_in_flight_queues--; +- if (bfq_bfqq_constantly_seeky(bfqq)) { +- BUG_ON(!bfqd-> +- const_seeky_busy_in_flight_queues); +- bfqd->const_seeky_busy_in_flight_queues--; +- } +- } +- } ++ + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues--; + +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + bfqg_stats_update_dequeue(bfqq_group(bfqq)); +-#endif + ++ BUG_ON(bfqq->entity.budget < 0); ++ + bfq_deactivate_bfqq(bfqd, bfqq, requeue); ++ ++ BUG_ON(bfqq->entity.budget < 0); + } + + /* +@@ -1185,16 +1447,11 @@ static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) + bfq_mark_bfqq_busy(bfqq); + bfqd->busy_queues++; + +- if (!bfqq->dispatched) { ++ if (!bfqq->dispatched) + if (bfqq->wr_coeff == 1) + bfq_weights_tree_add(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); +- if (!blk_queue_nonrot(bfqd->queue)) { +- bfqd->busy_in_flight_queues++; +- if (bfq_bfqq_constantly_seeky(bfqq)) +- bfqd->const_seeky_busy_in_flight_queues++; +- } +- } ++ + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues++; + } +diff --git a/block/bfq.h b/block/bfq.h +index f73c942..49d28b9 100644 +--- a/block/bfq.h ++++ b/block/bfq.h +@@ -1,5 +1,5 @@ + /* +- * BFQ-v7r11 for 4.5.0: data structures and common functions prototypes. ++ * BFQ-v8r3 for 4.7.0: data structures and common functions prototypes. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe +@@ -28,20 +28,21 @@ + + #define BFQ_DEFAULT_QUEUE_IOPRIO 4 + +-#define BFQ_DEFAULT_GRP_WEIGHT 10 ++#define BFQ_WEIGHT_LEGACY_DFL 100 + #define BFQ_DEFAULT_GRP_IOPRIO 0 + #define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE + ++/* ++ * Soft real-time applications are extremely more latency sensitive ++ * than interactive ones. Over-raise the weight of the former to ++ * privilege them against the latter. ++ */ ++#define BFQ_SOFTRT_WEIGHT_FACTOR 100 ++ + struct bfq_entity; + + /** + * struct bfq_service_tree - per ioprio_class service tree. +- * @active: tree for active entities (i.e., those backlogged). +- * @idle: tree for idle entities (i.e., those not backlogged, with V <= F_i). +- * @first_idle: idle entity with minimum F_i. +- * @last_idle: idle entity with maximum F_i. +- * @vtime: scheduler virtual time. +- * @wsum: scheduler weight sum; active and idle entities contribute to it. + * + * Each service tree represents a B-WF2Q+ scheduler on its own. Each + * ioprio_class has its own independent scheduler, and so its own +@@ -49,27 +50,28 @@ struct bfq_entity; + * of the containing bfqd. + */ + struct bfq_service_tree { ++ /* tree for active entities (i.e., those backlogged) */ + struct rb_root active; ++ /* tree for idle entities (i.e., not backlogged, with V <= F_i)*/ + struct rb_root idle; + +- struct bfq_entity *first_idle; +- struct bfq_entity *last_idle; ++ struct bfq_entity *first_idle; /* idle entity with minimum F_i */ ++ struct bfq_entity *last_idle; /* idle entity with maximum F_i */ + +- u64 vtime; ++ u64 vtime; /* scheduler virtual time */ ++ /* scheduler weight sum; active and idle entities contribute to it */ + unsigned long wsum; + }; + + /** + * struct bfq_sched_data - multi-class scheduler. +- * @in_service_entity: entity in service. +- * @next_in_service: head-of-the-line entity in the scheduler. +- * @service_tree: array of service trees, one per ioprio_class. + * + * bfq_sched_data is the basic scheduler queue. It supports three +- * ioprio_classes, and can be used either as a toplevel queue or as +- * an intermediate queue on a hierarchical setup. +- * @next_in_service points to the active entity of the sched_data +- * service trees that will be scheduled next. ++ * ioprio_classes, and can be used either as a toplevel queue or as an ++ * intermediate queue on a hierarchical setup. @next_in_service ++ * points to the active entity of the sched_data service trees that ++ * will be scheduled next. It is used to reduce the number of steps ++ * needed for each hierarchical-schedule update. + * + * The supported ioprio_classes are the same as in CFQ, in descending + * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE. +@@ -79,48 +81,29 @@ struct bfq_service_tree { + * All the fields are protected by the queue lock of the containing bfqd. + */ + struct bfq_sched_data { +- struct bfq_entity *in_service_entity; ++ struct bfq_entity *in_service_entity; /* entity in service */ ++ /* head-of-the-line entity in the scheduler (see comments above) */ + struct bfq_entity *next_in_service; ++ /* array of service trees, one per ioprio_class */ + struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES]; + }; + + /** + * struct bfq_weight_counter - counter of the number of all active entities + * with a given weight. +- * @weight: weight of the entities that this counter refers to. +- * @num_active: number of active entities with this weight. +- * @weights_node: weights tree member (see bfq_data's @queue_weights_tree +- * and @group_weights_tree). + */ + struct bfq_weight_counter { +- short int weight; +- unsigned int num_active; ++ unsigned int weight; /* weight of the entities this counter refers to */ ++ unsigned int num_active; /* nr of active entities with this weight */ ++ /* ++ * Weights tree member (see bfq_data's @queue_weights_tree and ++ * @group_weights_tree) ++ */ + struct rb_node weights_node; + }; + + /** + * struct bfq_entity - schedulable entity. +- * @rb_node: service_tree member. +- * @weight_counter: pointer to the weight counter associated with this entity. +- * @on_st: flag, true if the entity is on a tree (either the active or +- * the idle one of its service_tree). +- * @finish: B-WF2Q+ finish timestamp (aka F_i). +- * @start: B-WF2Q+ start timestamp (aka S_i). +- * @tree: tree the entity is enqueued into; %NULL if not on a tree. +- * @min_start: minimum start time of the (active) subtree rooted at +- * this entity; used for O(log N) lookups into active trees. +- * @service: service received during the last round of service. +- * @budget: budget used to calculate F_i; F_i = S_i + @budget / @weight. +- * @weight: weight of the queue +- * @parent: parent entity, for hierarchical scheduling. +- * @my_sched_data: for non-leaf nodes in the cgroup hierarchy, the +- * associated scheduler queue, %NULL on leaf nodes. +- * @sched_data: the scheduler queue this entity belongs to. +- * @ioprio: the ioprio in use. +- * @new_weight: when a weight change is requested, the new weight value. +- * @orig_weight: original weight, used to implement weight boosting +- * @prio_changed: flag, true when the user requested a weight, ioprio or +- * ioprio_class change. + * + * A bfq_entity is used to represent either a bfq_queue (leaf node in the + * cgroup hierarchy) or a bfq_group into the upper level scheduler. Each +@@ -147,27 +130,52 @@ struct bfq_weight_counter { + * containing bfqd. + */ + struct bfq_entity { +- struct rb_node rb_node; ++ struct rb_node rb_node; /* service_tree member */ ++ /* pointer to the weight counter associated with this entity */ + struct bfq_weight_counter *weight_counter; + ++ /* ++ * flag, true if the entity is on a tree (either the active or ++ * the idle one of its service_tree). ++ */ + int on_st; + +- u64 finish; +- u64 start; ++ u64 finish; /* B-WF2Q+ finish timestamp (aka F_i) */ ++ u64 start; /* B-WF2Q+ start timestamp (aka S_i) */ + ++ /* tree the entity is enqueued into; %NULL if not on a tree */ + struct rb_root *tree; + ++ /* ++ * minimum start time of the (active) subtree rooted at this ++ * entity; used for O(log N) lookups into active trees ++ */ + u64 min_start; + +- int service, budget; +- unsigned short weight, new_weight; +- unsigned short orig_weight; ++ /* amount of service received during the last service slot */ ++ int service; ++ ++ /* budget, used also to calculate F_i: F_i = S_i + @budget / @weight */ ++ int budget; ++ ++ unsigned int weight; /* weight of the queue */ ++ unsigned int new_weight; /* next weight if a change is in progress */ ++ ++ /* original weight, used to implement weight boosting */ ++ unsigned int orig_weight; + ++ /* parent entity, for hierarchical scheduling */ + struct bfq_entity *parent; + ++ /* ++ * For non-leaf nodes in the hierarchy, the associated ++ * scheduler queue, %NULL on leaf nodes. ++ */ + struct bfq_sched_data *my_sched_data; ++ /* the scheduler queue this entity belongs to */ + struct bfq_sched_data *sched_data; + ++ /* flag, set to request a weight, ioprio or ioprio_class change */ + int prio_changed; + }; + +@@ -175,56 +183,6 @@ struct bfq_group; + + /** + * struct bfq_queue - leaf schedulable entity. +- * @ref: reference counter. +- * @bfqd: parent bfq_data. +- * @new_ioprio: when an ioprio change is requested, the new ioprio value. +- * @ioprio_class: the ioprio_class in use. +- * @new_ioprio_class: when an ioprio_class change is requested, the new +- * ioprio_class value. +- * @new_bfqq: shared bfq_queue if queue is cooperating with +- * one or more other queues. +- * @pos_node: request-position tree member (see bfq_group's @rq_pos_tree). +- * @pos_root: request-position tree root (see bfq_group's @rq_pos_tree). +- * @sort_list: sorted list of pending requests. +- * @next_rq: if fifo isn't expired, next request to serve. +- * @queued: nr of requests queued in @sort_list. +- * @allocated: currently allocated requests. +- * @meta_pending: pending metadata requests. +- * @fifo: fifo list of requests in sort_list. +- * @entity: entity representing this queue in the scheduler. +- * @max_budget: maximum budget allowed from the feedback mechanism. +- * @budget_timeout: budget expiration (in jiffies). +- * @dispatched: number of requests on the dispatch list or inside driver. +- * @flags: status flags. +- * @bfqq_list: node for active/idle bfqq list inside our bfqd. +- * @burst_list_node: node for the device's burst list. +- * @seek_samples: number of seeks sampled +- * @seek_total: sum of the distances of the seeks sampled +- * @seek_mean: mean seek distance +- * @last_request_pos: position of the last request enqueued +- * @requests_within_timer: number of consecutive pairs of request completion +- * and arrival, such that the queue becomes idle +- * after the completion, but the next request arrives +- * within an idle time slice; used only if the queue's +- * IO_bound has been cleared. +- * @pid: pid of the process owning the queue, used for logging purposes. +- * @last_wr_start_finish: start time of the current weight-raising period if +- * the @bfq-queue is being weight-raised, otherwise +- * finish time of the last weight-raising period +- * @wr_cur_max_time: current max raising time for this queue +- * @soft_rt_next_start: minimum time instant such that, only if a new +- * request is enqueued after this time instant in an +- * idle @bfq_queue with no outstanding requests, then +- * the task associated with the queue it is deemed as +- * soft real-time (see the comments to the function +- * bfq_bfqq_softrt_next_start()) +- * @last_idle_bklogged: time of the last transition of the @bfq_queue from +- * idle to backlogged +- * @service_from_backlogged: cumulative service received from the @bfq_queue +- * since the last transition from idle to +- * backlogged +- * @bic: pointer to the bfq_io_cq owning the bfq_queue, set to %NULL if the +- * queue is shared + * + * A bfq_queue is a leaf request queue; it can be associated with an + * io_context or more, if it is async or shared between cooperating +@@ -235,117 +193,163 @@ struct bfq_group; + * All the fields are protected by the queue lock of the containing bfqd. + */ + struct bfq_queue { +- atomic_t ref; ++ /* reference counter */ ++ int ref; ++ /* parent bfq_data */ + struct bfq_data *bfqd; + +- unsigned short ioprio, new_ioprio; +- unsigned short ioprio_class, new_ioprio_class; ++ /* current ioprio and ioprio class */ ++ unsigned short ioprio, ioprio_class; ++ /* next ioprio and ioprio class if a change is in progress */ ++ unsigned short new_ioprio, new_ioprio_class; + +- /* fields for cooperating queues handling */ ++ /* ++ * Shared bfq_queue if queue is cooperating with one or more ++ * other queues. ++ */ + struct bfq_queue *new_bfqq; ++ /* request-position tree member (see bfq_group's @rq_pos_tree) */ + struct rb_node pos_node; ++ /* request-position tree root (see bfq_group's @rq_pos_tree) */ + struct rb_root *pos_root; + ++ /* sorted list of pending requests */ + struct rb_root sort_list; ++ /* if fifo isn't expired, next request to serve */ + struct request *next_rq; ++ /* number of sync and async requests queued */ + int queued[2]; ++ /* number of sync and async requests currently allocated */ + int allocated[2]; ++ /* number of pending metadata requests */ + int meta_pending; ++ /* fifo list of requests in sort_list */ + struct list_head fifo; + ++ /* entity representing this queue in the scheduler */ + struct bfq_entity entity; + ++ /* maximum budget allowed from the feedback mechanism */ + int max_budget; ++ /* budget expiration (in jiffies) */ + unsigned long budget_timeout; + ++ /* number of requests on the dispatch list or inside driver */ + int dispatched; + +- unsigned int flags; ++ unsigned int flags; /* status flags.*/ + ++ /* node for active/idle bfqq list inside parent bfqd */ + struct list_head bfqq_list; + ++ /* bit vector: a 1 for each seeky requests in history */ ++ u32 seek_history; ++ ++ /* node for the device's burst list */ + struct hlist_node burst_list_node; + +- unsigned int seek_samples; +- u64 seek_total; +- sector_t seek_mean; ++ /* position of the last request enqueued */ + sector_t last_request_pos; + ++ /* Number of consecutive pairs of request completion and ++ * arrival, such that the queue becomes idle after the ++ * completion, but the next request arrives within an idle ++ * time slice; used only if the queue's IO_bound flag has been ++ * cleared. ++ */ + unsigned int requests_within_timer; + ++ /* pid of the process owning the queue, used for logging purposes */ + pid_t pid; ++ ++ /* ++ * Pointer to the bfq_io_cq owning the bfq_queue, set to %NULL ++ * if the queue is shared. ++ */ + struct bfq_io_cq *bic; + +- /* weight-raising fields */ ++ /* current maximum weight-raising time for this queue */ + unsigned long wr_cur_max_time; ++ /* ++ * Minimum time instant such that, only if a new request is ++ * enqueued after this time instant in an idle @bfq_queue with ++ * no outstanding requests, then the task associated with the ++ * queue it is deemed as soft real-time (see the comments on ++ * the function bfq_bfqq_softrt_next_start()) ++ */ + unsigned long soft_rt_next_start; ++ /* ++ * Start time of the current weight-raising period if ++ * the @bfq-queue is being weight-raised, otherwise ++ * finish time of the last weight-raising period. ++ */ + unsigned long last_wr_start_finish; ++ /* factor by which the weight of this queue is multiplied */ + unsigned int wr_coeff; ++ /* ++ * Time of the last transition of the @bfq_queue from idle to ++ * backlogged. ++ */ + unsigned long last_idle_bklogged; ++ /* ++ * Cumulative service received from the @bfq_queue since the ++ * last transition from idle to backlogged. ++ */ + unsigned long service_from_backlogged; ++ ++ unsigned long split_time; /* time of last split */ + }; + + /** + * struct bfq_ttime - per process thinktime stats. +- * @ttime_total: total process thinktime +- * @ttime_samples: number of thinktime samples +- * @ttime_mean: average process thinktime + */ + struct bfq_ttime { +- unsigned long last_end_request; ++ unsigned long last_end_request; /* completion time of last request */ ++ ++ unsigned long ttime_total; /* total process thinktime */ ++ unsigned long ttime_samples; /* number of thinktime samples */ ++ unsigned long ttime_mean; /* average process thinktime */ + +- unsigned long ttime_total; +- unsigned long ttime_samples; +- unsigned long ttime_mean; + }; + + /** + * struct bfq_io_cq - per (request_queue, io_context) structure. +- * @icq: associated io_cq structure +- * @bfqq: array of two process queues, the sync and the async +- * @ttime: associated @bfq_ttime struct +- * @ioprio: per (request_queue, blkcg) ioprio. +- * @blkcg_id: id of the blkcg the related io_cq belongs to. +- * @wr_time_left: snapshot of the time left before weight raising ends +- * for the sync queue associated to this process; this +- * snapshot is taken to remember this value while the weight +- * raising is suspended because the queue is merged with a +- * shared queue, and is used to set @raising_cur_max_time +- * when the queue is split from the shared queue and its +- * weight is raised again +- * @saved_idle_window: same purpose as the previous field for the idle +- * window +- * @saved_IO_bound: same purpose as the previous two fields for the I/O +- * bound classification of a queue +- * @saved_in_large_burst: same purpose as the previous fields for the +- * value of the field keeping the queue's belonging +- * to a large burst +- * @was_in_burst_list: true if the queue belonged to a burst list +- * before its merge with another cooperating queue +- * @cooperations: counter of consecutive successful queue merges underwent +- * by any of the process' @bfq_queues +- * @failed_cooperations: counter of consecutive failed queue merges of any +- * of the process' @bfq_queues + */ + struct bfq_io_cq { ++ /* associated io_cq structure */ + struct io_cq icq; /* must be the first member */ ++ /* array of two process queues, the sync and the async */ + struct bfq_queue *bfqq[2]; ++ /* associated @bfq_ttime struct */ + struct bfq_ttime ttime; ++ /* per (request_queue, blkcg) ioprio */ + int ioprio; +- + #ifdef CONFIG_BFQ_GROUP_IOSCHED +- uint64_t blkcg_id; /* the current blkcg ID */ ++ uint64_t blkcg_serial_nr; /* the current blkcg serial */ + #endif + +- unsigned int wr_time_left; ++ /* ++ * Snapshot of the idle window before merging; taken to ++ * remember this value while the queue is merged, so as to be ++ * able to restore it in case of split. ++ */ + bool saved_idle_window; ++ /* ++ * Same purpose as the previous two fields for the I/O bound ++ * classification of a queue. ++ */ + bool saved_IO_bound; + ++ /* ++ * Same purpose as the previous fields for the value of the ++ * field keeping the queue's belonging to a large burst ++ */ + bool saved_in_large_burst; ++ /* ++ * True if the queue belonged to a burst list before its merge ++ * with another cooperating queue. ++ */ + bool was_in_burst_list; +- +- unsigned int cooperations; +- unsigned int failed_cooperations; + }; + + enum bfq_device_speed { +@@ -354,224 +358,216 @@ enum bfq_device_speed { + }; + + /** +- * struct bfq_data - per device data structure. +- * @queue: request queue for the managed device. +- * @root_group: root bfq_group for the device. +- * @active_numerous_groups: number of bfq_groups containing more than one +- * active @bfq_entity. +- * @queue_weights_tree: rbtree of weight counters of @bfq_queues, sorted by +- * weight. Used to keep track of whether all @bfq_queues +- * have the same weight. The tree contains one counter +- * for each distinct weight associated to some active +- * and not weight-raised @bfq_queue (see the comments to +- * the functions bfq_weights_tree_[add|remove] for +- * further details). +- * @group_weights_tree: rbtree of non-queue @bfq_entity weight counters, sorted +- * by weight. Used to keep track of whether all +- * @bfq_groups have the same weight. The tree contains +- * one counter for each distinct weight associated to +- * some active @bfq_group (see the comments to the +- * functions bfq_weights_tree_[add|remove] for further +- * details). +- * @busy_queues: number of bfq_queues containing requests (including the +- * queue in service, even if it is idling). +- * @busy_in_flight_queues: number of @bfq_queues containing pending or +- * in-flight requests, plus the @bfq_queue in +- * service, even if idle but waiting for the +- * possible arrival of its next sync request. This +- * field is updated only if the device is rotational, +- * but used only if the device is also NCQ-capable. +- * The reason why the field is updated also for non- +- * NCQ-capable rotational devices is related to the +- * fact that the value of @hw_tag may be set also +- * later than when busy_in_flight_queues may need to +- * be incremented for the first time(s). Taking also +- * this possibility into account, to avoid unbalanced +- * increments/decrements, would imply more overhead +- * than just updating busy_in_flight_queues +- * regardless of the value of @hw_tag. +- * @const_seeky_busy_in_flight_queues: number of constantly-seeky @bfq_queues +- * (that is, seeky queues that expired +- * for budget timeout at least once) +- * containing pending or in-flight +- * requests, including the in-service +- * @bfq_queue if constantly seeky. This +- * field is updated only if the device +- * is rotational, but used only if the +- * device is also NCQ-capable (see the +- * comments to @busy_in_flight_queues). +- * @wr_busy_queues: number of weight-raised busy @bfq_queues. +- * @queued: number of queued requests. +- * @rq_in_driver: number of requests dispatched and waiting for completion. +- * @sync_flight: number of sync requests in the driver. +- * @max_rq_in_driver: max number of reqs in driver in the last +- * @hw_tag_samples completed requests. +- * @hw_tag_samples: nr of samples used to calculate hw_tag. +- * @hw_tag: flag set to one if the driver is showing a queueing behavior. +- * @budgets_assigned: number of budgets assigned. +- * @idle_slice_timer: timer set when idling for the next sequential request +- * from the queue in service. +- * @unplug_work: delayed work to restart dispatching on the request queue. +- * @in_service_queue: bfq_queue in service. +- * @in_service_bic: bfq_io_cq (bic) associated with the @in_service_queue. +- * @last_position: on-disk position of the last served request. +- * @last_budget_start: beginning of the last budget. +- * @last_idling_start: beginning of the last idle slice. +- * @peak_rate: peak transfer rate observed for a budget. +- * @peak_rate_samples: number of samples used to calculate @peak_rate. +- * @bfq_max_budget: maximum budget allotted to a bfq_queue before +- * rescheduling. +- * @active_list: list of all the bfq_queues active on the device. +- * @idle_list: list of all the bfq_queues idle on the device. +- * @bfq_fifo_expire: timeout for async/sync requests; when it expires +- * requests are served in fifo order. +- * @bfq_back_penalty: weight of backward seeks wrt forward ones. +- * @bfq_back_max: maximum allowed backward seek. +- * @bfq_slice_idle: maximum idling time. +- * @bfq_user_max_budget: user-configured max budget value +- * (0 for auto-tuning). +- * @bfq_max_budget_async_rq: maximum budget (in nr of requests) allotted to +- * async queues. +- * @bfq_timeout: timeout for bfq_queues to consume their budget; used to +- * to prevent seeky queues to impose long latencies to well +- * behaved ones (this also implies that seeky queues cannot +- * receive guarantees in the service domain; after a timeout +- * they are charged for the whole allocated budget, to try +- * to preserve a behavior reasonably fair among them, but +- * without service-domain guarantees). +- * @bfq_coop_thresh: number of queue merges after which a @bfq_queue is +- * no more granted any weight-raising. +- * @bfq_failed_cooperations: number of consecutive failed cooperation +- * chances after which weight-raising is restored +- * to a queue subject to more than bfq_coop_thresh +- * queue merges. +- * @bfq_requests_within_timer: number of consecutive requests that must be +- * issued within the idle time slice to set +- * again idling to a queue which was marked as +- * non-I/O-bound (see the definition of the +- * IO_bound flag for further details). +- * @last_ins_in_burst: last time at which a queue entered the current +- * burst of queues being activated shortly after +- * each other; for more details about this and the +- * following parameters related to a burst of +- * activations, see the comments to the function +- * @bfq_handle_burst. +- * @bfq_burst_interval: reference time interval used to decide whether a +- * queue has been activated shortly after +- * @last_ins_in_burst. +- * @burst_size: number of queues in the current burst of queue activations. +- * @bfq_large_burst_thresh: maximum burst size above which the current +- * queue-activation burst is deemed as 'large'. +- * @large_burst: true if a large queue-activation burst is in progress. +- * @burst_list: head of the burst list (as for the above fields, more details +- * in the comments to the function bfq_handle_burst). +- * @low_latency: if set to true, low-latency heuristics are enabled. +- * @bfq_wr_coeff: maximum factor by which the weight of a weight-raised +- * queue is multiplied. +- * @bfq_wr_max_time: maximum duration of a weight-raising period (jiffies). +- * @bfq_wr_rt_max_time: maximum duration for soft real-time processes. +- * @bfq_wr_min_idle_time: minimum idle period after which weight-raising +- * may be reactivated for a queue (in jiffies). +- * @bfq_wr_min_inter_arr_async: minimum period between request arrivals +- * after which weight-raising may be +- * reactivated for an already busy queue +- * (in jiffies). +- * @bfq_wr_max_softrt_rate: max service-rate for a soft real-time queue, +- * sectors per seconds. +- * @RT_prod: cached value of the product R*T used for computing the maximum +- * duration of the weight raising automatically. +- * @device_speed: device-speed class for the low-latency heuristic. +- * @oom_bfqq: fallback dummy bfqq for extreme OOM conditions. ++ * struct bfq_data - per-device data structure. + * + * All the fields are protected by the @queue lock. + */ + struct bfq_data { ++ /* request queue for the device */ + struct request_queue *queue; + ++ /* root bfq_group for the device */ + struct bfq_group *root_group; + +-#ifdef CONFIG_BFQ_GROUP_IOSCHED +- int active_numerous_groups; +-#endif +- ++ /* ++ * rbtree of weight counters of @bfq_queues, sorted by ++ * weight. Used to keep track of whether all @bfq_queues have ++ * the same weight. The tree contains one counter for each ++ * distinct weight associated to some active and not ++ * weight-raised @bfq_queue (see the comments to the functions ++ * bfq_weights_tree_[add|remove] for further details). ++ */ + struct rb_root queue_weights_tree; ++ /* ++ * rbtree of non-queue @bfq_entity weight counters, sorted by ++ * weight. Used to keep track of whether all @bfq_groups have ++ * the same weight. The tree contains one counter for each ++ * distinct weight associated to some active @bfq_group (see ++ * the comments to the functions bfq_weights_tree_[add|remove] ++ * for further details). ++ */ + struct rb_root group_weights_tree; + ++ /* ++ * Number of bfq_queues containing requests (including the ++ * queue in service, even if it is idling). ++ */ + int busy_queues; +- int busy_in_flight_queues; +- int const_seeky_busy_in_flight_queues; ++ /* number of weight-raised busy @bfq_queues */ + int wr_busy_queues; ++ /* number of queued requests */ + int queued; ++ /* number of requests dispatched and waiting for completion */ + int rq_in_driver; +- int sync_flight; + ++ /* ++ * Maximum number of requests in driver in the last ++ * @hw_tag_samples completed requests. ++ */ + int max_rq_in_driver; ++ /* number of samples used to calculate hw_tag */ + int hw_tag_samples; ++ /* flag set to one if the driver is showing a queueing behavior */ + int hw_tag; + ++ /* number of budgets assigned */ + int budgets_assigned; + ++ /* ++ * Timer set when idling (waiting) for the next request from ++ * the queue in service. ++ */ + struct timer_list idle_slice_timer; ++ /* delayed work to restart dispatching on the request queue */ + struct work_struct unplug_work; + ++ /* bfq_queue in service */ + struct bfq_queue *in_service_queue; ++ /* bfq_io_cq (bic) associated with the @in_service_queue */ + struct bfq_io_cq *in_service_bic; + ++ /* on-disk position of the last served request */ + sector_t last_position; + ++ /* beginning of the last budget */ + ktime_t last_budget_start; ++ /* beginning of the last idle slice */ + ktime_t last_idling_start; ++ /* number of samples used to calculate @peak_rate */ + int peak_rate_samples; ++ /* peak transfer rate observed for a budget */ + u64 peak_rate; ++ /* maximum budget allotted to a bfq_queue before rescheduling */ + int bfq_max_budget; + ++ /* list of all the bfq_queues active on the device */ + struct list_head active_list; ++ /* list of all the bfq_queues idle on the device */ + struct list_head idle_list; + ++ /* ++ * Timeout for async/sync requests; when it fires, requests ++ * are served in fifo order. ++ */ + unsigned int bfq_fifo_expire[2]; ++ /* weight of backward seeks wrt forward ones */ + unsigned int bfq_back_penalty; ++ /* maximum allowed backward seek */ + unsigned int bfq_back_max; ++ /* maximum idling time */ + unsigned int bfq_slice_idle; ++ /* last time CLASS_IDLE was served */ + u64 bfq_class_idle_last_service; + ++ /* user-configured max budget value (0 for auto-tuning) */ + int bfq_user_max_budget; +- int bfq_max_budget_async_rq; +- unsigned int bfq_timeout[2]; +- +- unsigned int bfq_coop_thresh; +- unsigned int bfq_failed_cooperations; ++ /* ++ * Timeout for bfq_queues to consume their budget; used to ++ * prevent seeky queues from imposing long latencies to ++ * sequential or quasi-sequential ones (this also implies that ++ * seeky queues cannot receive guarantees in the service ++ * domain; after a timeout they are charged for the time they ++ * have been in service, to preserve fairness among them, but ++ * without service-domain guarantees). ++ */ ++ unsigned int bfq_timeout; ++ ++ /* ++ * Number of consecutive requests that must be issued within ++ * the idle time slice to set again idling to a queue which ++ * was marked as non-I/O-bound (see the definition of the ++ * IO_bound flag for further details). ++ */ + unsigned int bfq_requests_within_timer; + ++ /* ++ * Force device idling whenever needed to provide accurate ++ * service guarantees, without caring about throughput ++ * issues. CAVEAT: this may even increase latencies, in case ++ * of useless idling for processes that did stop doing I/O. ++ */ ++ bool strict_guarantees; ++ ++ /* ++ * Last time at which a queue entered the current burst of ++ * queues being activated shortly after each other; for more ++ * details about this and the following parameters related to ++ * a burst of activations, see the comments on the function ++ * bfq_handle_burst. ++ */ + unsigned long last_ins_in_burst; ++ /* ++ * Reference time interval used to decide whether a queue has ++ * been activated shortly after @last_ins_in_burst. ++ */ + unsigned long bfq_burst_interval; ++ /* number of queues in the current burst of queue activations */ + int burst_size; ++ ++ /* common parent entity for the queues in the burst */ ++ struct bfq_entity *burst_parent_entity; ++ /* Maximum burst size above which the current queue-activation ++ * burst is deemed as 'large'. ++ */ + unsigned long bfq_large_burst_thresh; ++ /* true if a large queue-activation burst is in progress */ + bool large_burst; ++ /* ++ * Head of the burst list (as for the above fields, more ++ * details in the comments on the function bfq_handle_burst). ++ */ + struct hlist_head burst_list; + ++ /* if set to true, low-latency heuristics are enabled */ + bool low_latency; +- +- /* parameters of the low_latency heuristics */ ++ /* ++ * Maximum factor by which the weight of a weight-raised queue ++ * is multiplied. ++ */ + unsigned int bfq_wr_coeff; ++ /* maximum duration of a weight-raising period (jiffies) */ + unsigned int bfq_wr_max_time; ++ ++ /* Maximum weight-raising duration for soft real-time processes */ + unsigned int bfq_wr_rt_max_time; ++ /* ++ * Minimum idle period after which weight-raising may be ++ * reactivated for a queue (in jiffies). ++ */ + unsigned int bfq_wr_min_idle_time; ++ /* ++ * Minimum period between request arrivals after which ++ * weight-raising may be reactivated for an already busy async ++ * queue (in jiffies). ++ */ + unsigned long bfq_wr_min_inter_arr_async; ++ ++ /* Max service-rate for a soft real-time queue, in sectors/sec */ + unsigned int bfq_wr_max_softrt_rate; ++ /* ++ * Cached value of the product R*T, used for computing the ++ * maximum duration of weight raising automatically. ++ */ + u64 RT_prod; ++ /* device-speed class for the low-latency heuristic */ + enum bfq_device_speed device_speed; + ++ /* fallback dummy bfqq for extreme OOM conditions */ + struct bfq_queue oom_bfqq; + }; + + enum bfqq_state_flags { +- BFQ_BFQQ_FLAG_busy = 0, /* has requests or is in service */ ++ BFQ_BFQQ_FLAG_just_created = 0, /* queue just allocated */ ++ BFQ_BFQQ_FLAG_busy, /* has requests or is in service */ + BFQ_BFQQ_FLAG_wait_request, /* waiting for a request */ ++ BFQ_BFQQ_FLAG_non_blocking_wait_rq, /* ++ * waiting for a request ++ * without idling the device ++ */ + BFQ_BFQQ_FLAG_must_alloc, /* must be allowed rq alloc */ + BFQ_BFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */ + BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ + BFQ_BFQQ_FLAG_sync, /* synchronous queue */ +- BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ + BFQ_BFQQ_FLAG_IO_bound, /* + * bfqq has timed-out at least once + * having consumed at most 2/10 of +@@ -581,17 +577,12 @@ enum bfqq_state_flags { + * bfqq activated in a large burst, + * see comments to bfq_handle_burst. + */ +- BFQ_BFQQ_FLAG_constantly_seeky, /* +- * bfqq has proved to be slow and +- * seeky until budget timeout +- */ + BFQ_BFQQ_FLAG_softrt_update, /* + * may need softrt-next-start + * update + */ + BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ +- BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be split */ +- BFQ_BFQQ_FLAG_just_split, /* queue has just been split */ ++ BFQ_BFQQ_FLAG_split_coop /* shared bfqq will be split */ + }; + + #define BFQ_BFQQ_FNS(name) \ +@@ -608,25 +599,53 @@ static int bfq_bfqq_##name(const struct bfq_queue *bfqq) \ + return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0; \ + } + ++BFQ_BFQQ_FNS(just_created); + BFQ_BFQQ_FNS(busy); + BFQ_BFQQ_FNS(wait_request); ++BFQ_BFQQ_FNS(non_blocking_wait_rq); + BFQ_BFQQ_FNS(must_alloc); + BFQ_BFQQ_FNS(fifo_expire); + BFQ_BFQQ_FNS(idle_window); + BFQ_BFQQ_FNS(sync); +-BFQ_BFQQ_FNS(budget_new); + BFQ_BFQQ_FNS(IO_bound); + BFQ_BFQQ_FNS(in_large_burst); +-BFQ_BFQQ_FNS(constantly_seeky); + BFQ_BFQQ_FNS(coop); + BFQ_BFQQ_FNS(split_coop); +-BFQ_BFQQ_FNS(just_split); + BFQ_BFQQ_FNS(softrt_update); + #undef BFQ_BFQQ_FNS + + /* Logging facilities. */ +-#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ +- blk_add_trace_msg((bfqd)->queue, "bfq%d " fmt, (bfqq)->pid, ##args) ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++static struct bfq_group *bfqq_group(struct bfq_queue *bfqq); ++static struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg); ++ ++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) do { \ ++ char __pbuf[128]; \ ++ \ ++ assert_spin_locked((bfqd)->queue->queue_lock); \ ++ blkg_path(bfqg_to_blkg(bfqq_group(bfqq)), __pbuf, sizeof(__pbuf)); \ ++ blk_add_trace_msg((bfqd)->queue, "bfq%d%c %s " fmt, \ ++ (bfqq)->pid, \ ++ bfq_bfqq_sync((bfqq)) ? 'S' : 'A', \ ++ __pbuf, ##args); \ ++} while (0) ++ ++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...) do { \ ++ char __pbuf[128]; \ ++ \ ++ blkg_path(bfqg_to_blkg(bfqg), __pbuf, sizeof(__pbuf)); \ ++ blk_add_trace_msg((bfqd)->queue, "%s " fmt, __pbuf, ##args); \ ++} while (0) ++ ++#else /* CONFIG_BFQ_GROUP_IOSCHED */ ++ ++#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ ++ blk_add_trace_msg((bfqd)->queue, "bfq%d%c " fmt, (bfqq)->pid, \ ++ bfq_bfqq_sync((bfqq)) ? 'S' : 'A', \ ++ ##args) ++#define bfq_log_bfqg(bfqd, bfqg, fmt, args...) do {} while (0) ++ ++#endif /* CONFIG_BFQ_GROUP_IOSCHED */ + + #define bfq_log(bfqd, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args) +@@ -640,15 +659,12 @@ enum bfqq_expiration { + BFQ_BFQQ_BUDGET_TIMEOUT, /* budget took too long to be used */ + BFQ_BFQQ_BUDGET_EXHAUSTED, /* budget consumed */ + BFQ_BFQQ_NO_MORE_REQUESTS, /* the queue has no more requests */ ++ BFQ_BFQQ_PREEMPTED /* preemption in progress */ + }; + +-#ifdef CONFIG_BFQ_GROUP_IOSCHED + + struct bfqg_stats { +- /* total bytes transferred */ +- struct blkg_rwstat service_bytes; +- /* total IOs serviced, post merge */ +- struct blkg_rwstat serviced; ++#ifdef CONFIG_BFQ_GROUP_IOSCHED + /* number of ios merged */ + struct blkg_rwstat merged; + /* total time spent on device in ns, may not be accurate w/ queueing */ +@@ -657,12 +673,8 @@ struct bfqg_stats { + struct blkg_rwstat wait_time; + /* number of IOs queued up */ + struct blkg_rwstat queued; +- /* total sectors transferred */ +- struct blkg_stat sectors; + /* total disk time and nr sectors dispatched by this group */ + struct blkg_stat time; +- /* time not charged to this cgroup */ +- struct blkg_stat unaccounted_time; + /* sum of number of ios queued across all samples */ + struct blkg_stat avg_queue_size_sum; + /* count of samples taken for average */ +@@ -680,8 +692,10 @@ struct bfqg_stats { + uint64_t start_idle_time; + uint64_t start_empty_time; + uint16_t flags; ++#endif + }; + ++#ifdef CONFIG_BFQ_GROUP_IOSCHED + /* + * struct bfq_group_data - per-blkcg storage for the blkio subsystem. + * +@@ -692,7 +706,7 @@ struct bfq_group_data { + /* must be the first member */ + struct blkcg_policy_data pd; + +- unsigned short weight; ++ unsigned int weight; + }; + + /** +@@ -712,7 +726,7 @@ struct bfq_group_data { + * unused for the root group. Used to know whether there + * are groups with more than one active @bfq_entity + * (see the comments to the function +- * bfq_bfqq_must_not_expire()). ++ * bfq_bfqq_may_idle()). + * @rq_pos_tree: rbtree sorted by next_request position, used when + * determining if two or more queues have interleaving + * requests (see bfq_find_close_cooperator()). +@@ -745,7 +759,6 @@ struct bfq_group { + struct rb_root rq_pos_tree; + + struct bfqg_stats stats; +- struct bfqg_stats dead_stats; /* stats pushed from dead children */ + }; + + #else +@@ -767,11 +780,25 @@ bfq_entity_service_tree(struct bfq_entity *entity) + struct bfq_sched_data *sched_data = entity->sched_data; + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + unsigned int idx = bfqq ? bfqq->ioprio_class - 1 : +- BFQ_DEFAULT_GRP_CLASS; ++ BFQ_DEFAULT_GRP_CLASS - 1; + + BUG_ON(idx >= BFQ_IOPRIO_CLASSES); + BUG_ON(sched_data == NULL); + ++ if (bfqq) ++ bfq_log_bfqq(bfqq->bfqd, bfqq, ++ "entity_service_tree %p %d", ++ sched_data->service_tree + idx, idx) ; ++#ifdef CONFIG_BFQ_GROUP_IOSCHED ++ else { ++ struct bfq_group *bfqg = ++ container_of(entity, struct bfq_group, entity); ++ ++ bfq_log_bfqg((struct bfq_data *)bfqg->bfqd, bfqg, ++ "entity_service_tree %p %d", ++ sched_data->service_tree + idx, idx) ; ++ } ++#endif + return sched_data->service_tree + idx; + } + +@@ -791,47 +818,6 @@ static struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic) + return bic->icq.q->elevator->elevator_data; + } + +-/** +- * bfq_get_bfqd_locked - get a lock to a bfqd using a RCU protected pointer. +- * @ptr: a pointer to a bfqd. +- * @flags: storage for the flags to be saved. +- * +- * This function allows bfqg->bfqd to be protected by the +- * queue lock of the bfqd they reference; the pointer is dereferenced +- * under RCU, so the storage for bfqd is assured to be safe as long +- * as the RCU read side critical section does not end. After the +- * bfqd->queue->queue_lock is taken the pointer is rechecked, to be +- * sure that no other writer accessed it. If we raced with a writer, +- * the function returns NULL, with the queue unlocked, otherwise it +- * returns the dereferenced pointer, with the queue locked. +- */ +-static struct bfq_data *bfq_get_bfqd_locked(void **ptr, unsigned long *flags) +-{ +- struct bfq_data *bfqd; +- +- rcu_read_lock(); +- bfqd = rcu_dereference(*(struct bfq_data **)ptr); +- +- if (bfqd != NULL) { +- spin_lock_irqsave(bfqd->queue->queue_lock, *flags); +- if (ptr == NULL) +- printk(KERN_CRIT "get_bfqd_locked pointer NULL\n"); +- else if (*ptr == bfqd) +- goto out; +- spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); +- } +- +- bfqd = NULL; +-out: +- rcu_read_unlock(); +- return bfqd; +-} +- +-static void bfq_put_bfqd_unlock(struct bfq_data *bfqd, unsigned long *flags) +-{ +- spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); +-} +- + #ifdef CONFIG_BFQ_GROUP_IOSCHED + + static struct bfq_group *bfq_bfqq_to_bfqg(struct bfq_queue *bfqq) +@@ -857,11 +843,13 @@ static void bfq_check_ioprio_change(struct bfq_io_cq *bic, struct bio *bio); + static void bfq_put_queue(struct bfq_queue *bfqq); + static void bfq_dispatch_insert(struct request_queue *q, struct request *rq); + static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, +- struct bio *bio, int is_sync, +- struct bfq_io_cq *bic, gfp_t gfp_mask); ++ struct bio *bio, bool is_sync, ++ struct bfq_io_cq *bic); + static void bfq_end_wr_async_queues(struct bfq_data *bfqd, + struct bfq_group *bfqg); ++#ifdef CONFIG_BFQ_GROUP_IOSCHED + static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg); ++#endif + static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq); + + #endif /* _BFQ_H */ +-- +1.9.1 + diff --git a/patches/BLD-4.7.patch b/patches/BLD-4.7.patch new file mode 100644 index 0000000..e252c45 --- /dev/null +++ b/patches/BLD-4.7.patch @@ -0,0 +1,708 @@ + BLD changes for Linux kernel version 4.7 + +--- + +diff --git a/init/Kconfig b/init/Kconfig +index c02d897..edf8697 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -36,6 +36,15 @@ config BROKEN_ON_SMP + depends on BROKEN || !SMP + default y + ++config BLD ++ bool "An alternate CPU load distribution technique for task scheduler" ++ depends on SMP ++ default y ++ help ++ This is an alternate CPU load distribution technique based for task ++ scheduler based on The Barbershop Load Distribution algorithm. Not ++ suitable for NUMA, should work well on SMP. ++ + config INIT_ENV_ARG_LIMIT + int + default 32 if !UML +diff --git a/kernel/sched/bld.h b/kernel/sched/bld.h +new file mode 100644 +index 0000000..f1f9fba +--- /dev/null ++++ b/kernel/sched/bld.h +@@ -0,0 +1,215 @@ ++#ifdef CONFIG_BLD ++ ++static DEFINE_RWLOCK(rt_list_lock); ++static LIST_HEAD(rt_rq_head); ++static LIST_HEAD(cfs_rq_head); ++static DEFINE_RWLOCK(cfs_list_lock); ++ ++#ifdef CONFIG_FAIR_GROUP_SCHED ++static inline struct rq *rq_of_cfs(struct cfs_rq *cfs_rq) ++{ ++ return cfs_rq->rq; ++} ++#else ++static inline struct rq *rq_of_cfs(struct cfs_rq *cfs_rq) ++{ ++ return container_of(cfs_rq, struct rq, cfs); ++} ++#endif ++ ++#ifdef CONFIG_RT_GROUP_SCHED ++static inline struct rq *rq_of_rt(struct rt_rq *rt_rq) ++{ ++ return rt_rq->rq; ++} ++#else ++static inline struct rq *rq_of_rt(struct rt_rq *rt_rq) ++{ ++ return container_of(rt_rq, struct rq, rt); ++} ++#endif ++ ++static int select_cpu_for_wakeup(int task_type, struct cpumask *mask) ++{ ++ int cpu = smp_processor_id(), i; ++ unsigned long load, varload; ++ struct rq *rq; ++ ++ if (task_type) { ++ varload = ULONG_MAX; ++ for_each_cpu(i, mask) { ++ rq = cpu_rq(i); ++ load = rq->cfs.load.weight; ++ if (load < varload) { ++ varload = load; ++ cpu = i; ++ } ++ } ++ } else { ++ /* Here's an attempt to get a CPU within the mask where ++ * we can preempt easily. To achieve this we tried to ++ * maintain a lowbit, which indicate the lowest bit set on ++ * array bitmap. Since all CPUs contains high priority ++ * kernel threads therefore we eliminate 0, so it might not ++ * be right every time, but it's just an indicator. ++ */ ++ varload = 1; ++ ++ for_each_cpu(i, mask) { ++ rq = cpu_rq(i); ++ load = rq->rt.lowbit; ++ if (load >= varload) { ++ varload = load; ++ cpu = i; ++ } ++ } ++ } ++ ++ return cpu; ++} ++ ++static int bld_pick_cpu_cfs(struct task_struct *p, int sd_flags, int wake_flags) ++{ ++ struct cfs_rq *cfs; ++ unsigned long flags; ++ unsigned int cpu = smp_processor_id(); ++ ++ read_lock_irqsave(&cfs_list_lock, flags); ++ list_for_each_entry(cfs, &cfs_rq_head, bld_cfs_list) { ++ cpu = cpu_of(rq_of_cfs(cfs)); ++ if (cpu_online(cpu)) ++ break; ++ } ++ read_unlock_irqrestore(&cfs_list_lock, flags); ++ return cpu; ++} ++ ++static int bld_pick_cpu_rt(struct task_struct *p, int sd_flags, int wake_flags) ++{ ++ struct rt_rq *rt; ++ unsigned long flags; ++ unsigned int cpu = smp_processor_id(); ++ ++ read_lock_irqsave(&rt_list_lock, flags); ++ list_for_each_entry(rt, &rt_rq_head, bld_rt_list) { ++ cpu = cpu_of(rq_of_rt(rt)); ++ if (cpu_online(cpu)) ++ break; ++ } ++ read_unlock_irqrestore(&rt_list_lock, flags); ++ return cpu; ++} ++ ++static int bld_pick_cpu_domain(struct task_struct *p, int sd_flags, int wake_flags) ++{ ++ unsigned int cpu = smp_processor_id(), want_affine = 0; ++ struct cpumask *tmpmask; ++ ++ if (p->nr_cpus_allowed == 1) ++ return task_cpu(p); ++ ++ if (sd_flags & SD_BALANCE_WAKE) { ++ if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) { ++ want_affine = 1; ++ } ++ } ++ ++ if (want_affine) ++ tmpmask = tsk_cpus_allowed(p); ++ else ++ tmpmask = sched_domain_span(cpu_rq(task_cpu(p))->sd); ++ ++ if (rt_task(p)) ++ cpu = select_cpu_for_wakeup(0, tmpmask); ++ else ++ cpu = select_cpu_for_wakeup(1, tmpmask); ++ ++ return cpu; ++} ++ ++static void track_load_rt(struct rq *rq, struct task_struct *p) ++{ ++ unsigned long flag; ++ int firstbit; ++ struct rt_rq *first; ++ struct rt_prio_array *array = &rq->rt.active; ++ ++ first = list_entry(rt_rq_head.next, struct rt_rq, bld_rt_list); ++ firstbit = sched_find_first_bit(array->bitmap); ++ ++ /* Maintaining rt.lowbit */ ++ if (firstbit > 0 && firstbit <= rq->rt.lowbit) ++ rq->rt.lowbit = firstbit; ++ ++ if (rq->rt.lowbit < first->lowbit) { ++ write_lock_irqsave(&rt_list_lock, flag); ++ list_del(&rq->rt.bld_rt_list); ++ list_add_tail(&rq->rt.bld_rt_list, &rt_rq_head); ++ write_unlock_irqrestore(&rt_list_lock, flag); ++ } ++} ++ ++static int bld_get_cpu(struct task_struct *p, int sd_flags, int wake_flags) ++{ ++ unsigned int cpu; ++ ++ if (sd_flags == SD_BALANCE_WAKE || (sd_flags == SD_BALANCE_EXEC && (get_nr_threads(p) > 1))) ++ cpu = bld_pick_cpu_domain(p, sd_flags, wake_flags); ++ else { ++ if (rt_task(p)) ++ cpu = bld_pick_cpu_rt(p, sd_flags, wake_flags); ++ else ++ cpu = bld_pick_cpu_cfs(p, sd_flags, wake_flags); ++ } ++ ++ return cpu; ++} ++ ++static void bld_track_load_activate(struct rq *rq, struct task_struct *p) ++{ ++ unsigned long flag; ++ if (rt_task(p)) { ++ track_load_rt(rq, p); ++ } else { ++ if (rq->cfs.pos != 2) { ++ struct cfs_rq *last; ++ last = list_entry(cfs_rq_head.prev, struct cfs_rq, bld_cfs_list); ++ if (rq->cfs.load.weight >= last->load.weight) { ++ write_lock_irqsave(&cfs_list_lock, flag); ++ list_del(&rq->cfs.bld_cfs_list); ++ list_add_tail(&rq->cfs.bld_cfs_list, &cfs_rq_head); ++ rq->cfs.pos = 2; last->pos = 1; ++ write_unlock_irqrestore(&cfs_list_lock, flag); ++ } ++ } ++ } ++} ++ ++static void bld_track_load_deactivate(struct rq *rq, struct task_struct *p) ++{ ++ unsigned long flag; ++ if (rt_task(p)) { ++ track_load_rt(rq, p); ++ } else { ++ if (rq->cfs.pos != 0) { ++ struct cfs_rq *first; ++ first = list_entry(cfs_rq_head.next, struct cfs_rq, bld_cfs_list); ++ if (rq->cfs.load.weight <= first->load.weight) { ++ write_lock_irqsave(&cfs_list_lock, flag); ++ list_del(&rq->cfs.bld_cfs_list); ++ list_add(&rq->cfs.bld_cfs_list, &cfs_rq_head); ++ rq->cfs.pos = 0; first->pos = 1; ++ write_unlock_irqrestore(&cfs_list_lock, flag); ++ } ++ } ++ } ++} ++#else ++static inline void bld_track_load_activate(struct rq *rq, struct task_struct *p) ++{ ++} ++ ++static inline void bld_track_load_deactivate(struct rq *rq, struct task_struct *p) ++{ ++} ++#endif /* CONFIG_BLD */ +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 97ee9ac..b2ddabc 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -24,6 +24,8 @@ + * 2007-07-01 Group scheduling enhancements by Srivatsa Vaddagiri + * 2007-11-29 RT balancing improvements by Steven Rostedt, Gregory Haskins, + * Thomas Gleixner, Mike Kravetz ++ * 2012-Feb The Barbershop Load Distribution (BLD) algorithm - an alternate ++ * CPU load distribution technique for kernel scheduler by Rakib Mullick. + */ + + #include +@@ -86,6 +88,7 @@ + #include "sched.h" + #include "../workqueue_internal.h" + #include "../smpboot.h" ++#include "bld.h" + + #define CREATE_TRACE_POINTS + #include +@@ -750,6 +753,8 @@ static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags) + if (!(flags & ENQUEUE_RESTORE)) + sched_info_queued(rq, p); + p->sched_class->enqueue_task(rq, p, flags); ++ if (!dl_task(p)) ++ bld_track_load_activate(rq, p); + } + + static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags) +@@ -758,6 +763,8 @@ static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags) + if (!(flags & DEQUEUE_SAVE)) + sched_info_dequeued(rq, p); + p->sched_class->dequeue_task(rq, p, flags); ++ if (!dl_task(p)) ++ bld_track_load_deactivate(rq, p); + } + + void activate_task(struct rq *rq, struct task_struct *p, int flags) +@@ -1587,11 +1594,17 @@ int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags) + { + lockdep_assert_held(&p->pi_lock); + ++#ifndef CONFIG_BLD + if (tsk_nr_cpus_allowed(p) > 1) + cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags); + else + cpu = cpumask_any(tsk_cpus_allowed(p)); +- ++#else ++ if (dl_task(p)) ++ cpu = dl_sched_class.select_task_rq(p, cpu, sd_flags, wake_flags); ++ else ++ cpu = bld_get_cpu(p, sd_flags, wake_flags); ++#endif + /* + * In order not to call set_task_cpu() on a blocking task we need + * to rely on ttwu() to place the task on a valid ->cpus_allowed +@@ -1794,7 +1807,11 @@ void scheduler_ipi(void) + */ + preempt_fold_need_resched(); + ++#ifndef CONFIG_BLD + if (llist_empty(&this_rq()->wake_list) && !got_nohz_idle_kick()) ++#else ++ if (llist_empty(&this_rq()->wake_list)) ++#endif + return; + + /* +@@ -1816,13 +1833,16 @@ void scheduler_ipi(void) + /* + * Check if someone kicked us for doing the nohz idle load balance. + */ ++#ifndef CONFIG_BLD + if (unlikely(got_nohz_idle_kick())) { + this_rq()->idle_balance = 1; + raise_softirq_irqoff(SCHED_SOFTIRQ); + } ++#endif + irq_exit(); + } + ++#ifndef CONFIG_BLD + static void ttwu_queue_remote(struct task_struct *p, int cpu, int wake_flags) + { + struct rq *rq = cpu_rq(cpu); +@@ -1836,6 +1856,7 @@ static void ttwu_queue_remote(struct task_struct *p, int cpu, int wake_flags) + trace_sched_wake_idle_without_ipi(cpu); + } + } ++#endif /* CONFIG_BLD */ + + void wake_up_if_idle(int cpu) + { +@@ -1872,7 +1893,7 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) + struct rq *rq = cpu_rq(cpu); + struct pin_cookie cookie; + +-#if defined(CONFIG_SMP) ++#if defined(CONFIG_SMP) && !defined(CONFIG_BLD) + if (sched_feat(TTWU_QUEUE) && !cpus_share_cache(smp_processor_id(), cpu)) { + sched_clock_cpu(cpu); /* sync clocks x-cpu */ + ttwu_queue_remote(p, cpu, wake_flags); +@@ -2394,7 +2415,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) + * Silence PROVE_RCU. + */ + raw_spin_lock_irqsave(&p->pi_lock, flags); +- set_task_cpu(p, cpu); ++ __set_task_cpu(p, cpu); + raw_spin_unlock_irqrestore(&p->pi_lock, flags); + + #ifdef CONFIG_SCHED_INFO +@@ -2941,7 +2962,14 @@ void sched_exec(void) + int dest_cpu; + + raw_spin_lock_irqsave(&p->pi_lock, flags); ++#ifndef CONFIG_BLD + dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0); ++#else ++ if (dl_task(p)) ++ dest_cpu = task_cpu(p); ++ else ++ dest_cpu = bld_get_cpu(p, SD_BALANCE_EXEC, 0); ++#endif + if (dest_cpu == smp_processor_id()) + goto unlock; + +@@ -3030,8 +3058,10 @@ void scheduler_tick(void) + + #ifdef CONFIG_SMP + rq->idle_balance = idle_cpu(cpu); ++#ifndef CONFIG_BLD + trigger_load_balance(rq); + #endif ++#endif + rq_last_tick_reset(rq); + } + +@@ -7262,7 +7292,9 @@ int sched_cpu_dying(unsigned int cpu) + raw_spin_unlock_irqrestore(&rq->lock, flags); + calc_load_migrate(rq); + update_max_interval(); ++#ifndef CONFIG_BLD + nohz_balance_exit_idle(cpu); ++#endif + hrtick_clear(rq); + return 0; + } +@@ -7468,6 +7500,15 @@ void __init sched_init(void) + #endif /* CONFIG_SMP */ + init_rq_hrtick(rq); + atomic_set(&rq->nr_iowait, 0); ++#ifdef CONFIG_BLD ++ INIT_LIST_HEAD(&rq->cfs.bld_cfs_list); ++ list_add_tail(&rq->cfs.bld_cfs_list, &cfs_rq_head); ++ rq->cfs.pos = 0; ++ ++ INIT_LIST_HEAD(&rq->rt.bld_rt_list); ++ list_add_tail(&rq->rt.bld_rt_list, &rt_rq_head); ++ rq->rt.lowbit = INT_MAX; ++#endif + } + + set_load_weight(&init_task); +@@ -7510,6 +7551,9 @@ void __init sched_init(void) + init_schedstats(); + + scheduler_running = 1; ++#ifdef CONFIG_BLD ++ printk(KERN_INFO "BLD: An Alternate CPU load distributor activated.\n"); ++#endif + } + + #ifdef CONFIG_DEBUG_ATOMIC_SLEEP +diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c +index c8c5d2d..5b694b3 100644 +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -4880,6 +4880,7 @@ static unsigned long cpu_avg_load_per_task(int cpu) + return 0; + } + ++#ifndef CONFIG_BLD + #ifdef CONFIG_FAIR_GROUP_SCHED + /* + * effective_load() calculates the load change as seen from the root_task_group +@@ -5411,6 +5412,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f + + return new_cpu; + } ++#endif /* CONFIG_BLD */ + + /* + * Called immediately before a task is migrated to a new cpu; task_cpu(p) and +@@ -5741,6 +5743,7 @@ idle: + * further scheduler activity on it and we're being very careful to + * re-start the picking loop. + */ ++#ifndef CONFIG_BLD + lockdep_unpin_lock(&rq->lock, cookie); + new_tasks = idle_balance(rq); + lockdep_repin_lock(&rq->lock, cookie); +@@ -5754,7 +5757,7 @@ idle: + + if (new_tasks > 0) + goto again; +- ++#endif /* CONFIG_BLD */ + return NULL; + } + +@@ -6415,8 +6418,9 @@ static unsigned long task_h_load(struct task_struct *p) + } + #endif + +-/********** Helpers for find_busiest_group ************************/ ++#ifndef CONFIG_BLD + ++/********** Helpers for find_busiest_group ************************/ + enum group_type { + group_other = 0, + group_imbalanced, +@@ -6507,6 +6511,7 @@ static inline int get_sd_load_idx(struct sched_domain *sd, + + return load_idx; + } ++#endif /* CONFIG_BLD */ + + static unsigned long scale_rt_capacity(int cpu) + { +@@ -6615,6 +6620,7 @@ void update_group_capacity(struct sched_domain *sd, int cpu) + sdg->sgc->capacity = capacity; + } + ++#ifndef CONFIG_BLD + /* + * Check whether the capacity of the rq has been noticeably reduced by side + * activity. The imbalance_pct is used for the threshold. +@@ -7848,6 +7854,7 @@ static inline int on_null_domain(struct rq *rq) + { + return unlikely(!rcu_dereference_sched(rq->sd)); + } ++#endif /* CONFIG_BLD */ + + #ifdef CONFIG_NO_HZ_COMMON + /* +@@ -7856,12 +7863,39 @@ static inline int on_null_domain(struct rq *rq) + * needed, they will kick the idle load balancer, which then does idle + * load balancing for all the idle CPUs. + */ ++#ifndef CONFIG_BLD + static struct { + cpumask_var_t idle_cpus_mask; + atomic_t nr_cpus; + unsigned long next_balance; /* in jiffy units */ + } nohz ____cacheline_aligned; + ++void nohz_balance_exit_idle(unsigned int cpu) ++{ ++ if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { ++ /* ++ * Completely isolated CPUs don't ever set, so we must test. ++ */ ++ if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) { ++ cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); ++ atomic_dec(&nohz.nr_cpus); ++ } ++ clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); ++ } ++} ++ ++static int sched_ilb_notifier(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) ++{ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_DYING: ++ nohz_balance_exit_idle(smp_processor_id()); ++ return NOTIFY_OK; ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ + static inline int find_new_ilb(void) + { + int ilb = cpumask_first(nohz.idle_cpus_mask); +@@ -7900,20 +7934,6 @@ static void nohz_balancer_kick(void) + return; + } + +-void nohz_balance_exit_idle(unsigned int cpu) +-{ +- if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { +- /* +- * Completely isolated CPUs don't ever set, so we must test. +- */ +- if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) { +- cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); +- atomic_dec(&nohz.nr_cpus); +- } +- clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); +- } +-} +- + static inline void set_cpu_sd_state_busy(void) + { + struct sched_domain *sd; +@@ -7930,6 +7950,8 @@ static inline void set_cpu_sd_state_busy(void) + unlock: + rcu_read_unlock(); + } ++#endif /* CONFIG_BLD */ ++#endif /* NO_HZ_COMMON */ + + void set_cpu_sd_state_idle(void) + { +@@ -7954,6 +7976,7 @@ unlock: + */ + void nohz_balance_enter_idle(int cpu) + { ++#ifndef CONFIG_BLD + /* + * If this cpu is going down, then nothing needs to be done. + */ +@@ -7972,10 +7995,8 @@ void nohz_balance_enter_idle(int cpu) + cpumask_set_cpu(cpu, nohz.idle_cpus_mask); + atomic_inc(&nohz.nr_cpus); + set_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); +-} + #endif +- +-static DEFINE_SPINLOCK(balancing); ++} + + /* + * Scale the max load_balance interval with the number of CPUs in the system. +@@ -7986,6 +8007,9 @@ void update_max_interval(void) + max_load_balance_interval = HZ*num_online_cpus()/10; + } + ++#ifndef CONFIG_BLD ++static DEFINE_SPINLOCK(balancing); ++ + /* + * It checks each scheduling domain to see if it is due to be balanced, + * and initiates a balancing operation if so. +@@ -8273,6 +8297,7 @@ void trigger_load_balance(struct rq *rq) + nohz_balancer_kick(); + #endif + } ++#endif /* CONFIG_BLD */ + + static void rq_online_fair(struct rq *rq) + { +@@ -8288,7 +8313,6 @@ static void rq_offline_fair(struct rq *rq) + /* Ensure any throttled groups are reachable by pick_next_task */ + unthrottle_offline_cfs_rqs(rq); + } +- + #endif /* CONFIG_SMP */ + + /* +@@ -8716,7 +8740,9 @@ const struct sched_class fair_sched_class = { + .put_prev_task = put_prev_task_fair, + + #ifdef CONFIG_SMP ++#ifndef CONFIG_BLD + .select_task_rq = select_task_rq_fair, ++#endif + .migrate_task_rq = migrate_task_rq_fair, + + .rq_online = rq_online_fair, +@@ -8777,6 +8803,7 @@ void show_numa_stats(struct task_struct *p, struct seq_file *m) + + __init void init_sched_fair_class(void) + { ++#ifndef CONFIG_BLD + #ifdef CONFIG_SMP + open_softirq(SCHED_SOFTIRQ, run_rebalance_domains); + +@@ -8785,5 +8812,5 @@ __init void init_sched_fair_class(void) + zalloc_cpumask_var(&nohz.idle_cpus_mask, GFP_NOWAIT); + #endif + #endif /* SMP */ +- ++#endif /* BLD */ + } +diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c +index d5690b7..6f3589e 100644 +--- a/kernel/sched/rt.c ++++ b/kernel/sched/rt.c +@@ -1375,6 +1375,7 @@ static void yield_task_rt(struct rq *rq) + #ifdef CONFIG_SMP + static int find_lowest_rq(struct task_struct *task); + ++#ifndef CONFIG_BLD + static int + select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags) + { +@@ -1430,6 +1431,7 @@ select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags) + out: + return cpu; + } ++#endif /* CONFIG_BLD */ + + static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p) + { +@@ -2335,7 +2337,9 @@ const struct sched_class rt_sched_class = { + .put_prev_task = put_prev_task_rt, + + #ifdef CONFIG_SMP ++#ifndef CONFIG_BLD + .select_task_rq = select_task_rq_rt, ++#endif + + .set_cpus_allowed = set_cpus_allowed_common, + .rq_online = rq_online_rt, +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index 898c0d2..720d524 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -415,9 +415,8 @@ struct cfs_rq { + #endif /* CONFIG_FAIR_GROUP_SCHED */ + #endif /* CONFIG_SMP */ + +-#ifdef CONFIG_FAIR_GROUP_SCHED + struct rq *rq; /* cpu runqueue to which this cfs_rq is attached */ +- ++#ifdef CONFIG_FAIR_GROUP_SCHED + /* + * leaf cfs_rqs are those that hold tasks (lowest schedulable entity in + * a hierarchy). Non-leaf lrqs hold other higher schedulable entities +@@ -441,6 +440,11 @@ struct cfs_rq { + struct list_head throttled_list; + #endif /* CONFIG_CFS_BANDWIDTH */ + #endif /* CONFIG_FAIR_GROUP_SCHED */ ++ ++#ifdef CONFIG_BLD ++ struct list_head bld_cfs_list; ++ char pos; ++#endif + }; + + static inline int rt_bandwidth_enabled(void) +@@ -486,12 +490,16 @@ struct rt_rq { + /* Nests inside the rq lock: */ + raw_spinlock_t rt_runtime_lock; + ++ struct rq *rq; + #ifdef CONFIG_RT_GROUP_SCHED + unsigned long rt_nr_boosted; + +- struct rq *rq; + struct task_group *tg; + #endif ++#ifdef CONFIG_BLD ++ struct list_head bld_rt_list; ++ int lowbit; ++#endif + }; + + /* Deadline class' related fields in a runqueue */ diff --git a/patches/uksm-0.1.2.5-for-v4.7.patch b/patches/uksm-0.1.2.5-for-v4.7.patch new file mode 100644 index 0000000..4ba4cbb --- /dev/null +++ b/patches/uksm-0.1.2.5-for-v4.7.patch @@ -0,0 +1,6894 @@ +diff --git a/Documentation/vm/00-INDEX b/Documentation/vm/00-INDEX +index 6a5e2a1..09eaa9a1 100644 +--- a/Documentation/vm/00-INDEX ++++ b/Documentation/vm/00-INDEX +@@ -18,6 +18,8 @@ idle_page_tracking.txt + - description of the idle page tracking feature. + ksm.txt + - how to use the Kernel Samepage Merging feature. ++uksm.txt ++ - Introduction to Ultra KSM + numa + - information about NUMA specific code in the Linux vm. + numa_memory_policy.txt +diff --git a/Documentation/vm/uksm.txt b/Documentation/vm/uksm.txt +new file mode 100644 +index 0000000..8fce86f +--- /dev/null ++++ b/Documentation/vm/uksm.txt +@@ -0,0 +1,60 @@ ++The Ultra Kernel Samepage Merging feature ++---------------------------------------------- ++/* ++ * Ultra KSM. Copyright (C) 2011-2012 Nai Xia ++ * ++ * This is an improvement upon KSM. Some basic data structures and routines ++ * are borrowed from ksm.c . ++ * ++ * Its new features: ++ * 1. Full system scan: ++ * It automatically scans all user processes' anonymous VMAs. Kernel-user ++ * interaction to submit a memory area to KSM is no longer needed. ++ * ++ * 2. Rich area detection: ++ * It automatically detects rich areas containing abundant duplicated ++ * pages based. Rich areas are given a full scan speed. Poor areas are ++ * sampled at a reasonable speed with very low CPU consumption. ++ * ++ * 3. Ultra Per-page scan speed improvement: ++ * A new hash algorithm is proposed. As a result, on a machine with ++ * Core(TM)2 Quad Q9300 CPU in 32-bit mode and 800MHZ DDR2 main memory, it ++ * can scan memory areas that does not contain duplicated pages at speed of ++ * 627MB/sec ~ 2445MB/sec and can merge duplicated areas at speed of ++ * 477MB/sec ~ 923MB/sec. ++ * ++ * 4. Thrashing area avoidance: ++ * Thrashing area(an VMA that has frequent Ksm page break-out) can be ++ * filtered out. My benchmark shows it's more efficient than KSM's per-page ++ * hash value based volatile page detection. ++ * ++ * ++ * 5. Misc changes upon KSM: ++ * * It has a fully x86-opitmized memcmp dedicated for 4-byte-aligned page ++ * comparison. It's much faster than default C version on x86. ++ * * rmap_item now has an struct *page member to loosely cache a ++ * address-->page mapping, which reduces too much time-costly ++ * follow_page(). ++ * * The VMA creation/exit procedures are hooked to let the Ultra KSM know. ++ * * try_to_merge_two_pages() now can revert a pte if it fails. No break_ ++ * ksm is needed for this case. ++ * ++ * 6. Full Zero Page consideration(contributed by Figo Zhang) ++ * Now uksmd consider full zero pages as special pages and merge them to an ++ * special unswappable uksm zero page. ++ */ ++ ++ChangeLog: ++ ++2012-05-05 The creation of this Doc ++2012-05-08 UKSM 0.1.1.1 libc crash bug fix, api clean up, doc clean up. ++2012-05-28 UKSM 0.1.1.2 bug fix release ++2012-06-26 UKSM 0.1.2-beta1 first beta release for 0.1.2 ++2012-07-2 UKSM 0.1.2-beta2 ++2012-07-10 UKSM 0.1.2-beta3 ++2012-07-26 UKSM 0.1.2 Fine grained speed control, more scan optimization. ++2012-10-13 UKSM 0.1.2.1 Bug fixes. ++2012-12-31 UKSM 0.1.2.2 Minor bug fixes. ++2014-07-02 UKSM 0.1.2.3 Fix a " __this_cpu_read() in preemptible bug". ++2015-04-22 UKSM 0.1.2.4 Fix a race condition that can sometimes trigger anonying warnings. ++2016-09-10 UKSM 0.1.2.5 Fix a bug in dedup ratio calculation. +diff --git a/fs/exec.c b/fs/exec.c +index 887c1c9..2bee16e 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -19,7 +19,7 @@ + * current->executable is only used by the procfs. This allows a dispatch + * table to check for several different types of binary formats. We keep + * trying until we recognize the file or we run out of supported binary +- * formats. ++ * formats. + */ + + #include +@@ -57,6 +57,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -1273,6 +1274,7 @@ void setup_new_exec(struct linux_binprm * bprm) + /* An exec changes our domain. We are no longer part of the thread + group */ + current->self_exec_id++; ++ + flush_signal_handlers(current, 0); + do_close_on_exec(current->files); + } +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 8372046..82aa2f4 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -89,6 +89,9 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + "SUnreclaim: %8lu kB\n" + "KernelStack: %8lu kB\n" + "PageTables: %8lu kB\n" ++#ifdef CONFIG_UKSM ++ "KsmZeroPages: %8lu kB\n" ++#endif + #ifdef CONFIG_QUICKLIST + "Quicklists: %8lu kB\n" + #endif +@@ -147,6 +150,9 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + K(global_page_state(NR_SLAB_UNRECLAIMABLE)), + global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024, + K(global_page_state(NR_PAGETABLE)), ++#ifdef CONFIG_UKSM ++ K(global_page_state(NR_UKSM_ZERO_PAGES)), ++#endif + #ifdef CONFIG_QUICKLIST + K(quicklist_total_size()), + #endif +diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h +index d4458b6..172ceb9 100644 +--- a/include/asm-generic/pgtable.h ++++ b/include/asm-generic/pgtable.h +@@ -601,12 +601,25 @@ extern void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, + extern void untrack_pfn_moved(struct vm_area_struct *vma); + #endif + ++#ifdef CONFIG_UKSM ++static inline int is_uksm_zero_pfn(unsigned long pfn) ++{ ++ extern unsigned long uksm_zero_pfn; ++ return pfn == uksm_zero_pfn; ++} ++#else ++static inline int is_uksm_zero_pfn(unsigned long pfn) ++{ ++ return 0; ++} ++#endif ++ + #ifdef __HAVE_COLOR_ZERO_PAGE + static inline int is_zero_pfn(unsigned long pfn) + { + extern unsigned long zero_pfn; + unsigned long offset_from_zero_pfn = pfn - zero_pfn; +- return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT); ++ return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT) || is_uksm_zero_pfn(pfn); + } + + #define my_zero_pfn(addr) page_to_pfn(ZERO_PAGE(addr)) +@@ -615,7 +628,7 @@ static inline int is_zero_pfn(unsigned long pfn) + static inline int is_zero_pfn(unsigned long pfn) + { + extern unsigned long zero_pfn; +- return pfn == zero_pfn; ++ return (pfn == zero_pfn) || (is_uksm_zero_pfn(pfn)); + } + + static inline unsigned long my_zero_pfn(unsigned long addr) +diff --git a/include/linux/ksm.h b/include/linux/ksm.h +index 7ae216a..06861d8 100644 +--- a/include/linux/ksm.h ++++ b/include/linux/ksm.h +@@ -19,21 +19,6 @@ struct mem_cgroup; + #ifdef CONFIG_KSM + int ksm_madvise(struct vm_area_struct *vma, unsigned long start, + unsigned long end, int advice, unsigned long *vm_flags); +-int __ksm_enter(struct mm_struct *mm); +-void __ksm_exit(struct mm_struct *mm); +- +-static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) +-{ +- if (test_bit(MMF_VM_MERGEABLE, &oldmm->flags)) +- return __ksm_enter(mm); +- return 0; +-} +- +-static inline void ksm_exit(struct mm_struct *mm) +-{ +- if (test_bit(MMF_VM_MERGEABLE, &mm->flags)) +- __ksm_exit(mm); +-} + + static inline struct stable_node *page_stable_node(struct page *page) + { +@@ -64,6 +49,33 @@ struct page *ksm_might_need_to_copy(struct page *page, + int rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc); + void ksm_migrate_page(struct page *newpage, struct page *oldpage); + ++#ifdef CONFIG_KSM_LEGACY ++int __ksm_enter(struct mm_struct *mm); ++void __ksm_exit(struct mm_struct *mm); ++static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) ++{ ++ if (test_bit(MMF_VM_MERGEABLE, &oldmm->flags)) ++ return __ksm_enter(mm); ++ return 0; ++} ++ ++static inline void ksm_exit(struct mm_struct *mm) ++{ ++ if (test_bit(MMF_VM_MERGEABLE, &mm->flags)) ++ __ksm_exit(mm); ++} ++ ++#elif defined(CONFIG_UKSM) ++static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) ++{ ++ return 0; ++} ++ ++static inline void ksm_exit(struct mm_struct *mm) ++{ ++} ++#endif /* !CONFIG_UKSM */ ++ + #else /* !CONFIG_KSM */ + + static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) +@@ -106,4 +118,6 @@ static inline void ksm_migrate_page(struct page *newpage, struct page *oldpage) + #endif /* CONFIG_MMU */ + #endif /* !CONFIG_KSM */ + ++#include ++ + #endif /* __LINUX_KSM_H */ +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index ca3e517..ae62e7d1 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -357,6 +357,9 @@ struct vm_area_struct { + struct mempolicy *vm_policy; /* NUMA policy for the VMA */ + #endif + struct vm_userfaultfd_ctx vm_userfaultfd_ctx; ++#ifdef CONFIG_UKSM ++ struct vma_slot *uksm_vma_slot; ++#endif + }; + + struct core_thread { +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +index 02069c2..f7cce50 100644 +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -153,6 +153,9 @@ enum zone_stat_item { + WORKINGSET_NODERECLAIM, + NR_ANON_TRANSPARENT_HUGEPAGES, + NR_FREE_CMA_PAGES, ++#ifdef CONFIG_UKSM ++ NR_UKSM_ZERO_PAGES, ++#endif + NR_VM_ZONE_STAT_ITEMS }; + + /* +@@ -817,7 +820,7 @@ static inline int is_highmem_idx(enum zone_type idx) + } + + /** +- * is_highmem - helper function to quickly check if a struct zone is a ++ * is_highmem - helper function to quickly check if a struct zone is a + * highmem zone or not. This is an attempt to keep references + * to ZONE_{DMA/NORMAL/HIGHMEM/etc} in general code to a minimum. + * @zone - pointer to struct zone variable +diff --git a/include/linux/sradix-tree.h b/include/linux/sradix-tree.h +new file mode 100644 +index 0000000..6780fdb +--- /dev/null ++++ b/include/linux/sradix-tree.h +@@ -0,0 +1,77 @@ ++#ifndef _LINUX_SRADIX_TREE_H ++#define _LINUX_SRADIX_TREE_H ++ ++ ++#define INIT_SRADIX_TREE(root, mask) \ ++do { \ ++ (root)->height = 0; \ ++ (root)->gfp_mask = (mask); \ ++ (root)->rnode = NULL; \ ++} while (0) ++ ++#define ULONG_BITS (sizeof(unsigned long) * 8) ++#define SRADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) ++//#define SRADIX_TREE_MAP_SHIFT 6 ++//#define SRADIX_TREE_MAP_SIZE (1UL << SRADIX_TREE_MAP_SHIFT) ++//#define SRADIX_TREE_MAP_MASK (SRADIX_TREE_MAP_SIZE-1) ++ ++struct sradix_tree_node { ++ unsigned int height; /* Height from the bottom */ ++ unsigned int count; ++ unsigned int fulls; /* Number of full sublevel trees */ ++ struct sradix_tree_node *parent; ++ void *stores[0]; ++}; ++ ++/* A simple radix tree implementation */ ++struct sradix_tree_root { ++ unsigned int height; ++ struct sradix_tree_node *rnode; ++ ++ /* Where found to have available empty stores in its sublevels */ ++ struct sradix_tree_node *enter_node; ++ unsigned int shift; ++ unsigned int stores_size; ++ unsigned int mask; ++ unsigned long min; /* The first hole index */ ++ unsigned long num; ++ //unsigned long *height_to_maxindex; ++ ++ /* How the node is allocated and freed. */ ++ struct sradix_tree_node *(*alloc)(void); ++ void (*free)(struct sradix_tree_node *node); ++ ++ /* When a new node is added and removed */ ++ void (*extend)(struct sradix_tree_node *parent, struct sradix_tree_node *child); ++ void (*assign)(struct sradix_tree_node *node, unsigned index, void *item); ++ void (*rm)(struct sradix_tree_node *node, unsigned offset); ++}; ++ ++struct sradix_tree_path { ++ struct sradix_tree_node *node; ++ int offset; ++}; ++ ++static inline ++void init_sradix_tree_root(struct sradix_tree_root *root, unsigned long shift) ++{ ++ root->height = 0; ++ root->rnode = NULL; ++ root->shift = shift; ++ root->stores_size = 1UL << shift; ++ root->mask = root->stores_size - 1; ++} ++ ++ ++extern void *sradix_tree_next(struct sradix_tree_root *root, ++ struct sradix_tree_node *node, unsigned long index, ++ int (*iter)(void *, unsigned long)); ++ ++extern int sradix_tree_enter(struct sradix_tree_root *root, void **item, int num); ++ ++extern void sradix_tree_delete_from_leaf(struct sradix_tree_root *root, ++ struct sradix_tree_node *node, unsigned long index); ++ ++extern void *sradix_tree_lookup(struct sradix_tree_root *root, unsigned long index); ++ ++#endif /* _LINUX_SRADIX_TREE_H */ +diff --git a/include/linux/uksm.h b/include/linux/uksm.h +new file mode 100644 +index 0000000..825f05e +--- /dev/null ++++ b/include/linux/uksm.h +@@ -0,0 +1,149 @@ ++#ifndef __LINUX_UKSM_H ++#define __LINUX_UKSM_H ++/* ++ * Memory merging support. ++ * ++ * This code enables dynamic sharing of identical pages found in different ++ * memory areas, even if they are not shared by fork(). ++ */ ++ ++/* if !CONFIG_UKSM this file should not be compiled at all. */ ++#ifdef CONFIG_UKSM ++ ++#include ++#include ++#include ++#include ++#include ++ ++extern unsigned long zero_pfn __read_mostly; ++extern unsigned long uksm_zero_pfn __read_mostly; ++extern struct page *empty_uksm_zero_page; ++ ++/* must be done before linked to mm */ ++extern void uksm_vma_add_new(struct vm_area_struct *vma); ++extern void uksm_remove_vma(struct vm_area_struct *vma); ++ ++#define UKSM_SLOT_NEED_SORT (1 << 0) ++#define UKSM_SLOT_NEED_RERAND (1 << 1) ++#define UKSM_SLOT_SCANNED (1 << 2) /* It's scanned in this round */ ++#define UKSM_SLOT_FUL_SCANNED (1 << 3) ++#define UKSM_SLOT_IN_UKSM (1 << 4) ++ ++struct vma_slot { ++ struct sradix_tree_node *snode; ++ unsigned long sindex; ++ ++ struct list_head slot_list; ++ unsigned long fully_scanned_round; ++ unsigned long dedup_num; ++ unsigned long pages_scanned; ++ unsigned long this_sampled; ++ unsigned long last_scanned; ++ unsigned long pages_to_scan; ++ struct scan_rung *rung; ++ struct page **rmap_list_pool; ++ unsigned int *pool_counts; ++ unsigned long pool_size; ++ struct vm_area_struct *vma; ++ struct mm_struct *mm; ++ unsigned long ctime_j; ++ unsigned long pages; ++ unsigned long flags; ++ unsigned long pages_cowed; /* pages cowed this round */ ++ unsigned long pages_merged; /* pages merged this round */ ++ unsigned long pages_bemerged; ++ ++ /* when it has page merged in this eval round */ ++ struct list_head dedup_list; ++}; ++ ++static inline void uksm_unmap_zero_page(pte_t pte) ++{ ++ if (pte_pfn(pte) == uksm_zero_pfn) ++ __dec_zone_page_state(empty_uksm_zero_page, NR_UKSM_ZERO_PAGES); ++} ++ ++static inline void uksm_map_zero_page(pte_t pte) ++{ ++ if (pte_pfn(pte) == uksm_zero_pfn) ++ __inc_zone_page_state(empty_uksm_zero_page, NR_UKSM_ZERO_PAGES); ++} ++ ++static inline void uksm_cow_page(struct vm_area_struct *vma, struct page *page) ++{ ++ if (vma->uksm_vma_slot && PageKsm(page)) ++ vma->uksm_vma_slot->pages_cowed++; ++} ++ ++static inline void uksm_cow_pte(struct vm_area_struct *vma, pte_t pte) ++{ ++ if (vma->uksm_vma_slot && pte_pfn(pte) == uksm_zero_pfn) ++ vma->uksm_vma_slot->pages_cowed++; ++} ++ ++static inline int uksm_flags_can_scan(unsigned long vm_flags) ++{ ++#ifdef VM_SAO ++ if (vm_flags & VM_SAO) ++ return 0; ++#endif ++ ++ return !(vm_flags & (VM_PFNMAP | VM_IO | VM_DONTEXPAND | ++ VM_HUGETLB | VM_MIXEDMAP | VM_SHARED ++ | VM_MAYSHARE | VM_GROWSUP | VM_GROWSDOWN)); ++} ++ ++static inline void uksm_vm_flags_mod(unsigned long *vm_flags_p) ++{ ++ if (uksm_flags_can_scan(*vm_flags_p)) ++ *vm_flags_p |= VM_MERGEABLE; ++} ++ ++/* ++ * Just a wrapper for BUG_ON for where ksm_zeropage must not be. TODO: it will ++ * be removed when uksm zero page patch is stable enough. ++ */ ++static inline void uksm_bugon_zeropage(pte_t pte) ++{ ++ BUG_ON(pte_pfn(pte) == uksm_zero_pfn); ++} ++#else ++static inline void uksm_vma_add_new(struct vm_area_struct *vma) ++{ ++} ++ ++static inline void uksm_remove_vma(struct vm_area_struct *vma) ++{ ++} ++ ++static inline void uksm_unmap_zero_page(pte_t pte) ++{ ++} ++ ++static inline void uksm_map_zero_page(pte_t pte) ++{ ++} ++ ++static inline void uksm_cow_page(struct vm_area_struct *vma, struct page *page) ++{ ++} ++ ++static inline void uksm_cow_pte(struct vm_area_struct *vma, pte_t pte) ++{ ++} ++ ++static inline int uksm_flags_can_scan(unsigned long vm_flags) ++{ ++ return 0; ++} ++ ++static inline void uksm_vm_flags_mod(unsigned long *vm_flags_p) ++{ ++} ++ ++static inline void uksm_bugon_zeropage(pte_t pte) ++{ ++} ++#endif /* !CONFIG_UKSM */ ++#endif /* __LINUX_UKSM_H */ +diff --git a/kernel/fork.c b/kernel/fork.c +index aea4f4d..f93e114 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -459,7 +459,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) + goto fail_nomem; + charge = len; + } +- tmp = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL); ++ tmp = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); + if (!tmp) + goto fail_nomem; + *tmp = *mpnt; +@@ -512,7 +512,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) + __vma_link_rb(mm, tmp, rb_link, rb_parent); + rb_link = &tmp->vm_rb.rb_right; + rb_parent = &tmp->vm_rb; +- ++ uksm_vma_add_new(tmp); + mm->map_count++; + retval = copy_page_range(mm, oldmm, mpnt); + +diff --git a/lib/Makefile b/lib/Makefile +index ff6a7a6..ac0bb55 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -20,7 +20,7 @@ KCOV_INSTRUMENT_dynamic_debug.o := n + KCOV_INSTRUMENT_hweight.o := n + + lib-y := ctype.o string.o vsprintf.o cmdline.o \ +- rbtree.o radix-tree.o dump_stack.o timerqueue.o\ ++ rbtree.o radix-tree.o sradix-tree.o dump_stack.o timerqueue.o\ + idr.o int_sqrt.o extable.o \ + sha1.o md5.o irq_regs.o argv_split.o \ + flex_proportions.o ratelimit.o show_mem.o \ +diff --git a/lib/sradix-tree.c b/lib/sradix-tree.c +new file mode 100644 +index 0000000..8d06329 +--- /dev/null ++++ b/lib/sradix-tree.c +@@ -0,0 +1,476 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static inline int sradix_node_full(struct sradix_tree_root *root, struct sradix_tree_node *node) ++{ ++ return node->fulls == root->stores_size || ++ (node->height == 1 && node->count == root->stores_size); ++} ++ ++/* ++ * Extend a sradix tree so it can store key @index. ++ */ ++static int sradix_tree_extend(struct sradix_tree_root *root, unsigned long index) ++{ ++ struct sradix_tree_node *node; ++ unsigned int height; ++ ++ if (unlikely(root->rnode == NULL)) { ++ if (!(node = root->alloc())) ++ return -ENOMEM; ++ ++ node->height = 1; ++ root->rnode = node; ++ root->height = 1; ++ } ++ ++ /* Figure out what the height should be. */ ++ height = root->height; ++ index >>= root->shift * height; ++ ++ while (index) { ++ index >>= root->shift; ++ height++; ++ } ++ ++ while (height > root->height) { ++ unsigned int newheight; ++ if (!(node = root->alloc())) ++ return -ENOMEM; ++ ++ /* Increase the height. */ ++ node->stores[0] = root->rnode; ++ root->rnode->parent = node; ++ if (root->extend) ++ root->extend(node, root->rnode); ++ ++ newheight = root->height + 1; ++ node->height = newheight; ++ node->count = 1; ++ if (sradix_node_full(root, root->rnode)) ++ node->fulls = 1; ++ ++ root->rnode = node; ++ root->height = newheight; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Search the next item from the current node, that is not NULL ++ * and can satify root->iter(). ++ */ ++void *sradix_tree_next(struct sradix_tree_root *root, ++ struct sradix_tree_node *node, unsigned long index, ++ int (*iter)(void *item, unsigned long height)) ++{ ++ unsigned long offset; ++ void *item; ++ ++ if (unlikely(node == NULL)) { ++ node = root->rnode; ++ for (offset = 0; offset < root->stores_size; offset++) { ++ item = node->stores[offset]; ++ if (item && (!iter || iter(item, node->height))) ++ break; ++ } ++ ++ if (unlikely(offset >= root->stores_size)) ++ return NULL; ++ ++ if (node->height == 1) ++ return item; ++ else ++ goto go_down; ++ } ++ ++ while (node) { ++ offset = (index & root->mask) + 1; ++ for (;offset < root->stores_size; offset++) { ++ item = node->stores[offset]; ++ if (item && (!iter || iter(item, node->height))) ++ break; ++ } ++ ++ if (offset < root->stores_size) ++ break; ++ ++ node = node->parent; ++ index >>= root->shift; ++ } ++ ++ if (!node) ++ return NULL; ++ ++ while (node->height > 1) { ++go_down: ++ node = item; ++ for (offset = 0; offset < root->stores_size; offset++) { ++ item = node->stores[offset]; ++ if (item && (!iter || iter(item, node->height))) ++ break; ++ } ++ ++ if (unlikely(offset >= root->stores_size)) ++ return NULL; ++ } ++ ++ BUG_ON(offset > root->stores_size); ++ ++ return item; ++} ++ ++/* ++ * Blindly insert the item to the tree. Typically, we reuse the ++ * first empty store item. ++ */ ++int sradix_tree_enter(struct sradix_tree_root *root, void **item, int num) ++{ ++ unsigned long index; ++ unsigned int height; ++ struct sradix_tree_node *node, *tmp = NULL; ++ int offset, offset_saved; ++ void **store = NULL; ++ int error, i, j, shift; ++ ++go_on: ++ index = root->min; ++ ++ if (root->enter_node && !sradix_node_full(root, root->enter_node)) { ++ node = root->enter_node; ++ BUG_ON((index >> (root->shift * root->height))); ++ } else { ++ node = root->rnode; ++ if (node == NULL || (index >> (root->shift * root->height)) ++ || sradix_node_full(root, node)) { ++ error = sradix_tree_extend(root, index); ++ if (error) ++ return error; ++ ++ node = root->rnode; ++ } ++ } ++ ++ ++ height = node->height; ++ shift = (height - 1) * root->shift; ++ offset = (index >> shift) & root->mask; ++ while (shift > 0) { ++ offset_saved = offset; ++ for (; offset < root->stores_size; offset++) { ++ store = &node->stores[offset]; ++ tmp = *store; ++ ++ if (!tmp || !sradix_node_full(root, tmp)) ++ break; ++ } ++ BUG_ON(offset >= root->stores_size); ++ ++ if (offset != offset_saved) { ++ index += (offset - offset_saved) << shift; ++ index &= ~((1UL << shift) - 1); ++ } ++ ++ if (!tmp) { ++ if (!(tmp = root->alloc())) ++ return -ENOMEM; ++ ++ tmp->height = shift / root->shift; ++ *store = tmp; ++ tmp->parent = node; ++ node->count++; ++// if (root->extend) ++// root->extend(node, tmp); ++ } ++ ++ node = tmp; ++ shift -= root->shift; ++ offset = (index >> shift) & root->mask; ++ } ++ ++ BUG_ON(node->height != 1); ++ ++ ++ store = &node->stores[offset]; ++ for (i = 0, j = 0; ++ j < root->stores_size - node->count && ++ i < root->stores_size - offset && j < num; i++) { ++ if (!store[i]) { ++ store[i] = item[j]; ++ if (root->assign) ++ root->assign(node, index + i, item[j]); ++ j++; ++ } ++ } ++ ++ node->count += j; ++ root->num += j; ++ num -= j; ++ ++ while (sradix_node_full(root, node)) { ++ node = node->parent; ++ if (!node) ++ break; ++ ++ node->fulls++; ++ } ++ ++ if (unlikely(!node)) { ++ /* All nodes are full */ ++ root->min = 1 << (root->height * root->shift); ++ root->enter_node = NULL; ++ } else { ++ root->min = index + i - 1; ++ root->min |= (1UL << (node->height - 1)) - 1; ++ root->min++; ++ root->enter_node = node; ++ } ++ ++ if (num) { ++ item += j; ++ goto go_on; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * sradix_tree_shrink - shrink height of a sradix tree to minimal ++ * @root sradix tree root ++ * ++ */ ++static inline void sradix_tree_shrink(struct sradix_tree_root *root) ++{ ++ /* try to shrink tree height */ ++ while (root->height > 1) { ++ struct sradix_tree_node *to_free = root->rnode; ++ ++ /* ++ * The candidate node has more than one child, or its child ++ * is not at the leftmost store, we cannot shrink. ++ */ ++ if (to_free->count != 1 || !to_free->stores[0]) ++ break; ++ ++ root->rnode = to_free->stores[0]; ++ root->rnode->parent = NULL; ++ root->height--; ++ if (unlikely(root->enter_node == to_free)) { ++ root->enter_node = NULL; ++ } ++ root->free(to_free); ++ } ++} ++ ++/* ++ * Del the item on the known leaf node and index ++ */ ++void sradix_tree_delete_from_leaf(struct sradix_tree_root *root, ++ struct sradix_tree_node *node, unsigned long index) ++{ ++ unsigned int offset; ++ struct sradix_tree_node *start, *end; ++ ++ BUG_ON(node->height != 1); ++ ++ start = node; ++ while (node && !(--node->count)) ++ node = node->parent; ++ ++ end = node; ++ if (!node) { ++ root->rnode = NULL; ++ root->height = 0; ++ root->min = 0; ++ root->num = 0; ++ root->enter_node = NULL; ++ } else { ++ offset = (index >> (root->shift * (node->height - 1))) & root->mask; ++ if (root->rm) ++ root->rm(node, offset); ++ node->stores[offset] = NULL; ++ root->num--; ++ if (root->min > index) { ++ root->min = index; ++ root->enter_node = node; ++ } ++ } ++ ++ if (start != end) { ++ do { ++ node = start; ++ start = start->parent; ++ if (unlikely(root->enter_node == node)) ++ root->enter_node = end; ++ root->free(node); ++ } while (start != end); ++ ++ /* ++ * Note that shrink may free "end", so enter_node still need to ++ * be checked inside. ++ */ ++ sradix_tree_shrink(root); ++ } else if (node->count == root->stores_size - 1) { ++ /* It WAS a full leaf node. Update the ancestors */ ++ node = node->parent; ++ while (node) { ++ node->fulls--; ++ if (node->fulls != root->stores_size - 1) ++ break; ++ ++ node = node->parent; ++ } ++ } ++} ++ ++void *sradix_tree_lookup(struct sradix_tree_root *root, unsigned long index) ++{ ++ unsigned int height, offset; ++ struct sradix_tree_node *node; ++ int shift; ++ ++ node = root->rnode; ++ if (node == NULL || (index >> (root->shift * root->height))) ++ return NULL; ++ ++ height = root->height; ++ shift = (height - 1) * root->shift; ++ ++ do { ++ offset = (index >> shift) & root->mask; ++ node = node->stores[offset]; ++ if (!node) ++ return NULL; ++ ++ shift -= root->shift; ++ } while (shift >= 0); ++ ++ return node; ++} ++ ++/* ++ * Return the item if it exists, otherwise create it in place ++ * and return the created item. ++ */ ++void *sradix_tree_lookup_create(struct sradix_tree_root *root, ++ unsigned long index, void *(*item_alloc)(void)) ++{ ++ unsigned int height, offset; ++ struct sradix_tree_node *node, *tmp; ++ void *item; ++ int shift, error; ++ ++ if (root->rnode == NULL || (index >> (root->shift * root->height))) { ++ if (item_alloc) { ++ error = sradix_tree_extend(root, index); ++ if (error) ++ return NULL; ++ } else { ++ return NULL; ++ } ++ } ++ ++ node = root->rnode; ++ height = root->height; ++ shift = (height - 1) * root->shift; ++ ++ do { ++ offset = (index >> shift) & root->mask; ++ if (!node->stores[offset]) { ++ if (!(tmp = root->alloc())) ++ return NULL; ++ ++ tmp->height = shift / root->shift; ++ node->stores[offset] = tmp; ++ tmp->parent = node; ++ node->count++; ++ node = tmp; ++ } else { ++ node = node->stores[offset]; ++ } ++ ++ shift -= root->shift; ++ } while (shift > 0); ++ ++ BUG_ON(node->height != 1); ++ offset = index & root->mask; ++ if (node->stores[offset]) { ++ return node->stores[offset]; ++ } else if (item_alloc) { ++ if (!(item = item_alloc())) ++ return NULL; ++ ++ node->stores[offset] = item; ++ ++ /* ++ * NOTE: we do NOT call root->assign here, since this item is ++ * newly created by us having no meaning. Caller can call this ++ * if it's necessary to do so. ++ */ ++ ++ node->count++; ++ root->num++; ++ ++ while (sradix_node_full(root, node)) { ++ node = node->parent; ++ if (!node) ++ break; ++ ++ node->fulls++; ++ } ++ ++ if (unlikely(!node)) { ++ /* All nodes are full */ ++ root->min = 1 << (root->height * root->shift); ++ } else { ++ if (root->min == index) { ++ root->min |= (1UL << (node->height - 1)) - 1; ++ root->min++; ++ root->enter_node = node; ++ } ++ } ++ ++ return item; ++ } else { ++ return NULL; ++ } ++ ++} ++ ++int sradix_tree_delete(struct sradix_tree_root *root, unsigned long index) ++{ ++ unsigned int height, offset; ++ struct sradix_tree_node *node; ++ int shift; ++ ++ node = root->rnode; ++ if (node == NULL || (index >> (root->shift * root->height))) ++ return -ENOENT; ++ ++ height = root->height; ++ shift = (height - 1) * root->shift; ++ ++ do { ++ offset = (index >> shift) & root->mask; ++ node = node->stores[offset]; ++ if (!node) ++ return -ENOENT; ++ ++ shift -= root->shift; ++ } while (shift > 0); ++ ++ offset = index & root->mask; ++ if (!node->stores[offset]) ++ return -ENOENT; ++ ++ sradix_tree_delete_from_leaf(root, node, index); ++ ++ return 0; ++} +diff --git a/mm/Kconfig b/mm/Kconfig +index 3e2daef..165b60e 100644 +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -332,6 +332,32 @@ config KSM + See Documentation/vm/ksm.txt for more information: KSM is inactive + until a program has madvised that an area is MADV_MERGEABLE, and + root has set /sys/kernel/mm/ksm/run to 1 (if CONFIG_SYSFS is set). ++choice ++ prompt "Choose UKSM/KSM strategy" ++ default UKSM ++ depends on KSM ++ help ++ This option allows to select a UKSM/KSM stragety. ++ ++config UKSM ++ bool "Ultra-KSM for page merging" ++ depends on KSM ++ help ++ UKSM is inspired by the Linux kernel project \u2014 KSM(Kernel Same ++ page Merging), but with a fundamentally rewritten core algorithm. With ++ an advanced algorithm, UKSM now can transparently scans all anonymously ++ mapped user space applications with an significantly improved scan speed ++ and CPU efficiency. Since KVM is friendly to KSM, KVM can also benefit from ++ UKSM. Now UKSM has its first stable release and first real world enterprise user. ++ For more information, please goto its project page. ++ (www.kerneldedup.org) ++ ++config KSM_LEGACY ++ bool "Legacy KSM implementation" ++ depends on KSM ++ help ++ The legacy KSM implementation from Redhat. ++endchoice + + config DEFAULT_MMAP_MIN_ADDR + int "Low address space to protect from user allocation" +diff --git a/mm/Makefile b/mm/Makefile +index 78c6f7d..7e7cd8a 100644 +--- a/mm/Makefile ++++ b/mm/Makefile +@@ -63,7 +63,8 @@ obj-$(CONFIG_SPARSEMEM) += sparse.o + obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o + obj-$(CONFIG_SLOB) += slob.o + obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o +-obj-$(CONFIG_KSM) += ksm.o ++obj-$(CONFIG_KSM_LEGACY) += ksm.o ++obj-$(CONFIG_UKSM) += uksm.o + obj-$(CONFIG_PAGE_POISONING) += page_poison.o + obj-$(CONFIG_SLAB) += slab.o + obj-$(CONFIG_SLUB) += slub.o +diff --git a/mm/memory.c b/mm/memory.c +index 9e04681..02200d3 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -124,6 +124,28 @@ unsigned long highest_memmap_pfn __read_mostly; + + EXPORT_SYMBOL(zero_pfn); + ++#ifdef CONFIG_UKSM ++unsigned long uksm_zero_pfn __read_mostly; ++EXPORT_SYMBOL_GPL(uksm_zero_pfn); ++struct page *empty_uksm_zero_page; ++ ++static int __init setup_uksm_zero_page(void) ++{ ++ unsigned long addr; ++ addr = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 0); ++ if (!addr) ++ panic("Oh boy, that early out of memory?"); ++ ++ empty_uksm_zero_page = virt_to_page((void *) addr); ++ SetPageReserved(empty_uksm_zero_page); ++ ++ uksm_zero_pfn = page_to_pfn(empty_uksm_zero_page); ++ ++ return 0; ++} ++core_initcall(setup_uksm_zero_page); ++#endif ++ + /* + * CONFIG_MMU architectures set up ZERO_PAGE in their paging_init() + */ +@@ -135,6 +157,7 @@ static int __init init_zero_pfn(void) + core_initcall(init_zero_pfn); + + ++ + #if defined(SPLIT_RSS_COUNTING) + + void sync_mm_rss(struct mm_struct *mm) +@@ -905,6 +928,11 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, + get_page(page); + page_dup_rmap(page, false); + rss[mm_counter(page)]++; ++ ++ /* Should return NULL in vm_normal_page() */ ++ uksm_bugon_zeropage(pte); ++ } else { ++ uksm_map_zero_page(pte); + } + + out_set_pte: +@@ -1138,8 +1166,10 @@ again: + ptent = ptep_get_and_clear_full(mm, addr, pte, + tlb->fullmm); + tlb_remove_tlb_entry(tlb, pte, addr); +- if (unlikely(!page)) ++ if (unlikely(!page)) { ++ uksm_unmap_zero_page(ptent); + continue; ++ } + + if (!PageAnon(page)) { + if (pte_dirty(ptent)) { +@@ -1995,8 +2025,10 @@ static inline void cow_user_page(struct page *dst, struct page *src, unsigned lo + clear_page(kaddr); + kunmap_atomic(kaddr); + flush_dcache_page(dst); +- } else ++ } else { + copy_user_highpage(dst, src, va, vma); ++ uksm_cow_page(vma, src); ++ } + } + + static gfp_t __get_fault_gfp_mask(struct vm_area_struct *vma) +@@ -2141,6 +2173,7 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma, + new_page = alloc_zeroed_user_highpage_movable(vma, address); + if (!new_page) + goto oom; ++ uksm_cow_pte(vma, orig_pte); + } else { + new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); + if (!new_page) +@@ -2166,7 +2199,9 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma, + mm_counter_file(old_page)); + inc_mm_counter_fast(mm, MM_ANONPAGES); + } ++ uksm_bugon_zeropage(orig_pte); + } else { ++ uksm_unmap_zero_page(orig_pte); + inc_mm_counter_fast(mm, MM_ANONPAGES); + } + flush_cache_page(vma, address, pte_pfn(orig_pte)); +diff --git a/mm/mmap.c b/mm/mmap.c +index de2c176..ce60715 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -164,6 +165,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) + if (vma->vm_file) + fput(vma->vm_file); + mpol_put(vma_policy(vma)); ++ uksm_remove_vma(vma); + kmem_cache_free(vm_area_cachep, vma); + return next; + } +@@ -629,9 +631,16 @@ int vma_adjust(struct vm_area_struct *vma, unsigned long start, + long adjust_next = 0; + int remove_next = 0; + ++/* ++ * to avoid deadlock, ksm_remove_vma must be done before any spin_lock is ++ * acquired ++ */ ++ uksm_remove_vma(vma); ++ + if (next && !insert) { + struct vm_area_struct *exporter = NULL; + ++ uksm_remove_vma(next); + if (end >= next->vm_end) { + /* + * vma expands, overlapping all the next, and +@@ -725,6 +734,7 @@ again: remove_next = 1 + (end > next->vm_end); + end_changed = true; + } + vma->vm_pgoff = pgoff; ++ + if (adjust_next) { + next->vm_start += adjust_next << PAGE_SHIFT; + next->vm_pgoff += adjust_next; +@@ -795,16 +805,22 @@ again: remove_next = 1 + (end > next->vm_end); + * up the code too much to do both in one go. + */ + next = vma->vm_next; +- if (remove_next == 2) ++ if (remove_next == 2) { ++ uksm_remove_vma(next); + goto again; +- else if (next) ++ } else if (next) { + vma_gap_update(next); +- else ++ } else { + mm->highest_vm_end = end; ++ } ++ } else { ++ if (next && !insert) ++ uksm_vma_add_new(next); + } + if (insert && file) + uprobe_mmap(insert); + ++ uksm_vma_add_new(vma); + validate_mm(mm); + + return 0; +@@ -1196,6 +1212,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr, + vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) | + mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; + ++ /* If uksm is enabled, we add VM_MERGABLE to new VMAs. */ ++ uksm_vm_flags_mod(&vm_flags); ++ + if (flags & MAP_LOCKED) + if (!can_do_mlock()) + return -EPERM; +@@ -1534,6 +1553,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, + allow_write_access(file); + } + file = vma->vm_file; ++ uksm_vma_add_new(vma); + out: + perf_event_mmap(vma); + +@@ -1575,6 +1595,7 @@ allow_write_and_free_vma: + if (vm_flags & VM_DENYWRITE) + allow_write_access(file); + free_vma: ++ uksm_remove_vma(vma); + kmem_cache_free(vm_area_cachep, vma); + unacct_error: + if (charged) +@@ -2369,6 +2390,8 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, + else + err = vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new); + ++ uksm_vma_add_new(new); ++ + /* Success. */ + if (!err) + return 0; +@@ -2639,6 +2662,7 @@ static int do_brk(unsigned long addr, unsigned long len) + return 0; + + flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags; ++ uksm_vm_flags_mod(&flags); + + error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED); + if (offset_in_page(error)) +@@ -2696,6 +2720,7 @@ static int do_brk(unsigned long addr, unsigned long len) + vma->vm_flags = flags; + vma->vm_page_prot = vm_get_page_prot(flags); + vma_link(mm, vma, prev, rb_link, rb_parent); ++ uksm_vma_add_new(vma); + out: + perf_event_mmap(vma); + mm->total_vm += len >> PAGE_SHIFT; +@@ -2734,6 +2759,12 @@ void exit_mmap(struct mm_struct *mm) + /* mm's last user has gone, and its about to be pulled down */ + mmu_notifier_release(mm); + ++ /* ++ * Taking write lock on mmap_sem does not harm others, ++ * but it's crucial for uksm to avoid races. ++ */ ++ down_write(&mm->mmap_sem); ++ + if (mm->locked_vm) { + vma = mm->mmap; + while (vma) { +@@ -2769,6 +2800,11 @@ void exit_mmap(struct mm_struct *mm) + vma = remove_vma(vma); + } + vm_unacct_memory(nr_accounted); ++ ++ mm->mmap = NULL; ++ mm->mm_rb = RB_ROOT; ++ vmacache_invalidate(mm); ++ up_write(&mm->mmap_sem); + } + + /* Insert vm structure into process list sorted by address +@@ -2878,6 +2914,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, + new_vma->vm_ops->open(new_vma); + vma_link(mm, new_vma, prev, rb_link, rb_parent); + *need_rmap_locks = false; ++ uksm_vma_add_new(new_vma); + } + return new_vma; + +@@ -3015,6 +3052,7 @@ static struct vm_area_struct *__install_special_mapping( + vm_stat_account(mm, vma->vm_flags, len >> PAGE_SHIFT); + + perf_event_mmap(vma); ++ uksm_vma_add_new(vma); + + return vma; + +diff --git a/mm/rmap.c b/mm/rmap.c +index 701b93f..64ba784 100644 +--- a/mm/rmap.c ++++ b/mm/rmap.c +@@ -1110,9 +1110,9 @@ void page_move_anon_rmap(struct page *page, struct vm_area_struct *vma) + + /** + * __page_set_anon_rmap - set up new anonymous rmap +- * @page: Page to add to rmap ++ * @page: Page to add to rmap + * @vma: VM area to add page to. +- * @address: User virtual address of the mapping ++ * @address: User virtual address of the mapping + * @exclusive: the page is exclusively owned by the current process + */ + static void __page_set_anon_rmap(struct page *page, +diff --git a/mm/uksm.c b/mm/uksm.c +new file mode 100644 +index 0000000..039192f +--- /dev/null ++++ b/mm/uksm.c +@@ -0,0 +1,5518 @@ ++/* ++ * Ultra KSM. Copyright (C) 2011-2012 Nai Xia ++ * ++ * This is an improvement upon KSM. Some basic data structures and routines ++ * are borrowed from ksm.c . ++ * ++ * Its new features: ++ * 1. Full system scan: ++ * It automatically scans all user processes' anonymous VMAs. Kernel-user ++ * interaction to submit a memory area to KSM is no longer needed. ++ * ++ * 2. Rich area detection: ++ * It automatically detects rich areas containing abundant duplicated ++ * pages based. Rich areas are given a full scan speed. Poor areas are ++ * sampled at a reasonable speed with very low CPU consumption. ++ * ++ * 3. Ultra Per-page scan speed improvement: ++ * A new hash algorithm is proposed. As a result, on a machine with ++ * Core(TM)2 Quad Q9300 CPU in 32-bit mode and 800MHZ DDR2 main memory, it ++ * can scan memory areas that does not contain duplicated pages at speed of ++ * 627MB/sec ~ 2445MB/sec and can merge duplicated areas at speed of ++ * 477MB/sec ~ 923MB/sec. ++ * ++ * 4. Thrashing area avoidance: ++ * Thrashing area(an VMA that has frequent Ksm page break-out) can be ++ * filtered out. My benchmark shows it's more efficient than KSM's per-page ++ * hash value based volatile page detection. ++ * ++ * ++ * 5. Misc changes upon KSM: ++ * * It has a fully x86-opitmized memcmp dedicated for 4-byte-aligned page ++ * comparison. It's much faster than default C version on x86. ++ * * rmap_item now has an struct *page member to loosely cache a ++ * address-->page mapping, which reduces too much time-costly ++ * follow_page(). ++ * * The VMA creation/exit procedures are hooked to let the Ultra KSM know. ++ * * try_to_merge_two_pages() now can revert a pte if it fails. No break_ ++ * ksm is needed for this case. ++ * ++ * 6. Full Zero Page consideration(contributed by Figo Zhang) ++ * Now uksmd consider full zero pages as special pages and merge them to an ++ * special unswappable uksm zero page. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "internal.h" ++ ++#ifdef CONFIG_X86 ++#undef memcmp ++ ++#ifdef CONFIG_X86_32 ++#define memcmp memcmpx86_32 ++/* ++ * Compare 4-byte-aligned address s1 and s2, with length n ++ */ ++int memcmpx86_32(void *s1, void *s2, size_t n) ++{ ++ size_t num = n / 4; ++ register int res; ++ ++ __asm__ __volatile__ ++ ( ++ "testl %3,%3\n\t" ++ "repe; cmpsd\n\t" ++ "je 1f\n\t" ++ "sbbl %0,%0\n\t" ++ "orl $1,%0\n" ++ "1:" ++ : "=&a" (res), "+&S" (s1), "+&D" (s2), "+&c" (num) ++ : "0" (0) ++ : "cc"); ++ ++ return res; ++} ++ ++/* ++ * Check the page is all zero ? ++ */ ++static int is_full_zero(const void *s1, size_t len) ++{ ++ unsigned char same; ++ ++ len /= 4; ++ ++ __asm__ __volatile__ ++ ("repe; scasl;" ++ "sete %0" ++ : "=qm" (same), "+D" (s1), "+c" (len) ++ : "a" (0) ++ : "cc"); ++ ++ return same; ++} ++ ++ ++#elif defined(CONFIG_X86_64) ++#define memcmp memcmpx86_64 ++/* ++ * Compare 8-byte-aligned address s1 and s2, with length n ++ */ ++int memcmpx86_64(void *s1, void *s2, size_t n) ++{ ++ size_t num = n / 8; ++ register int res; ++ ++ __asm__ __volatile__ ++ ( ++ "testq %q3,%q3\n\t" ++ "repe; cmpsq\n\t" ++ "je 1f\n\t" ++ "sbbq %q0,%q0\n\t" ++ "orq $1,%q0\n" ++ "1:" ++ : "=&a" (res), "+&S" (s1), "+&D" (s2), "+&c" (num) ++ : "0" (0) ++ : "cc"); ++ ++ return res; ++} ++ ++static int is_full_zero(const void *s1, size_t len) ++{ ++ unsigned char same; ++ ++ len /= 8; ++ ++ __asm__ __volatile__ ++ ("repe; scasq;" ++ "sete %0" ++ : "=qm" (same), "+D" (s1), "+c" (len) ++ : "a" (0) ++ : "cc"); ++ ++ return same; ++} ++ ++#endif ++#else ++static int is_full_zero(const void *s1, size_t len) ++{ ++ unsigned long *src = s1; ++ int i; ++ ++ len /= sizeof(*src); ++ ++ for (i = 0; i < len; i++) { ++ if (src[i]) ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++#define UKSM_RUNG_ROUND_FINISHED (1 << 0) ++#define TIME_RATIO_SCALE 10000 ++ ++#define SLOT_TREE_NODE_SHIFT 8 ++#define SLOT_TREE_NODE_STORE_SIZE (1UL << SLOT_TREE_NODE_SHIFT) ++struct slot_tree_node { ++ unsigned long size; ++ struct sradix_tree_node snode; ++ void *stores[SLOT_TREE_NODE_STORE_SIZE]; ++}; ++ ++static struct kmem_cache *slot_tree_node_cachep; ++ ++static struct sradix_tree_node *slot_tree_node_alloc(void) ++{ ++ struct slot_tree_node *p; ++ p = kmem_cache_zalloc(slot_tree_node_cachep, GFP_KERNEL); ++ if (!p) ++ return NULL; ++ ++ return &p->snode; ++} ++ ++static void slot_tree_node_free(struct sradix_tree_node *node) ++{ ++ struct slot_tree_node *p; ++ ++ p = container_of(node, struct slot_tree_node, snode); ++ kmem_cache_free(slot_tree_node_cachep, p); ++} ++ ++static void slot_tree_node_extend(struct sradix_tree_node *parent, ++ struct sradix_tree_node *child) ++{ ++ struct slot_tree_node *p, *c; ++ ++ p = container_of(parent, struct slot_tree_node, snode); ++ c = container_of(child, struct slot_tree_node, snode); ++ ++ p->size += c->size; ++} ++ ++void slot_tree_node_assign(struct sradix_tree_node *node, ++ unsigned index, void *item) ++{ ++ struct vma_slot *slot = item; ++ struct slot_tree_node *cur; ++ ++ slot->snode = node; ++ slot->sindex = index; ++ ++ while (node) { ++ cur = container_of(node, struct slot_tree_node, snode); ++ cur->size += slot->pages; ++ node = node->parent; ++ } ++} ++ ++void slot_tree_node_rm(struct sradix_tree_node *node, unsigned offset) ++{ ++ struct vma_slot *slot; ++ struct slot_tree_node *cur; ++ unsigned long pages; ++ ++ if (node->height == 1) { ++ slot = node->stores[offset]; ++ pages = slot->pages; ++ } else { ++ cur = container_of(node->stores[offset], ++ struct slot_tree_node, snode); ++ pages = cur->size; ++ } ++ ++ while (node) { ++ cur = container_of(node, struct slot_tree_node, snode); ++ cur->size -= pages; ++ node = node->parent; ++ } ++} ++ ++unsigned long slot_iter_index; ++int slot_iter(void *item, unsigned long height) ++{ ++ struct slot_tree_node *node; ++ struct vma_slot *slot; ++ ++ if (height == 1) { ++ slot = item; ++ if (slot_iter_index < slot->pages) { ++ /*in this one*/ ++ return 1; ++ } else { ++ slot_iter_index -= slot->pages; ++ return 0; ++ } ++ ++ } else { ++ node = container_of(item, struct slot_tree_node, snode); ++ if (slot_iter_index < node->size) { ++ /*in this one*/ ++ return 1; ++ } else { ++ slot_iter_index -= node->size; ++ return 0; ++ } ++ } ++} ++ ++ ++static inline void slot_tree_init_root(struct sradix_tree_root *root) ++{ ++ init_sradix_tree_root(root, SLOT_TREE_NODE_SHIFT); ++ root->alloc = slot_tree_node_alloc; ++ root->free = slot_tree_node_free; ++ root->extend = slot_tree_node_extend; ++ root->assign = slot_tree_node_assign; ++ root->rm = slot_tree_node_rm; ++} ++ ++void slot_tree_init(void) ++{ ++ slot_tree_node_cachep = kmem_cache_create("slot_tree_node", ++ sizeof(struct slot_tree_node), 0, ++ SLAB_PANIC | SLAB_RECLAIM_ACCOUNT, ++ NULL); ++} ++ ++ ++/* Each rung of this ladder is a list of VMAs having a same scan ratio */ ++struct scan_rung { ++ //struct list_head scanned_list; ++ struct sradix_tree_root vma_root; ++ struct sradix_tree_root vma_root2; ++ ++ struct vma_slot *current_scan; ++ unsigned long current_offset; ++ ++ /* ++ * The initial value for current_offset, it should loop over ++ * [0~ step - 1] to let all slot have its chance to be scanned. ++ */ ++ unsigned long offset_init; ++ unsigned long step; /* dynamic step for current_offset */ ++ unsigned int flags; ++ unsigned long pages_to_scan; ++ //unsigned long fully_scanned_slots; ++ /* ++ * a little bit tricky - if cpu_time_ratio > 0, then the value is the ++ * the cpu time ratio it can spend in rung_i for every scan ++ * period. if < 0, then it is the cpu time ratio relative to the ++ * max cpu percentage user specified. Both in unit of ++ * 1/TIME_RATIO_SCALE ++ */ ++ int cpu_ratio; ++ ++ /* ++ * How long it will take for all slots in this rung to be fully ++ * scanned? If it's zero, we don't care about the cover time: ++ * it's fully scanned. ++ */ ++ unsigned int cover_msecs; ++ //unsigned long vma_num; ++ //unsigned long pages; /* Sum of all slot's pages in rung */ ++}; ++ ++/** ++ * node of either the stable or unstale rbtree ++ * ++ */ ++struct tree_node { ++ struct rb_node node; /* link in the main (un)stable rbtree */ ++ struct rb_root sub_root; /* rb_root for sublevel collision rbtree */ ++ u32 hash; ++ unsigned long count; /* TODO: merged with sub_root */ ++ struct list_head all_list; /* all tree nodes in stable/unstable tree */ ++}; ++ ++/** ++ * struct stable_node - node of the stable rbtree ++ * @node: rb node of this ksm page in the stable tree ++ * @hlist: hlist head of rmap_items using this ksm page ++ * @kpfn: page frame number of this ksm page ++ */ ++struct stable_node { ++ struct rb_node node; /* link in sub-rbtree */ ++ struct tree_node *tree_node; /* it's tree node root in stable tree, NULL if it's in hell list */ ++ struct hlist_head hlist; ++ unsigned long kpfn; ++ u32 hash_max; /* if ==0 then it's not been calculated yet */ ++ struct list_head all_list; /* in a list for all stable nodes */ ++}; ++ ++/** ++ * struct node_vma - group rmap_items linked in a same stable ++ * node together. ++ */ ++struct node_vma { ++ union { ++ struct vma_slot *slot; ++ unsigned long key; /* slot is used as key sorted on hlist */ ++ }; ++ struct hlist_node hlist; ++ struct hlist_head rmap_hlist; ++ struct stable_node *head; ++}; ++ ++/** ++ * struct rmap_item - reverse mapping item for virtual addresses ++ * @rmap_list: next rmap_item in mm_slot's singly-linked rmap_list ++ * @anon_vma: pointer to anon_vma for this mm,address, when in stable tree ++ * @mm: the memory structure this rmap_item is pointing into ++ * @address: the virtual address this rmap_item tracks (+ flags in low bits) ++ * @node: rb node of this rmap_item in the unstable tree ++ * @head: pointer to stable_node heading this list in the stable tree ++ * @hlist: link into hlist of rmap_items hanging off that stable_node ++ */ ++struct rmap_item { ++ struct vma_slot *slot; ++ struct page *page; ++ unsigned long address; /* + low bits used for flags below */ ++ unsigned long hash_round; ++ unsigned long entry_index; ++ union { ++ struct {/* when in unstable tree */ ++ struct rb_node node; ++ struct tree_node *tree_node; ++ u32 hash_max; ++ }; ++ struct { /* when in stable tree */ ++ struct node_vma *head; ++ struct hlist_node hlist; ++ struct anon_vma *anon_vma; ++ }; ++ }; ++} __attribute__((aligned(4))); ++ ++struct rmap_list_entry { ++ union { ++ struct rmap_item *item; ++ unsigned long addr; ++ }; ++ /* lowest bit is used for is_addr tag */ ++} __attribute__((aligned(4))); /* 4 aligned to fit in to pages*/ ++ ++ ++/* Basic data structure definition ends */ ++ ++ ++/* ++ * Flags for rmap_item to judge if it's listed in the stable/unstable tree. ++ * The flags use the low bits of rmap_item.address ++ */ ++#define UNSTABLE_FLAG 0x1 ++#define STABLE_FLAG 0x2 ++#define get_rmap_addr(x) ((x)->address & PAGE_MASK) ++ ++/* ++ * rmap_list_entry helpers ++ */ ++#define IS_ADDR_FLAG 1 ++#define is_addr(ptr) ((unsigned long)(ptr) & IS_ADDR_FLAG) ++#define set_is_addr(ptr) ((ptr) |= IS_ADDR_FLAG) ++#define get_clean_addr(ptr) (((ptr) & ~(__typeof__(ptr))IS_ADDR_FLAG)) ++ ++ ++/* ++ * High speed caches for frequently allocated and freed structs ++ */ ++static struct kmem_cache *rmap_item_cache; ++static struct kmem_cache *stable_node_cache; ++static struct kmem_cache *node_vma_cache; ++static struct kmem_cache *vma_slot_cache; ++static struct kmem_cache *tree_node_cache; ++#define UKSM_KMEM_CACHE(__struct, __flags) kmem_cache_create("uksm_"#__struct,\ ++ sizeof(struct __struct), __alignof__(struct __struct),\ ++ (__flags), NULL) ++ ++/* Array of all scan_rung, uksm_scan_ladder[0] having the minimum scan ratio */ ++#define SCAN_LADDER_SIZE 4 ++static struct scan_rung uksm_scan_ladder[SCAN_LADDER_SIZE]; ++ ++/* The evaluation rounds uksmd has finished */ ++static unsigned long long uksm_eval_round = 1; ++ ++/* ++ * we add 1 to this var when we consider we should rebuild the whole ++ * unstable tree. ++ */ ++static unsigned long uksm_hash_round = 1; ++ ++/* ++ * How many times the whole memory is scanned. ++ */ ++static unsigned long long fully_scanned_round = 1; ++ ++/* The total number of virtual pages of all vma slots */ ++static u64 uksm_pages_total; ++ ++/* The number of pages has been scanned since the start up */ ++static u64 uksm_pages_scanned; ++ ++static u64 scanned_virtual_pages; ++ ++/* The number of pages has been scanned since last encode_benefit call */ ++static u64 uksm_pages_scanned_last; ++ ++/* If the scanned number is tooo large, we encode it here */ ++static u64 pages_scanned_stored; ++ ++static unsigned long pages_scanned_base; ++ ++/* The number of nodes in the stable tree */ ++static unsigned long uksm_pages_shared; ++ ++/* The number of page slots additionally sharing those nodes */ ++static unsigned long uksm_pages_sharing; ++ ++/* The number of nodes in the unstable tree */ ++static unsigned long uksm_pages_unshared; ++ ++/* ++ * Milliseconds ksmd should sleep between scans, ++ * >= 100ms to be consistent with ++ * scan_time_to_sleep_msec() ++ */ ++static unsigned int uksm_sleep_jiffies; ++ ++/* The real value for the uksmd next sleep */ ++static unsigned int uksm_sleep_real; ++ ++/* Saved value for user input uksm_sleep_jiffies when it's enlarged */ ++static unsigned int uksm_sleep_saved; ++ ++/* Max percentage of cpu utilization ksmd can take to scan in one batch */ ++static unsigned int uksm_max_cpu_percentage; ++ ++static int uksm_cpu_governor; ++ ++static char *uksm_cpu_governor_str[4] = { "full", "medium", "low", "quiet" }; ++ ++struct uksm_cpu_preset_s { ++ int cpu_ratio[SCAN_LADDER_SIZE]; ++ unsigned int cover_msecs[SCAN_LADDER_SIZE]; ++ unsigned int max_cpu; /* percentage */ ++}; ++ ++struct uksm_cpu_preset_s uksm_cpu_preset[4] = { ++ { {20, 40, -2500, -10000}, {1000, 500, 200, 50}, 95}, ++ { {20, 30, -2500, -10000}, {1000, 500, 400, 100}, 50}, ++ { {10, 20, -5000, -10000}, {1500, 1000, 1000, 250}, 20}, ++ { {10, 20, 40, 75}, {2000, 1000, 1000, 1000}, 1}, ++}; ++ ++/* The default value for uksm_ema_page_time if it's not initialized */ ++#define UKSM_PAGE_TIME_DEFAULT 500 ++ ++/*cost to scan one page by expotional moving average in nsecs */ ++static unsigned long uksm_ema_page_time = UKSM_PAGE_TIME_DEFAULT; ++ ++/* The expotional moving average alpha weight, in percentage. */ ++#define EMA_ALPHA 20 ++ ++/* ++ * The threshold used to filter out thrashing areas, ++ * If it == 0, filtering is disabled, otherwise it's the percentage up-bound ++ * of the thrashing ratio of all areas. Any area with a bigger thrashing ratio ++ * will be considered as having a zero duplication ratio. ++ */ ++static unsigned int uksm_thrash_threshold = 50; ++ ++/* How much dedup ratio is considered to be abundant*/ ++static unsigned int uksm_abundant_threshold = 10; ++ ++/* All slots having merged pages in this eval round. */ ++struct list_head vma_slot_dedup = LIST_HEAD_INIT(vma_slot_dedup); ++ ++/* How many times the ksmd has slept since startup */ ++static unsigned long long uksm_sleep_times; ++ ++#define UKSM_RUN_STOP 0 ++#define UKSM_RUN_MERGE 1 ++static unsigned int uksm_run = 1; ++ ++static DECLARE_WAIT_QUEUE_HEAD(uksm_thread_wait); ++static DEFINE_MUTEX(uksm_thread_mutex); ++ ++/* ++ * List vma_slot_new is for newly created vma_slot waiting to be added by ++ * ksmd. If one cannot be added(e.g. due to it's too small), it's moved to ++ * vma_slot_noadd. vma_slot_del is the list for vma_slot whose corresponding ++ * VMA has been removed/freed. ++ */ ++struct list_head vma_slot_new = LIST_HEAD_INIT(vma_slot_new); ++struct list_head vma_slot_noadd = LIST_HEAD_INIT(vma_slot_noadd); ++struct list_head vma_slot_del = LIST_HEAD_INIT(vma_slot_del); ++static DEFINE_SPINLOCK(vma_slot_list_lock); ++ ++/* The unstable tree heads */ ++static struct rb_root root_unstable_tree = RB_ROOT; ++ ++/* ++ * All tree_nodes are in a list to be freed at once when unstable tree is ++ * freed after each scan round. ++ */ ++static struct list_head unstable_tree_node_list = ++ LIST_HEAD_INIT(unstable_tree_node_list); ++ ++/* List contains all stable nodes */ ++static struct list_head stable_node_list = LIST_HEAD_INIT(stable_node_list); ++ ++/* ++ * When the hash strength is changed, the stable tree must be delta_hashed and ++ * re-structured. We use two set of below structs to speed up the ++ * re-structuring of stable tree. ++ */ ++static struct list_head ++stable_tree_node_list[2] = {LIST_HEAD_INIT(stable_tree_node_list[0]), ++ LIST_HEAD_INIT(stable_tree_node_list[1])}; ++ ++static struct list_head *stable_tree_node_listp = &stable_tree_node_list[0]; ++static struct rb_root root_stable_tree[2] = {RB_ROOT, RB_ROOT}; ++static struct rb_root *root_stable_treep = &root_stable_tree[0]; ++static unsigned long stable_tree_index; ++ ++/* The hash strength needed to hash a full page */ ++#define HASH_STRENGTH_FULL (PAGE_SIZE / sizeof(u32)) ++ ++/* The hash strength needed for loop-back hashing */ ++#define HASH_STRENGTH_MAX (HASH_STRENGTH_FULL + 10) ++ ++/* The random offsets in a page */ ++static u32 *random_nums; ++ ++/* The hash strength */ ++static unsigned long hash_strength = HASH_STRENGTH_FULL >> 4; ++ ++/* The delta value each time the hash strength increases or decreases */ ++static unsigned long hash_strength_delta; ++#define HASH_STRENGTH_DELTA_MAX 5 ++ ++/* The time we have saved due to random_sample_hash */ ++static u64 rshash_pos; ++ ++/* The time we have wasted due to hash collision */ ++static u64 rshash_neg; ++ ++struct uksm_benefit { ++ u64 pos; ++ u64 neg; ++ u64 scanned; ++ unsigned long base; ++} benefit; ++ ++/* ++ * The relative cost of memcmp, compared to 1 time unit of random sample ++ * hash, this value is tested when ksm module is initialized ++ */ ++static unsigned long memcmp_cost; ++ ++static unsigned long rshash_neg_cont_zero; ++static unsigned long rshash_cont_obscure; ++ ++/* The possible states of hash strength adjustment heuristic */ ++enum rshash_states { ++ RSHASH_STILL, ++ RSHASH_TRYUP, ++ RSHASH_TRYDOWN, ++ RSHASH_NEW, ++ RSHASH_PRE_STILL, ++}; ++ ++/* The possible direction we are about to adjust hash strength */ ++enum rshash_direct { ++ GO_UP, ++ GO_DOWN, ++ OBSCURE, ++ STILL, ++}; ++ ++/* random sampling hash state machine */ ++static struct { ++ enum rshash_states state; ++ enum rshash_direct pre_direct; ++ u8 below_count; ++ /* Keep a lookup window of size 5, iff above_count/below_count > 3 ++ * in this window we stop trying. ++ */ ++ u8 lookup_window_index; ++ u64 stable_benefit; ++ unsigned long turn_point_down; ++ unsigned long turn_benefit_down; ++ unsigned long turn_point_up; ++ unsigned long turn_benefit_up; ++ unsigned long stable_point; ++} rshash_state; ++ ++/*zero page hash table, hash_strength [0 ~ HASH_STRENGTH_MAX]*/ ++static u32 *zero_hash_table; ++ ++static inline struct node_vma *alloc_node_vma(void) ++{ ++ struct node_vma *node_vma; ++ node_vma = kmem_cache_zalloc(node_vma_cache, GFP_KERNEL); ++ if (node_vma) { ++ INIT_HLIST_HEAD(&node_vma->rmap_hlist); ++ INIT_HLIST_NODE(&node_vma->hlist); ++ } ++ return node_vma; ++} ++ ++static inline void free_node_vma(struct node_vma *node_vma) ++{ ++ kmem_cache_free(node_vma_cache, node_vma); ++} ++ ++ ++static inline struct vma_slot *alloc_vma_slot(void) ++{ ++ struct vma_slot *slot; ++ ++ /* ++ * In case ksm is not initialized by now. ++ * Oops, we need to consider the call site of uksm_init() in the future. ++ */ ++ if (!vma_slot_cache) ++ return NULL; ++ ++ slot = kmem_cache_zalloc(vma_slot_cache, GFP_KERNEL); ++ if (slot) { ++ INIT_LIST_HEAD(&slot->slot_list); ++ INIT_LIST_HEAD(&slot->dedup_list); ++ slot->flags |= UKSM_SLOT_NEED_RERAND; ++ } ++ return slot; ++} ++ ++static inline void free_vma_slot(struct vma_slot *vma_slot) ++{ ++ kmem_cache_free(vma_slot_cache, vma_slot); ++} ++ ++ ++ ++static inline struct rmap_item *alloc_rmap_item(void) ++{ ++ struct rmap_item *rmap_item; ++ ++ rmap_item = kmem_cache_zalloc(rmap_item_cache, GFP_KERNEL); ++ if (rmap_item) { ++ /* bug on lowest bit is not clear for flag use */ ++ BUG_ON(is_addr(rmap_item)); ++ } ++ return rmap_item; ++} ++ ++static inline void free_rmap_item(struct rmap_item *rmap_item) ++{ ++ rmap_item->slot = NULL; /* debug safety */ ++ kmem_cache_free(rmap_item_cache, rmap_item); ++} ++ ++static inline struct stable_node *alloc_stable_node(void) ++{ ++ struct stable_node *node; ++ node = kmem_cache_alloc(stable_node_cache, GFP_KERNEL | GFP_ATOMIC); ++ if (!node) ++ return NULL; ++ ++ INIT_HLIST_HEAD(&node->hlist); ++ list_add(&node->all_list, &stable_node_list); ++ return node; ++} ++ ++static inline void free_stable_node(struct stable_node *stable_node) ++{ ++ list_del(&stable_node->all_list); ++ kmem_cache_free(stable_node_cache, stable_node); ++} ++ ++static inline struct tree_node *alloc_tree_node(struct list_head *list) ++{ ++ struct tree_node *node; ++ node = kmem_cache_zalloc(tree_node_cache, GFP_KERNEL | GFP_ATOMIC); ++ if (!node) ++ return NULL; ++ ++ list_add(&node->all_list, list); ++ return node; ++} ++ ++static inline void free_tree_node(struct tree_node *node) ++{ ++ list_del(&node->all_list); ++ kmem_cache_free(tree_node_cache, node); ++} ++ ++static void uksm_drop_anon_vma(struct rmap_item *rmap_item) ++{ ++ struct anon_vma *anon_vma = rmap_item->anon_vma; ++ ++ put_anon_vma(anon_vma); ++} ++ ++ ++/** ++ * Remove a stable node from stable_tree, may unlink from its tree_node and ++ * may remove its parent tree_node if no other stable node is pending. ++ * ++ * @stable_node The node need to be removed ++ * @unlink_rb Will this node be unlinked from the rbtree? ++ * @remove_tree_ node Will its tree_node be removed if empty? ++ */ ++static void remove_node_from_stable_tree(struct stable_node *stable_node, ++ int unlink_rb, int remove_tree_node) ++{ ++ struct node_vma *node_vma; ++ struct rmap_item *rmap_item; ++ struct hlist_node *n; ++ ++ if (!hlist_empty(&stable_node->hlist)) { ++ hlist_for_each_entry_safe(node_vma, n, ++ &stable_node->hlist, hlist) { ++ hlist_for_each_entry(rmap_item, &node_vma->rmap_hlist, hlist) { ++ uksm_pages_sharing--; ++ ++ uksm_drop_anon_vma(rmap_item); ++ rmap_item->address &= PAGE_MASK; ++ } ++ free_node_vma(node_vma); ++ cond_resched(); ++ } ++ ++ /* the last one is counted as shared */ ++ uksm_pages_shared--; ++ uksm_pages_sharing++; ++ } ++ ++ if (stable_node->tree_node && unlink_rb) { ++ rb_erase(&stable_node->node, ++ &stable_node->tree_node->sub_root); ++ ++ if (RB_EMPTY_ROOT(&stable_node->tree_node->sub_root) && ++ remove_tree_node) { ++ rb_erase(&stable_node->tree_node->node, ++ root_stable_treep); ++ free_tree_node(stable_node->tree_node); ++ } else { ++ stable_node->tree_node->count--; ++ } ++ } ++ ++ free_stable_node(stable_node); ++} ++ ++ ++/* ++ * get_uksm_page: checks if the page indicated by the stable node ++ * is still its ksm page, despite having held no reference to it. ++ * In which case we can trust the content of the page, and it ++ * returns the gotten page; but if the page has now been zapped, ++ * remove the stale node from the stable tree and return NULL. ++ * ++ * You would expect the stable_node to hold a reference to the ksm page. ++ * But if it increments the page's count, swapping out has to wait for ++ * ksmd to come around again before it can free the page, which may take ++ * seconds or even minutes: much too unresponsive. So instead we use a ++ * "keyhole reference": access to the ksm page from the stable node peeps ++ * out through its keyhole to see if that page still holds the right key, ++ * pointing back to this stable node. This relies on freeing a PageAnon ++ * page to reset its page->mapping to NULL, and relies on no other use of ++ * a page to put something that might look like our key in page->mapping. ++ * ++ * include/linux/pagemap.h page_cache_get_speculative() is a good reference, ++ * but this is different - made simpler by uksm_thread_mutex being held, but ++ * interesting for assuming that no other use of the struct page could ever ++ * put our expected_mapping into page->mapping (or a field of the union which ++ * coincides with page->mapping). The RCU calls are not for KSM at all, but ++ * to keep the page_count protocol described with page_cache_get_speculative. ++ * ++ * Note: it is possible that get_uksm_page() will return NULL one moment, ++ * then page the next, if the page is in between page_freeze_refs() and ++ * page_unfreeze_refs(): this shouldn't be a problem anywhere, the page ++ * is on its way to being freed; but it is an anomaly to bear in mind. ++ * ++ * @unlink_rb: if the removal of this node will firstly unlink from ++ * its rbtree. stable_node_reinsert will prevent this when restructuring the ++ * node from its old tree. ++ * ++ * @remove_tree_node: if this is the last one of its tree_node, will the ++ * tree_node be freed ? If we are inserting stable node, this tree_node may ++ * be reused, so don't free it. ++ */ ++static struct page *get_uksm_page(struct stable_node *stable_node, ++ int unlink_rb, int remove_tree_node) ++{ ++ struct page *page; ++ void *expected_mapping; ++ ++ page = pfn_to_page(stable_node->kpfn); ++ expected_mapping = (void *)stable_node + ++ (PAGE_MAPPING_ANON | PAGE_MAPPING_KSM); ++ rcu_read_lock(); ++ if (page->mapping != expected_mapping) ++ goto stale; ++ if (!get_page_unless_zero(page)) ++ goto stale; ++ if (page->mapping != expected_mapping) { ++ put_page(page); ++ goto stale; ++ } ++ rcu_read_unlock(); ++ return page; ++stale: ++ rcu_read_unlock(); ++ remove_node_from_stable_tree(stable_node, unlink_rb, remove_tree_node); ++ ++ return NULL; ++} ++ ++/* ++ * Removing rmap_item from stable or unstable tree. ++ * This function will clean the information from the stable/unstable tree. ++ */ ++static inline void remove_rmap_item_from_tree(struct rmap_item *rmap_item) ++{ ++ if (rmap_item->address & STABLE_FLAG) { ++ struct stable_node *stable_node; ++ struct node_vma *node_vma; ++ struct page *page; ++ ++ node_vma = rmap_item->head; ++ stable_node = node_vma->head; ++ page = get_uksm_page(stable_node, 1, 1); ++ if (!page) ++ goto out; ++ ++ /* ++ * page lock is needed because it's racing with ++ * try_to_unmap_ksm(), etc. ++ */ ++ lock_page(page); ++ hlist_del(&rmap_item->hlist); ++ ++ if (hlist_empty(&node_vma->rmap_hlist)) { ++ hlist_del(&node_vma->hlist); ++ free_node_vma(node_vma); ++ } ++ unlock_page(page); ++ ++ put_page(page); ++ if (hlist_empty(&stable_node->hlist)) { ++ /* do NOT call remove_node_from_stable_tree() here, ++ * it's possible for a forked rmap_item not in ++ * stable tree while the in-tree rmap_items were ++ * deleted. ++ */ ++ uksm_pages_shared--; ++ } else ++ uksm_pages_sharing--; ++ ++ ++ uksm_drop_anon_vma(rmap_item); ++ } else if (rmap_item->address & UNSTABLE_FLAG) { ++ if (rmap_item->hash_round == uksm_hash_round) { ++ ++ rb_erase(&rmap_item->node, ++ &rmap_item->tree_node->sub_root); ++ if (RB_EMPTY_ROOT(&rmap_item->tree_node->sub_root)) { ++ rb_erase(&rmap_item->tree_node->node, ++ &root_unstable_tree); ++ ++ free_tree_node(rmap_item->tree_node); ++ } else ++ rmap_item->tree_node->count--; ++ } ++ uksm_pages_unshared--; ++ } ++ ++ rmap_item->address &= PAGE_MASK; ++ rmap_item->hash_max = 0; ++ ++out: ++ cond_resched(); /* we're called from many long loops */ ++} ++ ++static inline int slot_in_uksm(struct vma_slot *slot) ++{ ++ return list_empty(&slot->slot_list); ++} ++ ++/* ++ * Test if the mm is exiting ++ */ ++static inline bool uksm_test_exit(struct mm_struct *mm) ++{ ++ return atomic_read(&mm->mm_users) == 0; ++} ++ ++static inline unsigned long vma_pool_size(struct vma_slot *slot) ++{ ++ return round_up(sizeof(struct rmap_list_entry) * slot->pages, ++ PAGE_SIZE) >> PAGE_SHIFT; ++} ++ ++#define CAN_OVERFLOW_U64(x, delta) (U64_MAX - (x) < (delta)) ++ ++/* must be done with sem locked */ ++static int slot_pool_alloc(struct vma_slot *slot) ++{ ++ unsigned long pool_size; ++ ++ if (slot->rmap_list_pool) ++ return 0; ++ ++ pool_size = vma_pool_size(slot); ++ slot->rmap_list_pool = kzalloc(sizeof(struct page *) * ++ pool_size, GFP_KERNEL); ++ if (!slot->rmap_list_pool) ++ return -ENOMEM; ++ ++ slot->pool_counts = kzalloc(sizeof(unsigned int) * pool_size, ++ GFP_KERNEL); ++ if (!slot->pool_counts) { ++ kfree(slot->rmap_list_pool); ++ return -ENOMEM; ++ } ++ ++ slot->pool_size = pool_size; ++ BUG_ON(CAN_OVERFLOW_U64(uksm_pages_total, slot->pages)); ++ slot->flags |= UKSM_SLOT_IN_UKSM; ++ uksm_pages_total += slot->pages; ++ ++ return 0; ++} ++ ++/* ++ * Called after vma is unlinked from its mm ++ */ ++void uksm_remove_vma(struct vm_area_struct *vma) ++{ ++ struct vma_slot *slot; ++ ++ if (!vma->uksm_vma_slot) ++ return; ++ ++ spin_lock(&vma_slot_list_lock); ++ slot = vma->uksm_vma_slot; ++ if (!slot) ++ goto out; ++ ++ if (slot_in_uksm(slot)) { ++ /** ++ * This slot has been added by ksmd, so move to the del list ++ * waiting ksmd to free it. ++ */ ++ list_add_tail(&slot->slot_list, &vma_slot_del); ++ } else { ++ /** ++ * It's still on new list. It's ok to free slot directly. ++ */ ++ list_del(&slot->slot_list); ++ free_vma_slot(slot); ++ } ++out: ++ vma->uksm_vma_slot = NULL; ++ spin_unlock(&vma_slot_list_lock); ++} ++ ++/** ++ * Need to do two things: ++ * 1. check if slot was moved to del list ++ * 2. make sure the mmap_sem is manipulated under valid vma. ++ * ++ * My concern here is that in some cases, this may make ++ * vma_slot_list_lock() waiters to serialized further by some ++ * sem->wait_lock, can this really be expensive? ++ * ++ * ++ * @return ++ * 0: if successfully locked mmap_sem ++ * -ENOENT: this slot was moved to del list ++ * -EBUSY: vma lock failed ++ */ ++static int try_down_read_slot_mmap_sem(struct vma_slot *slot) ++{ ++ struct vm_area_struct *vma; ++ struct mm_struct *mm; ++ struct rw_semaphore *sem; ++ ++ spin_lock(&vma_slot_list_lock); ++ ++ /* the slot_list was removed and inited from new list, when it enters ++ * uksm_list. If now it's not empty, then it must be moved to del list ++ */ ++ if (!slot_in_uksm(slot)) { ++ spin_unlock(&vma_slot_list_lock); ++ return -ENOENT; ++ } ++ ++ BUG_ON(slot->pages != vma_pages(slot->vma)); ++ /* Ok, vma still valid */ ++ vma = slot->vma; ++ mm = vma->vm_mm; ++ sem = &mm->mmap_sem; ++ ++ if (uksm_test_exit(mm)) { ++ spin_unlock(&vma_slot_list_lock); ++ return -ENOENT; ++ } ++ ++ if (down_read_trylock(sem)) { ++ spin_unlock(&vma_slot_list_lock); ++ if (slot_pool_alloc(slot)) { ++ uksm_remove_vma(vma); ++ up_read(sem); ++ return -ENOENT; ++ } ++ return 0; ++ } ++ ++ spin_unlock(&vma_slot_list_lock); ++ return -EBUSY; ++} ++ ++static inline unsigned long ++vma_page_address(struct page *page, struct vm_area_struct *vma) ++{ ++ pgoff_t pgoff = page->index; ++ unsigned long address; ++ ++ address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); ++ if (unlikely(address < vma->vm_start || address >= vma->vm_end)) { ++ /* page should be within @vma mapping range */ ++ return -EFAULT; ++ } ++ return address; ++} ++ ++ ++/* return 0 on success with the item's mmap_sem locked */ ++static inline int get_mergeable_page_lock_mmap(struct rmap_item *item) ++{ ++ struct mm_struct *mm; ++ struct vma_slot *slot = item->slot; ++ int err = -EINVAL; ++ ++ struct page *page; ++ ++ /* ++ * try_down_read_slot_mmap_sem() returns non-zero if the slot ++ * has been removed by uksm_remove_vma(). ++ */ ++ if (try_down_read_slot_mmap_sem(slot)) ++ return -EBUSY; ++ ++ mm = slot->vma->vm_mm; ++ ++ if (uksm_test_exit(mm)) ++ goto failout_up; ++ ++ page = item->page; ++ rcu_read_lock(); ++ if (!get_page_unless_zero(page)) { ++ rcu_read_unlock(); ++ goto failout_up; ++ } ++ ++ /* No need to consider huge page here. */ ++ if (item->slot->vma->anon_vma != page_anon_vma(page) || ++ vma_page_address(page, item->slot->vma) != get_rmap_addr(item)) { ++ /* ++ * TODO: ++ * should we release this item becase of its stale page ++ * mapping? ++ */ ++ put_page(page); ++ rcu_read_unlock(); ++ goto failout_up; ++ } ++ rcu_read_unlock(); ++ return 0; ++ ++failout_up: ++ up_read(&mm->mmap_sem); ++ return err; ++} ++ ++/* ++ * What kind of VMA is considered ? ++ */ ++static inline int vma_can_enter(struct vm_area_struct *vma) ++{ ++ return uksm_flags_can_scan(vma->vm_flags); ++} ++ ++/* ++ * Called whenever a fresh new vma is created A new vma_slot. ++ * is created and inserted into a global list Must be called. ++ * after vma is inserted to its mm . ++ */ ++void uksm_vma_add_new(struct vm_area_struct *vma) ++{ ++ struct vma_slot *slot; ++ ++ if (!vma_can_enter(vma)) { ++ vma->uksm_vma_slot = NULL; ++ return; ++ } ++ ++ slot = alloc_vma_slot(); ++ if (!slot) { ++ vma->uksm_vma_slot = NULL; ++ return; ++ } ++ ++ vma->uksm_vma_slot = slot; ++ vma->vm_flags |= VM_MERGEABLE; ++ slot->vma = vma; ++ slot->mm = vma->vm_mm; ++ slot->ctime_j = jiffies; ++ slot->pages = vma_pages(vma); ++ spin_lock(&vma_slot_list_lock); ++ list_add_tail(&slot->slot_list, &vma_slot_new); ++ spin_unlock(&vma_slot_list_lock); ++} ++ ++/* 32/3 < they < 32/2 */ ++#define shiftl 8 ++#define shiftr 12 ++ ++#define HASH_FROM_TO(from, to) \ ++for (index = from; index < to; index++) { \ ++ pos = random_nums[index]; \ ++ hash += key[pos]; \ ++ hash += (hash << shiftl); \ ++ hash ^= (hash >> shiftr); \ ++} ++ ++ ++#define HASH_FROM_DOWN_TO(from, to) \ ++for (index = from - 1; index >= to; index--) { \ ++ hash ^= (hash >> shiftr); \ ++ hash ^= (hash >> (shiftr*2)); \ ++ hash -= (hash << shiftl); \ ++ hash += (hash << (shiftl*2)); \ ++ pos = random_nums[index]; \ ++ hash -= key[pos]; \ ++} ++ ++/* ++ * The main random sample hash function. ++ */ ++static u32 random_sample_hash(void *addr, u32 hash_strength) ++{ ++ u32 hash = 0xdeadbeef; ++ int index, pos, loop = hash_strength; ++ u32 *key = (u32 *)addr; ++ ++ if (loop > HASH_STRENGTH_FULL) ++ loop = HASH_STRENGTH_FULL; ++ ++ HASH_FROM_TO(0, loop); ++ ++ if (hash_strength > HASH_STRENGTH_FULL) { ++ loop = hash_strength - HASH_STRENGTH_FULL; ++ HASH_FROM_TO(0, loop); ++ } ++ ++ return hash; ++} ++ ++ ++/** ++ * It's used when hash strength is adjusted ++ * ++ * @addr The page's virtual address ++ * @from The original hash strength ++ * @to The hash strength changed to ++ * @hash The hash value generated with "from" hash value ++ * ++ * return the hash value ++ */ ++static u32 delta_hash(void *addr, int from, int to, u32 hash) ++{ ++ u32 *key = (u32 *)addr; ++ int index, pos; /* make sure they are int type */ ++ ++ if (to > from) { ++ if (from >= HASH_STRENGTH_FULL) { ++ from -= HASH_STRENGTH_FULL; ++ to -= HASH_STRENGTH_FULL; ++ HASH_FROM_TO(from, to); ++ } else if (to <= HASH_STRENGTH_FULL) { ++ HASH_FROM_TO(from, to); ++ } else { ++ HASH_FROM_TO(from, HASH_STRENGTH_FULL); ++ HASH_FROM_TO(0, to - HASH_STRENGTH_FULL); ++ } ++ } else { ++ if (from <= HASH_STRENGTH_FULL) { ++ HASH_FROM_DOWN_TO(from, to); ++ } else if (to >= HASH_STRENGTH_FULL) { ++ from -= HASH_STRENGTH_FULL; ++ to -= HASH_STRENGTH_FULL; ++ HASH_FROM_DOWN_TO(from, to); ++ } else { ++ HASH_FROM_DOWN_TO(from - HASH_STRENGTH_FULL, 0); ++ HASH_FROM_DOWN_TO(HASH_STRENGTH_FULL, to); ++ } ++ } ++ ++ return hash; ++} ++ ++/** ++ * ++ * Called when: rshash_pos or rshash_neg is about to overflow or a scan round ++ * has finished. ++ * ++ * return 0 if no page has been scanned since last call, 1 otherwise. ++ */ ++static inline int encode_benefit(void) ++{ ++ u64 scanned_delta, pos_delta, neg_delta; ++ unsigned long base = benefit.base; ++ ++ scanned_delta = uksm_pages_scanned - uksm_pages_scanned_last; ++ ++ if (!scanned_delta) ++ return 0; ++ ++ scanned_delta >>= base; ++ pos_delta = rshash_pos >> base; ++ neg_delta = rshash_neg >> base; ++ ++ if (CAN_OVERFLOW_U64(benefit.pos, pos_delta) || ++ CAN_OVERFLOW_U64(benefit.neg, neg_delta) || ++ CAN_OVERFLOW_U64(benefit.scanned, scanned_delta)) { ++ benefit.scanned >>= 1; ++ benefit.neg >>= 1; ++ benefit.pos >>= 1; ++ benefit.base++; ++ scanned_delta >>= 1; ++ pos_delta >>= 1; ++ neg_delta >>= 1; ++ } ++ ++ benefit.pos += pos_delta; ++ benefit.neg += neg_delta; ++ benefit.scanned += scanned_delta; ++ ++ BUG_ON(!benefit.scanned); ++ ++ rshash_pos = rshash_neg = 0; ++ uksm_pages_scanned_last = uksm_pages_scanned; ++ ++ return 1; ++} ++ ++static inline void reset_benefit(void) ++{ ++ benefit.pos = 0; ++ benefit.neg = 0; ++ benefit.base = 0; ++ benefit.scanned = 0; ++} ++ ++static inline void inc_rshash_pos(unsigned long delta) ++{ ++ if (CAN_OVERFLOW_U64(rshash_pos, delta)) ++ encode_benefit(); ++ ++ rshash_pos += delta; ++} ++ ++static inline void inc_rshash_neg(unsigned long delta) ++{ ++ if (CAN_OVERFLOW_U64(rshash_neg, delta)) ++ encode_benefit(); ++ ++ rshash_neg += delta; ++} ++ ++ ++static inline u32 page_hash(struct page *page, unsigned long hash_strength, ++ int cost_accounting) ++{ ++ u32 val; ++ unsigned long delta; ++ ++ void *addr = kmap_atomic(page); ++ ++ val = random_sample_hash(addr, hash_strength); ++ kunmap_atomic(addr); ++ ++ if (cost_accounting) { ++ if (HASH_STRENGTH_FULL > hash_strength) ++ delta = HASH_STRENGTH_FULL - hash_strength; ++ else ++ delta = 0; ++ ++ inc_rshash_pos(delta); ++ } ++ ++ return val; ++} ++ ++static int memcmp_pages(struct page *page1, struct page *page2, ++ int cost_accounting) ++{ ++ char *addr1, *addr2; ++ int ret; ++ ++ addr1 = kmap_atomic(page1); ++ addr2 = kmap_atomic(page2); ++ ret = memcmp(addr1, addr2, PAGE_SIZE); ++ kunmap_atomic(addr2); ++ kunmap_atomic(addr1); ++ ++ if (cost_accounting) ++ inc_rshash_neg(memcmp_cost); ++ ++ return ret; ++} ++ ++static inline int pages_identical(struct page *page1, struct page *page2) ++{ ++ return !memcmp_pages(page1, page2, 0); ++} ++ ++static inline int is_page_full_zero(struct page *page) ++{ ++ char *addr; ++ int ret; ++ ++ addr = kmap_atomic(page); ++ ret = is_full_zero(addr, PAGE_SIZE); ++ kunmap_atomic(addr); ++ ++ return ret; ++} ++ ++static int write_protect_page(struct vm_area_struct *vma, struct page *page, ++ pte_t *orig_pte, pte_t *old_pte) ++{ ++ struct mm_struct *mm = vma->vm_mm; ++ unsigned long addr; ++ pte_t *ptep; ++ spinlock_t *ptl; ++ int swapped; ++ int err = -EFAULT; ++ unsigned long mmun_start; /* For mmu_notifiers */ ++ unsigned long mmun_end; /* For mmu_notifiers */ ++ ++ addr = page_address_in_vma(page, vma); ++ if (addr == -EFAULT) ++ goto out; ++ ++ BUG_ON(PageTransCompound(page)); ++ ++ mmun_start = addr; ++ mmun_end = addr + PAGE_SIZE; ++ mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); ++ ++ ptep = page_check_address(page, mm, addr, &ptl, 0); ++ if (!ptep) ++ goto out_mn; ++ ++ if (old_pte) ++ *old_pte = *ptep; ++ ++ if (pte_write(*ptep) || pte_dirty(*ptep)) { ++ pte_t entry; ++ ++ swapped = PageSwapCache(page); ++ flush_cache_page(vma, addr, page_to_pfn(page)); ++ /* ++ * Ok this is tricky, when get_user_pages_fast() run it doesnt ++ * take any lock, therefore the check that we are going to make ++ * with the pagecount against the mapcount is racey and ++ * O_DIRECT can happen right after the check. ++ * So we clear the pte and flush the tlb before the check ++ * this assure us that no O_DIRECT can happen after the check ++ * or in the middle of the check. ++ */ ++ entry = ptep_clear_flush_notify(vma, addr, ptep); ++ /* ++ * Check that no O_DIRECT or similar I/O is in progress on the ++ * page ++ */ ++ if (page_mapcount(page) + 1 + swapped != page_count(page)) { ++ set_pte_at(mm, addr, ptep, entry); ++ goto out_unlock; ++ } ++ if (pte_dirty(entry)) ++ set_page_dirty(page); ++ entry = pte_mkclean(pte_wrprotect(entry)); ++ set_pte_at_notify(mm, addr, ptep, entry); ++ } ++ *orig_pte = *ptep; ++ err = 0; ++ ++out_unlock: ++ pte_unmap_unlock(ptep, ptl); ++out_mn: ++ mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); ++out: ++ return err; ++} ++ ++#define MERGE_ERR_PGERR 1 /* the page is invalid cannot continue */ ++#define MERGE_ERR_COLLI 2 /* there is a collision */ ++#define MERGE_ERR_COLLI_MAX 3 /* collision at the max hash strength */ ++#define MERGE_ERR_CHANGED 4 /* the page has changed since last hash */ ++ ++ ++/** ++ * replace_page - replace page in vma by new ksm page ++ * @vma: vma that holds the pte pointing to page ++ * @page: the page we are replacing by kpage ++ * @kpage: the ksm page we replace page by ++ * @orig_pte: the original value of the pte ++ * ++ * Returns 0 on success, MERGE_ERR_PGERR on failure. ++ */ ++static int replace_page(struct vm_area_struct *vma, struct page *page, ++ struct page *kpage, pte_t orig_pte) ++{ ++ struct mm_struct *mm = vma->vm_mm; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *ptep; ++ spinlock_t *ptl; ++ pte_t entry; ++ ++ unsigned long addr; ++ int err = MERGE_ERR_PGERR; ++ unsigned long mmun_start; /* For mmu_notifiers */ ++ unsigned long mmun_end; /* For mmu_notifiers */ ++ ++ addr = page_address_in_vma(page, vma); ++ if (addr == -EFAULT) ++ goto out; ++ ++ pgd = pgd_offset(mm, addr); ++ if (!pgd_present(*pgd)) ++ goto out; ++ ++ pud = pud_offset(pgd, addr); ++ if (!pud_present(*pud)) ++ goto out; ++ ++ pmd = pmd_offset(pud, addr); ++ BUG_ON(pmd_trans_huge(*pmd)); ++ if (!pmd_present(*pmd)) ++ goto out; ++ ++ mmun_start = addr; ++ mmun_end = addr + PAGE_SIZE; ++ mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); ++ ++ ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); ++ if (!pte_same(*ptep, orig_pte)) { ++ pte_unmap_unlock(ptep, ptl); ++ goto out_mn; ++ } ++ ++ flush_cache_page(vma, addr, pte_pfn(*ptep)); ++ ptep_clear_flush_notify(vma, addr, ptep); ++ entry = mk_pte(kpage, vma->vm_page_prot); ++ ++ /* special treatment is needed for zero_page */ ++ if ((page_to_pfn(kpage) == uksm_zero_pfn) || ++ (page_to_pfn(kpage) == zero_pfn)) { ++ entry = pte_mkspecial(entry); ++ dec_mm_counter(mm, MM_ANONPAGES); ++ } else { ++ get_page(kpage); ++ page_add_anon_rmap(kpage, vma, addr, false); ++ } ++ ++ set_pte_at_notify(mm, addr, ptep, entry); ++ ++ page_remove_rmap(page, false); ++ if (!page_mapped(page)) ++ try_to_free_swap(page); ++ put_page(page); ++ ++ pte_unmap_unlock(ptep, ptl); ++ err = 0; ++out_mn: ++ mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); ++out: ++ return err; ++} ++ ++ ++/** ++ * Fully hash a page with HASH_STRENGTH_MAX return a non-zero hash value. The ++ * zero hash value at HASH_STRENGTH_MAX is used to indicated that its ++ * hash_max member has not been calculated. ++ * ++ * @page The page needs to be hashed ++ * @hash_old The hash value calculated with current hash strength ++ * ++ * return the new hash value calculated at HASH_STRENGTH_MAX ++ */ ++static inline u32 page_hash_max(struct page *page, u32 hash_old) ++{ ++ u32 hash_max = 0; ++ void *addr; ++ ++ addr = kmap_atomic(page); ++ hash_max = delta_hash(addr, hash_strength, ++ HASH_STRENGTH_MAX, hash_old); ++ ++ kunmap_atomic(addr); ++ ++ if (!hash_max) ++ hash_max = 1; ++ ++ inc_rshash_neg(HASH_STRENGTH_MAX - hash_strength); ++ return hash_max; ++} ++ ++/* ++ * We compare the hash again, to ensure that it is really a hash collision ++ * instead of being caused by page write. ++ */ ++static inline int check_collision(struct rmap_item *rmap_item, ++ u32 hash) ++{ ++ int err; ++ struct page *page = rmap_item->page; ++ ++ /* if this rmap_item has already been hash_maxed, then the collision ++ * must appears in the second-level rbtree search. In this case we check ++ * if its hash_max value has been changed. Otherwise, the collision ++ * happens in the first-level rbtree search, so we check against it's ++ * current hash value. ++ */ ++ if (rmap_item->hash_max) { ++ inc_rshash_neg(memcmp_cost); ++ inc_rshash_neg(HASH_STRENGTH_MAX - hash_strength); ++ ++ if (rmap_item->hash_max == page_hash_max(page, hash)) ++ err = MERGE_ERR_COLLI; ++ else ++ err = MERGE_ERR_CHANGED; ++ } else { ++ inc_rshash_neg(memcmp_cost + hash_strength); ++ ++ if (page_hash(page, hash_strength, 0) == hash) ++ err = MERGE_ERR_COLLI; ++ else ++ err = MERGE_ERR_CHANGED; ++ } ++ ++ return err; ++} ++ ++/** ++ * Try to merge a rmap_item.page with a kpage in stable node. kpage must ++ * already be a ksm page. ++ * ++ * @return 0 if the pages were merged, -EFAULT otherwise. ++ */ ++static int try_to_merge_with_uksm_page(struct rmap_item *rmap_item, ++ struct page *kpage, u32 hash) ++{ ++ struct vm_area_struct *vma = rmap_item->slot->vma; ++ struct mm_struct *mm = vma->vm_mm; ++ pte_t orig_pte = __pte(0); ++ int err = MERGE_ERR_PGERR; ++ struct page *page; ++ ++ if (uksm_test_exit(mm)) ++ goto out; ++ ++ page = rmap_item->page; ++ ++ if (page == kpage) { /* ksm page forked */ ++ err = 0; ++ goto out; ++ } ++ ++ /* ++ * We need the page lock to read a stable PageSwapCache in ++ * write_protect_page(). We use trylock_page() instead of ++ * lock_page() because we don't want to wait here - we ++ * prefer to continue scanning and merging different pages, ++ * then come back to this page when it is unlocked. ++ */ ++ if (!trylock_page(page)) ++ goto out; ++ ++ if (!PageAnon(page) || !PageKsm(kpage)) ++ goto out_unlock; ++ ++ if (PageTransCompound(page)) { ++ err = split_huge_page(page); ++ if (err) ++ goto out_unlock; ++ } ++ ++ /* ++ * If this anonymous page is mapped only here, its pte may need ++ * to be write-protected. If it's mapped elsewhere, all of its ++ * ptes are necessarily already write-protected. But in either ++ * case, we need to lock and check page_count is not raised. ++ */ ++ if (write_protect_page(vma, page, &orig_pte, NULL) == 0) { ++ if (pages_identical(page, kpage)) ++ err = replace_page(vma, page, kpage, orig_pte); ++ else ++ err = check_collision(rmap_item, hash); ++ } ++ ++ if ((vma->vm_flags & VM_LOCKED) && kpage && !err) { ++ munlock_vma_page(page); ++ if (!PageMlocked(kpage)) { ++ unlock_page(page); ++ lock_page(kpage); ++ mlock_vma_page(kpage); ++ page = kpage; /* for final unlock */ ++ } ++ } ++ ++out_unlock: ++ unlock_page(page); ++out: ++ return err; ++} ++ ++ ++ ++/** ++ * If two pages fail to merge in try_to_merge_two_pages, then we have a chance ++ * to restore a page mapping that has been changed in try_to_merge_two_pages. ++ * ++ * @return 0 on success. ++ */ ++static int restore_uksm_page_pte(struct vm_area_struct *vma, unsigned long addr, ++ pte_t orig_pte, pte_t wprt_pte) ++{ ++ struct mm_struct *mm = vma->vm_mm; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *ptep; ++ spinlock_t *ptl; ++ ++ int err = -EFAULT; ++ ++ pgd = pgd_offset(mm, addr); ++ if (!pgd_present(*pgd)) ++ goto out; ++ ++ pud = pud_offset(pgd, addr); ++ if (!pud_present(*pud)) ++ goto out; ++ ++ pmd = pmd_offset(pud, addr); ++ if (!pmd_present(*pmd)) ++ goto out; ++ ++ ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); ++ if (!pte_same(*ptep, wprt_pte)) { ++ /* already copied, let it be */ ++ pte_unmap_unlock(ptep, ptl); ++ goto out; ++ } ++ ++ /* ++ * Good boy, still here. When we still get the ksm page, it does not ++ * return to the free page pool, there is no way that a pte was changed ++ * to other page and gets back to this page. And remind that ksm page ++ * do not reuse in do_wp_page(). So it's safe to restore the original ++ * pte. ++ */ ++ flush_cache_page(vma, addr, pte_pfn(*ptep)); ++ ptep_clear_flush_notify(vma, addr, ptep); ++ set_pte_at_notify(mm, addr, ptep, orig_pte); ++ ++ pte_unmap_unlock(ptep, ptl); ++ err = 0; ++out: ++ return err; ++} ++ ++/** ++ * try_to_merge_two_pages() - take two identical pages and prepare ++ * them to be merged into one page(rmap_item->page) ++ * ++ * @return 0 if we successfully merged two identical pages into ++ * one ksm page. MERGE_ERR_COLLI if it's only a hash collision ++ * search in rbtree. MERGE_ERR_CHANGED if rmap_item has been ++ * changed since it's hashed. MERGE_ERR_PGERR otherwise. ++ * ++ */ ++static int try_to_merge_two_pages(struct rmap_item *rmap_item, ++ struct rmap_item *tree_rmap_item, ++ u32 hash) ++{ ++ pte_t orig_pte1 = __pte(0), orig_pte2 = __pte(0); ++ pte_t wprt_pte1 = __pte(0), wprt_pte2 = __pte(0); ++ struct vm_area_struct *vma1 = rmap_item->slot->vma; ++ struct vm_area_struct *vma2 = tree_rmap_item->slot->vma; ++ struct page *page = rmap_item->page; ++ struct page *tree_page = tree_rmap_item->page; ++ int err = MERGE_ERR_PGERR; ++ struct address_space *saved_mapping; ++ ++ ++ if (rmap_item->page == tree_rmap_item->page) ++ goto out; ++ ++ if (!trylock_page(page)) ++ goto out; ++ ++ if (!PageAnon(page)) ++ goto out_unlock; ++ ++ if (PageTransCompound(page)) { ++ err = split_huge_page(page); ++ if (err) ++ goto out_unlock; ++ } ++ ++ if (write_protect_page(vma1, page, &wprt_pte1, &orig_pte1) != 0) { ++ unlock_page(page); ++ goto out; ++ } ++ ++ /* ++ * While we hold page lock, upgrade page from ++ * PageAnon+anon_vma to PageKsm+NULL stable_node: ++ * stable_tree_insert() will update stable_node. ++ */ ++ saved_mapping = page->mapping; ++ set_page_stable_node(page, NULL); ++ mark_page_accessed(page); ++ if (!PageDirty(page)) ++ SetPageDirty(page); ++ ++ unlock_page(page); ++ ++ if (!trylock_page(tree_page)) ++ goto restore_out; ++ ++ if (!PageAnon(tree_page)) { ++ unlock_page(tree_page); ++ goto restore_out; ++ } ++ ++ if (PageTransCompound(tree_page)) { ++ err = split_huge_page(tree_page); ++ if (err) { ++ unlock_page(tree_page); ++ goto restore_out; ++ } ++ } ++ ++ if (write_protect_page(vma2, tree_page, &wprt_pte2, &orig_pte2) != 0) { ++ unlock_page(tree_page); ++ goto restore_out; ++ } ++ ++ if (pages_identical(page, tree_page)) { ++ err = replace_page(vma2, tree_page, page, wprt_pte2); ++ if (err) { ++ unlock_page(tree_page); ++ goto restore_out; ++ } ++ ++ if ((vma2->vm_flags & VM_LOCKED)) { ++ munlock_vma_page(tree_page); ++ if (!PageMlocked(page)) { ++ unlock_page(tree_page); ++ lock_page(page); ++ mlock_vma_page(page); ++ tree_page = page; /* for final unlock */ ++ } ++ } ++ ++ unlock_page(tree_page); ++ ++ goto out; /* success */ ++ ++ } else { ++ if (tree_rmap_item->hash_max && ++ tree_rmap_item->hash_max == rmap_item->hash_max) { ++ err = MERGE_ERR_COLLI_MAX; ++ } else if (page_hash(page, hash_strength, 0) == ++ page_hash(tree_page, hash_strength, 0)) { ++ inc_rshash_neg(memcmp_cost + hash_strength * 2); ++ err = MERGE_ERR_COLLI; ++ } else { ++ err = MERGE_ERR_CHANGED; ++ } ++ ++ unlock_page(tree_page); ++ } ++ ++restore_out: ++ lock_page(page); ++ if (!restore_uksm_page_pte(vma1, get_rmap_addr(rmap_item), ++ orig_pte1, wprt_pte1)) ++ page->mapping = saved_mapping; ++ ++out_unlock: ++ unlock_page(page); ++out: ++ return err; ++} ++ ++static inline int hash_cmp(u32 new_val, u32 node_val) ++{ ++ if (new_val > node_val) ++ return 1; ++ else if (new_val < node_val) ++ return -1; ++ else ++ return 0; ++} ++ ++static inline u32 rmap_item_hash_max(struct rmap_item *item, u32 hash) ++{ ++ u32 hash_max = item->hash_max; ++ ++ if (!hash_max) { ++ hash_max = page_hash_max(item->page, hash); ++ ++ item->hash_max = hash_max; ++ } ++ ++ return hash_max; ++} ++ ++ ++ ++/** ++ * stable_tree_search() - search the stable tree for a page ++ * ++ * @item: the rmap_item we are comparing with ++ * @hash: the hash value of this item->page already calculated ++ * ++ * @return the page we have found, NULL otherwise. The page returned has ++ * been gotten. ++ */ ++static struct page *stable_tree_search(struct rmap_item *item, u32 hash) ++{ ++ struct rb_node *node = root_stable_treep->rb_node; ++ struct tree_node *tree_node; ++ unsigned long hash_max; ++ struct page *page = item->page; ++ struct stable_node *stable_node; ++ ++ stable_node = page_stable_node(page); ++ if (stable_node) { ++ /* ksm page forked, that is ++ * if (PageKsm(page) && !in_stable_tree(rmap_item)) ++ * it's actually gotten once outside. ++ */ ++ get_page(page); ++ return page; ++ } ++ ++ while (node) { ++ int cmp; ++ ++ tree_node = rb_entry(node, struct tree_node, node); ++ ++ cmp = hash_cmp(hash, tree_node->hash); ++ ++ if (cmp < 0) ++ node = node->rb_left; ++ else if (cmp > 0) ++ node = node->rb_right; ++ else ++ break; ++ } ++ ++ if (!node) ++ return NULL; ++ ++ if (tree_node->count == 1) { ++ stable_node = rb_entry(tree_node->sub_root.rb_node, ++ struct stable_node, node); ++ BUG_ON(!stable_node); ++ ++ goto get_page_out; ++ } ++ ++ /* ++ * ok, we have to search the second ++ * level subtree, hash the page to a ++ * full strength. ++ */ ++ node = tree_node->sub_root.rb_node; ++ BUG_ON(!node); ++ hash_max = rmap_item_hash_max(item, hash); ++ ++ while (node) { ++ int cmp; ++ ++ stable_node = rb_entry(node, struct stable_node, node); ++ ++ cmp = hash_cmp(hash_max, stable_node->hash_max); ++ ++ if (cmp < 0) ++ node = node->rb_left; ++ else if (cmp > 0) ++ node = node->rb_right; ++ else ++ goto get_page_out; ++ } ++ ++ return NULL; ++ ++get_page_out: ++ page = get_uksm_page(stable_node, 1, 1); ++ return page; ++} ++ ++static int try_merge_rmap_item(struct rmap_item *item, ++ struct page *kpage, ++ struct page *tree_page) ++{ ++ spinlock_t *ptl; ++ pte_t *ptep; ++ unsigned long addr; ++ struct vm_area_struct *vma = item->slot->vma; ++ ++ addr = get_rmap_addr(item); ++ ptep = page_check_address(kpage, vma->vm_mm, addr, &ptl, 0); ++ if (!ptep) ++ return 0; ++ ++ if (pte_write(*ptep)) { ++ /* has changed, abort! */ ++ pte_unmap_unlock(ptep, ptl); ++ return 0; ++ } ++ ++ get_page(tree_page); ++ page_add_anon_rmap(tree_page, vma, addr, false); ++ ++ flush_cache_page(vma, addr, pte_pfn(*ptep)); ++ ptep_clear_flush_notify(vma, addr, ptep); ++ set_pte_at_notify(vma->vm_mm, addr, ptep, ++ mk_pte(tree_page, vma->vm_page_prot)); ++ ++ page_remove_rmap(kpage, false); ++ put_page(kpage); ++ ++ pte_unmap_unlock(ptep, ptl); ++ ++ return 1; ++} ++ ++/** ++ * try_to_merge_with_stable_page() - when two rmap_items need to be inserted ++ * into stable tree, the page was found to be identical to a stable ksm page, ++ * this is the last chance we can merge them into one. ++ * ++ * @item1: the rmap_item holding the page which we wanted to insert ++ * into stable tree. ++ * @item2: the other rmap_item we found when unstable tree search ++ * @oldpage: the page currently mapped by the two rmap_items ++ * @tree_page: the page we found identical in stable tree node ++ * @success1: return if item1 is successfully merged ++ * @success2: return if item2 is successfully merged ++ */ ++static void try_merge_with_stable(struct rmap_item *item1, ++ struct rmap_item *item2, ++ struct page **kpage, ++ struct page *tree_page, ++ int *success1, int *success2) ++{ ++ struct vm_area_struct *vma1 = item1->slot->vma; ++ struct vm_area_struct *vma2 = item2->slot->vma; ++ *success1 = 0; ++ *success2 = 0; ++ ++ if (unlikely(*kpage == tree_page)) { ++ /* I don't think this can really happen */ ++ printk(KERN_WARNING "UKSM: unexpected condition detected in " ++ "try_merge_with_stable() -- *kpage == tree_page !\n"); ++ *success1 = 1; ++ *success2 = 1; ++ return; ++ } ++ ++ if (!PageAnon(*kpage) || !PageKsm(*kpage)) ++ goto failed; ++ ++ if (!trylock_page(tree_page)) ++ goto failed; ++ ++ /* If the oldpage is still ksm and still pointed ++ * to in the right place, and still write protected, ++ * we are confident it's not changed, no need to ++ * memcmp anymore. ++ * be ware, we cannot take nested pte locks, ++ * deadlock risk. ++ */ ++ if (!try_merge_rmap_item(item1, *kpage, tree_page)) ++ goto unlock_failed; ++ ++ /* ok, then vma2, remind that pte1 already set */ ++ if (!try_merge_rmap_item(item2, *kpage, tree_page)) ++ goto success_1; ++ ++ *success2 = 1; ++success_1: ++ *success1 = 1; ++ ++ ++ if ((*success1 && vma1->vm_flags & VM_LOCKED) || ++ (*success2 && vma2->vm_flags & VM_LOCKED)) { ++ munlock_vma_page(*kpage); ++ if (!PageMlocked(tree_page)) ++ mlock_vma_page(tree_page); ++ } ++ ++ /* ++ * We do not need oldpage any more in the caller, so can break the lock ++ * now. ++ */ ++ unlock_page(*kpage); ++ *kpage = tree_page; /* Get unlocked outside. */ ++ return; ++ ++unlock_failed: ++ unlock_page(tree_page); ++failed: ++ return; ++} ++ ++static inline void stable_node_hash_max(struct stable_node *node, ++ struct page *page, u32 hash) ++{ ++ u32 hash_max = node->hash_max; ++ ++ if (!hash_max) { ++ hash_max = page_hash_max(page, hash); ++ node->hash_max = hash_max; ++ } ++} ++ ++static inline ++struct stable_node *new_stable_node(struct tree_node *tree_node, ++ struct page *kpage, u32 hash_max) ++{ ++ struct stable_node *new_stable_node; ++ ++ new_stable_node = alloc_stable_node(); ++ if (!new_stable_node) ++ return NULL; ++ ++ new_stable_node->kpfn = page_to_pfn(kpage); ++ new_stable_node->hash_max = hash_max; ++ new_stable_node->tree_node = tree_node; ++ set_page_stable_node(kpage, new_stable_node); ++ ++ return new_stable_node; ++} ++ ++static inline ++struct stable_node *first_level_insert(struct tree_node *tree_node, ++ struct rmap_item *rmap_item, ++ struct rmap_item *tree_rmap_item, ++ struct page **kpage, u32 hash, ++ int *success1, int *success2) ++{ ++ int cmp; ++ struct page *tree_page; ++ u32 hash_max = 0; ++ struct stable_node *stable_node, *new_snode; ++ struct rb_node *parent = NULL, **new; ++ ++ /* this tree node contains no sub-tree yet */ ++ stable_node = rb_entry(tree_node->sub_root.rb_node, ++ struct stable_node, node); ++ ++ tree_page = get_uksm_page(stable_node, 1, 0); ++ if (tree_page) { ++ cmp = memcmp_pages(*kpage, tree_page, 1); ++ if (!cmp) { ++ try_merge_with_stable(rmap_item, tree_rmap_item, kpage, ++ tree_page, success1, success2); ++ put_page(tree_page); ++ if (!*success1 && !*success2) ++ goto failed; ++ ++ return stable_node; ++ ++ } else { ++ /* ++ * collision in first level try to create a subtree. ++ * A new node need to be created. ++ */ ++ put_page(tree_page); ++ ++ stable_node_hash_max(stable_node, tree_page, ++ tree_node->hash); ++ hash_max = rmap_item_hash_max(rmap_item, hash); ++ cmp = hash_cmp(hash_max, stable_node->hash_max); ++ ++ parent = &stable_node->node; ++ if (cmp < 0) { ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ new = &parent->rb_right; ++ } else { ++ goto failed; ++ } ++ } ++ ++ } else { ++ /* the only stable_node deleted, we reuse its tree_node. ++ */ ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ } ++ ++ new_snode = new_stable_node(tree_node, *kpage, hash_max); ++ if (!new_snode) ++ goto failed; ++ ++ rb_link_node(&new_snode->node, parent, new); ++ rb_insert_color(&new_snode->node, &tree_node->sub_root); ++ tree_node->count++; ++ *success1 = *success2 = 1; ++ ++ return new_snode; ++ ++failed: ++ return NULL; ++} ++ ++static inline ++struct stable_node *stable_subtree_insert(struct tree_node *tree_node, ++ struct rmap_item *rmap_item, ++ struct rmap_item *tree_rmap_item, ++ struct page **kpage, u32 hash, ++ int *success1, int *success2) ++{ ++ struct page *tree_page; ++ u32 hash_max; ++ struct stable_node *stable_node, *new_snode; ++ struct rb_node *parent, **new; ++ ++research: ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ BUG_ON(!*new); ++ hash_max = rmap_item_hash_max(rmap_item, hash); ++ while (*new) { ++ int cmp; ++ ++ stable_node = rb_entry(*new, struct stable_node, node); ++ ++ cmp = hash_cmp(hash_max, stable_node->hash_max); ++ ++ if (cmp < 0) { ++ parent = *new; ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ parent = *new; ++ new = &parent->rb_right; ++ } else { ++ tree_page = get_uksm_page(stable_node, 1, 0); ++ if (tree_page) { ++ cmp = memcmp_pages(*kpage, tree_page, 1); ++ if (!cmp) { ++ try_merge_with_stable(rmap_item, ++ tree_rmap_item, kpage, ++ tree_page, success1, success2); ++ ++ put_page(tree_page); ++ if (!*success1 && !*success2) ++ goto failed; ++ /* ++ * successfully merged with a stable ++ * node ++ */ ++ return stable_node; ++ } else { ++ put_page(tree_page); ++ goto failed; ++ } ++ } else { ++ /* ++ * stable node may be deleted, ++ * and subtree maybe ++ * restructed, cannot ++ * continue, research it. ++ */ ++ if (tree_node->count) { ++ goto research; ++ } else { ++ /* reuse the tree node*/ ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ } ++ } ++ } ++ } ++ ++ new_snode = new_stable_node(tree_node, *kpage, hash_max); ++ if (!new_snode) ++ goto failed; ++ ++ rb_link_node(&new_snode->node, parent, new); ++ rb_insert_color(&new_snode->node, &tree_node->sub_root); ++ tree_node->count++; ++ *success1 = *success2 = 1; ++ ++ return new_snode; ++ ++failed: ++ return NULL; ++} ++ ++ ++/** ++ * stable_tree_insert() - try to insert a merged page in unstable tree to ++ * the stable tree ++ * ++ * @kpage: the page need to be inserted ++ * @hash: the current hash of this page ++ * @rmap_item: the rmap_item being scanned ++ * @tree_rmap_item: the rmap_item found on unstable tree ++ * @success1: return if rmap_item is merged ++ * @success2: return if tree_rmap_item is merged ++ * ++ * @return the stable_node on stable tree if at least one ++ * rmap_item is inserted into stable tree, NULL ++ * otherwise. ++ */ ++static struct stable_node * ++stable_tree_insert(struct page **kpage, u32 hash, ++ struct rmap_item *rmap_item, ++ struct rmap_item *tree_rmap_item, ++ int *success1, int *success2) ++{ ++ struct rb_node **new = &root_stable_treep->rb_node; ++ struct rb_node *parent = NULL; ++ struct stable_node *stable_node; ++ struct tree_node *tree_node; ++ u32 hash_max = 0; ++ ++ *success1 = *success2 = 0; ++ ++ while (*new) { ++ int cmp; ++ ++ tree_node = rb_entry(*new, struct tree_node, node); ++ ++ cmp = hash_cmp(hash, tree_node->hash); ++ ++ if (cmp < 0) { ++ parent = *new; ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ parent = *new; ++ new = &parent->rb_right; ++ } else ++ break; ++ } ++ ++ if (*new) { ++ if (tree_node->count == 1) { ++ stable_node = first_level_insert(tree_node, rmap_item, ++ tree_rmap_item, kpage, ++ hash, success1, success2); ++ } else { ++ stable_node = stable_subtree_insert(tree_node, ++ rmap_item, tree_rmap_item, kpage, ++ hash, success1, success2); ++ } ++ } else { ++ ++ /* no tree node found */ ++ tree_node = alloc_tree_node(stable_tree_node_listp); ++ if (!tree_node) { ++ stable_node = NULL; ++ goto out; ++ } ++ ++ stable_node = new_stable_node(tree_node, *kpage, hash_max); ++ if (!stable_node) { ++ free_tree_node(tree_node); ++ goto out; ++ } ++ ++ tree_node->hash = hash; ++ rb_link_node(&tree_node->node, parent, new); ++ rb_insert_color(&tree_node->node, root_stable_treep); ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ ++ rb_link_node(&stable_node->node, parent, new); ++ rb_insert_color(&stable_node->node, &tree_node->sub_root); ++ tree_node->count++; ++ *success1 = *success2 = 1; ++ } ++ ++out: ++ return stable_node; ++} ++ ++ ++/** ++ * get_tree_rmap_item_page() - try to get the page and lock the mmap_sem ++ * ++ * @return 0 on success, -EBUSY if unable to lock the mmap_sem, ++ * -EINVAL if the page mapping has been changed. ++ */ ++static inline int get_tree_rmap_item_page(struct rmap_item *tree_rmap_item) ++{ ++ int err; ++ ++ err = get_mergeable_page_lock_mmap(tree_rmap_item); ++ ++ if (err == -EINVAL) { ++ /* its page map has been changed, remove it */ ++ remove_rmap_item_from_tree(tree_rmap_item); ++ } ++ ++ /* The page is gotten and mmap_sem is locked now. */ ++ return err; ++} ++ ++ ++/** ++ * unstable_tree_search_insert() - search an unstable tree rmap_item with the ++ * same hash value. Get its page and trylock the mmap_sem ++ */ ++static inline ++struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item, ++ u32 hash) ++ ++{ ++ struct rb_node **new = &root_unstable_tree.rb_node; ++ struct rb_node *parent = NULL; ++ struct tree_node *tree_node; ++ u32 hash_max; ++ struct rmap_item *tree_rmap_item; ++ ++ while (*new) { ++ int cmp; ++ ++ tree_node = rb_entry(*new, struct tree_node, node); ++ ++ cmp = hash_cmp(hash, tree_node->hash); ++ ++ if (cmp < 0) { ++ parent = *new; ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ parent = *new; ++ new = &parent->rb_right; ++ } else ++ break; ++ } ++ ++ if (*new) { ++ /* got the tree_node */ ++ if (tree_node->count == 1) { ++ tree_rmap_item = rb_entry(tree_node->sub_root.rb_node, ++ struct rmap_item, node); ++ BUG_ON(!tree_rmap_item); ++ ++ goto get_page_out; ++ } ++ ++ /* well, search the collision subtree */ ++ new = &tree_node->sub_root.rb_node; ++ BUG_ON(!*new); ++ hash_max = rmap_item_hash_max(rmap_item, hash); ++ ++ while (*new) { ++ int cmp; ++ ++ tree_rmap_item = rb_entry(*new, struct rmap_item, ++ node); ++ ++ cmp = hash_cmp(hash_max, tree_rmap_item->hash_max); ++ parent = *new; ++ if (cmp < 0) ++ new = &parent->rb_left; ++ else if (cmp > 0) ++ new = &parent->rb_right; ++ else ++ goto get_page_out; ++ } ++ } else { ++ /* alloc a new tree_node */ ++ tree_node = alloc_tree_node(&unstable_tree_node_list); ++ if (!tree_node) ++ return NULL; ++ ++ tree_node->hash = hash; ++ rb_link_node(&tree_node->node, parent, new); ++ rb_insert_color(&tree_node->node, &root_unstable_tree); ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ } ++ ++ /* did not found even in sub-tree */ ++ rmap_item->tree_node = tree_node; ++ rmap_item->address |= UNSTABLE_FLAG; ++ rmap_item->hash_round = uksm_hash_round; ++ rb_link_node(&rmap_item->node, parent, new); ++ rb_insert_color(&rmap_item->node, &tree_node->sub_root); ++ ++ uksm_pages_unshared++; ++ return NULL; ++ ++get_page_out: ++ if (tree_rmap_item->page == rmap_item->page) ++ return NULL; ++ ++ if (get_tree_rmap_item_page(tree_rmap_item)) ++ return NULL; ++ ++ return tree_rmap_item; ++} ++ ++static void hold_anon_vma(struct rmap_item *rmap_item, ++ struct anon_vma *anon_vma) ++{ ++ rmap_item->anon_vma = anon_vma; ++ get_anon_vma(anon_vma); ++} ++ ++ ++/** ++ * stable_tree_append() - append a rmap_item to a stable node. Deduplication ++ * ratio statistics is done in this function. ++ * ++ */ ++static void stable_tree_append(struct rmap_item *rmap_item, ++ struct stable_node *stable_node, int logdedup) ++{ ++ struct node_vma *node_vma = NULL, *new_node_vma, *node_vma_cont = NULL; ++ unsigned long key = (unsigned long)rmap_item->slot; ++ unsigned long factor = rmap_item->slot->rung->step; ++ ++ BUG_ON(!stable_node); ++ rmap_item->address |= STABLE_FLAG; ++ ++ if (hlist_empty(&stable_node->hlist)) { ++ uksm_pages_shared++; ++ goto node_vma_new; ++ } else { ++ uksm_pages_sharing++; ++ } ++ ++ hlist_for_each_entry(node_vma, &stable_node->hlist, hlist) { ++ if (node_vma->key >= key) ++ break; ++ ++ if (logdedup) { ++ node_vma->slot->pages_bemerged += factor; ++ if (list_empty(&node_vma->slot->dedup_list)) ++ list_add(&node_vma->slot->dedup_list, ++ &vma_slot_dedup); ++ } ++ } ++ ++ if (node_vma) { ++ if (node_vma->key == key) { ++ node_vma_cont = hlist_entry_safe(node_vma->hlist.next, struct node_vma, hlist); ++ goto node_vma_ok; ++ } else if (node_vma->key > key) { ++ node_vma_cont = node_vma; ++ } ++ } ++ ++node_vma_new: ++ /* no same vma already in node, alloc a new node_vma */ ++ new_node_vma = alloc_node_vma(); ++ BUG_ON(!new_node_vma); ++ new_node_vma->head = stable_node; ++ new_node_vma->slot = rmap_item->slot; ++ ++ if (!node_vma) { ++ hlist_add_head(&new_node_vma->hlist, &stable_node->hlist); ++ } else if (node_vma->key != key) { ++ if (node_vma->key < key) ++ hlist_add_behind(&new_node_vma->hlist, &node_vma->hlist); ++ else { ++ hlist_add_before(&new_node_vma->hlist, ++ &node_vma->hlist); ++ } ++ ++ } ++ node_vma = new_node_vma; ++ ++node_vma_ok: /* ok, ready to add to the list */ ++ rmap_item->head = node_vma; ++ hlist_add_head(&rmap_item->hlist, &node_vma->rmap_hlist); ++ hold_anon_vma(rmap_item, rmap_item->slot->vma->anon_vma); ++ if (logdedup) { ++ rmap_item->slot->pages_merged++; ++ if (node_vma_cont) { ++ node_vma = node_vma_cont; ++ hlist_for_each_entry_continue(node_vma, hlist) { ++ node_vma->slot->pages_bemerged += factor; ++ if (list_empty(&node_vma->slot->dedup_list)) ++ list_add(&node_vma->slot->dedup_list, ++ &vma_slot_dedup); ++ } ++ } ++ } ++} ++ ++/* ++ * We use break_ksm to break COW on a ksm page: it's a stripped down ++ * ++ * if (get_user_pages(addr, 1, 1, 1, &page, NULL) == 1) ++ * put_page(page); ++ * ++ * but taking great care only to touch a ksm page, in a VM_MERGEABLE vma, ++ * in case the application has unmapped and remapped mm,addr meanwhile. ++ * Could a ksm page appear anywhere else? Actually yes, in a VM_PFNMAP ++ * mmap of /dev/mem or /dev/kmem, where we would not want to touch it. ++ */ ++static int break_ksm(struct vm_area_struct *vma, unsigned long addr) ++{ ++ struct page *page; ++ int ret = 0; ++ ++ do { ++ cond_resched(); ++ page = follow_page(vma, addr, FOLL_GET | FOLL_MIGRATION | FOLL_REMOTE); ++ if (IS_ERR_OR_NULL(page)) ++ break; ++ if (PageKsm(page)) { ++ ret = handle_mm_fault(vma->vm_mm, vma, addr, ++ FAULT_FLAG_WRITE | FAULT_FLAG_REMOTE); ++ } else ++ ret = VM_FAULT_WRITE; ++ put_page(page); ++ } while (!(ret & (VM_FAULT_WRITE | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | VM_FAULT_OOM))); ++ /* ++ * We must loop because handle_mm_fault() may back out if there's ++ * any difficulty e.g. if pte accessed bit gets updated concurrently. ++ * ++ * VM_FAULT_WRITE is what we have been hoping for: it indicates that ++ * COW has been broken, even if the vma does not permit VM_WRITE; ++ * but note that a concurrent fault might break PageKsm for us. ++ * ++ * VM_FAULT_SIGBUS could occur if we race with truncation of the ++ * backing file, which also invalidates anonymous pages: that's ++ * okay, that truncation will have unmapped the PageKsm for us. ++ * ++ * VM_FAULT_OOM: at the time of writing (late July 2009), setting ++ * aside mem_cgroup limits, VM_FAULT_OOM would only be set if the ++ * current task has TIF_MEMDIE set, and will be OOM killed on return ++ * to user; and ksmd, having no mm, would never be chosen for that. ++ * ++ * But if the mm is in a limited mem_cgroup, then the fault may fail ++ * with VM_FAULT_OOM even if the current task is not TIF_MEMDIE; and ++ * even ksmd can fail in this way - though it's usually breaking ksm ++ * just to undo a merge it made a moment before, so unlikely to oom. ++ * ++ * That's a pity: we might therefore have more kernel pages allocated ++ * than we're counting as nodes in the stable tree; but uksm_do_scan ++ * will retry to break_cow on each pass, so should recover the page ++ * in due course. The important thing is to not let VM_MERGEABLE ++ * be cleared while any such pages might remain in the area. ++ */ ++ return (ret & VM_FAULT_OOM) ? -ENOMEM : 0; ++} ++ ++static void break_cow(struct rmap_item *rmap_item) ++{ ++ struct vm_area_struct *vma = rmap_item->slot->vma; ++ struct mm_struct *mm = vma->vm_mm; ++ unsigned long addr = get_rmap_addr(rmap_item); ++ ++ if (uksm_test_exit(mm)) ++ goto out; ++ ++ break_ksm(vma, addr); ++out: ++ return; ++} ++ ++/* ++ * Though it's very tempting to unmerge in_stable_tree(rmap_item)s rather ++ * than check every pte of a given vma, the locking doesn't quite work for ++ * that - an rmap_item is assigned to the stable tree after inserting ksm ++ * page and upping mmap_sem. Nor does it fit with the way we skip dup'ing ++ * rmap_items from parent to child at fork time (so as not to waste time ++ * if exit comes before the next scan reaches it). ++ * ++ * Similarly, although we'd like to remove rmap_items (so updating counts ++ * and freeing memory) when unmerging an area, it's easier to leave that ++ * to the next pass of ksmd - consider, for example, how ksmd might be ++ * in cmp_and_merge_page on one of the rmap_items we would be removing. ++ */ ++inline int unmerge_uksm_pages(struct vm_area_struct *vma, ++ unsigned long start, unsigned long end) ++{ ++ unsigned long addr; ++ int err = 0; ++ ++ for (addr = start; addr < end && !err; addr += PAGE_SIZE) { ++ if (uksm_test_exit(vma->vm_mm)) ++ break; ++ if (signal_pending(current)) ++ err = -ERESTARTSYS; ++ else ++ err = break_ksm(vma, addr); ++ } ++ return err; ++} ++ ++static inline void inc_uksm_pages_scanned(void) ++{ ++ u64 delta; ++ ++ ++ if (uksm_pages_scanned == U64_MAX) { ++ encode_benefit(); ++ ++ delta = uksm_pages_scanned >> pages_scanned_base; ++ ++ if (CAN_OVERFLOW_U64(pages_scanned_stored, delta)) { ++ pages_scanned_stored >>= 1; ++ delta >>= 1; ++ pages_scanned_base++; ++ } ++ ++ pages_scanned_stored += delta; ++ ++ uksm_pages_scanned = uksm_pages_scanned_last = 0; ++ } ++ ++ uksm_pages_scanned++; ++} ++ ++static inline int find_zero_page_hash(int strength, u32 hash) ++{ ++ return (zero_hash_table[strength] == hash); ++} ++ ++static ++int cmp_and_merge_zero_page(struct vm_area_struct *vma, struct page *page) ++{ ++ struct page *zero_page = empty_uksm_zero_page; ++ struct mm_struct *mm = vma->vm_mm; ++ pte_t orig_pte = __pte(0); ++ int err = -EFAULT; ++ ++ if (uksm_test_exit(mm)) ++ goto out; ++ ++ if (!trylock_page(page)) ++ goto out; ++ ++ if (!PageAnon(page)) ++ goto out_unlock; ++ ++ if (PageTransCompound(page)) { ++ err = split_huge_page(page); ++ if (err) ++ goto out_unlock; ++ } ++ ++ if (write_protect_page(vma, page, &orig_pte, 0) == 0) { ++ if (is_page_full_zero(page)) ++ err = replace_page(vma, page, zero_page, orig_pte); ++ } ++ ++out_unlock: ++ unlock_page(page); ++out: ++ return err; ++} ++ ++/* ++ * cmp_and_merge_page() - first see if page can be merged into the stable ++ * tree; if not, compare hash to previous and if it's the same, see if page ++ * can be inserted into the unstable tree, or merged with a page already there ++ * and both transferred to the stable tree. ++ * ++ * @page: the page that we are searching identical page to. ++ * @rmap_item: the reverse mapping into the virtual address of this page ++ */ ++static void cmp_and_merge_page(struct rmap_item *rmap_item, u32 hash) ++{ ++ struct rmap_item *tree_rmap_item; ++ struct page *page; ++ struct page *kpage = NULL; ++ u32 hash_max; ++ int err; ++ unsigned int success1, success2; ++ struct stable_node *snode; ++ int cmp; ++ struct rb_node *parent = NULL, **new; ++ ++ remove_rmap_item_from_tree(rmap_item); ++ page = rmap_item->page; ++ ++ /* We first start with searching the page inside the stable tree */ ++ kpage = stable_tree_search(rmap_item, hash); ++ if (kpage) { ++ err = try_to_merge_with_uksm_page(rmap_item, kpage, ++ hash); ++ if (!err) { ++ /* ++ * The page was successfully merged, add ++ * its rmap_item to the stable tree. ++ * page lock is needed because it's ++ * racing with try_to_unmap_ksm(), etc. ++ */ ++ lock_page(kpage); ++ snode = page_stable_node(kpage); ++ stable_tree_append(rmap_item, snode, 1); ++ unlock_page(kpage); ++ put_page(kpage); ++ return; /* success */ ++ } ++ put_page(kpage); ++ ++ /* ++ * if it's a collision and it has been search in sub-rbtree ++ * (hash_max != 0), we want to abort, because if it is ++ * successfully merged in unstable tree, the collision trends to ++ * happen again. ++ */ ++ if (err == MERGE_ERR_COLLI && rmap_item->hash_max) ++ return; ++ } ++ ++ tree_rmap_item = ++ unstable_tree_search_insert(rmap_item, hash); ++ if (tree_rmap_item) { ++ err = try_to_merge_two_pages(rmap_item, tree_rmap_item, hash); ++ /* ++ * As soon as we merge this page, we want to remove the ++ * rmap_item of the page we have merged with from the unstable ++ * tree, and insert it instead as new node in the stable tree. ++ */ ++ if (!err) { ++ kpage = page; ++ remove_rmap_item_from_tree(tree_rmap_item); ++ lock_page(kpage); ++ snode = stable_tree_insert(&kpage, hash, ++ rmap_item, tree_rmap_item, ++ &success1, &success2); ++ ++ /* ++ * Do not log dedup for tree item, it's not counted as ++ * scanned in this round. ++ */ ++ if (success2) ++ stable_tree_append(tree_rmap_item, snode, 0); ++ ++ /* ++ * The order of these two stable append is important: ++ * we are scanning rmap_item. ++ */ ++ if (success1) ++ stable_tree_append(rmap_item, snode, 1); ++ ++ /* ++ * The original kpage may be unlocked inside ++ * stable_tree_insert() already. This page ++ * should be unlocked before doing ++ * break_cow(). ++ */ ++ unlock_page(kpage); ++ ++ if (!success1) ++ break_cow(rmap_item); ++ ++ if (!success2) ++ break_cow(tree_rmap_item); ++ ++ } else if (err == MERGE_ERR_COLLI) { ++ BUG_ON(tree_rmap_item->tree_node->count > 1); ++ ++ rmap_item_hash_max(tree_rmap_item, ++ tree_rmap_item->tree_node->hash); ++ ++ hash_max = rmap_item_hash_max(rmap_item, hash); ++ cmp = hash_cmp(hash_max, tree_rmap_item->hash_max); ++ parent = &tree_rmap_item->node; ++ if (cmp < 0) ++ new = &parent->rb_left; ++ else if (cmp > 0) ++ new = &parent->rb_right; ++ else ++ goto put_up_out; ++ ++ rmap_item->tree_node = tree_rmap_item->tree_node; ++ rmap_item->address |= UNSTABLE_FLAG; ++ rmap_item->hash_round = uksm_hash_round; ++ rb_link_node(&rmap_item->node, parent, new); ++ rb_insert_color(&rmap_item->node, ++ &tree_rmap_item->tree_node->sub_root); ++ rmap_item->tree_node->count++; ++ } else { ++ /* ++ * either one of the page has changed or they collide ++ * at the max hash, we consider them as ill items. ++ */ ++ remove_rmap_item_from_tree(tree_rmap_item); ++ } ++put_up_out: ++ put_page(tree_rmap_item->page); ++ up_read(&tree_rmap_item->slot->vma->vm_mm->mmap_sem); ++ } ++} ++ ++ ++ ++ ++static inline unsigned long get_pool_index(struct vma_slot *slot, ++ unsigned long index) ++{ ++ unsigned long pool_index; ++ ++ pool_index = (sizeof(struct rmap_list_entry *) * index) >> PAGE_SHIFT; ++ if (pool_index >= slot->pool_size) ++ BUG(); ++ return pool_index; ++} ++ ++static inline unsigned long index_page_offset(unsigned long index) ++{ ++ return offset_in_page(sizeof(struct rmap_list_entry *) * index); ++} ++ ++static inline ++struct rmap_list_entry *get_rmap_list_entry(struct vma_slot *slot, ++ unsigned long index, int need_alloc) ++{ ++ unsigned long pool_index; ++ struct page *page; ++ void *addr; ++ ++ ++ pool_index = get_pool_index(slot, index); ++ if (!slot->rmap_list_pool[pool_index]) { ++ if (!need_alloc) ++ return NULL; ++ ++ page = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN); ++ if (!page) ++ return NULL; ++ ++ slot->rmap_list_pool[pool_index] = page; ++ } ++ ++ addr = kmap(slot->rmap_list_pool[pool_index]); ++ addr += index_page_offset(index); ++ ++ return addr; ++} ++ ++static inline void put_rmap_list_entry(struct vma_slot *slot, ++ unsigned long index) ++{ ++ unsigned long pool_index; ++ ++ pool_index = get_pool_index(slot, index); ++ BUG_ON(!slot->rmap_list_pool[pool_index]); ++ kunmap(slot->rmap_list_pool[pool_index]); ++} ++ ++static inline int entry_is_new(struct rmap_list_entry *entry) ++{ ++ return !entry->item; ++} ++ ++static inline unsigned long get_index_orig_addr(struct vma_slot *slot, ++ unsigned long index) ++{ ++ return slot->vma->vm_start + (index << PAGE_SHIFT); ++} ++ ++static inline unsigned long get_entry_address(struct rmap_list_entry *entry) ++{ ++ unsigned long addr; ++ ++ if (is_addr(entry->addr)) ++ addr = get_clean_addr(entry->addr); ++ else if (entry->item) ++ addr = get_rmap_addr(entry->item); ++ else ++ BUG(); ++ ++ return addr; ++} ++ ++static inline struct rmap_item *get_entry_item(struct rmap_list_entry *entry) ++{ ++ if (is_addr(entry->addr)) ++ return NULL; ++ ++ return entry->item; ++} ++ ++static inline void inc_rmap_list_pool_count(struct vma_slot *slot, ++ unsigned long index) ++{ ++ unsigned long pool_index; ++ ++ pool_index = get_pool_index(slot, index); ++ BUG_ON(!slot->rmap_list_pool[pool_index]); ++ slot->pool_counts[pool_index]++; ++} ++ ++static inline void dec_rmap_list_pool_count(struct vma_slot *slot, ++ unsigned long index) ++{ ++ unsigned long pool_index; ++ ++ pool_index = get_pool_index(slot, index); ++ BUG_ON(!slot->rmap_list_pool[pool_index]); ++ BUG_ON(!slot->pool_counts[pool_index]); ++ slot->pool_counts[pool_index]--; ++} ++ ++static inline int entry_has_rmap(struct rmap_list_entry *entry) ++{ ++ return !is_addr(entry->addr) && entry->item; ++} ++ ++static inline void swap_entries(struct rmap_list_entry *entry1, ++ unsigned long index1, ++ struct rmap_list_entry *entry2, ++ unsigned long index2) ++{ ++ struct rmap_list_entry tmp; ++ ++ /* swapping two new entries is meaningless */ ++ BUG_ON(entry_is_new(entry1) && entry_is_new(entry2)); ++ ++ tmp = *entry1; ++ *entry1 = *entry2; ++ *entry2 = tmp; ++ ++ if (entry_has_rmap(entry1)) ++ entry1->item->entry_index = index1; ++ ++ if (entry_has_rmap(entry2)) ++ entry2->item->entry_index = index2; ++ ++ if (entry_has_rmap(entry1) && !entry_has_rmap(entry2)) { ++ inc_rmap_list_pool_count(entry1->item->slot, index1); ++ dec_rmap_list_pool_count(entry1->item->slot, index2); ++ } else if (!entry_has_rmap(entry1) && entry_has_rmap(entry2)) { ++ inc_rmap_list_pool_count(entry2->item->slot, index2); ++ dec_rmap_list_pool_count(entry2->item->slot, index1); ++ } ++} ++ ++static inline void free_entry_item(struct rmap_list_entry *entry) ++{ ++ unsigned long index; ++ struct rmap_item *item; ++ ++ if (!is_addr(entry->addr)) { ++ BUG_ON(!entry->item); ++ item = entry->item; ++ entry->addr = get_rmap_addr(item); ++ set_is_addr(entry->addr); ++ index = item->entry_index; ++ remove_rmap_item_from_tree(item); ++ dec_rmap_list_pool_count(item->slot, index); ++ free_rmap_item(item); ++ } ++} ++ ++static inline int pool_entry_boundary(unsigned long index) ++{ ++ unsigned long linear_addr; ++ ++ linear_addr = sizeof(struct rmap_list_entry *) * index; ++ return index && !offset_in_page(linear_addr); ++} ++ ++static inline void try_free_last_pool(struct vma_slot *slot, ++ unsigned long index) ++{ ++ unsigned long pool_index; ++ ++ pool_index = get_pool_index(slot, index); ++ if (slot->rmap_list_pool[pool_index] && ++ !slot->pool_counts[pool_index]) { ++ __free_page(slot->rmap_list_pool[pool_index]); ++ slot->rmap_list_pool[pool_index] = NULL; ++ slot->flags |= UKSM_SLOT_NEED_SORT; ++ } ++ ++} ++ ++static inline unsigned long vma_item_index(struct vm_area_struct *vma, ++ struct rmap_item *item) ++{ ++ return (get_rmap_addr(item) - vma->vm_start) >> PAGE_SHIFT; ++} ++ ++static int within_same_pool(struct vma_slot *slot, ++ unsigned long i, unsigned long j) ++{ ++ unsigned long pool_i, pool_j; ++ ++ pool_i = get_pool_index(slot, i); ++ pool_j = get_pool_index(slot, j); ++ ++ return (pool_i == pool_j); ++} ++ ++static void sort_rmap_entry_list(struct vma_slot *slot) ++{ ++ unsigned long i, j; ++ struct rmap_list_entry *entry, *swap_entry; ++ ++ entry = get_rmap_list_entry(slot, 0, 0); ++ for (i = 0; i < slot->pages; ) { ++ ++ if (!entry) ++ goto skip_whole_pool; ++ ++ if (entry_is_new(entry)) ++ goto next_entry; ++ ++ if (is_addr(entry->addr)) { ++ entry->addr = 0; ++ goto next_entry; ++ } ++ ++ j = vma_item_index(slot->vma, entry->item); ++ if (j == i) ++ goto next_entry; ++ ++ if (within_same_pool(slot, i, j)) ++ swap_entry = entry + j - i; ++ else ++ swap_entry = get_rmap_list_entry(slot, j, 1); ++ ++ swap_entries(entry, i, swap_entry, j); ++ if (!within_same_pool(slot, i, j)) ++ put_rmap_list_entry(slot, j); ++ continue; ++ ++skip_whole_pool: ++ i += PAGE_SIZE / sizeof(*entry); ++ if (i < slot->pages) ++ entry = get_rmap_list_entry(slot, i, 0); ++ continue; ++ ++next_entry: ++ if (i >= slot->pages - 1 || ++ !within_same_pool(slot, i, i + 1)) { ++ put_rmap_list_entry(slot, i); ++ if (i + 1 < slot->pages) ++ entry = get_rmap_list_entry(slot, i + 1, 0); ++ } else ++ entry++; ++ i++; ++ continue; ++ } ++ ++ /* free empty pool entries which contain no rmap_item */ ++ /* CAN be simplied to based on only pool_counts when bug freed !!!!! */ ++ for (i = 0; i < slot->pool_size; i++) { ++ unsigned char has_rmap; ++ void *addr; ++ ++ if (!slot->rmap_list_pool[i]) ++ continue; ++ ++ has_rmap = 0; ++ addr = kmap(slot->rmap_list_pool[i]); ++ BUG_ON(!addr); ++ for (j = 0; j < PAGE_SIZE / sizeof(*entry); j++) { ++ entry = (struct rmap_list_entry *)addr + j; ++ if (is_addr(entry->addr)) ++ continue; ++ if (!entry->item) ++ continue; ++ has_rmap = 1; ++ } ++ kunmap(slot->rmap_list_pool[i]); ++ if (!has_rmap) { ++ BUG_ON(slot->pool_counts[i]); ++ __free_page(slot->rmap_list_pool[i]); ++ slot->rmap_list_pool[i] = NULL; ++ } ++ } ++ ++ slot->flags &= ~UKSM_SLOT_NEED_SORT; ++} ++ ++/* ++ * vma_fully_scanned() - if all the pages in this slot have been scanned. ++ */ ++static inline int vma_fully_scanned(struct vma_slot *slot) ++{ ++ return slot->pages_scanned == slot->pages; ++} ++ ++/** ++ * get_next_rmap_item() - Get the next rmap_item in a vma_slot according to ++ * its random permutation. This function is embedded with the random ++ * permutation index management code. ++ */ ++static struct rmap_item *get_next_rmap_item(struct vma_slot *slot, u32 *hash) ++{ ++ unsigned long rand_range, addr, swap_index, scan_index; ++ struct rmap_item *item = NULL; ++ struct rmap_list_entry *scan_entry, *swap_entry = NULL; ++ struct page *page; ++ ++ scan_index = swap_index = slot->pages_scanned % slot->pages; ++ ++ if (pool_entry_boundary(scan_index)) ++ try_free_last_pool(slot, scan_index - 1); ++ ++ if (vma_fully_scanned(slot)) { ++ if (slot->flags & UKSM_SLOT_NEED_SORT) ++ slot->flags |= UKSM_SLOT_NEED_RERAND; ++ else ++ slot->flags &= ~UKSM_SLOT_NEED_RERAND; ++ if (slot->flags & UKSM_SLOT_NEED_SORT) ++ sort_rmap_entry_list(slot); ++ } ++ ++ scan_entry = get_rmap_list_entry(slot, scan_index, 1); ++ if (!scan_entry) ++ return NULL; ++ ++ if (entry_is_new(scan_entry)) { ++ scan_entry->addr = get_index_orig_addr(slot, scan_index); ++ set_is_addr(scan_entry->addr); ++ } ++ ++ if (slot->flags & UKSM_SLOT_NEED_RERAND) { ++ rand_range = slot->pages - scan_index; ++ BUG_ON(!rand_range); ++ swap_index = scan_index + (prandom_u32() % rand_range); ++ } ++ ++ if (swap_index != scan_index) { ++ swap_entry = get_rmap_list_entry(slot, swap_index, 1); ++ if (entry_is_new(swap_entry)) { ++ swap_entry->addr = get_index_orig_addr(slot, ++ swap_index); ++ set_is_addr(swap_entry->addr); ++ } ++ swap_entries(scan_entry, scan_index, swap_entry, swap_index); ++ } ++ ++ addr = get_entry_address(scan_entry); ++ item = get_entry_item(scan_entry); ++ BUG_ON(addr > slot->vma->vm_end || addr < slot->vma->vm_start); ++ ++ page = follow_page(slot->vma, addr, FOLL_GET); ++ if (IS_ERR_OR_NULL(page)) ++ goto nopage; ++ ++ if (!PageAnon(page)) ++ goto putpage; ++ ++ /*check is zero_page pfn or uksm_zero_page*/ ++ if ((page_to_pfn(page) == zero_pfn) ++ || (page_to_pfn(page) == uksm_zero_pfn)) ++ goto putpage; ++ ++ flush_anon_page(slot->vma, page, addr); ++ flush_dcache_page(page); ++ ++ ++ *hash = page_hash(page, hash_strength, 1); ++ inc_uksm_pages_scanned(); ++ /*if the page content all zero, re-map to zero-page*/ ++ if (find_zero_page_hash(hash_strength, *hash)) { ++ if (!cmp_and_merge_zero_page(slot->vma, page)) { ++ slot->pages_merged++; ++ inc_zone_page_state(page, NR_UKSM_ZERO_PAGES); ++ ++ /* For full-zero pages, no need to create rmap item */ ++ goto putpage; ++ } else { ++ inc_rshash_neg(memcmp_cost / 2); ++ } ++ } ++ ++ if (!item) { ++ item = alloc_rmap_item(); ++ if (item) { ++ /* It has already been zeroed */ ++ item->slot = slot; ++ item->address = addr; ++ item->entry_index = scan_index; ++ scan_entry->item = item; ++ inc_rmap_list_pool_count(slot, scan_index); ++ } else ++ goto putpage; ++ } ++ ++ BUG_ON(item->slot != slot); ++ /* the page may have changed */ ++ item->page = page; ++ put_rmap_list_entry(slot, scan_index); ++ if (swap_entry) ++ put_rmap_list_entry(slot, swap_index); ++ return item; ++ ++putpage: ++ put_page(page); ++ page = NULL; ++nopage: ++ /* no page, store addr back and free rmap_item if possible */ ++ free_entry_item(scan_entry); ++ put_rmap_list_entry(slot, scan_index); ++ if (swap_entry) ++ put_rmap_list_entry(slot, swap_index); ++ return NULL; ++} ++ ++static inline int in_stable_tree(struct rmap_item *rmap_item) ++{ ++ return rmap_item->address & STABLE_FLAG; ++} ++ ++/** ++ * scan_vma_one_page() - scan the next page in a vma_slot. Called with ++ * mmap_sem locked. ++ */ ++static noinline void scan_vma_one_page(struct vma_slot *slot) ++{ ++ u32 hash; ++ struct mm_struct *mm; ++ struct rmap_item *rmap_item = NULL; ++ struct vm_area_struct *vma = slot->vma; ++ ++ mm = vma->vm_mm; ++ BUG_ON(!mm); ++ BUG_ON(!slot); ++ ++ rmap_item = get_next_rmap_item(slot, &hash); ++ if (!rmap_item) ++ goto out1; ++ ++ if (PageKsm(rmap_item->page) && in_stable_tree(rmap_item)) ++ goto out2; ++ ++ cmp_and_merge_page(rmap_item, hash); ++out2: ++ put_page(rmap_item->page); ++out1: ++ slot->pages_scanned++; ++ slot->this_sampled++; ++ if (slot->fully_scanned_round != fully_scanned_round) ++ scanned_virtual_pages++; ++ ++ if (vma_fully_scanned(slot)) ++ slot->fully_scanned_round = fully_scanned_round; ++} ++ ++static inline unsigned long rung_get_pages(struct scan_rung *rung) ++{ ++ struct slot_tree_node *node; ++ ++ if (!rung->vma_root.rnode) ++ return 0; ++ ++ node = container_of(rung->vma_root.rnode, struct slot_tree_node, snode); ++ ++ return node->size; ++} ++ ++#define RUNG_SAMPLED_MIN 3 ++ ++static inline ++void uksm_calc_rung_step(struct scan_rung *rung, ++ unsigned long page_time, unsigned long ratio) ++{ ++ unsigned long sampled, pages; ++ ++ /* will be fully scanned ? */ ++ if (!rung->cover_msecs) { ++ rung->step = 1; ++ return; ++ } ++ ++ sampled = rung->cover_msecs * (NSEC_PER_MSEC / TIME_RATIO_SCALE) ++ * ratio / page_time; ++ ++ /* ++ * Before we finsish a scan round and expensive per-round jobs, ++ * we need to have a chance to estimate the per page time. So ++ * the sampled number can not be too small. ++ */ ++ if (sampled < RUNG_SAMPLED_MIN) ++ sampled = RUNG_SAMPLED_MIN; ++ ++ pages = rung_get_pages(rung); ++ if (likely(pages > sampled)) ++ rung->step = pages / sampled; ++ else ++ rung->step = 1; ++} ++ ++static inline int step_need_recalc(struct scan_rung *rung) ++{ ++ unsigned long pages, stepmax; ++ ++ pages = rung_get_pages(rung); ++ stepmax = pages / RUNG_SAMPLED_MIN; ++ ++ return pages && (rung->step > pages || ++ (stepmax && rung->step > stepmax)); ++} ++ ++static inline ++void reset_current_scan(struct scan_rung *rung, int finished, int step_recalc) ++{ ++ struct vma_slot *slot; ++ ++ if (finished) ++ rung->flags |= UKSM_RUNG_ROUND_FINISHED; ++ ++ if (step_recalc || step_need_recalc(rung)) { ++ uksm_calc_rung_step(rung, uksm_ema_page_time, rung->cpu_ratio); ++ BUG_ON(step_need_recalc(rung)); ++ } ++ ++ slot_iter_index = prandom_u32() % rung->step; ++ BUG_ON(!rung->vma_root.rnode); ++ slot = sradix_tree_next(&rung->vma_root, NULL, 0, slot_iter); ++ BUG_ON(!slot); ++ ++ rung->current_scan = slot; ++ rung->current_offset = slot_iter_index; ++} ++ ++static inline struct sradix_tree_root *slot_get_root(struct vma_slot *slot) ++{ ++ return &slot->rung->vma_root; ++} ++ ++/* ++ * return if resetted. ++ */ ++static int advance_current_scan(struct scan_rung *rung) ++{ ++ unsigned short n; ++ struct vma_slot *slot, *next = NULL; ++ ++ BUG_ON(!rung->vma_root.num); ++ ++ slot = rung->current_scan; ++ n = (slot->pages - rung->current_offset) % rung->step; ++ slot_iter_index = rung->step - n; ++ next = sradix_tree_next(&rung->vma_root, slot->snode, ++ slot->sindex, slot_iter); ++ ++ if (next) { ++ rung->current_offset = slot_iter_index; ++ rung->current_scan = next; ++ return 0; ++ } else { ++ reset_current_scan(rung, 1, 0); ++ return 1; ++ } ++} ++ ++static inline void rung_rm_slot(struct vma_slot *slot) ++{ ++ struct scan_rung *rung = slot->rung; ++ struct sradix_tree_root *root; ++ ++ if (rung->current_scan == slot) ++ advance_current_scan(rung); ++ ++ root = slot_get_root(slot); ++ sradix_tree_delete_from_leaf(root, slot->snode, slot->sindex); ++ slot->snode = NULL; ++ if (step_need_recalc(rung)) { ++ uksm_calc_rung_step(rung, uksm_ema_page_time, rung->cpu_ratio); ++ BUG_ON(step_need_recalc(rung)); ++ } ++ ++ /* In case advance_current_scan loop back to this slot again */ ++ if (rung->vma_root.num && rung->current_scan == slot) ++ reset_current_scan(slot->rung, 1, 0); ++} ++ ++static inline void rung_add_new_slots(struct scan_rung *rung, ++ struct vma_slot **slots, unsigned long num) ++{ ++ int err; ++ struct vma_slot *slot; ++ unsigned long i; ++ struct sradix_tree_root *root = &rung->vma_root; ++ ++ err = sradix_tree_enter(root, (void **)slots, num); ++ BUG_ON(err); ++ ++ for (i = 0; i < num; i++) { ++ slot = slots[i]; ++ slot->rung = rung; ++ BUG_ON(vma_fully_scanned(slot)); ++ } ++ ++ if (rung->vma_root.num == num) ++ reset_current_scan(rung, 0, 1); ++} ++ ++static inline int rung_add_one_slot(struct scan_rung *rung, ++ struct vma_slot *slot) ++{ ++ int err; ++ ++ err = sradix_tree_enter(&rung->vma_root, (void **)&slot, 1); ++ if (err) ++ return err; ++ ++ slot->rung = rung; ++ if (rung->vma_root.num == 1) ++ reset_current_scan(rung, 0, 1); ++ ++ return 0; ++} ++ ++/* ++ * Return true if the slot is deleted from its rung. ++ */ ++static inline int vma_rung_enter(struct vma_slot *slot, struct scan_rung *rung) ++{ ++ struct scan_rung *old_rung = slot->rung; ++ int err; ++ ++ if (old_rung == rung) ++ return 0; ++ ++ rung_rm_slot(slot); ++ err = rung_add_one_slot(rung, slot); ++ if (err) { ++ err = rung_add_one_slot(old_rung, slot); ++ WARN_ON(err); /* OOPS, badly OOM, we lost this slot */ ++ } ++ ++ return 1; ++} ++ ++static inline int vma_rung_up(struct vma_slot *slot) ++{ ++ struct scan_rung *rung; ++ ++ rung = slot->rung; ++ if (slot->rung != &uksm_scan_ladder[SCAN_LADDER_SIZE-1]) ++ rung++; ++ ++ return vma_rung_enter(slot, rung); ++} ++ ++static inline int vma_rung_down(struct vma_slot *slot) ++{ ++ struct scan_rung *rung; ++ ++ rung = slot->rung; ++ if (slot->rung != &uksm_scan_ladder[0]) ++ rung--; ++ ++ return vma_rung_enter(slot, rung); ++} ++ ++/** ++ * cal_dedup_ratio() - Calculate the deduplication ratio for this slot. ++ */ ++static unsigned long cal_dedup_ratio(struct vma_slot *slot) ++{ ++ unsigned long ret; ++ unsigned long pages; ++ ++ pages = slot->this_sampled; ++ if (!pages) ++ return 0; ++ ++ BUG_ON(slot->pages_scanned == slot->last_scanned); ++ ++ ret = slot->pages_merged; ++ ++ /* Thrashing area filtering */ ++ if (ret && uksm_thrash_threshold) { ++ if (slot->pages_cowed * 100 / slot->pages_merged ++ > uksm_thrash_threshold) { ++ ret = 0; ++ } else { ++ ret = slot->pages_merged - slot->pages_cowed; ++ } ++ } ++ ++ return ret * 100 / pages; ++} ++ ++/** ++ * cal_dedup_ratio() - Calculate the deduplication ratio for this slot. ++ */ ++static unsigned long cal_dedup_ratio_old(struct vma_slot *slot) ++{ ++ unsigned long ret; ++ unsigned long pages; ++ ++ pages = slot->pages; ++ if (!pages) ++ return 0; ++ ++ ret = slot->pages_bemerged; ++ ++ /* Thrashing area filtering */ ++ if (ret && uksm_thrash_threshold) { ++ if (slot->pages_cowed * 100 / slot->pages_bemerged ++ > uksm_thrash_threshold) { ++ ret = 0; ++ } else { ++ ret = slot->pages_bemerged - slot->pages_cowed; ++ } ++ } ++ ++ return ret * 100 / pages; ++} ++ ++/** ++ * stable_node_reinsert() - When the hash_strength has been adjusted, the ++ * stable tree need to be restructured, this is the function re-inserting the ++ * stable node. ++ */ ++static inline void stable_node_reinsert(struct stable_node *new_node, ++ struct page *page, ++ struct rb_root *root_treep, ++ struct list_head *tree_node_listp, ++ u32 hash) ++{ ++ struct rb_node **new = &root_treep->rb_node; ++ struct rb_node *parent = NULL; ++ struct stable_node *stable_node; ++ struct tree_node *tree_node; ++ struct page *tree_page; ++ int cmp; ++ ++ while (*new) { ++ int cmp; ++ ++ tree_node = rb_entry(*new, struct tree_node, node); ++ ++ cmp = hash_cmp(hash, tree_node->hash); ++ ++ if (cmp < 0) { ++ parent = *new; ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ parent = *new; ++ new = &parent->rb_right; ++ } else ++ break; ++ } ++ ++ if (*new) { ++ /* find a stable tree node with same first level hash value */ ++ stable_node_hash_max(new_node, page, hash); ++ if (tree_node->count == 1) { ++ stable_node = rb_entry(tree_node->sub_root.rb_node, ++ struct stable_node, node); ++ tree_page = get_uksm_page(stable_node, 1, 0); ++ if (tree_page) { ++ stable_node_hash_max(stable_node, ++ tree_page, hash); ++ put_page(tree_page); ++ ++ /* prepare for stable node insertion */ ++ ++ cmp = hash_cmp(new_node->hash_max, ++ stable_node->hash_max); ++ parent = &stable_node->node; ++ if (cmp < 0) ++ new = &parent->rb_left; ++ else if (cmp > 0) ++ new = &parent->rb_right; ++ else ++ goto failed; ++ ++ goto add_node; ++ } else { ++ /* the only stable_node deleted, the tree node ++ * was not deleted. ++ */ ++ goto tree_node_reuse; ++ } ++ } ++ ++ /* well, search the collision subtree */ ++ new = &tree_node->sub_root.rb_node; ++ parent = NULL; ++ BUG_ON(!*new); ++ while (*new) { ++ int cmp; ++ ++ stable_node = rb_entry(*new, struct stable_node, node); ++ ++ cmp = hash_cmp(new_node->hash_max, ++ stable_node->hash_max); ++ ++ if (cmp < 0) { ++ parent = *new; ++ new = &parent->rb_left; ++ } else if (cmp > 0) { ++ parent = *new; ++ new = &parent->rb_right; ++ } else { ++ /* oh, no, still a collision */ ++ goto failed; ++ } ++ } ++ ++ goto add_node; ++ } ++ ++ /* no tree node found */ ++ tree_node = alloc_tree_node(tree_node_listp); ++ if (!tree_node) { ++ printk(KERN_ERR "UKSM: memory allocation error!\n"); ++ goto failed; ++ } else { ++ tree_node->hash = hash; ++ rb_link_node(&tree_node->node, parent, new); ++ rb_insert_color(&tree_node->node, root_treep); ++ ++tree_node_reuse: ++ /* prepare for stable node insertion */ ++ parent = NULL; ++ new = &tree_node->sub_root.rb_node; ++ } ++ ++add_node: ++ rb_link_node(&new_node->node, parent, new); ++ rb_insert_color(&new_node->node, &tree_node->sub_root); ++ new_node->tree_node = tree_node; ++ tree_node->count++; ++ return; ++ ++failed: ++ /* This can only happen when two nodes have collided ++ * in two levels. ++ */ ++ new_node->tree_node = NULL; ++ return; ++} ++ ++static inline void free_all_tree_nodes(struct list_head *list) ++{ ++ struct tree_node *node, *tmp; ++ ++ list_for_each_entry_safe(node, tmp, list, all_list) { ++ free_tree_node(node); ++ } ++} ++ ++/** ++ * stable_tree_delta_hash() - Delta hash the stable tree from previous hash ++ * strength to the current hash_strength. It re-structures the hole tree. ++ */ ++static inline void stable_tree_delta_hash(u32 prev_hash_strength) ++{ ++ struct stable_node *node, *tmp; ++ struct rb_root *root_new_treep; ++ struct list_head *new_tree_node_listp; ++ ++ stable_tree_index = (stable_tree_index + 1) % 2; ++ root_new_treep = &root_stable_tree[stable_tree_index]; ++ new_tree_node_listp = &stable_tree_node_list[stable_tree_index]; ++ *root_new_treep = RB_ROOT; ++ BUG_ON(!list_empty(new_tree_node_listp)); ++ ++ /* ++ * we need to be safe, the node could be removed by get_uksm_page() ++ */ ++ list_for_each_entry_safe(node, tmp, &stable_node_list, all_list) { ++ void *addr; ++ struct page *node_page; ++ u32 hash; ++ ++ /* ++ * We are completely re-structuring the stable nodes to a new ++ * stable tree. We don't want to touch the old tree unlinks and ++ * old tree_nodes. The old tree_nodes will be freed at once. ++ */ ++ node_page = get_uksm_page(node, 0, 0); ++ if (!node_page) ++ continue; ++ ++ if (node->tree_node) { ++ hash = node->tree_node->hash; ++ ++ addr = kmap_atomic(node_page); ++ ++ hash = delta_hash(addr, prev_hash_strength, ++ hash_strength, hash); ++ kunmap_atomic(addr); ++ } else { ++ /* ++ *it was not inserted to rbtree due to collision in last ++ *round scan. ++ */ ++ hash = page_hash(node_page, hash_strength, 0); ++ } ++ ++ stable_node_reinsert(node, node_page, root_new_treep, ++ new_tree_node_listp, hash); ++ put_page(node_page); ++ } ++ ++ root_stable_treep = root_new_treep; ++ free_all_tree_nodes(stable_tree_node_listp); ++ BUG_ON(!list_empty(stable_tree_node_listp)); ++ stable_tree_node_listp = new_tree_node_listp; ++} ++ ++static inline void inc_hash_strength(unsigned long delta) ++{ ++ hash_strength += 1 << delta; ++ if (hash_strength > HASH_STRENGTH_MAX) ++ hash_strength = HASH_STRENGTH_MAX; ++} ++ ++static inline void dec_hash_strength(unsigned long delta) ++{ ++ unsigned long change = 1 << delta; ++ ++ if (hash_strength <= change + 1) ++ hash_strength = 1; ++ else ++ hash_strength -= change; ++} ++ ++static inline void inc_hash_strength_delta(void) ++{ ++ hash_strength_delta++; ++ if (hash_strength_delta > HASH_STRENGTH_DELTA_MAX) ++ hash_strength_delta = HASH_STRENGTH_DELTA_MAX; ++} ++ ++/* ++static inline unsigned long get_current_neg_ratio(void) ++{ ++ if (!rshash_pos || rshash_neg > rshash_pos) ++ return 100; ++ ++ return div64_u64(100 * rshash_neg , rshash_pos); ++} ++*/ ++ ++static inline unsigned long get_current_neg_ratio(void) ++{ ++ u64 pos = benefit.pos; ++ u64 neg = benefit.neg; ++ ++ if (!neg) ++ return 0; ++ ++ if (!pos || neg > pos) ++ return 100; ++ ++ if (neg > div64_u64(U64_MAX, 100)) ++ pos = div64_u64(pos, 100); ++ else ++ neg *= 100; ++ ++ return div64_u64(neg, pos); ++} ++ ++static inline unsigned long get_current_benefit(void) ++{ ++ u64 pos = benefit.pos; ++ u64 neg = benefit.neg; ++ u64 scanned = benefit.scanned; ++ ++ if (neg > pos) ++ return 0; ++ ++ return div64_u64((pos - neg), scanned); ++} ++ ++static inline int judge_rshash_direction(void) ++{ ++ u64 current_neg_ratio, stable_benefit; ++ u64 current_benefit, delta = 0; ++ int ret = STILL; ++ ++ /* Try to probe a value after the boot, and in case the system ++ are still for a long time. */ ++ if ((fully_scanned_round & 0xFFULL) == 10) { ++ ret = OBSCURE; ++ goto out; ++ } ++ ++ current_neg_ratio = get_current_neg_ratio(); ++ ++ if (current_neg_ratio == 0) { ++ rshash_neg_cont_zero++; ++ if (rshash_neg_cont_zero > 2) ++ return GO_DOWN; ++ else ++ return STILL; ++ } ++ rshash_neg_cont_zero = 0; ++ ++ if (current_neg_ratio > 90) { ++ ret = GO_UP; ++ goto out; ++ } ++ ++ current_benefit = get_current_benefit(); ++ stable_benefit = rshash_state.stable_benefit; ++ ++ if (!stable_benefit) { ++ ret = OBSCURE; ++ goto out; ++ } ++ ++ if (current_benefit > stable_benefit) ++ delta = current_benefit - stable_benefit; ++ else if (current_benefit < stable_benefit) ++ delta = stable_benefit - current_benefit; ++ ++ delta = div64_u64(100 * delta , stable_benefit); ++ ++ if (delta > 50) { ++ rshash_cont_obscure++; ++ if (rshash_cont_obscure > 2) ++ return OBSCURE; ++ else ++ return STILL; ++ } ++ ++out: ++ rshash_cont_obscure = 0; ++ return ret; ++} ++ ++/** ++ * rshash_adjust() - The main function to control the random sampling state ++ * machine for hash strength adapting. ++ * ++ * return true if hash_strength has changed. ++ */ ++static inline int rshash_adjust(void) ++{ ++ unsigned long prev_hash_strength = hash_strength; ++ ++ if (!encode_benefit()) ++ return 0; ++ ++ switch (rshash_state.state) { ++ case RSHASH_STILL: ++ switch (judge_rshash_direction()) { ++ case GO_UP: ++ if (rshash_state.pre_direct == GO_DOWN) ++ hash_strength_delta = 0; ++ ++ inc_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ rshash_state.stable_benefit = get_current_benefit(); ++ rshash_state.pre_direct = GO_UP; ++ break; ++ ++ case GO_DOWN: ++ if (rshash_state.pre_direct == GO_UP) ++ hash_strength_delta = 0; ++ ++ dec_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ rshash_state.stable_benefit = get_current_benefit(); ++ rshash_state.pre_direct = GO_DOWN; ++ break; ++ ++ case OBSCURE: ++ rshash_state.stable_point = hash_strength; ++ rshash_state.turn_point_down = hash_strength; ++ rshash_state.turn_point_up = hash_strength; ++ rshash_state.turn_benefit_down = get_current_benefit(); ++ rshash_state.turn_benefit_up = get_current_benefit(); ++ rshash_state.lookup_window_index = 0; ++ rshash_state.state = RSHASH_TRYDOWN; ++ dec_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ break; ++ ++ case STILL: ++ break; ++ default: ++ BUG(); ++ } ++ break; ++ ++ case RSHASH_TRYDOWN: ++ if (rshash_state.lookup_window_index++ % 5 == 0) ++ rshash_state.below_count = 0; ++ ++ if (get_current_benefit() < rshash_state.stable_benefit) ++ rshash_state.below_count++; ++ else if (get_current_benefit() > ++ rshash_state.turn_benefit_down) { ++ rshash_state.turn_point_down = hash_strength; ++ rshash_state.turn_benefit_down = get_current_benefit(); ++ } ++ ++ if (rshash_state.below_count >= 3 || ++ judge_rshash_direction() == GO_UP || ++ hash_strength == 1) { ++ hash_strength = rshash_state.stable_point; ++ hash_strength_delta = 0; ++ inc_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ rshash_state.lookup_window_index = 0; ++ rshash_state.state = RSHASH_TRYUP; ++ hash_strength_delta = 0; ++ } else { ++ dec_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ } ++ break; ++ ++ case RSHASH_TRYUP: ++ if (rshash_state.lookup_window_index++ % 5 == 0) ++ rshash_state.below_count = 0; ++ ++ if (get_current_benefit() < rshash_state.turn_benefit_down) ++ rshash_state.below_count++; ++ else if (get_current_benefit() > rshash_state.turn_benefit_up) { ++ rshash_state.turn_point_up = hash_strength; ++ rshash_state.turn_benefit_up = get_current_benefit(); ++ } ++ ++ if (rshash_state.below_count >= 3 || ++ judge_rshash_direction() == GO_DOWN || ++ hash_strength == HASH_STRENGTH_MAX) { ++ hash_strength = rshash_state.turn_benefit_up > ++ rshash_state.turn_benefit_down ? ++ rshash_state.turn_point_up : ++ rshash_state.turn_point_down; ++ ++ rshash_state.state = RSHASH_PRE_STILL; ++ } else { ++ inc_hash_strength(hash_strength_delta); ++ inc_hash_strength_delta(); ++ } ++ ++ break; ++ ++ case RSHASH_NEW: ++ case RSHASH_PRE_STILL: ++ rshash_state.stable_benefit = get_current_benefit(); ++ rshash_state.state = RSHASH_STILL; ++ hash_strength_delta = 0; ++ break; ++ default: ++ BUG(); ++ } ++ ++ /* rshash_neg = rshash_pos = 0; */ ++ reset_benefit(); ++ ++ if (prev_hash_strength != hash_strength) ++ stable_tree_delta_hash(prev_hash_strength); ++ ++ return prev_hash_strength != hash_strength; ++} ++ ++/** ++ * round_update_ladder() - The main function to do update of all the ++ * adjustments whenever a scan round is finished. ++ */ ++static noinline void round_update_ladder(void) ++{ ++ int i; ++ unsigned long dedup; ++ struct vma_slot *slot, *tmp_slot; ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ uksm_scan_ladder[i].flags &= ~UKSM_RUNG_ROUND_FINISHED; ++ } ++ ++ list_for_each_entry_safe(slot, tmp_slot, &vma_slot_dedup, dedup_list) { ++ ++ /* slot may be rung_rm_slot() when mm exits */ ++ if (slot->snode) { ++ dedup = cal_dedup_ratio_old(slot); ++ if (dedup && dedup >= uksm_abundant_threshold) ++ vma_rung_up(slot); ++ } ++ ++ slot->pages_bemerged = 0; ++ slot->pages_cowed = 0; ++ ++ list_del_init(&slot->dedup_list); ++ } ++} ++ ++static void uksm_del_vma_slot(struct vma_slot *slot) ++{ ++ int i, j; ++ struct rmap_list_entry *entry; ++ ++ if (slot->snode) { ++ /* ++ * In case it just failed when entering the rung, it's not ++ * necessary. ++ */ ++ rung_rm_slot(slot); ++ } ++ ++ if (!list_empty(&slot->dedup_list)) ++ list_del(&slot->dedup_list); ++ ++ if (!slot->rmap_list_pool || !slot->pool_counts) { ++ /* In case it OOMed in uksm_vma_enter() */ ++ goto out; ++ } ++ ++ for (i = 0; i < slot->pool_size; i++) { ++ void *addr; ++ ++ if (!slot->rmap_list_pool[i]) ++ continue; ++ ++ addr = kmap(slot->rmap_list_pool[i]); ++ for (j = 0; j < PAGE_SIZE / sizeof(*entry); j++) { ++ entry = (struct rmap_list_entry *)addr + j; ++ if (is_addr(entry->addr)) ++ continue; ++ if (!entry->item) ++ continue; ++ ++ remove_rmap_item_from_tree(entry->item); ++ free_rmap_item(entry->item); ++ slot->pool_counts[i]--; ++ } ++ BUG_ON(slot->pool_counts[i]); ++ kunmap(slot->rmap_list_pool[i]); ++ __free_page(slot->rmap_list_pool[i]); ++ } ++ kfree(slot->rmap_list_pool); ++ kfree(slot->pool_counts); ++ ++out: ++ slot->rung = NULL; ++ if (slot->flags & UKSM_SLOT_IN_UKSM) { ++ BUG_ON(uksm_pages_total < slot->pages); ++ uksm_pages_total -= slot->pages; ++ } ++ ++ if (slot->fully_scanned_round == fully_scanned_round) ++ scanned_virtual_pages -= slot->pages; ++ else ++ scanned_virtual_pages -= slot->pages_scanned; ++ free_vma_slot(slot); ++} ++ ++ ++#define SPIN_LOCK_PERIOD 32 ++static struct vma_slot *cleanup_slots[SPIN_LOCK_PERIOD]; ++static inline void cleanup_vma_slots(void) ++{ ++ struct vma_slot *slot; ++ int i; ++ ++ i = 0; ++ spin_lock(&vma_slot_list_lock); ++ while (!list_empty(&vma_slot_del)) { ++ slot = list_entry(vma_slot_del.next, ++ struct vma_slot, slot_list); ++ list_del(&slot->slot_list); ++ cleanup_slots[i++] = slot; ++ if (i == SPIN_LOCK_PERIOD) { ++ spin_unlock(&vma_slot_list_lock); ++ while (--i >= 0) ++ uksm_del_vma_slot(cleanup_slots[i]); ++ i = 0; ++ spin_lock(&vma_slot_list_lock); ++ } ++ } ++ spin_unlock(&vma_slot_list_lock); ++ ++ while (--i >= 0) ++ uksm_del_vma_slot(cleanup_slots[i]); ++} ++ ++/* ++*expotional moving average formula ++*/ ++static inline unsigned long ema(unsigned long curr, unsigned long last_ema) ++{ ++ /* ++ * For a very high burst, even the ema cannot work well, a false very ++ * high per-page time estimation can result in feedback in very high ++ * overhead of context swith and rung update -- this will then lead ++ * to higher per-paper time, this may not converge. ++ * ++ * Instead, we try to approach this value in a binary manner. ++ */ ++ if (curr > last_ema * 10) ++ return last_ema * 2; ++ ++ return (EMA_ALPHA * curr + (100 - EMA_ALPHA) * last_ema) / 100; ++} ++ ++/* ++ * convert cpu ratio in 1/TIME_RATIO_SCALE configured by user to ++ * nanoseconds based on current uksm_sleep_jiffies. ++ */ ++static inline unsigned long cpu_ratio_to_nsec(unsigned int ratio) ++{ ++ return NSEC_PER_USEC * jiffies_to_usecs(uksm_sleep_jiffies) / ++ (TIME_RATIO_SCALE - ratio) * ratio; ++} ++ ++ ++static inline unsigned long rung_real_ratio(int cpu_time_ratio) ++{ ++ unsigned long ret; ++ ++ BUG_ON(!cpu_time_ratio); ++ ++ if (cpu_time_ratio > 0) ++ ret = cpu_time_ratio; ++ else ++ ret = (unsigned long)(-cpu_time_ratio) * ++ uksm_max_cpu_percentage / 100UL; ++ ++ return ret ? ret : 1; ++} ++ ++static noinline void uksm_calc_scan_pages(void) ++{ ++ struct scan_rung *ladder = uksm_scan_ladder; ++ unsigned long sleep_usecs, nsecs; ++ unsigned long ratio; ++ int i; ++ unsigned long per_page; ++ ++ if (uksm_ema_page_time > 100000 || ++ (((unsigned long) uksm_eval_round & (256UL - 1)) == 0UL)) ++ uksm_ema_page_time = UKSM_PAGE_TIME_DEFAULT; ++ ++ per_page = uksm_ema_page_time; ++ BUG_ON(!per_page); ++ ++ /* ++ * For every 8 eval round, we try to probe a uksm_sleep_jiffies value ++ * based on saved user input. ++ */ ++ if (((unsigned long) uksm_eval_round & (8UL - 1)) == 0UL) ++ uksm_sleep_jiffies = uksm_sleep_saved; ++ ++ /* We require a rung scan at least 1 page in a period. */ ++ nsecs = per_page; ++ ratio = rung_real_ratio(ladder[0].cpu_ratio); ++ if (cpu_ratio_to_nsec(ratio) < nsecs) { ++ sleep_usecs = nsecs * (TIME_RATIO_SCALE - ratio) / ratio ++ / NSEC_PER_USEC; ++ uksm_sleep_jiffies = usecs_to_jiffies(sleep_usecs) + 1; ++ } ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ ratio = rung_real_ratio(ladder[i].cpu_ratio); ++ ladder[i].pages_to_scan = cpu_ratio_to_nsec(ratio) / ++ per_page; ++ BUG_ON(!ladder[i].pages_to_scan); ++ uksm_calc_rung_step(&ladder[i], per_page, ratio); ++ } ++} ++ ++/* ++ * From the scan time of this round (ns) to next expected min sleep time ++ * (ms), be careful of the possible overflows. ratio is taken from ++ * rung_real_ratio() ++ */ ++static inline ++unsigned int scan_time_to_sleep(unsigned long long scan_time, unsigned long ratio) ++{ ++ scan_time >>= 20; /* to msec level now */ ++ BUG_ON(scan_time > (ULONG_MAX / TIME_RATIO_SCALE)); ++ ++ return (unsigned int) ((unsigned long) scan_time * ++ (TIME_RATIO_SCALE - ratio) / ratio); ++} ++ ++#define __round_mask(x, y) ((__typeof__(x))((y)-1)) ++#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) ++ ++static void uksm_vma_enter(struct vma_slot **slots, unsigned long num) ++{ ++ struct scan_rung *rung; ++ ++ rung = &uksm_scan_ladder[0]; ++ rung_add_new_slots(rung, slots, num); ++} ++ ++static struct vma_slot *batch_slots[SLOT_TREE_NODE_STORE_SIZE]; ++ ++static void uksm_enter_all_slots(void) ++{ ++ struct vma_slot *slot; ++ unsigned long index; ++ struct list_head empty_vma_list; ++ int i; ++ ++ i = 0; ++ index = 0; ++ INIT_LIST_HEAD(&empty_vma_list); ++ ++ spin_lock(&vma_slot_list_lock); ++ while (!list_empty(&vma_slot_new)) { ++ slot = list_entry(vma_slot_new.next, ++ struct vma_slot, slot_list); ++ ++ if (!slot->vma->anon_vma) { ++ list_move(&slot->slot_list, &empty_vma_list); ++ } else if (vma_can_enter(slot->vma)) { ++ batch_slots[index++] = slot; ++ list_del_init(&slot->slot_list); ++ } else { ++ list_move(&slot->slot_list, &vma_slot_noadd); ++ } ++ ++ if (++i == SPIN_LOCK_PERIOD || ++ (index && !(index % SLOT_TREE_NODE_STORE_SIZE))) { ++ spin_unlock(&vma_slot_list_lock); ++ ++ if (index && !(index % SLOT_TREE_NODE_STORE_SIZE)) { ++ uksm_vma_enter(batch_slots, index); ++ index = 0; ++ } ++ i = 0; ++ cond_resched(); ++ spin_lock(&vma_slot_list_lock); ++ } ++ } ++ ++ list_splice(&empty_vma_list, &vma_slot_new); ++ ++ spin_unlock(&vma_slot_list_lock); ++ ++ if (index) ++ uksm_vma_enter(batch_slots, index); ++ ++} ++ ++static inline int rung_round_finished(struct scan_rung *rung) ++{ ++ return rung->flags & UKSM_RUNG_ROUND_FINISHED; ++} ++ ++static inline void judge_slot(struct vma_slot *slot) ++{ ++ struct scan_rung *rung = slot->rung; ++ unsigned long dedup; ++ int deleted; ++ ++ dedup = cal_dedup_ratio(slot); ++ if (vma_fully_scanned(slot) && uksm_thrash_threshold) ++ deleted = vma_rung_enter(slot, &uksm_scan_ladder[0]); ++ else if (dedup && dedup >= uksm_abundant_threshold) ++ deleted = vma_rung_up(slot); ++ else ++ deleted = vma_rung_down(slot); ++ ++ slot->pages_merged = 0; ++ slot->pages_cowed = 0; ++ slot->this_sampled = 0; ++ ++ if (vma_fully_scanned(slot)) { ++ slot->pages_scanned = 0; ++ } ++ ++ slot->last_scanned = slot->pages_scanned; ++ ++ /* If its deleted in above, then rung was already advanced. */ ++ if (!deleted) ++ advance_current_scan(rung); ++} ++ ++ ++static inline int hash_round_finished(void) ++{ ++ if (scanned_virtual_pages > (uksm_pages_total >> 2)) { ++ scanned_virtual_pages = 0; ++ if (uksm_pages_scanned) ++ fully_scanned_round++; ++ ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++#define UKSM_MMSEM_BATCH 5 ++#define BUSY_RETRY 100 ++ ++/** ++ * uksm_do_scan() - the main worker function. ++ */ ++static noinline void uksm_do_scan(void) ++{ ++ struct vma_slot *slot, *iter; ++ struct mm_struct *busy_mm; ++ unsigned char round_finished, all_rungs_emtpy; ++ int i, err, mmsem_batch; ++ unsigned long pcost; ++ long long delta_exec; ++ unsigned long vpages, max_cpu_ratio; ++ unsigned long long start_time, end_time, scan_time; ++ unsigned int expected_jiffies; ++ ++ might_sleep(); ++ ++ vpages = 0; ++ ++ start_time = task_sched_runtime(current); ++ max_cpu_ratio = 0; ++ mmsem_batch = 0; ++ ++ for (i = 0; i < SCAN_LADDER_SIZE;) { ++ struct scan_rung *rung = &uksm_scan_ladder[i]; ++ unsigned long ratio; ++ int busy_retry; ++ ++ if (!rung->pages_to_scan) { ++ i++; ++ continue; ++ } ++ ++ if (!rung->vma_root.num) { ++ rung->pages_to_scan = 0; ++ i++; ++ continue; ++ } ++ ++ ratio = rung_real_ratio(rung->cpu_ratio); ++ if (ratio > max_cpu_ratio) ++ max_cpu_ratio = ratio; ++ ++ busy_retry = BUSY_RETRY; ++ /* ++ * Do not consider rung_round_finished() here, just used up the ++ * rung->pages_to_scan quota. ++ */ ++ while (rung->pages_to_scan && rung->vma_root.num && ++ likely(!freezing(current))) { ++ int reset = 0; ++ ++ slot = rung->current_scan; ++ ++ BUG_ON(vma_fully_scanned(slot)); ++ ++ if (mmsem_batch) { ++ err = 0; ++ } else { ++ err = try_down_read_slot_mmap_sem(slot); ++ } ++ ++ if (err == -ENOENT) { ++rm_slot: ++ rung_rm_slot(slot); ++ continue; ++ } ++ ++ busy_mm = slot->mm; ++ ++ if (err == -EBUSY) { ++ /* skip other vmas on the same mm */ ++ do { ++ reset = advance_current_scan(rung); ++ iter = rung->current_scan; ++ busy_retry--; ++ if (iter->vma->vm_mm != busy_mm || ++ !busy_retry || reset) ++ break; ++ } while (1); ++ ++ if (iter->vma->vm_mm != busy_mm) { ++ continue; ++ } else { ++ /* scan round finsished */ ++ break; ++ } ++ } ++ ++ BUG_ON(!vma_can_enter(slot->vma)); ++ if (uksm_test_exit(slot->vma->vm_mm)) { ++ mmsem_batch = 0; ++ up_read(&slot->vma->vm_mm->mmap_sem); ++ goto rm_slot; ++ } ++ ++ if (mmsem_batch) ++ mmsem_batch--; ++ else ++ mmsem_batch = UKSM_MMSEM_BATCH; ++ ++ /* Ok, we have take the mmap_sem, ready to scan */ ++ scan_vma_one_page(slot); ++ rung->pages_to_scan--; ++ vpages++; ++ ++ if (rung->current_offset + rung->step > slot->pages - 1 ++ || vma_fully_scanned(slot)) { ++ up_read(&slot->vma->vm_mm->mmap_sem); ++ judge_slot(slot); ++ mmsem_batch = 0; ++ } else { ++ rung->current_offset += rung->step; ++ if (!mmsem_batch) ++ up_read(&slot->vma->vm_mm->mmap_sem); ++ } ++ ++ busy_retry = BUSY_RETRY; ++ cond_resched(); ++ } ++ ++ if (mmsem_batch) { ++ up_read(&slot->vma->vm_mm->mmap_sem); ++ mmsem_batch = 0; ++ } ++ ++ if (freezing(current)) ++ break; ++ ++ cond_resched(); ++ } ++ end_time = task_sched_runtime(current); ++ delta_exec = end_time - start_time; ++ ++ if (freezing(current)) ++ return; ++ ++ cleanup_vma_slots(); ++ uksm_enter_all_slots(); ++ ++ round_finished = 1; ++ all_rungs_emtpy = 1; ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ struct scan_rung *rung = &uksm_scan_ladder[i]; ++ ++ if (rung->vma_root.num) { ++ all_rungs_emtpy = 0; ++ if (!rung_round_finished(rung)) ++ round_finished = 0; ++ } ++ } ++ ++ if (all_rungs_emtpy) ++ round_finished = 0; ++ ++ if (round_finished) { ++ round_update_ladder(); ++ uksm_eval_round++; ++ ++ if (hash_round_finished() && rshash_adjust()) { ++ /* Reset the unstable root iff hash strength changed */ ++ uksm_hash_round++; ++ root_unstable_tree = RB_ROOT; ++ free_all_tree_nodes(&unstable_tree_node_list); ++ } ++ ++ /* ++ * A number of pages can hang around indefinitely on per-cpu ++ * pagevecs, raised page count preventing write_protect_page ++ * from merging them. Though it doesn't really matter much, ++ * it is puzzling to see some stuck in pages_volatile until ++ * other activity jostles them out, and they also prevented ++ * LTP's KSM test from succeeding deterministically; so drain ++ * them here (here rather than on entry to uksm_do_scan(), ++ * so we don't IPI too often when pages_to_scan is set low). ++ */ ++ lru_add_drain_all(); ++ } ++ ++ ++ if (vpages && delta_exec > 0) { ++ pcost = (unsigned long) delta_exec / vpages; ++ if (likely(uksm_ema_page_time)) ++ uksm_ema_page_time = ema(pcost, uksm_ema_page_time); ++ else ++ uksm_ema_page_time = pcost; ++ } ++ ++ uksm_calc_scan_pages(); ++ uksm_sleep_real = uksm_sleep_jiffies; ++ /* in case of radical cpu bursts, apply the upper bound */ ++ end_time = task_sched_runtime(current); ++ if (max_cpu_ratio && end_time > start_time) { ++ scan_time = end_time - start_time; ++ expected_jiffies = msecs_to_jiffies( ++ scan_time_to_sleep(scan_time, max_cpu_ratio)); ++ ++ if (expected_jiffies > uksm_sleep_real) ++ uksm_sleep_real = expected_jiffies; ++ ++ /* We have a 1 second up bound for responsiveness. */ ++ if (jiffies_to_msecs(uksm_sleep_real) > MSEC_PER_SEC) ++ uksm_sleep_real = msecs_to_jiffies(1000); ++ } ++ ++ return; ++} ++ ++static int ksmd_should_run(void) ++{ ++ return uksm_run & UKSM_RUN_MERGE; ++} ++ ++static int uksm_scan_thread(void *nothing) ++{ ++ set_freezable(); ++ set_user_nice(current, 5); ++ ++ while (!kthread_should_stop()) { ++ mutex_lock(&uksm_thread_mutex); ++ if (ksmd_should_run()) { ++ uksm_do_scan(); ++ } ++ mutex_unlock(&uksm_thread_mutex); ++ ++ try_to_freeze(); ++ ++ if (ksmd_should_run()) { ++ schedule_timeout_interruptible(uksm_sleep_real); ++ uksm_sleep_times++; ++ } else { ++ wait_event_freezable(uksm_thread_wait, ++ ksmd_should_run() || kthread_should_stop()); ++ } ++ } ++ return 0; ++} ++ ++int rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc) ++{ ++ struct stable_node *stable_node; ++ struct node_vma *node_vma; ++ struct rmap_item *rmap_item; ++ int ret = SWAP_AGAIN; ++ int search_new_forks = 0; ++ unsigned long address; ++ ++ VM_BUG_ON_PAGE(!PageKsm(page), page); ++ VM_BUG_ON_PAGE(!PageLocked(page), page); ++ ++ stable_node = page_stable_node(page); ++ if (!stable_node) ++ return ret; ++again: ++ hlist_for_each_entry(node_vma, &stable_node->hlist, hlist) { ++ hlist_for_each_entry(rmap_item, &node_vma->rmap_hlist, hlist) { ++ struct anon_vma *anon_vma = rmap_item->anon_vma; ++ struct anon_vma_chain *vmac; ++ struct vm_area_struct *vma; ++ ++ cond_resched(); ++ anon_vma_lock_read(anon_vma); ++ anon_vma_interval_tree_foreach(vmac, &anon_vma->rb_root, ++ 0, ULONG_MAX) { ++ cond_resched(); ++ vma = vmac->vma; ++ address = get_rmap_addr(rmap_item); ++ ++ if (address < vma->vm_start || ++ address >= vma->vm_end) ++ continue; ++ ++ if ((rmap_item->slot->vma == vma) == ++ search_new_forks) ++ continue; ++ ++ if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg)) ++ continue; ++ ++ ret = rwc->rmap_one(page, vma, address, rwc->arg); ++ if (ret != SWAP_AGAIN) { ++ anon_vma_unlock_read(anon_vma); ++ goto out; ++ } ++ ++ if (rwc->done && rwc->done(page)) { ++ anon_vma_unlock_read(anon_vma); ++ goto out; ++ } ++ } ++ anon_vma_unlock_read(anon_vma); ++ } ++ } ++ if (!search_new_forks++) ++ goto again; ++out: ++ return ret; ++} ++ ++#ifdef CONFIG_MIGRATION ++/* Common ksm interface but may be specific to uksm */ ++void ksm_migrate_page(struct page *newpage, struct page *oldpage) ++{ ++ struct stable_node *stable_node; ++ ++ VM_BUG_ON_PAGE(!PageLocked(oldpage), oldpage); ++ VM_BUG_ON_PAGE(!PageLocked(newpage), newpage); ++ VM_BUG_ON(newpage->mapping != oldpage->mapping); ++ ++ stable_node = page_stable_node(newpage); ++ if (stable_node) { ++ VM_BUG_ON(stable_node->kpfn != page_to_pfn(oldpage)); ++ stable_node->kpfn = page_to_pfn(newpage); ++ } ++} ++#endif /* CONFIG_MIGRATION */ ++ ++#ifdef CONFIG_MEMORY_HOTREMOVE ++static struct stable_node *uksm_check_stable_tree(unsigned long start_pfn, ++ unsigned long end_pfn) ++{ ++ struct rb_node *node; ++ ++ for (node = rb_first(root_stable_treep); node; node = rb_next(node)) { ++ struct stable_node *stable_node; ++ ++ stable_node = rb_entry(node, struct stable_node, node); ++ if (stable_node->kpfn >= start_pfn && ++ stable_node->kpfn < end_pfn) ++ return stable_node; ++ } ++ return NULL; ++} ++ ++static int uksm_memory_callback(struct notifier_block *self, ++ unsigned long action, void *arg) ++{ ++ struct memory_notify *mn = arg; ++ struct stable_node *stable_node; ++ ++ switch (action) { ++ case MEM_GOING_OFFLINE: ++ /* ++ * Keep it very simple for now: just lock out ksmd and ++ * MADV_UNMERGEABLE while any memory is going offline. ++ * mutex_lock_nested() is necessary because lockdep was alarmed ++ * that here we take uksm_thread_mutex inside notifier chain ++ * mutex, and later take notifier chain mutex inside ++ * uksm_thread_mutex to unlock it. But that's safe because both ++ * are inside mem_hotplug_mutex. ++ */ ++ mutex_lock_nested(&uksm_thread_mutex, SINGLE_DEPTH_NESTING); ++ break; ++ ++ case MEM_OFFLINE: ++ /* ++ * Most of the work is done by page migration; but there might ++ * be a few stable_nodes left over, still pointing to struct ++ * pages which have been offlined: prune those from the tree. ++ */ ++ while ((stable_node = uksm_check_stable_tree(mn->start_pfn, ++ mn->start_pfn + mn->nr_pages)) != NULL) ++ remove_node_from_stable_tree(stable_node, 1, 1); ++ /* fallthrough */ ++ ++ case MEM_CANCEL_OFFLINE: ++ mutex_unlock(&uksm_thread_mutex); ++ break; ++ } ++ return NOTIFY_OK; ++} ++#endif /* CONFIG_MEMORY_HOTREMOVE */ ++ ++#ifdef CONFIG_SYSFS ++/* ++ * This all compiles without CONFIG_SYSFS, but is a waste of space. ++ */ ++ ++#define UKSM_ATTR_RO(_name) \ ++ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) ++#define UKSM_ATTR(_name) \ ++ static struct kobj_attribute _name##_attr = \ ++ __ATTR(_name, 0644, _name##_show, _name##_store) ++ ++static ssize_t max_cpu_percentage_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", uksm_max_cpu_percentage); ++} ++ ++static ssize_t max_cpu_percentage_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long max_cpu_percentage; ++ int err; ++ ++ err = kstrtoul(buf, 10, &max_cpu_percentage); ++ if (err || max_cpu_percentage > 100) ++ return -EINVAL; ++ ++ if (max_cpu_percentage == 100) ++ max_cpu_percentage = 99; ++ else if (max_cpu_percentage < 10) ++ max_cpu_percentage = 10; ++ ++ uksm_max_cpu_percentage = max_cpu_percentage; ++ ++ return count; ++} ++UKSM_ATTR(max_cpu_percentage); ++ ++static ssize_t sleep_millisecs_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", jiffies_to_msecs(uksm_sleep_jiffies)); ++} ++ ++static ssize_t sleep_millisecs_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ unsigned long msecs; ++ int err; ++ ++ err = kstrtoul(buf, 10, &msecs); ++ if (err || msecs > MSEC_PER_SEC) ++ return -EINVAL; ++ ++ uksm_sleep_jiffies = msecs_to_jiffies(msecs); ++ uksm_sleep_saved = uksm_sleep_jiffies; ++ ++ return count; ++} ++UKSM_ATTR(sleep_millisecs); ++ ++ ++static ssize_t cpu_governor_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ int n = sizeof(uksm_cpu_governor_str) / sizeof(char *); ++ int i; ++ ++ buf[0] = '\0'; ++ for (i = 0; i < n ; i++) { ++ if (uksm_cpu_governor == i) ++ strcat(buf, "["); ++ ++ strcat(buf, uksm_cpu_governor_str[i]); ++ ++ if (uksm_cpu_governor == i) ++ strcat(buf, "]"); ++ ++ strcat(buf, " "); ++ } ++ strcat(buf, "\n"); ++ ++ return strlen(buf); ++} ++ ++static inline void init_performance_values(void) ++{ ++ int i; ++ struct scan_rung *rung; ++ struct uksm_cpu_preset_s *preset = uksm_cpu_preset + uksm_cpu_governor; ++ ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = uksm_scan_ladder + i; ++ rung->cpu_ratio = preset->cpu_ratio[i]; ++ rung->cover_msecs = preset->cover_msecs[i]; ++ } ++ ++ uksm_max_cpu_percentage = preset->max_cpu; ++} ++ ++static ssize_t cpu_governor_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int n = sizeof(uksm_cpu_governor_str) / sizeof(char *); ++ ++ for (n--; n >=0 ; n--) { ++ if (!strncmp(buf, uksm_cpu_governor_str[n], ++ strlen(uksm_cpu_governor_str[n]))) ++ break; ++ } ++ ++ if (n < 0) ++ return -EINVAL; ++ else ++ uksm_cpu_governor = n; ++ ++ init_performance_values(); ++ ++ return count; ++} ++UKSM_ATTR(cpu_governor); ++ ++static ssize_t run_show(struct kobject *kobj, struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%u\n", uksm_run); ++} ++ ++static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int err; ++ unsigned long flags; ++ ++ err = kstrtoul(buf, 10, &flags); ++ if (err || flags > UINT_MAX) ++ return -EINVAL; ++ if (flags > UKSM_RUN_MERGE) ++ return -EINVAL; ++ ++ mutex_lock(&uksm_thread_mutex); ++ if (uksm_run != flags) { ++ uksm_run = flags; ++ } ++ mutex_unlock(&uksm_thread_mutex); ++ ++ if (flags & UKSM_RUN_MERGE) ++ wake_up_interruptible(&uksm_thread_wait); ++ ++ return count; ++} ++UKSM_ATTR(run); ++ ++static ssize_t abundant_threshold_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", uksm_abundant_threshold); ++} ++ ++static ssize_t abundant_threshold_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int err; ++ unsigned long flags; ++ ++ err = kstrtoul(buf, 10, &flags); ++ if (err || flags > 99) ++ return -EINVAL; ++ ++ uksm_abundant_threshold = flags; ++ ++ return count; ++} ++UKSM_ATTR(abundant_threshold); ++ ++static ssize_t thrash_threshold_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", uksm_thrash_threshold); ++} ++ ++static ssize_t thrash_threshold_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int err; ++ unsigned long flags; ++ ++ err = kstrtoul(buf, 10, &flags); ++ if (err || flags > 99) ++ return -EINVAL; ++ ++ uksm_thrash_threshold = flags; ++ ++ return count; ++} ++UKSM_ATTR(thrash_threshold); ++ ++static ssize_t cpu_ratios_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ int i, size; ++ struct scan_rung *rung; ++ char *p = buf; ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = &uksm_scan_ladder[i]; ++ ++ if (rung->cpu_ratio > 0) ++ size = sprintf(p, "%d ", rung->cpu_ratio); ++ else ++ size = sprintf(p, "MAX/%d ", ++ TIME_RATIO_SCALE / -rung->cpu_ratio); ++ ++ p += size; ++ } ++ ++ *p++ = '\n'; ++ *p = '\0'; ++ ++ return p - buf; ++} ++ ++static ssize_t cpu_ratios_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int i, cpuratios[SCAN_LADDER_SIZE], err; ++ unsigned long value; ++ struct scan_rung *rung; ++ char *p, *end = NULL; ++ ++ p = kzalloc(count, GFP_KERNEL); ++ if (!p) ++ return -ENOMEM; ++ ++ memcpy(p, buf, count); ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ if (i != SCAN_LADDER_SIZE -1) { ++ end = strchr(p, ' '); ++ if (!end) ++ return -EINVAL; ++ ++ *end = '\0'; ++ } ++ ++ if (strstr(p, "MAX/")) { ++ p = strchr(p, '/') + 1; ++ err = kstrtoul(p, 10, &value); ++ if (err || value > TIME_RATIO_SCALE || !value) ++ return -EINVAL; ++ ++ cpuratios[i] = - (int) (TIME_RATIO_SCALE / value); ++ } else { ++ err = kstrtoul(p, 10, &value); ++ if (err || value > TIME_RATIO_SCALE || !value) ++ return -EINVAL; ++ ++ cpuratios[i] = value; ++ } ++ ++ p = end + 1; ++ } ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = &uksm_scan_ladder[i]; ++ ++ rung->cpu_ratio = cpuratios[i]; ++ } ++ ++ return count; ++} ++UKSM_ATTR(cpu_ratios); ++ ++static ssize_t eval_intervals_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ int i, size; ++ struct scan_rung *rung; ++ char *p = buf; ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = &uksm_scan_ladder[i]; ++ size = sprintf(p, "%u ", rung->cover_msecs); ++ p += size; ++ } ++ ++ *p++ = '\n'; ++ *p = '\0'; ++ ++ return p - buf; ++} ++ ++static ssize_t eval_intervals_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int i, err; ++ unsigned long values[SCAN_LADDER_SIZE]; ++ struct scan_rung *rung; ++ char *p, *end = NULL; ++ ssize_t ret = count; ++ ++ p = kzalloc(count + 2, GFP_KERNEL); ++ if (!p) ++ return -ENOMEM; ++ ++ memcpy(p, buf, count); ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ if (i != SCAN_LADDER_SIZE -1) { ++ end = strchr(p, ' '); ++ if (!end) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ *end = '\0'; ++ } ++ ++ err = kstrtoul(p, 10, &values[i]); ++ if (err) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ p = end + 1; ++ } ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = &uksm_scan_ladder[i]; ++ ++ rung->cover_msecs = values[i]; ++ } ++ ++out: ++ kfree(p); ++ return ret; ++} ++UKSM_ATTR(eval_intervals); ++ ++static ssize_t ema_per_page_time_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", uksm_ema_page_time); ++} ++UKSM_ATTR_RO(ema_per_page_time); ++ ++static ssize_t pages_shared_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", uksm_pages_shared); ++} ++UKSM_ATTR_RO(pages_shared); ++ ++static ssize_t pages_sharing_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", uksm_pages_sharing); ++} ++UKSM_ATTR_RO(pages_sharing); ++ ++static ssize_t pages_unshared_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", uksm_pages_unshared); ++} ++UKSM_ATTR_RO(pages_unshared); ++ ++static ssize_t full_scans_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%llu\n", fully_scanned_round); ++} ++UKSM_ATTR_RO(full_scans); ++ ++static ssize_t pages_scanned_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ unsigned long base = 0; ++ u64 delta, ret; ++ ++ if (pages_scanned_stored) { ++ base = pages_scanned_base; ++ ret = pages_scanned_stored; ++ delta = uksm_pages_scanned >> base; ++ if (CAN_OVERFLOW_U64(ret, delta)) { ++ ret >>= 1; ++ delta >>= 1; ++ base++; ++ ret += delta; ++ } ++ } else { ++ ret = uksm_pages_scanned; ++ } ++ ++ while (ret > ULONG_MAX) { ++ ret >>= 1; ++ base++; ++ } ++ ++ if (base) ++ return sprintf(buf, "%lu * 2^%lu\n", (unsigned long)ret, base); ++ else ++ return sprintf(buf, "%lu\n", (unsigned long)ret); ++} ++UKSM_ATTR_RO(pages_scanned); ++ ++static ssize_t hash_strength_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%lu\n", hash_strength); ++} ++UKSM_ATTR_RO(hash_strength); ++ ++static ssize_t sleep_times_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%llu\n", uksm_sleep_times); ++} ++UKSM_ATTR_RO(sleep_times); ++ ++ ++static struct attribute *uksm_attrs[] = { ++ &max_cpu_percentage_attr.attr, ++ &sleep_millisecs_attr.attr, ++ &cpu_governor_attr.attr, ++ &run_attr.attr, ++ &ema_per_page_time_attr.attr, ++ &pages_shared_attr.attr, ++ &pages_sharing_attr.attr, ++ &pages_unshared_attr.attr, ++ &full_scans_attr.attr, ++ &pages_scanned_attr.attr, ++ &hash_strength_attr.attr, ++ &sleep_times_attr.attr, ++ &thrash_threshold_attr.attr, ++ &abundant_threshold_attr.attr, ++ &cpu_ratios_attr.attr, ++ &eval_intervals_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group uksm_attr_group = { ++ .attrs = uksm_attrs, ++ .name = "uksm", ++}; ++#endif /* CONFIG_SYSFS */ ++ ++static inline void init_scan_ladder(void) ++{ ++ int i; ++ struct scan_rung *rung; ++ ++ for (i = 0; i < SCAN_LADDER_SIZE; i++) { ++ rung = uksm_scan_ladder + i; ++ slot_tree_init_root(&rung->vma_root); ++ } ++ ++ init_performance_values(); ++ uksm_calc_scan_pages(); ++} ++ ++static inline int cal_positive_negative_costs(void) ++{ ++ struct page *p1, *p2; ++ unsigned char *addr1, *addr2; ++ unsigned long i, time_start, hash_cost; ++ unsigned long loopnum = 0; ++ ++ /*IMPORTANT: volatile is needed to prevent over-optimization by gcc. */ ++ volatile u32 hash; ++ volatile int ret; ++ ++ p1 = alloc_page(GFP_KERNEL); ++ if (!p1) ++ return -ENOMEM; ++ ++ p2 = alloc_page(GFP_KERNEL); ++ if (!p2) ++ return -ENOMEM; ++ ++ addr1 = kmap_atomic(p1); ++ addr2 = kmap_atomic(p2); ++ memset(addr1, prandom_u32(), PAGE_SIZE); ++ memcpy(addr2, addr1, PAGE_SIZE); ++ ++ /* make sure that the two pages differ in last byte */ ++ addr2[PAGE_SIZE-1] = ~addr2[PAGE_SIZE-1]; ++ kunmap_atomic(addr2); ++ kunmap_atomic(addr1); ++ ++ time_start = jiffies; ++ while (jiffies - time_start < 100) { ++ for (i = 0; i < 100; i++) ++ hash = page_hash(p1, HASH_STRENGTH_FULL, 0); ++ loopnum += 100; ++ } ++ hash_cost = (jiffies - time_start); ++ ++ time_start = jiffies; ++ for (i = 0; i < loopnum; i++) ++ ret = pages_identical(p1, p2); ++ memcmp_cost = HASH_STRENGTH_FULL * (jiffies - time_start); ++ memcmp_cost /= hash_cost; ++ printk(KERN_INFO "UKSM: relative memcmp_cost = %lu " ++ "hash=%u cmp_ret=%d.\n", ++ memcmp_cost, hash, ret); ++ ++ __free_page(p1); ++ __free_page(p2); ++ return 0; ++} ++ ++static int init_zeropage_hash_table(void) ++{ ++ struct page *page; ++ char *addr; ++ int i; ++ ++ page = alloc_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ ++ addr = kmap_atomic(page); ++ memset(addr, 0, PAGE_SIZE); ++ kunmap_atomic(addr); ++ ++ zero_hash_table = kmalloc(HASH_STRENGTH_MAX * sizeof(u32), ++ GFP_KERNEL); ++ if (!zero_hash_table) ++ return -ENOMEM; ++ ++ for (i = 0; i < HASH_STRENGTH_MAX; i++) ++ zero_hash_table[i] = page_hash(page, i, 0); ++ ++ __free_page(page); ++ ++ return 0; ++} ++ ++static inline int init_random_sampling(void) ++{ ++ unsigned long i; ++ random_nums = kmalloc(PAGE_SIZE, GFP_KERNEL); ++ if (!random_nums) ++ return -ENOMEM; ++ ++ for (i = 0; i < HASH_STRENGTH_FULL; i++) ++ random_nums[i] = i; ++ ++ for (i = 0; i < HASH_STRENGTH_FULL; i++) { ++ unsigned long rand_range, swap_index, tmp; ++ ++ rand_range = HASH_STRENGTH_FULL - i; ++ swap_index = i + prandom_u32() % rand_range; ++ tmp = random_nums[i]; ++ random_nums[i] = random_nums[swap_index]; ++ random_nums[swap_index] = tmp; ++ } ++ ++ rshash_state.state = RSHASH_NEW; ++ rshash_state.below_count = 0; ++ rshash_state.lookup_window_index = 0; ++ ++ return cal_positive_negative_costs(); ++} ++ ++static int __init uksm_slab_init(void) ++{ ++ rmap_item_cache = UKSM_KMEM_CACHE(rmap_item, 0); ++ if (!rmap_item_cache) ++ goto out; ++ ++ stable_node_cache = UKSM_KMEM_CACHE(stable_node, 0); ++ if (!stable_node_cache) ++ goto out_free1; ++ ++ node_vma_cache = UKSM_KMEM_CACHE(node_vma, 0); ++ if (!node_vma_cache) ++ goto out_free2; ++ ++ vma_slot_cache = UKSM_KMEM_CACHE(vma_slot, 0); ++ if (!vma_slot_cache) ++ goto out_free3; ++ ++ tree_node_cache = UKSM_KMEM_CACHE(tree_node, 0); ++ if (!tree_node_cache) ++ goto out_free4; ++ ++ return 0; ++ ++out_free4: ++ kmem_cache_destroy(vma_slot_cache); ++out_free3: ++ kmem_cache_destroy(node_vma_cache); ++out_free2: ++ kmem_cache_destroy(stable_node_cache); ++out_free1: ++ kmem_cache_destroy(rmap_item_cache); ++out: ++ return -ENOMEM; ++} ++ ++static void __init uksm_slab_free(void) ++{ ++ kmem_cache_destroy(stable_node_cache); ++ kmem_cache_destroy(rmap_item_cache); ++ kmem_cache_destroy(node_vma_cache); ++ kmem_cache_destroy(vma_slot_cache); ++ kmem_cache_destroy(tree_node_cache); ++} ++ ++/* Common interface to ksm, different to it. */ ++int ksm_madvise(struct vm_area_struct *vma, unsigned long start, ++ unsigned long end, int advice, unsigned long *vm_flags) ++{ ++ int err; ++ ++ switch (advice) { ++ case MADV_MERGEABLE: ++ return 0; /* just ignore the advice */ ++ ++ case MADV_UNMERGEABLE: ++ if (!(*vm_flags & VM_MERGEABLE)) ++ return 0; /* just ignore the advice */ ++ ++ if (vma->anon_vma) { ++ err = unmerge_uksm_pages(vma, start, end); ++ if (err) ++ return err; ++ } ++ ++ uksm_remove_vma(vma); ++ *vm_flags &= ~VM_MERGEABLE; ++ break; ++ } ++ ++ return 0; ++} ++ ++/* Common interface to ksm, actually the same. */ ++struct page *ksm_might_need_to_copy(struct page *page, ++ struct vm_area_struct *vma, unsigned long address) ++{ ++ struct anon_vma *anon_vma = page_anon_vma(page); ++ struct page *new_page; ++ ++ if (PageKsm(page)) { ++ if (page_stable_node(page)) ++ return page; /* no need to copy it */ ++ } else if (!anon_vma) { ++ return page; /* no need to copy it */ ++ } else if (anon_vma->root == vma->anon_vma->root && ++ page->index == linear_page_index(vma, address)) { ++ return page; /* still no need to copy it */ ++ } ++ if (!PageUptodate(page)) ++ return page; /* let do_swap_page report the error */ ++ ++ new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); ++ if (new_page) { ++ copy_user_highpage(new_page, page, address, vma); ++ ++ SetPageDirty(new_page); ++ __SetPageUptodate(new_page); ++ __SetPageLocked(new_page); ++ } ++ ++ return new_page; ++} ++ ++static int __init uksm_init(void) ++{ ++ struct task_struct *uksm_thread; ++ int err; ++ ++ uksm_sleep_jiffies = msecs_to_jiffies(100); ++ uksm_sleep_saved = uksm_sleep_jiffies; ++ ++ slot_tree_init(); ++ init_scan_ladder(); ++ ++ ++ err = init_random_sampling(); ++ if (err) ++ goto out_free2; ++ ++ err = uksm_slab_init(); ++ if (err) ++ goto out_free1; ++ ++ err = init_zeropage_hash_table(); ++ if (err) ++ goto out_free0; ++ ++ uksm_thread = kthread_run(uksm_scan_thread, NULL, "uksmd"); ++ if (IS_ERR(uksm_thread)) { ++ printk(KERN_ERR "uksm: creating kthread failed\n"); ++ err = PTR_ERR(uksm_thread); ++ goto out_free; ++ } ++ ++#ifdef CONFIG_SYSFS ++ err = sysfs_create_group(mm_kobj, &uksm_attr_group); ++ if (err) { ++ printk(KERN_ERR "uksm: register sysfs failed\n"); ++ kthread_stop(uksm_thread); ++ goto out_free; ++ } ++#else ++ uksm_run = UKSM_RUN_MERGE; /* no way for user to start it */ ++ ++#endif /* CONFIG_SYSFS */ ++ ++#ifdef CONFIG_MEMORY_HOTREMOVE ++ /* ++ * Choose a high priority since the callback takes uksm_thread_mutex: ++ * later callbacks could only be taking locks which nest within that. ++ */ ++ hotplug_memory_notifier(uksm_memory_callback, 100); ++#endif ++ return 0; ++ ++out_free: ++ kfree(zero_hash_table); ++out_free0: ++ uksm_slab_free(); ++out_free1: ++ kfree(random_nums); ++out_free2: ++ kfree(uksm_scan_ladder); ++ return err; ++} ++ ++#ifdef MODULE ++subsys_initcall(ksm_init); ++#else ++late_initcall(uksm_init); ++#endif ++ +diff --git a/mm/vmstat.c b/mm/vmstat.c +index cb2a67b..912b86f 100644 +--- a/mm/vmstat.c ++++ b/mm/vmstat.c +@@ -733,6 +733,9 @@ const char * const vmstat_text[] = { + "nr_anon_transparent_hugepages", + "nr_free_cma", + ++#ifdef CONFIG_UKSM ++ "nr_uksm_zero_pages", ++#endif + /* enum writeback_stat_item counters */ + "nr_dirty_threshold", + "nr_dirty_background_threshold",