From patchwork Wed Jul 14 18:47:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josef Bacik X-Patchwork-Id: 478349 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 831A7C11F67 for ; Wed, 14 Jul 2021 18:47:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6A8D6613C8 for ; Wed, 14 Jul 2021 18:47:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240071AbhGNSuW (ORCPT ); Wed, 14 Jul 2021 14:50:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36236 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239819AbhGNSuW (ORCPT ); Wed, 14 Jul 2021 14:50:22 -0400 Received: from mail-qv1-xf35.google.com (mail-qv1-xf35.google.com [IPv6:2607:f8b0:4864:20::f35]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A142C061764 for ; Wed, 14 Jul 2021 11:47:29 -0700 (PDT) Received: by mail-qv1-xf35.google.com with SMTP id gh6so1541437qvb.3 for ; Wed, 14 Jul 2021 11:47:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=toxicpanda-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NEIgkiTj4t3jttXcyN/5zz8YqcqMXxtRu1nEAsbHwxE=; b=p+t/WKtRaf1tvAClAUr+ZmzhvTPUK1x9+pxpPkUkK9ebhmfVYlIezYHVv5zK8FI0EC dOS9cdzvmSuG/oXQ7jwhUdwwolA2ZtTOQqP115r4SFQR8iaZUuNnF2phPKQTzCoKEqhs B63TppqB5ueS98zoW2li+i9GcNO+iokQJpwOKXm/lh0ruStga2kj1s3UONzCxtt//8lT o7lOzjkzs+i1M9TkP4ZfKWbmzokra4mxYfYEkM9VMSIH8OEPzN+n2/GmyilJ1F+3Ed+V OosJ8lNyw44JzjXumqin6jJvOJqoFGfMpNJPTjiCycK7kPks4XB9N50Wdk/Cu03ga2Qq uFcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NEIgkiTj4t3jttXcyN/5zz8YqcqMXxtRu1nEAsbHwxE=; b=jSosJHIhiRAqCOkZufieNksq8rpntuutbTW+kX01l2LjiS+AXSc94Wsqg70fQ+dB9Y DdDp29jL+Ho5+srfkdqWhYEDpXUP1sCHYQuDso43E5FJVi//UxX3RT87Jv8vWHGGR2ZZ u0JfHxtY3oWviFQuwz9AdtdcRufbH/mrKbumUJWD4jeiIH+Z0/gkVJj4IG9yMIawIyBY lyAgi9ba5XnUi2FOWxCV3JTMNjWwqEkFCzLLD9VG1hFn8Za8HK8Xj/70gaTJlsLPdX5N g0HS9t+0pKTQ+vDJ/PhTySj2YeTknWt7htmVnpDYLqBp11NDZObVjNurY9Q9Cn+Db7KZ hLOA== X-Gm-Message-State: AOAM530KlfcknRq+3oBhtOu+wwC0EcQDljQ0UXu9nvaan3hH0iqK+8ri pGX0DBiA9/7MwMiawvtzMzm0+A== X-Google-Smtp-Source: ABdhPJxZG8yTJlXAWrF55wKnikwJzt6/9WAyS7O1ymLPDGQRhmqUeoOYUjprybts1WfL3aPcNUKmsw== X-Received: by 2002:a05:6214:d08:: with SMTP id 8mr12484989qvh.32.1626288448684; Wed, 14 Jul 2021 11:47:28 -0700 (PDT) Received: from localhost (cpe-174-109-172-136.nc.res.rr.com. [174.109.172.136]) by smtp.gmail.com with ESMTPSA id 3sm1077237qtz.5.2021.07.14.11.47.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Jul 2021 11:47:28 -0700 (PDT) From: Josef Bacik To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org, Nikolay Borisov Subject: [PATCH v3 1/9] btrfs: wake up async_delalloc_pages waiters after submit Date: Wed, 14 Jul 2021 14:47:17 -0400 Message-Id: X-Mailer: git-send-email 2.26.3 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: stable@vger.kernel.org We use the async_delalloc_pages mechanism to make sure that we've completed our async work before trying to continue our delalloc flushing. The reason for this is we need to see any ordered extents that were created by our delalloc flushing. However we're waking up before we do the submit work, which is before we create the ordered extents. This is a pretty wide race window where we could potentially think there are no ordered extents and thus exit shrink_delalloc prematurely. Fix this by waking us up after we've done the work to create ordered extents. cc: stable@vger.kernel.org Signed-off-by: Josef Bacik Reviewed-by: Nikolay Borisov --- fs/btrfs/inode.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e6eb20987351..0f42864d5dd4 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1290,11 +1290,6 @@ static noinline void async_cow_submit(struct btrfs_work *work) nr_pages = (async_chunk->end - async_chunk->start + PAGE_SIZE) >> PAGE_SHIFT; - /* atomic_sub_return implies a barrier */ - if (atomic_sub_return(nr_pages, &fs_info->async_delalloc_pages) < - 5 * SZ_1M) - cond_wake_up_nomb(&fs_info->async_submit_wait); - /* * ->inode could be NULL if async_chunk_start has failed to compress, * in which case we don't have anything to submit, yet we need to @@ -1303,6 +1298,11 @@ static noinline void async_cow_submit(struct btrfs_work *work) */ if (async_chunk->inode) submit_compressed_extents(async_chunk); + + /* atomic_sub_return implies a barrier */ + if (atomic_sub_return(nr_pages, &fs_info->async_delalloc_pages) < + 5 * SZ_1M) + cond_wake_up_nomb(&fs_info->async_submit_wait); } static noinline void async_cow_free(struct btrfs_work *work) From patchwork Wed Jul 14 18:47:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josef Bacik X-Patchwork-Id: 477376 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1FBCAC07E9C for ; Wed, 14 Jul 2021 18:47:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0611C613C9 for ; Wed, 14 Jul 2021 18:47:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240091AbhGNSu0 (ORCPT ); Wed, 14 Jul 2021 14:50:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240084AbhGNSu0 (ORCPT ); Wed, 14 Jul 2021 14:50:26 -0400 Received: from mail-qk1-x732.google.com (mail-qk1-x732.google.com [IPv6:2607:f8b0:4864:20::732]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E7609C061766 for ; Wed, 14 Jul 2021 11:47:33 -0700 (PDT) Received: by mail-qk1-x732.google.com with SMTP id j184so2616659qkd.6 for ; Wed, 14 Jul 2021 11:47:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=toxicpanda-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=nfMkZOCop8rCTuWPUwcOk31VcZ9PT/ircxMz2xSNZAU=; b=IwiHhNV3l/UEgD9whti5lVVACGaczf1jo57Ss1JEf3gSmq0fhgqReJcT3LMVOP1a9L lumz/2SOxWy7+pAU7AVONMuRzgBkU+qpEpFp336RHsjA705cFqCsObOJxJNG4WORfCzI Y9Jd3+ZP7rCivrNHg0SW7cgYuijke9k5I01AqBITHKHOU9WW0ZwgZ71XRZA1a1qq2okP Q+UUjI9aLsqQeM1wcafqZMHApQS1YxcqZiq9qpoVMWjijKYcHhRtokCWppuSpTMe4Xwb v1Juv5gZdvWte8tjA2dgMMwlMdQOcSTlptb9sSE0Td2TkI0DrRq+0xsgAJ8U2JUAVTAV rdjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=nfMkZOCop8rCTuWPUwcOk31VcZ9PT/ircxMz2xSNZAU=; b=IAbXxLmrNv9YDtmkxgwai4G/jdEm+W3OdeT+xQlGVjlu4bnrD4qtNiVyZiePzvDdSz PL0u9lEFNV+3Yt6LOcYMucInKD087QyztrxNTzMe9/zLCIJaq1xHs4VyEFzYwuelgLfP MURzuimO8so/kP+M961No++/yqTQK/kRATkGiyMKA8Ppq/sTxvFLztwbzqG3p0xdcS+z bLgH0LPx4xGOQUqfJ0Pl5c+kxe1AhdUC4qJKkf5pWs1dKVN4kaaZ5aJR9ve3WfYEXi8I PLX43Kt4I6okJ5fypdMqXzHcjr/mBqNkRsJy0JMYkkTAbcfODA35uvr9Z46hJJkWWGcP ClVw== X-Gm-Message-State: AOAM532m+dfht/ecOEtzOKsBnW/2emI9gghiwHWCIW5tG1XOiPo3vQni KhR5YZq9POqyTx7zgRx1b/iFlg== X-Google-Smtp-Source: ABdhPJyhvidQvC8DnXNrL68MUooA9iRV9+8wJnjGK5T+vZpH7D5dEeEfPxYsoYVtQ73vues3H30gqQ== X-Received: by 2002:a37:4685:: with SMTP id t127mr11397515qka.384.1626288452995; Wed, 14 Jul 2021 11:47:32 -0700 (PDT) Received: from localhost (cpe-174-109-172-136.nc.res.rr.com. [174.109.172.136]) by smtp.gmail.com with ESMTPSA id k24sm1397062qkk.25.2021.07.14.11.47.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Jul 2021 11:47:32 -0700 (PDT) From: Josef Bacik To: linux-btrfs@vger.kernel.org, kernel-team@fb.com, linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org Subject: [PATCH v3 4/9] btrfs: handle shrink_delalloc pages calculation differently Date: Wed, 14 Jul 2021 14:47:20 -0400 Message-Id: <725c04f4a0f9d495e0ba56b7fe1276c6bc4bc9a1.1626288241.git.josef@toxicpanda.com> X-Mailer: git-send-email 2.26.3 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: stable@vger.kernel.org We have been hitting some early ENOSPC issues in production with more recent kernels, and I tracked it down to us simply not flushing delalloc as aggressively as we should be. With tracing I was seeing us failing all tickets with all of the block rsvs at or around 0, with very little pinned space, but still around 120MiB of outstanding bytes_may_used. Upon further investigation I saw that we were flushing around 14 pages per shrink call for delalloc, despite having around 2GiB of delalloc outstanding. Consider the example of a 8 way machine, all CPUs trying to create a file in parallel, which at the time of this commit requires 5 items to do. Assuming a 16k leaf size, we have 10MiB of total metadata reclaim size waiting on reservations. Now assume we have 128MiB of delalloc outstanding. With our current math we would set items to 20, and then set to_reclaim to 20 * 256k, or 5MiB. Assuming that we went through this loop all 3 times, for both FLUSH_DELALLOC and FLUSH_DELALLOC_WAIT, and then did the full loop twice, we'd only flush 60MiB of the 128MiB delalloc space. This could leave a fair bit of delalloc reservations still hanging around by the time we go to ENOSPC out all the remaining tickets. Fix this two ways. First, change the calculations to be a fraction of the total delalloc bytes on the system. Prior to this change we were calculating based on dirty inodes so our math made more sense, now it's just completely unrelated to what we're actually doing. Second add a FLUSH_DELALLOC_FULL state, that we hold off until we've gone through the flush states at least once. This will empty the system of all delalloc so we're sure to be truly out of space when we start failing tickets. I'm tagging stable 5.10 and forward, because this is where we started using the page stuff heavily again. This affects earlier kernel versions as well, but would be a pain to backport to them as the flushing mechanisms aren't the same. CC: stable@vger.kernel.org # 5.10+ Signed-off-by: Josef Bacik --- fs/btrfs/ctree.h | 9 ++++---- fs/btrfs/space-info.c | 40 +++++++++++++++++++++++++----------- include/trace/events/btrfs.h | 1 + 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index d7ef4d7d2c1a..232ff1a49ca6 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2783,10 +2783,11 @@ enum btrfs_flush_state { FLUSH_DELAYED_REFS = 4, FLUSH_DELALLOC = 5, FLUSH_DELALLOC_WAIT = 6, - ALLOC_CHUNK = 7, - ALLOC_CHUNK_FORCE = 8, - RUN_DELAYED_IPUTS = 9, - COMMIT_TRANS = 10, + FLUSH_DELALLOC_FULL = 7, + ALLOC_CHUNK = 8, + ALLOC_CHUNK_FORCE = 9, + RUN_DELAYED_IPUTS = 10, + COMMIT_TRANS = 11, }; int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index af161eb808a2..1a7e44dd0ba7 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -494,6 +494,11 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, long time_left; int loops; + delalloc_bytes = percpu_counter_sum_positive(&fs_info->delalloc_bytes); + ordered_bytes = percpu_counter_sum_positive(&fs_info->ordered_bytes); + if (delalloc_bytes == 0 && ordered_bytes == 0) + return; + /* Calc the number of the pages we need flush for space reservation */ if (to_reclaim == U64_MAX) { items = U64_MAX; @@ -501,22 +506,21 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, /* * to_reclaim is set to however much metadata we need to * reclaim, but reclaiming that much data doesn't really track - * exactly, so increase the amount to reclaim by 2x in order to - * make sure we're flushing enough delalloc to hopefully reclaim - * some metadata reservations. + * exactly. What we really want to do is reclaim full inode's + * worth of reservations, however that's not available to us + * here. We will take a fraction of the delalloc bytes for our + * flushing loops and hope for the best. Delalloc will expand + * the amount we write to cover an entire dirty extent, which + * will reclaim the metadata reservation for that range. If + * it's not enough subsequent flush stages will be more + * aggressive. */ + to_reclaim = max(to_reclaim, delalloc_bytes >> 3); items = calc_reclaim_items_nr(fs_info, to_reclaim) * 2; - to_reclaim = items * EXTENT_SIZE_PER_ITEM; } trans = (struct btrfs_trans_handle *)current->journal_info; - delalloc_bytes = percpu_counter_sum_positive( - &fs_info->delalloc_bytes); - ordered_bytes = percpu_counter_sum_positive(&fs_info->ordered_bytes); - if (delalloc_bytes == 0 && ordered_bytes == 0) - return; - /* * If we are doing more ordered than delalloc we need to just wait on * ordered extents, otherwise we'll waste time trying to flush delalloc @@ -596,8 +600,11 @@ static void flush_space(struct btrfs_fs_info *fs_info, break; case FLUSH_DELALLOC: case FLUSH_DELALLOC_WAIT: + case FLUSH_DELALLOC_FULL: + if (state == FLUSH_DELALLOC_FULL) + num_bytes = U64_MAX; shrink_delalloc(fs_info, space_info, num_bytes, - state == FLUSH_DELALLOC_WAIT, for_preempt); + state != FLUSH_DELALLOC, for_preempt); break; case FLUSH_DELAYED_REFS_NR: case FLUSH_DELAYED_REFS: @@ -907,6 +914,14 @@ static void btrfs_async_reclaim_metadata_space(struct work_struct *work) commit_cycles--; } + /* + * We do not want to empty the system of delalloc unless we're + * under heavy pressure, so allow one trip through the flushing + * logic before we start doing a FLUSH_DELALLOC_FULL. + */ + if (flush_state == FLUSH_DELALLOC_FULL && !commit_cycles) + flush_state++; + /* * We don't want to force a chunk allocation until we've tried * pretty hard to reclaim space. Think of the case where we @@ -1070,7 +1085,7 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work) * so if we now have space to allocate do the force chunk allocation. */ static const enum btrfs_flush_state data_flush_states[] = { - FLUSH_DELALLOC_WAIT, + FLUSH_DELALLOC_FULL, RUN_DELAYED_IPUTS, COMMIT_TRANS, ALLOC_CHUNK_FORCE, @@ -1159,6 +1174,7 @@ static const enum btrfs_flush_state evict_flush_states[] = { FLUSH_DELAYED_REFS, FLUSH_DELALLOC, FLUSH_DELALLOC_WAIT, + FLUSH_DELALLOC_FULL, ALLOC_CHUNK, COMMIT_TRANS, }; diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index d754c6ec1797..e21744a3cdb6 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -94,6 +94,7 @@ struct btrfs_space_info; EM( FLUSH_DELAYED_ITEMS, "FLUSH_DELAYED_ITEMS") \ EM( FLUSH_DELALLOC, "FLUSH_DELALLOC") \ EM( FLUSH_DELALLOC_WAIT, "FLUSH_DELALLOC_WAIT") \ + EM( FLUSH_DELALLOC_FULL, "FLUSH_DELALLOC_FULL") \ EM( FLUSH_DELAYED_REFS_NR, "FLUSH_DELAYED_REFS_NR") \ EM( FLUSH_DELAYED_REFS, "FLUSH_ELAYED_REFS") \ EM( ALLOC_CHUNK, "ALLOC_CHUNK") \