From patchwork Mon Apr 3 06:27:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peng Zhang X-Patchwork-Id: 13197686 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id C7A1BC76196 for ; Mon, 3 Apr 2023 06:28:36 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id ECEB76B0072; Mon, 3 Apr 2023 02:28:35 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id E57A16B0074; Mon, 3 Apr 2023 02:28:35 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id CD0EA6B0075; Mon, 3 Apr 2023 02:28:35 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0012.hostedemail.com [216.40.44.12]) by kanga.kvack.org (Postfix) with ESMTP id B6C546B0072 for ; Mon, 3 Apr 2023 02:28:35 -0400 (EDT) Received: from smtpin06.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay01.hostedemail.com (Postfix) with ESMTP id 74CEF1C5115 for ; Mon, 3 Apr 2023 06:28:32 +0000 (UTC) X-FDA: 80639100864.06.8140F97 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) by imf30.hostedemail.com (Postfix) with ESMTP id DCBDD8001A for ; Mon, 3 Apr 2023 06:28:29 +0000 (UTC) Authentication-Results: imf30.hostedemail.com; dkim=pass header.d=bytedance.com header.s=google header.b=fIWatEyL; spf=pass (imf30.hostedemail.com: domain of zhangpeng.00@bytedance.com designates 209.85.210.169 as permitted sender) smtp.mailfrom=zhangpeng.00@bytedance.com; dmarc=pass (policy=quarantine) header.from=bytedance.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1680503310; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:references:dkim-signature; bh=8EZusFPyKlVLlAuor8AkBQgzEgMdx910d6xeQgDfz4M=; b=cJatEt2wybA5bI11NJMr9pjxrq4yN3GARH6agj4XiXh4KKBwM9Z6KtmXjfTtuAZv3u5D1L YjSYRSmqTsU7kvxLLJoRZxFOiXf3yyIxRLuCac3zxQKlOuJSxetgrjejf+npYWPyE2yQEg K8WeeSXEjxMxwtEfWRNiLqk2vyDLi/8= ARC-Authentication-Results: i=1; imf30.hostedemail.com; dkim=pass header.d=bytedance.com header.s=google header.b=fIWatEyL; spf=pass (imf30.hostedemail.com: domain of zhangpeng.00@bytedance.com designates 209.85.210.169 as permitted sender) smtp.mailfrom=zhangpeng.00@bytedance.com; dmarc=pass (policy=quarantine) header.from=bytedance.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1680503310; a=rsa-sha256; cv=none; b=IFa0B+z5bO4GysyfN6mt7BB0S5YCQyuki4si4tNqNLmE5XK4nfoUDpp87BOYx75kwS2Hs3 iY8RkGYxcN8lJ6coaQ28dY5jf1d0CMNidZX3PYiNucITb/NAqQr4nucS2uYLfc2MXd9I66 0lebv6PcTztizAqoQ/Gia/ERxJrAKYM= Received: by mail-pf1-f169.google.com with SMTP id cm5so12487473pfb.0 for ; Sun, 02 Apr 2023 23:28:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bytedance.com; s=google; t=1680503308; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=8EZusFPyKlVLlAuor8AkBQgzEgMdx910d6xeQgDfz4M=; b=fIWatEyLoScrepvqscXmHq9GJz2vt6UKbk2sx3R0X+13Ep7wmpbX4g8JC2uMSoe6XB KcNhgN4ewZZlPO461eyunk4MsIBmHCgOz8gx3BA3plKGpVJRV+F7PDSdfyqqobevZIvL K0sMWYD2YsXXsMFonhLk1/F+kJOnS33tGMao7Z67GJsryB9P70aGe6xBSFwppQAG4TAa 7xNcZVIYgsDXpTkAM2Wi1Z+gXhellW906rdkj9Xei1JeDf7G1KbSKsLQke+ekM5c8Vqh hC8nUD+Bb5M5f/wv1rkqjPqr8iMHkU0OAXOLFI7fjQzzUsQrusXthvFiDY7bzfMon2ZK TXVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680503308; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=8EZusFPyKlVLlAuor8AkBQgzEgMdx910d6xeQgDfz4M=; b=yh6VUmPXo71KxwyZFeY/6tuj2fwxrmPpGATPEgkfOYJEXercgLF+cQZp7R9MrXM7KL HfIw6n/Td14p6VzhiBo99A9m8QrP5fkuyz+x7qRWr/xIlXUhdKwP1iZqKkOaB8LhBVJk c5bPXA7LghXU3TF15vZ12WA0w+1LN+UkfCBy1L1HtYzbXAaNltYHiZJYeYDgFArxj268 +8kMyqAOPAkw/eqE8Qsuw0AZgU2h5NRkPqKG+acUHQw5Hqg83MHVtHqE9Ryvi/WZbERK /dwcd7dGYwbIoXoNnktXJ7TMV/Z/GbcNE/CRiAvuPEvzfPs4E2qyBxjkMyV3U1SFTnWY A2Og== X-Gm-Message-State: AAQBX9cQuAY33MJS4UqoFrIkGthY1ljQv2GqY6t7jeRX8DwcDiPQ8vae 3FSM5BgdvpxTE2XokaTfr7zv/A== X-Google-Smtp-Source: AKy350ZnTBEW9Zpd0Fn5hv+VcvWBXlRzv4xlVmZ3piblvUgpvHgtuUQpA8IsT1/HfkkszbAuZksayw== X-Received: by 2002:a62:6346:0:b0:626:cc72:51a7 with SMTP id x67-20020a626346000000b00626cc7251a7mr34281634pfb.9.1680503308511; Sun, 02 Apr 2023 23:28:28 -0700 (PDT) Received: from GL4FX4PXWL.bytedance.net ([139.177.225.248]) by smtp.gmail.com with ESMTPSA id k14-20020aa7820e000000b00625ee4c50eesm6013919pfi.77.2023.04.02.23.28.24 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 02 Apr 2023 23:28:28 -0700 (PDT) From: Peng Zhang To: glider@google.com, elver@google.com, dvyukov@google.com, akpm@linux-foundation.org Cc: kasan-dev@googlegroups.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Peng Zhang Subject: [PATCH] mm: kfence: Improve the performance of __kfence_alloc() and __kfence_free() Date: Mon, 3 Apr 2023 14:27:57 +0800 Message-Id: <20230403062757.74057-1-zhangpeng.00@bytedance.com> X-Mailer: git-send-email 2.37.0 (Apple Git-136) MIME-Version: 1.0 X-Rspamd-Server: rspam05 X-Rspamd-Queue-Id: DCBDD8001A X-Stat-Signature: dq36ssezkks1s33rc3e7w4rpr4h3osgn X-Rspam-User: X-HE-Tag: 1680503309-23657 X-HE-Meta: U2FsdGVkX19PDQKcoOzlnz8CMwMHuAMDaEV9euLlRdg9v6xoNS4KxSpw4dei4899UJ7R9eFTm6wLXnEMLD7/f12ml1y7Ahk8RIwEhHGtHl9pde/N7B6hdAQcrGFV/VYVsb4Qg969BQ4FcyzNvZXG9f5Lb/yFtFzjIoYa3X1d0dLJy01LrM4R1AqMY8noVj+qPf0M+d5XzX9Mvghcehv4+eEqQvCjcHy2S8rS/lbV4njGVRM9jM1wJ+YQg6kY6xN5pD6w3FrkM06aDsPdBCsDIK07+VtFojVl6IMtj50+srj2l43ikMtcgiW4LaYfmpIXwAVgkaI7fTzNdSjuAFcRHNRAH3aL+aTc1OnK1D2yOXGiMz93V1g1/8d6bTKDPe3yzk3H3ej9GWEdkdo87nkXQEhhKXAQTa5HgvvTN2Cl6nMqB4DJDPJ7XIz+mbhoNnxdcf+0uXQzxd/VrEL84lADyWviRSkhg4u2JS95EDFIO2tGhXcHxI7DnvoOrtlQWBZwWs7/lFgAjZ4Jkrex2o08oN8xhKpa22s9Ul/oztqHn1MXvfc682LlEYO7BtiBHOArq/oxpbc+/HKJ54fzcfTjD3muIfFl6W+tpZLWzEoGVEEdqGtldNx+44yOGsSZw4G5nEjuVAl7C6Ph7BM0R70uBlB/7rVlKwaHCNbNkpMTdIPuvjFVPowMQ1mSv5pZ9ur7IWYo56CJG4uBoSoYWfZXTqxgA1FmHX9KnVsgkvFVi7girJ1V2e1Cpd7i9gT8rZVEHU7VmYaZzmRUkFLZhdwD/r5iWf/pQythoZRWnk5HN3S9YupotcOBw/gW4qJaFk/gJgLZc5QAu2kjXKTk/zGvL7w2cBTHzO/iL2mNNaegTG5VuZGP4ROEC2bTZ331YZ/VUEVoQ+i+FiPLABj1yUTNgjv2VGZ6seyCg4BF+N9Duu81RNLmmT3QfV1Jr6+Vg6yF6ZqKcxEhNjxWkv7PpVI KqCipv3u e7wDfNoAh34BP/HpzwIR7fYkTBFcrlAxV02yL0LqlrCXMzMe9IZTmJOXS6BkZbF5KOL6WL8EUYIXn4N1ZoaEYE2dqmhV52tJpQCynP21ZCFaKSSMf8ZbCjM33FyxBPbrzMAIN6EfZcSKmPRfnmdnij61MaWbJYe3EHcb8xrtdev1+IomhzIm1ySuiDXNO7OD2xnlHPJtNO6p2PztB0f0sxzAmZnnrPA3/q8I4E01dpIk1JUB0XlxnOY3kvEQWwRV3otf5UjILgPwUBu6Gwsa6Ujn/7EwOuRPIAXY/zAyxkMwz3BMsw8s7kHN+UtwYMWKJvKKrqcbqlw5nbDpdQx73t7uQ7XBj7yY4RwfJjGP/+PpAJT5ZjPODvNjGpA8SVDMKhjmTWed2KmxxGIvHoVWhA3aGqF2vPqWjsYEIfIeA3TySbyVMs1PgY+IIRord8iTYCw47NHpCLqMVZ2DCGa8xvGraRUYbviieO7tO X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: In __kfence_alloc() and __kfence_free(), we will set and check canary. Assuming that the size of the object is close to 0, nearly 4k memory accesses are required because setting and checking canary is executed byte by byte. canary is now defined like this: KFENCE_CANARY_PATTERN(addr) ((u8)0xaa ^ (u8)((unsigned long)(addr) & 0x7)) Observe that canary is only related to the lower three bits of the address, so every 8 bytes of canary are the same. We can access 8-byte canary each time instead of byte-by-byte, thereby optimizing nearly 4k memory accesses to 4k/8 times. Use the bcc tool funclatency to measure the latency of __kfence_alloc() and __kfence_free(), the numbers (deleted the distribution of latency) is posted below. Though different object sizes will have an impact on the measurement, we ignore it for now and assume the average object size is roughly equal. Before playing patch: __kfence_alloc: avg = 5055 nsecs, total: 5515252 nsecs, count: 1091 __kfence_free: avg = 5319 nsecs, total: 9735130 nsecs, count: 1830 After playing patch: __kfence_alloc: avg = 3597 nsecs, total: 6428491 nsecs, count: 1787 __kfence_free: avg = 3046 nsecs, total: 3415390 nsecs, count: 1121 The numbers indicate that there is ~30% - ~40% performance improvement. Signed-off-by: Peng Zhang --- mm/kfence/core.c | 71 +++++++++++++++++++++++++++++++++------------- mm/kfence/kfence.h | 10 ++++++- mm/kfence/report.c | 2 +- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 79c94ee55f97..0b1b1298c738 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -297,20 +297,13 @@ metadata_update_state(struct kfence_metadata *meta, enum kfence_object_state nex WRITE_ONCE(meta->state, next); } -/* Write canary byte to @addr. */ -static inline bool set_canary_byte(u8 *addr) -{ - *addr = KFENCE_CANARY_PATTERN(addr); - return true; -} - /* Check canary byte at @addr. */ static inline bool check_canary_byte(u8 *addr) { struct kfence_metadata *meta; unsigned long flags; - if (likely(*addr == KFENCE_CANARY_PATTERN(addr))) + if (likely(*addr == KFENCE_CANARY_PATTERN_U8(addr))) return true; atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]); @@ -323,11 +316,27 @@ static inline bool check_canary_byte(u8 *addr) return false; } -/* __always_inline this to ensure we won't do an indirect call to fn. */ -static __always_inline void for_each_canary(const struct kfence_metadata *meta, bool (*fn)(u8 *)) +static inline void set_canary(const struct kfence_metadata *meta) { const unsigned long pageaddr = ALIGN_DOWN(meta->addr, PAGE_SIZE); - unsigned long addr; + unsigned long addr = pageaddr; + + /* + * The canary may be written to part of the object memory, but it does + * not affect it. The user should initialize the object before using it. + */ + for (; addr < meta->addr; addr += sizeof(u64)) + *((u64 *)addr) = KFENCE_CANARY_PATTERN_U64; + + addr = ALIGN_DOWN(meta->addr + meta->size, sizeof(u64)); + for (; addr - pageaddr < PAGE_SIZE; addr += sizeof(u64)) + *((u64 *)addr) = KFENCE_CANARY_PATTERN_U64; +} + +static inline void check_canary(const struct kfence_metadata *meta) +{ + const unsigned long pageaddr = ALIGN_DOWN(meta->addr, PAGE_SIZE); + unsigned long addr = pageaddr; /* * We'll iterate over each canary byte per-side until fn() returns @@ -339,14 +348,38 @@ static __always_inline void for_each_canary(const struct kfence_metadata *meta, */ /* Apply to left of object. */ - for (addr = pageaddr; addr < meta->addr; addr++) { - if (!fn((u8 *)addr)) + for (; meta->addr - addr >= sizeof(u64); addr += sizeof(u64)) { + if (unlikely(*((u64 *)addr) != KFENCE_CANARY_PATTERN_U64)) break; } - /* Apply to right of object. */ - for (addr = meta->addr + meta->size; addr < pageaddr + PAGE_SIZE; addr++) { - if (!fn((u8 *)addr)) + /* + * If the canary is damaged in a certain 64 bytes, or the canay memory + * cannot be completely covered by multiple consecutive 64 bytes, it + * needs to be checked one by one. + */ + for (; addr < meta->addr; addr++) { + if (unlikely(!check_canary_byte((u8 *)addr))) + break; + } + + /* + * Apply to right of object. + * For easier implementation, check from high address to low address. + */ + addr = pageaddr + PAGE_SIZE - sizeof(u64); + for (; addr >= meta->addr + meta->size ; addr -= sizeof(u64)) { + if (unlikely(*((u64 *)addr) != KFENCE_CANARY_PATTERN_U64)) + break; + } + + /* + * Same as above, checking byte by byte, but here is the reverse of + * the above. + */ + addr = addr + sizeof(u64) - 1; + for (; addr >= meta->addr + meta->size; addr--) { + if (unlikely(!check_canary_byte((u8 *)addr))) break; } } @@ -434,7 +467,7 @@ static void *kfence_guarded_alloc(struct kmem_cache *cache, size_t size, gfp_t g #endif /* Memory initialization. */ - for_each_canary(meta, set_canary_byte); + set_canary(meta); /* * We check slab_want_init_on_alloc() ourselves, rather than letting @@ -495,7 +528,7 @@ static void kfence_guarded_free(void *addr, struct kfence_metadata *meta, bool z alloc_covered_add(meta->alloc_stack_hash, -1); /* Check canary bytes for memory corruption. */ - for_each_canary(meta, check_canary_byte); + check_canary(meta); /* * Clear memory if init-on-free is set. While we protect the page, the @@ -751,7 +784,7 @@ static void kfence_check_all_canary(void) struct kfence_metadata *meta = &kfence_metadata[i]; if (meta->state == KFENCE_OBJECT_ALLOCATED) - for_each_canary(meta, check_canary_byte); + check_canary(meta); } } diff --git a/mm/kfence/kfence.h b/mm/kfence/kfence.h index 600f2e2431d6..2aafc46a4aaf 100644 --- a/mm/kfence/kfence.h +++ b/mm/kfence/kfence.h @@ -21,7 +21,15 @@ * lower 3 bits of the address, to detect memory corruptions with higher * probability, where similar constants are used. */ -#define KFENCE_CANARY_PATTERN(addr) ((u8)0xaa ^ (u8)((unsigned long)(addr) & 0x7)) +#define KFENCE_CANARY_PATTERN_U8(addr) ((u8)0xaa ^ (u8)((unsigned long)(addr) & 0x7)) + +/* + * Define a continuous 8-byte canary starting from a multiple of 8. The canary + * of each byte is only related to the lowest three bits of its address, so the + * canary of every 8 bytes is the same. 64-bit memory can be filled and checked + * at a time instead of byte by byte to improve performance. + */ +#define KFENCE_CANARY_PATTERN_U64 ((u64)0xaaaaaaaaaaaaaaaa ^ (u64)(0x0706050403020100)) /* Maximum stack depth for reports. */ #define KFENCE_STACK_DEPTH 64 diff --git a/mm/kfence/report.c b/mm/kfence/report.c index 60205f1257ef..197430a5be4a 100644 --- a/mm/kfence/report.c +++ b/mm/kfence/report.c @@ -168,7 +168,7 @@ static void print_diff_canary(unsigned long address, size_t bytes_to_show, pr_cont("["); for (cur = (const u8 *)address; cur < end; cur++) { - if (*cur == KFENCE_CANARY_PATTERN(cur)) + if (*cur == KFENCE_CANARY_PATTERN_U8(cur)) pr_cont(" ."); else if (no_hash_pointers) pr_cont(" 0x%02x", *cur);