From patchwork Thu Jan 15 08:53:08 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gui Hecheng X-Patchwork-Id: 5637721 Return-Path: X-Original-To: patchwork-linux-btrfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 9E3499F860 for ; Thu, 15 Jan 2015 08:57:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 806AC20160 for ; Thu, 15 Jan 2015 08:56:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 516A92015A for ; Thu, 15 Jan 2015 08:56:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751550AbbAOI4n (ORCPT ); Thu, 15 Jan 2015 03:56:43 -0500 Received: from cn.fujitsu.com ([59.151.112.132]:60030 "EHLO heian.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1751175AbbAOI4m (ORCPT ); Thu, 15 Jan 2015 03:56:42 -0500 X-IronPort-AV: E=Sophos;i="5.04,848,1406563200"; d="scan'208";a="56102946" Received: from unknown (HELO edo.cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 15 Jan 2015 16:53:10 +0800 Received: from G08CNEXCHPEKD02.g08.fujitsu.local (localhost.localdomain [127.0.0.1]) by edo.cn.fujitsu.com (8.14.3/8.13.1) with ESMTP id t0F8u2cU030005; Thu, 15 Jan 2015 16:56:02 +0800 Received: from localhost.localdomain (10.167.226.111) by G08CNEXCHPEKD02.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.181.6; Thu, 15 Jan 2015 16:56:37 +0800 From: Gui Hecheng To: CC: , , Gui Hecheng Subject: [PATCH 2/2 RESEND] btrfs: introduce shrinker for rb_tree that keeps valid btrfs_devices Date: Thu, 15 Jan 2015 16:53:08 +0800 Message-ID: <1421311988-11976-2-git-send-email-guihc.fnst@cn.fujitsu.com> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1421311988-11976-1-git-send-email-guihc.fnst@cn.fujitsu.com> References: <1421311988-11976-1-git-send-email-guihc.fnst@cn.fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.226.111] Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The following patch: btrfs: remove empty fs_devices to prevent memory runout introduces @valid_dev_root aiming at recording @btrfs_device objects that have corresponding block devices with btrfs. But if a block device is broken or unplugged, no one tells the @valid_dev_root to cleanup the "dead" objects. To recycle the memory occuppied by those "dead"s, we could rely on the shrinker. The shrinker's scan function will traverse the @valid_dev_root and trys to open the devices one by one, if it fails or encounters a non-btrfs it will remove the "dead" @btrfs_device. A special case to deal with is that a block device is unplugged and replugged, then it appears with a new @bdev->bd_dev as devnum. In this case, we should remove the older since we should have a new one for that block device already. Signed-off-by: Gui Hecheng --- fs/btrfs/super.c | 10 ++++++++ fs/btrfs/volumes.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/btrfs/volumes.h | 4 +++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 001cba5..022381e 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2017,6 +2017,12 @@ static struct miscdevice btrfs_misc = { .fops = &btrfs_ctl_fops }; +static struct shrinker btrfs_valid_dev_shrinker = { + .scan_objects = btrfs_valid_dev_scan, + .count_objects = btrfs_valid_dev_count, + .seeks = DEFAULT_SEEKS, +}; + MODULE_ALIAS_MISCDEV(BTRFS_MINOR); MODULE_ALIAS("devname:btrfs-control"); @@ -2130,6 +2136,8 @@ static int __init init_btrfs_fs(void) btrfs_init_lockdep(); + register_shrinker(&btrfs_valid_dev_shrinker); + btrfs_print_info(); err = btrfs_run_sanity_tests(); @@ -2143,6 +2151,7 @@ static int __init init_btrfs_fs(void) return 0; unregister_ioctl: + unregister_shrinker(&btrfs_valid_dev_shrinker); btrfs_interface_exit(); free_end_io_wq: btrfs_end_io_wq_exit(); @@ -2183,6 +2192,7 @@ static void __exit exit_btrfs_fs(void) btrfs_interface_exit(); btrfs_end_io_wq_exit(); unregister_filesystem(&btrfs_fs_type); + unregister_shrinker(&btrfs_valid_dev_shrinker); btrfs_exit_sysfs(); btrfs_cleanup_valid_dev_root(); btrfs_cleanup_fs_uuids(); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 228a7e0..5462557 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -54,6 +54,7 @@ static void btrfs_dev_stat_print_on_load(struct btrfs_device *device); DEFINE_MUTEX(uuid_mutex); static LIST_HEAD(fs_uuids); static struct rb_root valid_dev_root = RB_ROOT; +static atomic_long_t unopened_dev_count = ATOMIC_LONG_INIT(0); static struct btrfs_device *insert_valid_device(struct btrfs_device *new_dev) { @@ -130,6 +131,8 @@ static void free_invalid_device(struct btrfs_device *invalid_dev) { struct btrfs_fs_devices *old_fs; + atomic_long_dec(&unopened_dev_count); + old_fs = invalid_dev->fs_devices; mutex_lock(&old_fs->device_list_mutex); list_del(&invalid_dev->dev_list); @@ -605,6 +608,7 @@ static noinline int device_list_add(const char *path, list_add_rcu(&device->dev_list, &fs_devices->devices); fs_devices->num_devices++; mutex_unlock(&fs_devices->device_list_mutex); + atomic_long_inc(&unopened_dev_count); ret = 1; device->fs_devices = fs_devices; @@ -778,6 +782,7 @@ again: blkdev_put(device->bdev, device->mode); device->bdev = NULL; fs_devices->open_devices--; + atomic_long_inc(&unopened_dev_count); } if (device->writeable) { list_del_init(&device->dev_alloc_list); @@ -840,8 +845,10 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) struct btrfs_device *new_device; struct rcu_string *name; - if (device->bdev) + if (device->bdev) { fs_devices->open_devices--; + atomic_long_inc(&unopened_dev_count); + } if (device->writeable && device->devid != BTRFS_DEV_REPLACE_DEVID) { @@ -971,6 +978,7 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices, fs_devices->rotating = 1; fs_devices->open_devices++; + atomic_long_dec(&unopened_dev_count); if (device->writeable && device->devid != BTRFS_DEV_REPLACE_DEVID) { fs_devices->rw_devices++; @@ -6848,3 +6856,67 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, } unlock_chunks(root); } + +static unsigned long shrink_valid_dev_root(void) +{ + struct rb_node *n; + struct btrfs_device *device; + struct block_device *bdev; + struct buffer_head *bh; + unsigned long freed = 0; + unsigned long possible_deads; + int ret = 0; + dev_t cur_devnum; + + mutex_lock(&uuid_mutex); + + possible_deads = atomic_long_read(&unopened_dev_count); + if (!possible_deads) + goto out; + + for (n = rb_first(&valid_dev_root); n ; n = rb_next(n)) { + device = rb_entry(n, struct btrfs_device, rb_node); + + if (device->bdev) + continue; + if (!device->name) + continue; + + ret = btrfs_get_bdev_and_sb(device->name->str, FMODE_READ, + NULL, 0, &bdev, &bh); + /* can't open as btrfs, not valid, drop it */ + if (ret) + goto shrink; + + cur_devnum = bdev->bd_dev; + + brelse(bh); + blkdev_put(bdev, FMODE_READ); + + if (device->devnum == cur_devnum) + continue; + /* bdev->bd_dev changed, not valid, drop it */ + +shrink: + rb_erase(n, &valid_dev_root); + free_invalid_device(device); + + freed++; + } + +out: + mutex_unlock(&uuid_mutex); + return freed; +} + +unsigned long btrfs_valid_dev_scan(struct shrinker *shrink, + struct shrink_control *sc) +{ + return shrink_valid_dev_root(); +} + +unsigned long btrfs_valid_dev_count(struct shrinker *shrink, + struct shrink_control *sc) +{ + return atomic_long_read(&unopened_dev_count); +} diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 7f5c7ea..49f4fff 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -432,6 +432,10 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u8 *uuid); int btrfs_rm_device(struct btrfs_root *root, char *device_path); void btrfs_cleanup_valid_dev_root(void); +unsigned long btrfs_valid_dev_scan(struct shrinker *shrink, + struct shrink_control *sc); +unsigned long btrfs_valid_dev_count(struct shrinker *shrink, + struct shrink_control *sc); void btrfs_cleanup_fs_uuids(void); int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len); int btrfs_grow_device(struct btrfs_trans_handle *trans,