Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3472886imu; Sun, 11 Nov 2018 15:54:46 -0800 (PST) X-Google-Smtp-Source: AJdET5eRTP3zEUKQnvfBzTkGb6Tn3D4yZBvrw1oMn7pxpijy498FbhET0m0Mw1WeH9uMWQCSat8w X-Received: by 2002:a62:6bc8:: with SMTP id g191-v6mr18617290pfc.134.1541980486330; Sun, 11 Nov 2018 15:54:46 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1541980486; cv=none; d=google.com; s=arc-20160816; b=yutJ3nIzzqlhtMVaWdUnW0QfckccCK0N87eBdl2PwfYu1nPnyXVGT2rOTx8ox4UBlo I27T/O414xaRxfg1SGlNJ5liUzfKyFBUfu0pNAlu3gCnZRAOU8ja8PpeAt2QGWAWAMNU 61SymB++cCkf5GOdUmIg+XJc0/Gxm0TlwFWjAbhlasXcNuf2S2vvRJ/nDA6H6I2A2OR0 F3pgjgXYfI4qwJ82Pj1oiWWd8VrH5DF7Iz7Gume6BgVmcFYCmgyGluKSfpA463XgpHml pu+XxXPgtrnkExSbBvt3u+oHJr60HNM5mhipg+rnKf8T9foP53VEDq54sdt2c/nDkhMH GAtw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=c662BEQqG5gzCrAv0CdGwjWEtpy3kY2E+dlm2rvpgqQ=; b=BzT69py/PdM/e9X+U45TRZWGIkLtSJsWe2AfvypfVX+KAFpkTcHEm336MTmCssiSSY PYqx+yhbE0xl7FBULdDqj22B6Bw7kMgdG3QyDZjOD9pEj5QgoMCtzVMGj7mjPIJYvpU9 WH/vJyIsShQgQ5RrrO266RgLe8UI8AFkDtOepMekDQz32To2iLpetnBXTr2QpDG3PL0V GsYSBMGOvFgw1soFzw/y4rx5zJOvieeIYOyotsrMEkzeYSqs7ke85AOiy/i0qCCxWAjD 8INGlI9rZu4jFi6uIRQm4UBLReVx0qgVdnaSz5tM7gDtdEx1lzsMo/SgL9Snj23DcLa+ kV/A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=vjRGqg9Q; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id r4-v6si16916892pfb.43.2018.11.11.15.54.31; Sun, 11 Nov 2018 15:54:46 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=vjRGqg9Q; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1733136AbeKLJo0 (ORCPT + 99 others); Mon, 12 Nov 2018 04:44:26 -0500 Received: from mail.kernel.org ([198.145.29.99]:37256 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732747AbeKLISX (ORCPT ); Mon, 12 Nov 2018 03:18:23 -0500 Received: from localhost (unknown [206.108.79.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 305B7215EA; Sun, 11 Nov 2018 22:28:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1541975307; bh=TIBLo5zqbhOMDjOhwb+EfcbiB/1SpVxjP6m81d18leI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vjRGqg9Qhg3/qAZm+Cru4FxB9LqacnkhQNYOHaXqMWKmgPv2NOiDBtvaCFgIhzNG6 IWPvQiS4EuCFy9Tn8KcE/7FaYjALdgtn4dU1+tpUOfkd9RbJFJv0AuqUrYN5LIjVri /xBdPOU35QCOmsot+JEkn3LGHkoWONP48farG7EQ= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Damien Le Moal , Mike Snitzer Subject: [PATCH 4.19 277/361] dm zoned: fix various dmz_get_mblock() issues Date: Sun, 11 Nov 2018 14:20:24 -0800 Message-Id: <20181111221655.517803068@linuxfoundation.org> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181111221619.915519183@linuxfoundation.org> References: <20181111221619.915519183@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.19-stable review patch. If anyone has any objections, please let me know. ------------------ From: Damien Le Moal commit 3d4e738311327bc4ba1d55fbe2f1da3de4a475f9 upstream. dmz_fetch_mblock() called from dmz_get_mblock() has a race since the allocation of the new metadata block descriptor and its insertion in the cache rbtree with the READING state is not atomic. Two different contexts requesting the same block may end up each adding two different descriptors of the same block to the cache. Another problem for this function is that the BIO for processing the block read is allocated after the metadata block descriptor is inserted in the cache rbtree. If the BIO allocation fails, the metadata block descriptor is freed without first being removed from the rbtree. Fix the first problem by checking again if the requested block is not in the cache right before inserting the newly allocated descriptor, atomically under the mblk_lock spinlock. The second problem is fixed by simply allocating the BIO before inserting the new block in the cache. Finally, since dmz_fetch_mblock() also increments a block reference counter, rename the function to dmz_get_mblock_slow(). To be symmetric and clear, also rename dmz_lookup_mblock() to dmz_get_mblock_fast() and increment the block reference counter directly in that function rather than in dmz_get_mblock(). Fixes: 3b1a94c88b79 ("dm zoned: drive-managed zoned block device target") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-zoned-metadata.c | 66 ++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -339,10 +339,11 @@ static void dmz_insert_mblock(struct dmz } /* - * Lookup a metadata block in the rbtree. + * Lookup a metadata block in the rbtree. If the block is found, increment + * its reference count. */ -static struct dmz_mblock *dmz_lookup_mblock(struct dmz_metadata *zmd, - sector_t mblk_no) +static struct dmz_mblock *dmz_get_mblock_fast(struct dmz_metadata *zmd, + sector_t mblk_no) { struct rb_root *root = &zmd->mblk_rbtree; struct rb_node *node = root->rb_node; @@ -350,8 +351,17 @@ static struct dmz_mblock *dmz_lookup_mbl while (node) { mblk = container_of(node, struct dmz_mblock, node); - if (mblk->no == mblk_no) + if (mblk->no == mblk_no) { + /* + * If this is the first reference to the block, + * remove it from the LRU list. + */ + mblk->ref++; + if (mblk->ref == 1 && + !test_bit(DMZ_META_DIRTY, &mblk->state)) + list_del_init(&mblk->link); return mblk; + } node = (mblk->no < mblk_no) ? node->rb_left : node->rb_right; } @@ -382,32 +392,47 @@ static void dmz_mblock_bio_end_io(struct } /* - * Read a metadata block from disk. + * Read an uncached metadata block from disk and add it to the cache. */ -static struct dmz_mblock *dmz_fetch_mblock(struct dmz_metadata *zmd, - sector_t mblk_no) +static struct dmz_mblock *dmz_get_mblock_slow(struct dmz_metadata *zmd, + sector_t mblk_no) { - struct dmz_mblock *mblk; + struct dmz_mblock *mblk, *m; sector_t block = zmd->sb[zmd->mblk_primary].block + mblk_no; struct bio *bio; - /* Get block and insert it */ + /* Get a new block and a BIO to read it */ mblk = dmz_alloc_mblock(zmd, mblk_no); if (!mblk) return NULL; - spin_lock(&zmd->mblk_lock); - mblk->ref++; - set_bit(DMZ_META_READING, &mblk->state); - dmz_insert_mblock(zmd, mblk); - spin_unlock(&zmd->mblk_lock); - bio = bio_alloc(GFP_NOIO, 1); if (!bio) { dmz_free_mblock(zmd, mblk); return NULL; } + spin_lock(&zmd->mblk_lock); + + /* + * Make sure that another context did not start reading + * the block already. + */ + m = dmz_get_mblock_fast(zmd, mblk_no); + if (m) { + spin_unlock(&zmd->mblk_lock); + dmz_free_mblock(zmd, mblk); + bio_put(bio); + return m; + } + + mblk->ref++; + set_bit(DMZ_META_READING, &mblk->state); + dmz_insert_mblock(zmd, mblk); + + spin_unlock(&zmd->mblk_lock); + + /* Submit read BIO */ bio->bi_iter.bi_sector = dmz_blk2sect(block); bio_set_dev(bio, zmd->dev->bdev); bio->bi_private = mblk; @@ -509,19 +534,12 @@ static struct dmz_mblock *dmz_get_mblock /* Check rbtree */ spin_lock(&zmd->mblk_lock); - mblk = dmz_lookup_mblock(zmd, mblk_no); - if (mblk) { - /* Cache hit: remove block from LRU list */ - mblk->ref++; - if (mblk->ref == 1 && - !test_bit(DMZ_META_DIRTY, &mblk->state)) - list_del_init(&mblk->link); - } + mblk = dmz_get_mblock_fast(zmd, mblk_no); spin_unlock(&zmd->mblk_lock); if (!mblk) { /* Cache miss: read the block from disk */ - mblk = dmz_fetch_mblock(zmd, mblk_no); + mblk = dmz_get_mblock_slow(zmd, mblk_no); if (!mblk) return ERR_PTR(-ENOMEM); }