diff options
-rw-r--r-- | adb/commandline.c | 3 | ||||
-rw-r--r-- | include/utils/BitSet.h | 195 | ||||
-rw-r--r-- | libutils/tests/BitSet_test.cpp | 191 | ||||
-rw-r--r-- | lmkd/Android.mk | 10 | ||||
-rw-r--r-- | lmkd/lmkd.c | 735 | ||||
-rw-r--r-- | rootdir/init.rc | 9 |
6 files changed, 1116 insertions, 27 deletions
diff --git a/adb/commandline.c b/adb/commandline.c index 241cefc..c4b5174 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -137,10 +137,11 @@ void help() " adb forward --remove <local> - remove a specific forward socket connection\n" " adb forward --remove-all - remove all forward socket connections\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" + " adb install [-l] [-r] [-d] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" " - push this package file to the device and install it\n" " ('-l' means forward-lock the app)\n" " ('-r' means reinstall the app, keeping its data)\n" + " ('-d' means allow version code downgrade)\n" " ('-s' means install on SD card instead of internal storage)\n" " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 19c03d1..f1d68a0 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -30,73 +30,103 @@ namespace android { struct BitSet32 { uint32_t value; - inline BitSet32() : value(0) { } + inline BitSet32() : value(0UL) { } explicit inline BitSet32(uint32_t value) : value(value) { } // Gets the value associated with a particular bit index. - static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } + static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; } // Clears the bit set. - inline void clear() { value = 0; } + inline void clear() { clear(value); } + + static inline void clear(uint32_t& value) { value = 0UL; } // Returns the number of marked bits in the set. - inline uint32_t count() const { return __builtin_popcount(value); } + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); } // Returns true if the bit set does not contain any marked bits. - inline bool isEmpty() const { return ! value; } + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint32_t value) { return ! value; } // Returns true if the bit set does not contain any unmarked bits. - inline bool isFull() const { return value == 0xffffffff; } + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; } // Returns true if the specified bit is marked. - inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); } // Marks the specified bit. - inline void markBit(uint32_t n) { value |= valueForBit(n); } + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); } // Clears the specified bit. - inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); } // Finds the first marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static uint32_t firstMarkedBit(uint32_t value) { return __builtin_clzl(value); } // Finds the first unmarked bit in the set. // Result is undefined if all bits are marked. - inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint32_t value) { return __builtin_clzl(~ value); } // Finds the last marked bit in the set. // Result is undefined if all bits are unmarked. - inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - __builtin_ctzl(value); } // Finds the first marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearFirstMarkedBit() { - uint32_t n = firstMarkedBit(); - clearBit(n); + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint32_t& value) { + uint32_t n = firstMarkedBit(value); + clearBit(value, n); return n; } // Finds the first unmarked bit in the set and marks it. Returns the bit index. // Result is undefined if all bits are marked. - inline uint32_t markFirstUnmarkedBit() { - uint32_t n = firstUnmarkedBit(); - markBit(n); + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint32_t& value) { + uint32_t n = firstUnmarkedBit(value); + markBit(value, n); return n; } // Finds the last marked bit in the set and clears it. Returns the bit index. // Result is undefined if all bits are unmarked. - inline uint32_t clearLastMarkedBit() { - uint32_t n = lastMarkedBit(); - clearBit(n); + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint32_t& value) { + uint32_t n = lastMarkedBit(value); + clearBit(value, n); return n; } // Gets the index of the specified bit in the set, which is the number of // marked bits that appear before the specified bit. inline uint32_t getIndexOfBit(uint32_t n) const { - return __builtin_popcount(value & ~(0xffffffffUL >> n)); + return getIndexOfBit(value, n); + } + + static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) { + return __builtin_popcountl(value & ~(0xffffffffUL >> n)); } inline bool operator== (const BitSet32& other) const { return value == other.value; } @@ -119,6 +149,127 @@ struct BitSet32 { ANDROID_BASIC_TYPES_TRAITS(BitSet32) +// A simple set of 64 bits that can be individually marked or cleared. +struct BitSet64 { + uint64_t value; + + inline BitSet64() : value(0ULL) { } + explicit inline BitSet64(uint64_t value) : value(value) { } + + // Gets the value associated with a particular bit index. + static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; } + + // Clears the bit set. + inline void clear() { clear(value); } + + static inline void clear(uint64_t& value) { value = 0ULL; } + + // Returns the number of marked bits in the set. + inline uint32_t count() const { return count(value); } + + static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); } + + // Returns true if the bit set does not contain any marked bits. + inline bool isEmpty() const { return isEmpty(value); } + + static inline bool isEmpty(uint64_t value) { return ! value; } + + // Returns true if the bit set does not contain any unmarked bits. + inline bool isFull() const { return isFull(value); } + + static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; } + + // Returns true if the specified bit is marked. + inline bool hasBit(uint32_t n) const { return hasBit(value, n); } + + static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); } + + // Marks the specified bit. + inline void markBit(uint32_t n) { markBit(value, n); } + + static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); } + + // Clears the specified bit. + inline void clearBit(uint32_t n) { clearBit(value, n); } + + static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); } + + // Finds the first marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); } + + static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); } + + // Finds the first unmarked bit in the set. + // Result is undefined if all bits are marked. + inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); } + + static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); } + + // Finds the last marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); } + + static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); } + + // Finds the first marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); } + + static inline uint32_t clearFirstMarkedBit(uint64_t& value) { + uint64_t n = firstMarkedBit(value); + clearBit(value, n); + return n; + } + + // Finds the first unmarked bit in the set and marks it. Returns the bit index. + // Result is undefined if all bits are marked. + inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); } + + static inline uint32_t markFirstUnmarkedBit(uint64_t& value) { + uint64_t n = firstUnmarkedBit(value); + markBit(value, n); + return n; + } + + // Finds the last marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); } + + static inline uint32_t clearLastMarkedBit(uint64_t& value) { + uint64_t n = lastMarkedBit(value); + clearBit(value, n); + return n; + } + + // Gets the index of the specified bit in the set, which is the number of + // marked bits that appear before the specified bit. + inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); } + + static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) { + return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)); + } + + inline bool operator== (const BitSet64& other) const { return value == other.value; } + inline bool operator!= (const BitSet64& other) const { return value != other.value; } + inline BitSet64 operator& (const BitSet64& other) const { + return BitSet64(value & other.value); + } + inline BitSet64& operator&= (const BitSet64& other) { + value &= other.value; + return *this; + } + inline BitSet64 operator| (const BitSet64& other) const { + return BitSet64(value | other.value); + } + inline BitSet64& operator|= (const BitSet64& other) { + value |= other.value; + return *this; + } +}; + +ANDROID_BASIC_TYPES_TRAITS(BitSet64) + } // namespace android #endif // UTILS_BITSET_H diff --git a/libutils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp index 752e56d..38b668a 100644 --- a/libutils/tests/BitSet_test.cpp +++ b/libutils/tests/BitSet_test.cpp @@ -23,7 +23,7 @@ namespace android { -class BitSetTest : public testing::Test { +class BitSet32Test : public testing::Test { protected: BitSet32 b1; BitSet32 b2; @@ -34,7 +34,7 @@ protected: }; -TEST_F(BitSetTest, BitWiseOr) { +TEST_F(BitSet32Test, BitWiseOr) { b1.markBit(2); b2.markBit(4); @@ -49,7 +49,7 @@ TEST_F(BitSetTest, BitWiseOr) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4)); EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u); } -TEST_F(BitSetTest, BitWiseAnd_Disjoint) { +TEST_F(BitSet32Test, BitWiseAnd_Disjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -65,7 +65,7 @@ TEST_F(BitSetTest, BitWiseAnd_Disjoint) { EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6)); } -TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { +TEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) { b1.markBit(2); b1.markBit(4); b1.markBit(6); @@ -84,4 +84,187 @@ TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { EXPECT_EQ(b2.count(), 3u); EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9)); } + +TEST_F(BitSet32Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet32Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(30); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(30)); +} + +TEST_F(BitSet32Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(31); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet32Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 32; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet32Test, GetIndexOfBit) { + b1.markBit(1); + b1.markBit(4); + EXPECT_EQ(b1.getIndexOfBit(1), 0); + EXPECT_EQ(b1.getIndexOfBit(4), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(1), 1); + EXPECT_EQ(b1.getIndexOfBit(4), 2); +} + +class BitSet64Test : public testing::Test { +protected: + BitSet64 b1; + BitSet64 b2; + virtual void TearDown() { + b1.clear(); + b2.clear(); + } +}; + + +TEST_F(BitSet64Test, BitWiseOr) { + b1.markBit(20); + b2.markBit(40); + + BitSet64 tmp = b1 | b2; + EXPECT_EQ(tmp.count(), 2u); + EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 | b1) == (b1 | b2)); + + b1 |= b2; + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40)); + EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u); +} +TEST_F(BitSet64Test, BitWiseAnd_Disjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + + BitSet64 tmp = b1 & b2; + EXPECT_TRUE(tmp.isEmpty()); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b2 &= b1; + EXPECT_TRUE(b2.isEmpty()); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60)); +} + +TEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) { + b1.markBit(20); + b1.markBit(40); + b1.markBit(60); + b2.markBit(30); + b2.markBit(60); + b2.markBit(63); + + BitSet64 tmp = b1 & b2; + EXPECT_EQ(tmp.count(), 1u); + EXPECT_TRUE(tmp.hasBit(60)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b1 &= b2; + EXPECT_EQ(b1.count(), 1u); + EXPECT_EQ(b2.count(), 3u); + EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63)); +} + +TEST_F(BitSet64Test, MarkFirstUnmarkedBit) { + b1.markBit(1); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1)); + + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2)); +} + +TEST_F(BitSet64Test, ClearFirstMarkedBit) { + b1.markBit(0); + b1.markBit(10); + + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(50); + b1.clearFirstMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(50)); +} + +TEST_F(BitSet64Test, ClearLastMarkedBit) { + b1.markBit(10); + b1.markBit(63); + + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(10)); + + b1.markBit(5); + b1.clearLastMarkedBit(); + EXPECT_EQ(b1.count(), 1u); + EXPECT_TRUE(b1.hasBit(5)); +} + +TEST_F(BitSet64Test, FillAndClear) { + EXPECT_TRUE(b1.isEmpty()); + for (size_t i = 0; i < 64; i++) { + b1.markFirstUnmarkedBit(); + } + EXPECT_TRUE(b1.isFull()); + b1.clear(); + EXPECT_TRUE(b1.isEmpty()); +} + +TEST_F(BitSet64Test, GetIndexOfBit) { + b1.markBit(10); + b1.markBit(40); + EXPECT_EQ(b1.getIndexOfBit(10), 0); + EXPECT_EQ(b1.getIndexOfBit(40), 1); + b1.markFirstUnmarkedBit(); + EXPECT_EQ(b1.getIndexOfBit(10), 1); + EXPECT_EQ(b1.getIndexOfBit(40), 2); +} + } // namespace android diff --git a/lmkd/Android.mk b/lmkd/Android.mk new file mode 100644 index 0000000..5d6d1d2 --- /dev/null +++ b/lmkd/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := lmkd.c +LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc +LOCAL_FORCE_STATIC_EXECUTABLE := true + +LOCAL_MODULE := lmkd + +include $(BUILD_EXECUTABLE) diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c new file mode 100644 index 0000000..376410b --- /dev/null +++ b/lmkd/lmkd.c @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "lowmemorykiller" + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <cutils/log.h> +#include <cutils/sockets.h> + +#define MEMCG_SYSFS_PATH "/dev/memcg/" +#define MEMPRESSURE_WATCH_LEVEL "medium" +#define ZONEINFO_PATH "/proc/zoneinfo" +#define LINE_MAX 128 + +#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree" +#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +enum lmk_cmd { + LMK_TARGET, + LMK_PROCPRIO, + LMK_PROCREMOVE, +}; + +#define MAX_TARGETS 6 +/* + * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio + * values + */ +#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1)) + +/* default to old in-kernel interface if no memory pressure events */ +static int use_inkernel_interface = 1; + +/* memory pressure level medium event */ +static int mpevfd; + +/* control socket listen and data */ +static int ctrl_lfd; +static int ctrl_dfd = -1; +static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ + +/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */ +#define MAX_EPOLL_EVENTS 3 +static int epollfd; +static int maxevents; + +#define OOM_DISABLE (-17) +/* inclusive */ +#define OOM_ADJUST_MIN (-16) +#define OOM_ADJUST_MAX 15 + +static int lowmem_adj[MAX_TARGETS]; +static int lowmem_minfree[MAX_TARGETS]; +static int lowmem_targets_size; + +struct sysmeminfo { + int nr_free_pages; + int nr_file_pages; + int nr_shmem; + int totalreserve_pages; +}; + +struct adjslot_list { + struct adjslot_list *next; + struct adjslot_list *prev; +}; + +struct proc { + struct adjslot_list asl; + int pid; + int oomadj; + struct proc *pidhash_next; +}; + +#define PIDHASH_SZ 1024 +static struct proc *pidhash[PIDHASH_SZ]; +#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) + +#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN) +static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1]; + +/* + * Wait 1-2 seconds for the death report of a killed process prior to + * considering killing more processes. + */ +#define KILL_TIMEOUT 2 +/* Time of last process kill we initiated, stop me before I kill again */ +static time_t kill_lasttime; + +/* PAGE_SIZE / 1024 */ +static long page_k; + +static struct proc *pid_lookup(int pid) { + struct proc *procp; + + for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; + procp = procp->pidhash_next) + ; + + return procp; +} + +static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new) +{ + struct adjslot_list *next = head->next; + new->prev = head; + new->next = next; + next->prev = new; + head->next = new; +} + +static void adjslot_remove(struct adjslot_list *old) +{ + struct adjslot_list *prev = old->prev; + struct adjslot_list *next = old->next; + next->prev = prev; + prev->next = next; +} + +static struct adjslot_list *adjslot_tail(struct adjslot_list *head) { + struct adjslot_list *asl = head->prev; + + return asl == head ? NULL : asl; +} + +static void proc_slot(struct proc *procp) { + int adjslot = ADJTOSLOT(procp->oomadj); + + adjslot_insert(&procadjslot_list[adjslot], &procp->asl); +} + +static void proc_unslot(struct proc *procp) { + adjslot_remove(&procp->asl); +} + +static void proc_insert(struct proc *procp) { + int hval = pid_hashfn(procp->pid); + + procp->pidhash_next = pidhash[hval]; + pidhash[hval] = procp; + proc_slot(procp); +} + +static int pid_remove(int pid) { + int hval = pid_hashfn(pid); + struct proc *procp; + struct proc *prevp; + + for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid; + procp = procp->pidhash_next) + prevp = procp; + + if (!procp) + return -1; + + if (!prevp) + pidhash[hval] = procp->pidhash_next; + else + prevp->pidhash_next = procp->pidhash_next; + + proc_unslot(procp); + free(procp); + return 0; +} + +static void writefilestring(char *path, char *s) { + int fd = open(path, O_WRONLY); + int len = strlen(s); + int ret; + + if (fd < 0) { + ALOGE("Error opening %s; errno=%d", path, errno); + return; + } + + ret = write(fd, s, len); + if (ret < 0) { + ALOGE("Error writing %s; errno=%d", path, errno); + } else if (ret < len) { + ALOGE("Short write on %s; length=%d", path, ret); + } + + close(fd); +} + +static void cmd_procprio(int pid, int oomadj) { + struct proc *procp; + char path[80]; + char val[20]; + + if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) { + ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); + return; + } + + snprintf(path, sizeof(path), "/proc/%d/oom_adj", pid); + snprintf(val, sizeof(val), "%d", oomadj); + writefilestring(path, val); + + if (use_inkernel_interface) + return; + + procp = pid_lookup(pid); + if (!procp) { + procp = malloc(sizeof(struct proc)); + if (!procp) { + // Oh, the irony. May need to rebuild our state. + return; + } + + procp->pid = pid; + procp->oomadj = oomadj; + proc_insert(procp); + } else { + proc_unslot(procp); + procp->oomadj = oomadj; + proc_slot(procp); + } +} + +static void cmd_procremove(int pid) { + struct proc *procp; + + if (use_inkernel_interface) + return; + + pid_remove(pid); + kill_lasttime = 0; +} + +static void cmd_target(int ntargets, int *params) { + int i; + + if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) + return; + + for (i = 0; i < ntargets; i++) { + lowmem_minfree[i] = ntohl(*params++); + lowmem_adj[i] = ntohl(*params++); + } + + lowmem_targets_size = ntargets; + + if (use_inkernel_interface) { + char minfreestr[128]; + char killpriostr[128]; + + minfreestr[0] = '\0'; + killpriostr[0] = '\0'; + + for (i = 0; i < lowmem_targets_size; i++) { + char val[40]; + + if (i) { + strlcat(minfreestr, ",", sizeof(minfreestr)); + strlcat(killpriostr, ",", sizeof(killpriostr)); + } + + snprintf(val, sizeof(val), "%d", lowmem_minfree[i]); + strlcat(minfreestr, val, sizeof(minfreestr)); + snprintf(val, sizeof(val), "%d", lowmem_adj[i]); + strlcat(killpriostr, val, sizeof(killpriostr)); + } + + writefilestring(INKERNEL_MINFREE_PATH, minfreestr); + writefilestring(INKERNEL_ADJ_PATH, killpriostr); + } +} + +static void ctrl_data_close(void) { + ALOGI("Closing Activity Manager data connection"); + close(ctrl_dfd); + ctrl_dfd = -1; + maxevents--; +} + +static int ctrl_data_read(char *buf, size_t bufsz) { + int ret = 0; + + ret = read(ctrl_dfd, buf, bufsz); + + if (ret == -1) { + ALOGE("control data socket read failed; errno=%d", errno); + } else if (ret == 0) { + ALOGE("Got EOF on control data socket"); + ret = -1; + } + + return ret; +} + +static void ctrl_command_handler(void) { + int ibuf[CTRL_PACKET_MAX / sizeof(int)]; + int len; + int cmd = -1; + int nargs; + int targets; + + len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX); + if (len <= 0) + return; + + nargs = len / sizeof(int) - 1; + if (nargs < 0) + goto wronglen; + + cmd = ntohl(ibuf[0]); + + switch(cmd) { + case LMK_TARGET: + targets = nargs / 2; + if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) + goto wronglen; + cmd_target(targets, &ibuf[1]); + break; + case LMK_PROCPRIO: + if (nargs != 2) + goto wronglen; + cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2])); + break; + case LMK_PROCREMOVE: + if (nargs != 1) + goto wronglen; + cmd_procremove(ntohl(ibuf[1])); + break; + default: + ALOGE("Received unknown command code %d", cmd); + return; + } + + return; + +wronglen: + ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len); +} + +static void ctrl_data_handler(uint32_t events) { + if (events & EPOLLHUP) { + ALOGI("ActivityManager disconnected"); + if (!ctrl_dfd_reopened) + ctrl_data_close(); + } else if (events & EPOLLIN) { + ctrl_command_handler(); + } +} + +static void ctrl_connect_handler(uint32_t events) { + struct sockaddr addr; + socklen_t alen; + struct epoll_event epev; + + if (ctrl_dfd >= 0) { + ctrl_data_close(); + ctrl_dfd_reopened = 1; + } + + alen = sizeof(addr); + ctrl_dfd = accept(ctrl_lfd, &addr, &alen); + + if (ctrl_dfd < 0) { + ALOGE("lmkd control socket accept failed; errno=%d", errno); + return; + } + + ALOGI("ActivityManager connected"); + maxevents++; + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_data_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) { + ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno); + ctrl_data_close(); + return; + } +} + +static int zoneinfo_parse_protection(char *cp) { + int max = 0; + int zoneval; + + if (*cp++ != '(') + return 0; + + do { + zoneval = strtol(cp, &cp, 0); + if ((*cp != ',') && (*cp != ')')) + return 0; + if (zoneval > max) + max = zoneval; + } while (cp = strtok(NULL, " ")); + + return max; +} + +static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) { + char *cp = line; + char *ap; + + cp = strtok(line, " "); + if (!cp) + return; + + ap = strtok(NULL, " "); + if (!ap) + return; + + if (!strcmp(cp, "nr_free_pages")) + mip->nr_free_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_file_pages")) + mip->nr_file_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "nr_shmem")) + mip->nr_shmem += strtol(ap, NULL, 0); + else if (!strcmp(cp, "high")) + mip->totalreserve_pages += strtol(ap, NULL, 0); + else if (!strcmp(cp, "protection:")) + mip->totalreserve_pages += zoneinfo_parse_protection(ap); +} + +static int zoneinfo_parse(struct sysmeminfo *mip) { + FILE *f; + char *cp; + char line[LINE_MAX]; + + memset(mip, 0, sizeof(struct sysmeminfo)); + f = fopen(ZONEINFO_PATH, "r"); + if (!f) { + ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno); + return -1; + } + + while (fgets(line, LINE_MAX, f)) + zoneinfo_parse_line(line, mip); + + fclose(f); + return 0; +} + +static int proc_get_size(int pid) { + char path[PATH_MAX]; + char line[LINE_MAX]; + FILE *f; + int rss = 0; + int total; + + snprintf(path, PATH_MAX, "/proc/%d/statm", pid); + f = fopen(path, "r"); + if (!f) + return -1; + if (!fgets(line, LINE_MAX, f)) { + fclose(f); + return -1; + } + + sscanf(line, "%d %d ", &total, &rss); + fclose(f); + return rss; +} + +static char *proc_get_name(int pid) { + char path[PATH_MAX]; + static char line[LINE_MAX]; + FILE *f; + char *cp; + + snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid); + f = fopen(path, "r"); + if (!f) + return NULL; + if (!fgets(line, LINE_MAX, f)) { + fclose(f); + return NULL; + } + + cp = strchr(line, ' '); + if (cp) + *cp = '\0'; + + return line; +} + +static struct proc *proc_adj_lru(int oomadj) { + return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]); +} + +static void mp_event(uint32_t events) { + int i; + int ret; + unsigned long long evcount; + struct sysmeminfo mi; + int other_free; + int other_file; + int minfree = 0; + int min_score_adj = OOM_ADJUST_MAX + 1; + + ret = read(mpevfd, &evcount, sizeof(evcount)); + if (ret < 0) + ALOGE("Error reading memory pressure event fd; errno=%d", + errno); + + if (time(NULL) - kill_lasttime < KILL_TIMEOUT) + return; + + if (zoneinfo_parse(&mi) < 0) + return; + + other_free = mi.nr_free_pages - mi.totalreserve_pages; + other_file = mi.nr_file_pages - mi.nr_shmem; + + for (i = 0; i < lowmem_targets_size; i++) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { + min_score_adj = lowmem_adj[i]; + break; + } + } + + if (min_score_adj == OOM_ADJUST_MAX + 1) + return; + + for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) { + struct proc *procp; + + retry: + procp = proc_adj_lru(i); + + if (procp) { + int pid = procp->pid; + char *taskname; + int tasksize; + int r; + + taskname = proc_get_name(pid); + if (!taskname) { + pid_remove(pid); + goto retry; + } + + tasksize = proc_get_size(pid); + if (tasksize < 0) { + pid_remove(pid); + goto retry; + } + + ALOGI("Killing '%s' (%d), adj %d\n" + " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n" + " Free memory is %ldkB %s reserved", + taskname, pid, procp->oomadj, tasksize * page_k, + other_file * page_k, minfree * page_k, min_score_adj, + other_free * page_k, other_free >= 0 ? "above" : "below"); + r = kill(pid, SIGKILL); + pid_remove(pid); + + if (r) { + ALOGE("kill(%d): errno=%d", procp->pid, errno); + goto retry; + } else { + time(&kill_lasttime); + break; + } + } + } +} + +static int init_mp(char *levelstr, void *event_handler) +{ + int mpfd; + int evfd; + int evctlfd; + char buf[256]; + struct epoll_event epev; + int ret; + + mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY); + if (mpfd < 0) { + ALOGI("No kernel memory.pressure_level support (errno=%d)", errno); + goto err_open_mpfd; + } + + evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY); + if (evctlfd < 0) { + ALOGI("No kernel memory cgroup event control (errno=%d)", errno); + goto err_open_evctlfd; + } + + evfd = eventfd(0, EFD_NONBLOCK); + if (evfd < 0) { + ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno); + goto err_eventfd; + } + + ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr); + if (ret >= (ssize_t)sizeof(buf)) { + ALOGE("cgroup.event_control line overflow for level %s", levelstr); + goto err; + } + + ret = write(evctlfd, buf, strlen(buf) + 1); + if (ret == -1) { + ALOGE("cgroup.event_control write failed for level %s; errno=%d", + levelstr, errno); + goto err; + } + + epev.events = EPOLLIN; + epev.data.ptr = event_handler; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev); + if (ret == -1) { + ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno); + goto err; + } + maxevents++; + mpevfd = evfd; + return 0; + +err: + close(evfd); +err_eventfd: + close(evctlfd); +err_open_evctlfd: + close(mpfd); +err_open_mpfd: + return -1; +} + +static int init(void) { + struct epoll_event epev; + int i; + int ret; + + page_k = sysconf(_SC_PAGESIZE); + if (page_k == -1) + page_k = PAGE_SIZE; + page_k /= 1024; + + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + ALOGE("epoll_create failed (errno=%d)", errno); + return -1; + } + + ctrl_lfd = android_get_control_socket("lmkd"); + if (ctrl_lfd < 0) { + ALOGE("get lmkd control socket failed"); + return -1; + } + + ret = listen(ctrl_lfd, 1); + if (ret < 0) { + ALOGE("lmkd control socket listen failed (errno=%d)", errno); + return -1; + } + + epev.events = EPOLLIN; + epev.data.ptr = (void *)ctrl_connect_handler; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) { + ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno); + return -1; + } + maxevents++; + + use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK); + + if (use_inkernel_interface) { + ALOGI("Using in-kernel low memory killer interface"); + } else { + ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event); + if (ret) + ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); + } + + for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) { + procadjslot_list[i].next = &procadjslot_list[i]; + procadjslot_list[i].prev = &procadjslot_list[i]; + } + + return 0; +} + +static void mainloop(void) { + while (1) { + struct epoll_event events[maxevents]; + int nevents; + int i; + + ctrl_dfd_reopened = 0; + nevents = epoll_wait(epollfd, events, maxevents, -1); + + if (nevents == -1) { + if (errno == EINTR) + continue; + ALOGE("epoll_wait failed (errno=%d)", errno); + continue; + } + + for (i = 0; i < nevents; ++i) { + if (events[i].events & EPOLLERR) + ALOGD("EPOLLERR on event #%d", i); + if (events[i].data.ptr) + (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events); + } + } +} + +int main(int argc, char **argv) { + if (!init()) + mainloop(); + + ALOGI("exiting"); + return 0; +} diff --git a/rootdir/init.rc b/rootdir/init.rc index 81d173a..b5abfb0 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -89,6 +89,10 @@ loglevel 3 mkdir /mnt/obb 0700 root system mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + # memory control cgroup + mkdir /dev/memcg 0700 root system + mount cgroup none /dev/memcg memory + write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 @@ -477,6 +481,11 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0 on property:ro.kernel.qemu=1 start adbd +service lmkd /system/bin/lmkd + class core + critical + socket lmkd seqpacket 0660 system system + service servicemanager /system/bin/servicemanager class core user system |