From patchwork Mon Sep 19 21:27:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Damien Le Moal X-Patchwork-Id: 9340571 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 89E6C607D0 for ; Mon, 19 Sep 2016 21:29:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 78BA829907 for ; Mon, 19 Sep 2016 21:29:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6D69729921; Mon, 19 Sep 2016 21:29:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 175E529907 for ; Mon, 19 Sep 2016 21:29:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932802AbcISV25 (ORCPT ); Mon, 19 Sep 2016 17:28:57 -0400 Received: from esa2.hgst.iphmx.com ([68.232.143.124]:20108 "EHLO esa2.hgst.iphmx.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932580AbcISV2y (ORCPT ); Mon, 19 Sep 2016 17:28:54 -0400 X-IronPort-AV: E=Sophos;i="5.30,363,1470672000"; d="scan'208";a="15969664" Received: from mail-bn3nam01lp0180.outbound.protection.outlook.com (HELO NAM01-BN3-obe.outbound.protection.outlook.com) ([216.32.180.180]) by ob1.hgst.iphmx.com with ESMTP; 20 Sep 2016 05:28:04 +0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sharedspace.onmicrosoft.com; s=selector1-hgst-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=fqJHrXTer8msuYPpKI8mgApZ5yldJQKWZilfiGxbndM=; b=WDlqzDIdZP5BwSjZ1UHi/1CmO6WttboHxzR98hGHwhDPpM0p2+QutrADxwf4ttmFvywXfTlQjCYHEty5+Zhm9kHh6iLXMORFvvJJ9Y8DNWHyh/XAVQ9QeyVHvdJwO6DuGdzkCWuH7kItYhmRCv5BaZ7+SfxV8N1jQv3LubNwBz4= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Damien.LeMoal@hgst.com; Received: from localhost.localdomain.localdomain (4.28.11.153) by BY2PR04MB1975.namprd04.prod.outlook.com (10.166.111.15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.629.8; Mon, 19 Sep 2016 21:27:57 +0000 From: Damien Le Moal To: , CC: , , , , Damien Le Moal Subject: [PATCH 5/9] block: Implement support for zoned block devices Date: Tue, 20 Sep 2016 06:27:30 +0900 Message-ID: <1474320454-5264-6-git-send-email-damien.lemoal@hgst.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1474320454-5264-1-git-send-email-damien.lemoal@hgst.com> References: <1474320454-5264-1-git-send-email-damien.lemoal@hgst.com> MIME-Version: 1.0 X-Originating-IP: [4.28.11.153] X-ClientProxiedBy: BY2PR12CA0038.namprd12.prod.outlook.com (10.160.121.48) To BY2PR04MB1975.namprd04.prod.outlook.com (10.166.111.15) X-MS-Office365-Filtering-Correlation-Id: e1ebab3e-26ba-4ec5-2ba6-08d3e0d3d4b0 X-Microsoft-Exchange-Diagnostics: 1; BY2PR04MB1975; 2:4mrz45n0rkz1bU2gRn1+SW+rpO2WLRlmCpzG6R0+ekPzLpyIHknEzIVZfui0twIY8/2L6HulU21Fuv4u6wElpBJfV1PtcTq7/9ggQO9w7e4KyYtdZDefSGdFlqDi1bI/K8VT9e2bPljzVemM0sdppuGbhYsk7/COfUQr3t01EDYpRM8u4I5QxhJkKXWp57NW; 3:GJ3x0tNTJwjgXK0TBl9SfWIysMnz/HYHCysaUz6pMj0MCOwmSF5n2UN39Kp25KbQ15evtBuI8G13jZmvUZBUbTNm5JqPyzZoFnM4o9wKCocj3eizF7h6kFGHJOC4ZU99; 25:U0mUhzk0dndsYlWCo/zUGhXeJ68xwJMyAcwqH9gpYbRKcxrlEwZTEpV/tbrpD/qBpot0hY+yT3lPVldV1hsC6lq/0bmi9YRKw4bKyc+sBvtRhVGe38XaPwWb3GfiygpjMArqwUyHwntNUN7JAJPV6KrK/UsYlf4xDulhaodcf1hOM2EcaaGjiIaus5H+mWeJe3joujvIt2oIpjvI+Xzb9Lm7QqlUjNRPOk9aLXbLIJ6HUHypxeFX94Dbc8yC1SJ9dxZYK5upwKRQQMQ5oqa22+gnRjcDxL+KSKSDzp3FMPaUenZGEJzosoaf1eJ3nuW3oA6hqzQzaBYPfQubm73E2n62uyDkMOjvaQ/5BVJt7RF6UcggTfR/N8sLcGpYbFuGfhDN7sfikX/FduD4H4aHNbnYSiNWoMUejHo8qm/Y7Z0= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BY2PR04MB1975; X-Microsoft-Exchange-Diagnostics: 1; BY2PR04MB1975; 31:pxhp4QHqVsNi9HHZWQilD0GgvB5mfl44Yf21ALxun5GKPPvsdvrXN1h4+9HYMhgNMiJPSByJE2NuwIKJEoPtj6Qbaau8WZZznDHSU545mE+hfdtlUyHpjLyr2acZlcZfxshtezKidvextbU1n3PqnPLin6S1yr+p+ewOOLKDVPpHgHDnm13mWoQcqNWKnmV5ZTSIiwWvRkaZunyn6ZcHE5wPYxQv5ZohFEG9o7qpDh0=; 20:3tNzDMFxU2Ojlg0cy6M3zdFjPETEPyblXKCpECay4sH7XtZaOHbPGsDyz2ZkaXpLerUchmX2I4CBBdGy713zS3J/x5OzNi2gjSgggqKcGFLUvAthBmGk7VWISsCZe4nuA6O9gxebMOnAOioYnBtNUueeHT/a/BWSrpWu9h+NopKZ0/a7EyWkDFeR87c0m2aPqqJcLjnoiMV1RDwkK1M+K9KfySQv12a5sRAfeJvLIUgmGaISIEZf1kywColAxekTRF9BFG/aoZSW53kw6fxX1zB0YMopY7Dg+DQUKm+DTpjZoaoplCl+ITvfkrbdz2NI08lnKJSTKHxDYoDw7XR+KgLA2qmFkexBZRj4H2XCTQUvfFEeT1DyOJPpD4bapOqZvC4YPYBnOLs1qHi5OxRu3Z4uV0N7I8eaDHyU634+ck5e95dg3+WOajcu9f95eYr2D1sbLOPsU9EmNcxCNu2uaMs5nw4C5F+lKjCaXdqHmmsB+KlL1lSpkapMNiJZq1Fo X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(79135771888625); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040176)(601004)(2401047)(8121501046)(5005006)(3002001)(10201501046)(6055026); SRVR:BY2PR04MB1975; BCL:0; PCL:0; RULEID:; SRVR:BY2PR04MB1975; X-Microsoft-Exchange-Diagnostics: 1; BY2PR04MB1975; 4:AkViAjH6LW3X6VaSCSnx5VxCz+m2hLSYzkq43HBVpwouLtWCXR0i1W0mJqRb/Ct7hUPMjoXnXU3zd3qURLZ/u5p2gLNAMAzMBPiPBz2v6aoCtIdvFSeJhzeCL8NeJPQpmE3z4U9Q2E3kp3OKOg17T+xjoHZbumpDP15GEvm7mFuLPkNrcwbkK4rPrKWzpuXUDp0L6uMMORWVTw+MvtK+hDpmYj3zQgLEvZU8TV2pIx5tR/ItZIeEYxLZ5R/f1yi2qCbCj5RoeMSKC8WsLKwqlG1nR7Cd7Ihz+N7TQYZlWP0EVJ7xRV9V9sGf0TpMRYeG23wARn6vinCyP8Pw3cKQ3KmE0ePwjdNHkAYwzswhSyozA1WmuS0jrV6qVLYON6JsoVei6OViu8+WgQKneFjgbgRUCA8wjMuqFxqlShUg8BmeQUOhIL/ibHrKQioAFB1vyd4SBcrmJTXtxuMI8xNSWQ== X-Forefront-PRVS: 0070A8666B X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(6069001)(7916002)(50944005)(199003)(54534003)(189002)(4001430100002)(305945005)(5660300001)(2950100001)(5003940100001)(86362001)(47776003)(33646002)(66066001)(97736004)(5001770100001)(50986999)(2906002)(229853001)(6116002)(4326007)(3846002)(76176999)(189998001)(101416001)(106356001)(105586002)(586003)(42186005)(77096005)(36756003)(50466002)(48376002)(19580405001)(81166006)(37156001)(92566002)(7736002)(8676002)(50226002)(68736007)(19580395003)(81156014)(7846002)(107886002); DIR:OUT; SFP:1102; SCL:1; SRVR:BY2PR04MB1975; H:localhost.localdomain.localdomain; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; Received-SPF: None (protection.outlook.com: hgst.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BY2PR04MB1975; 23:C5Cwzcq1n1xmholI9ItPtea5lJuW7yymSERuObIP0?= =?us-ascii?Q?tJ4mBLI1KXPnm01evup7DqQPrgfY8mmmb+SNExBhhYmmLdrpD1wYGZ6XJq7v?= =?us-ascii?Q?NiMwURbG3ez0ZvQ5hlQMKut28LzyLe3Un+lXOUTD/Bo2twZHv7VVr1kAff3Z?= =?us-ascii?Q?SGM/ANphNUOoWc9m3H32Vk/VKhDRxW9EAe2cph3lR7Dg8DzR2YJ0FEa8ZNbh?= =?us-ascii?Q?cKpy4AaID8eOUWN8/z2+lNONfIBdOFlwLer2VHyfavjdulUShGMsG8oxJqi9?= =?us-ascii?Q?q4eZSOVhii1iGGB62t5Z+MhJD2mYCXsiWqSACrqtdOkU40KjV3YbhcsUwxkH?= =?us-ascii?Q?PXdIPfVFffT6ffael9eVa5gTHBoXVBU49gOwa4XGivSFR+eXDEAlastyjXa7?= =?us-ascii?Q?FxQHHe6ENvF/EMmW1LFMsUzhLYc894qxdrpetT2fseAddzHDgPUerxOONO4I?= =?us-ascii?Q?GbCutorVe0NTxodJhJAzFY0O9Nd65P34MYuvRGJqPp8wrLXztRTQqAIv8p/c?= =?us-ascii?Q?Nz8ekeZ36Zb8l+ixQ8ru03UbLIH89y9O4g0BYZpvnel4k21XqGcbj/ChO5O3?= =?us-ascii?Q?8RDmuHR22hWsHdOB00RiYHJOIF09w/AIZ0O+Tpmagf+Na+lmnlSIgWQIe4ot?= =?us-ascii?Q?g5i+Y09mGRYidmIP/mV9xHX5WVu3XTx0qtjX5dM1gs+DnHNeqkNVJIj2RBEL?= =?us-ascii?Q?3roq/YaxyYMh7yMW4GHHA65VEKcMjuEjvhI9vpaQf+QNfbUiUbmolXGvyiWo?= =?us-ascii?Q?SzIaOzMxoA1dVAI+Yk+xWVP2wxHMP5PDmdz9TskihYfZ6xk5hlVS0yBdLYXS?= =?us-ascii?Q?1OmXcToiydSSctkXUR7jpLpwKDUqMpBU2cjlockk05BsUlP5TrZ+/0XxsAAz?= =?us-ascii?Q?6CDJzKPHJCypq+LzIssA8M+14dPFwdTXNQ7L/xo0X2zYQTEl+RgmJdNCOhig?= =?us-ascii?Q?Bd9vgZn9W67QP04h1uytvPsVfDzNFA9I7g3u9J8yrSzu2bh8XkA4iiyo0yZM?= =?us-ascii?Q?joGpDfJMQ5HwyO493K89XFj518Zi5q/vb6R/1XGk4D1BkiRgaHmvf3i/BGt4?= =?us-ascii?Q?Qq61Tu2ARizSL6Q9wnaggl1dcxdhJjzaHf1cvXKWamfe+LI33YtFouLXDDn0?= =?us-ascii?Q?NJ2jXcqH5NAsXGC8fe7Px1T17K6DXbHvsdP8g4R5u2R7wMlM8e/CKXLGx+XI?= =?us-ascii?Q?NznSvWtHmralTQ=3D?= X-Microsoft-Exchange-Diagnostics: 1; BY2PR04MB1975; 6:UPN7OLC1C7OGJJhfO1MFHDVniKlH2QWfPombKARxkxqnsjRX8X/XkSQMr/TbQLhHpRlEVNWv8n/mN7Mn5/HnzNd8OXfp2c2SyKNU5YtBMolsKDW//bmXxCWNZxuPNPMC6yd5p2QtuyGKK4lUk003pZf3H4WoRsTX7lzxNJ596CzkUJzAojFRytEBa6baGv4JDlyMmJY8mhWHkqHwdQDBcPjoeePa3agLPnPTtjyk8HAzvuTsAvtrc060mu8mvS6iilaNsGdWjd28TLBLQ+ngRiVYbrcnHhOzxWyWZklBCpCefZwRFE3rAO2buqoVSwCXakb2GBZd5LJUrSA8C2hxRg==; 5:gXfYKw1zRxvf7CL6bIG3Zs5Ekpz1QWlUOKBUu9eJsBLcUD4GGv2SExuaWSjKBjR+USPF1bPklhUst9oChmh7WEVHnCwJBZ8n60v4SItwrdsWNhfNTJzowXeERnb2+H7N37tK1dY5lI2X6M4LHWFSYQ==; 24:3jq8IKH8dkJGdX4ULcHeM5I/dY01emfqrv6MW5xm3IzwDLKCd7oueBM9Rh5Z2f+oA8yChHODNrURBFwC0ZNe5A/gSOXFJzMmcJCJSA6qgJU=; 7:6wIxGbRngyp1sGYYKuM2fFo3ZKrYEur9rpTH0C0n3wCJAC5J9OFzW6FPmvOvrCP+oRNth0Z1/KntMVjp7M+LgU3JUaBQEKguCwJxdPOwjL0hKe7cgRnw66nu02upApVMEfBjk8ua36ipwq1oh/vf6VtUDLsAt/pEdlqyWKoOAp9fvPBVr1/C+WlBdbXdM+4/mQMyOhVF91ZGYC4N7BY6FBzpJ0xp9uJQsQTvCPLwwQ3RblcN0auuLqMnBJL+ukm6+r6nlHxNh4EFtoUgVe7CRfzsHatroEdM/MFDs5SeUf7bu7vMz8nHwgafAKRv33lk SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; BY2PR04MB1975; 20:E5IvlDQO7Eg/ThGokCnM8vaq41C/EJS/sEkBj7jlTdx0IYXYLfWVSs+rWuw5spcazBNg2i2CKcBJoaZ53oESP2T0OzYqN5WruLcrljdsYzgge0XfxOU6DRNSyKgd84HBASXlgCHCaQkXv4giyte8S2om+Pw+Dt64Rs3vFlI/5CzWRojZnt1L7sFvlnUQRo2cpDUehA3M2ulmlfz7O1a4+PG7VFQS3CuAJN2L2Rt6++y/9L0HIeiMF4XG1Yx0Bjyo X-OriginatorOrg: hgst.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Sep 2016 21:27:57.5047 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2PR04MB1975 Sender: linux-block-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-block@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Hannes Reinecke Implement a RB-Tree holding a zoned block device zone information (struct blk_zone) and add support functions for maintaining the RB-Tree and manipulating zone structs. The block layer support does not differentiate between host-aware and host-managed devices. The different constraints for these different zone models are handled by the generic SCSI layer sd driver down the stack. Signed-off-by: Hannes Reinecke Changelog (Damien): * Changed struct blk_zone to be more compact (64B) * Changed zone locking to use bit_spin_lock in place of a regular spinlock * Request zone operations to the underlying block device driver through BIO operations with the operation codes REQ_OP_ZONE_*. Signed-off-by: Damien Le Moal --- block/Kconfig | 8 ++ block/Makefile | 1 + block/blk-core.c | 4 + block/blk-zoned.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 113 +++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 block/blk-zoned.c diff --git a/block/Kconfig b/block/Kconfig index 161491d..c3a18f0 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -88,6 +88,14 @@ config BLK_DEV_INTEGRITY T10/SCSI Data Integrity Field or the T13/ATA External Path Protection. If in doubt, say N. +config BLK_DEV_ZONED + bool "Zoned block device support" + ---help--- + Block layer zoned block device support. This option enables + support for ZAC/ZBC host-managed and host-aware zoned block devices. + + Say yes here if you have a ZAC or ZBC storage device. + config BLK_DEV_THROTTLING bool "Block layer bio throttling support" depends on BLK_CGROUP=y diff --git a/block/Makefile b/block/Makefile index 9eda232..aee67fa 100644 --- a/block/Makefile +++ b/block/Makefile @@ -22,4 +22,5 @@ obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o +obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned.o diff --git a/block/blk-core.c b/block/blk-core.c index 4a7f7ba..2c5d069d 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -590,6 +590,8 @@ void blk_cleanup_queue(struct request_queue *q) blk_mq_free_queue(q); percpu_ref_exit(&q->q_usage_counter); + blk_drop_zones(q); + spin_lock_irq(lock); if (q->queue_lock != &q->__queue_lock) q->queue_lock = &q->__queue_lock; @@ -728,6 +730,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) #endif INIT_DELAYED_WORK(&q->delay_work, blk_delay_work); + blk_init_zones(q); + kobject_init(&q->kobj, &blk_queue_ktype); mutex_init(&q->sysfs_lock); diff --git a/block/blk-zoned.c b/block/blk-zoned.c new file mode 100644 index 0000000..a107940 --- /dev/null +++ b/block/blk-zoned.c @@ -0,0 +1,338 @@ +/* + * Zoned block device handling + * + * Copyright (c) 2015, Hannes Reinecke + * Copyright (c) 2015, SUSE Linux GmbH + * + * Copyright (c) 2016, Damien Le Moal + * Copyright (c) 2016, Western Digital + */ + +#include +#include +#include +#include + +void blk_init_zones(struct request_queue *q) +{ + spin_lock_init(&q->zones_lock); + q->zones = RB_ROOT; +} + +/** + * blk_drop_zones - Empty a zoned device zone tree. + * @q: queue of the zoned device to operate on + * + * Free all zone descriptors added to the queue zone tree. + */ +void blk_drop_zones(struct request_queue *q) +{ + struct rb_root *root = &q->zones; + struct blk_zone *zone, *next; + + rbtree_postorder_for_each_entry_safe(zone, next, root, node) + kfree(zone); + q->zones = RB_ROOT; +} +EXPORT_SYMBOL_GPL(blk_drop_zones); + +/** + * blk_insert_zone - Add a new zone struct to the queue RB-tree. + * @q: queue of the zoned device to operate on + * @new_zone: The zone struct to add + * + * If @new_zone is not already added to the zone tree, add it. + * Otherwise, return the existing entry. + */ +struct blk_zone *blk_insert_zone(struct request_queue *q, + struct blk_zone *new_zone) +{ + struct rb_root *root = &q->zones; + struct rb_node **new = &(root->rb_node), *parent = NULL; + struct blk_zone *zone = NULL; + unsigned long flags; + + spin_lock_irqsave(&q->zones_lock, flags); + + /* Figure out where to put new node */ + while (*new) { + zone = container_of(*new, struct blk_zone, node); + parent = *new; + if (new_zone->start + new_zone->len <= zone->start) + new = &((*new)->rb_left); + else if (new_zone->start >= zone->start + zone->len) + new = &((*new)->rb_right); + else + /* Return existing zone */ + break; + zone = NULL; + } + + if (!zone) { + /* No existing zone: add new node and rebalance tree */ + rb_link_node(&new_zone->node, parent, new); + rb_insert_color(&new_zone->node, root); + } + + spin_unlock_irqrestore(&q->zones_lock, flags); + + return zone; +} +EXPORT_SYMBOL_GPL(blk_insert_zone); + +/** + * blk_lookup_zone - Search a zone in a zoned device zone tree. + * @q: queue of the zoned device tree to search + * @sector: A sector within the zone to search for + * + * Search the zone containing @sector in the zone tree owned + * by @q. NULL is returned if the zone is not found. Since this + * can be called concurrently with blk_insert_zone during device + * initialization, the tree traversal is protected using the + * zones_lock of the queue. + */ +struct blk_zone *blk_lookup_zone(struct request_queue *q, sector_t sector) +{ + struct rb_root *root = &q->zones; + struct rb_node *node = root->rb_node; + struct blk_zone *zone = NULL; + unsigned long flags; + + spin_lock_irqsave(&q->zones_lock, flags); + + while (node) { + zone = container_of(node, struct blk_zone, node); + if (sector < zone->start) + node = node->rb_left; + else if (sector >= zone->start + zone->len) + node = node->rb_right; + else + break; + zone = NULL; + } + + spin_unlock_irqrestore(&q->zones_lock, flags); + + return zone; +} +EXPORT_SYMBOL_GPL(blk_lookup_zone); + +/** + * Execute a zone operation (REQ_OP_ZONE*) + */ +static int blkdev_issue_zone_operation(struct block_device *bdev, + unsigned int op, + sector_t sector, sector_t nr_sects, + gfp_t gfp_mask) +{ + struct bio *bio; + int ret; + + if (!bdev_zoned(bdev)) + return -EOPNOTSUPP; + + /* + * Make sure bi_size does not overflow because + * of some weird very large zone size. + */ + if (nr_sects && (unsigned long long)nr_sects << 9 > UINT_MAX) + return -EINVAL; + + bio = bio_alloc(gfp_mask, 1); + if (!bio) + return -ENOMEM; + + bio->bi_iter.bi_sector = sector; + bio->bi_iter.bi_size = nr_sects << 9; + bio->bi_vcnt = 0; + bio->bi_bdev = bdev; + bio_set_op_attrs(bio, op, 0); + + ret = submit_bio_wait(bio); + + bio_put(bio); + + return ret; +} + +/** + * blkdev_update_zones - Force an update of a device zone information + * @bdev: Target block device + * + * Force an update of all zones information of @bdev. This call does not + * block waiting for the update to complete. On return, all zones are only + * marked as "in-update". Waiting on the zone update to complete can be done + * on a per zone basis using the function blk_wait_for_zone_update. + */ +int blkdev_update_zones(struct block_device *bdev, + gfp_t gfp_mask) +{ + return blkdev_issue_zone_operation(bdev, REQ_OP_ZONE_REPORT, + 0, 0, gfp_mask); +} + +/* + * Wait for a zone update to complete. + */ +static void __blk_wait_for_zone_update(struct blk_zone *zone) +{ + might_sleep(); + if (test_bit(BLK_ZONE_IN_UPDATE, &zone->flags)) + wait_on_bit_io(&zone->flags, BLK_ZONE_IN_UPDATE, + TASK_UNINTERRUPTIBLE); +} + +/** + * blk_wait_for_zone_update - Wait for a zone information update + * @zone: The zone to wait for + * + * This must be called with the zone lock held. If @zone is not + * under update, returns immediately. Otherwise, wait for the + * update flag to be cleared on completion of the zone information + * update by the device driver. + */ +void blk_wait_for_zone_update(struct blk_zone *zone) +{ + WARN_ON_ONCE(!test_bit(BLK_ZONE_LOCKED, &zone->flags)); + while (test_bit(BLK_ZONE_IN_UPDATE, &zone->flags)) { + blk_unlock_zone(zone); + __blk_wait_for_zone_update(zone); + blk_lock_zone(zone); + } +} + +/** + * blkdev_report_zone - Get a zone information + * @bdev: Target block device + * @sector: A sector of the zone to report + * @update: Force an update of the zone information + * @gfp_mask: Memory allocation flags (for bio_alloc) + * + * Get a zone from the zone cache. And return it. + * If update is requested, issue a report zone operation + * and wait for the zone information to be updated. + */ +struct blk_zone *blkdev_report_zone(struct block_device *bdev, + sector_t sector, + bool update, + gfp_t gfp_mask) +{ + struct request_queue *q = bdev_get_queue(bdev); + struct blk_zone *zone; + int ret; + + zone = blk_lookup_zone(q, sector); + if (!zone) + return ERR_PTR(-ENXIO); + + if (update) { + ret = blkdev_issue_zone_operation(bdev, REQ_OP_ZONE_REPORT, + zone->start, zone->len, + gfp_mask); + if (ret) + return ERR_PTR(ret); + __blk_wait_for_zone_update(zone); + } + + return zone; +} + +/** + * Execute a zone action (open, close, reset or finish). + */ +static int blkdev_issue_zone_action(struct block_device *bdev, + sector_t sector, unsigned int op, + gfp_t gfp_mask) +{ + struct request_queue *q = bdev_get_queue(bdev); + struct blk_zone *zone; + sector_t nr_sects; + int ret; + + if (!blk_queue_zoned(q)) + return -EOPNOTSUPP; + + if (sector == ~0ULL) { + /* All zones */ + sector = 0; + nr_sects = 0; + } else { + /* This zone */ + zone = blk_lookup_zone(q, sector); + if (!zone) + return -ENXIO; + sector = zone->start; + nr_sects = zone->len; + } + + ret = blkdev_issue_zone_operation(bdev, op, sector, + nr_sects, gfp_mask); + if (ret == 0 && !nr_sects) + blkdev_update_zones(bdev, gfp_mask); + + return ret; +} + +/** + * blkdev_reset_zone - Reset a zone write pointer + * @bdev: target block device + * @sector: A sector of the zone to reset or ~0ul for all zones. + * @gfp_mask: memory allocation flags (for bio_alloc) + * + * Description: + * Reset a zone or all zones write pointer. + */ +int blkdev_reset_zone(struct block_device *bdev, + sector_t sector, gfp_t gfp_mask) +{ + return blkdev_issue_zone_action(bdev, sector, REQ_OP_ZONE_RESET, + gfp_mask); +} + +/** + * blkdev_open_zone - Explicitely open a zone + * @bdev: target block device + * @sector: A sector of the zone to open or ~0ul for all zones. + * @gfp_mask: memory allocation flags (for bio_alloc) + * + * Description: + * Open a zone or all possible zones. + */ +int blkdev_open_zone(struct block_device *bdev, + sector_t sector, gfp_t gfp_mask) +{ + return blkdev_issue_zone_action(bdev, sector, REQ_OP_ZONE_OPEN, + gfp_mask); +} + +/** + * blkdev_close_zone - Close an open zone + * @bdev: target block device + * @sector: A sector of the zone to close or ~0ul for all zones. + * @gfp_mask: memory allocation flags (for bio_alloc) + * + * Description: + * Close a zone or all open zones. + */ +int blkdev_close_zone(struct block_device *bdev, + sector_t sector, gfp_t gfp_mask) +{ + return blkdev_issue_zone_action(bdev, sector, REQ_OP_ZONE_CLOSE, + gfp_mask); +} + +/** + * blkdev_finish_zone - Finish a zone (make it full) + * @bdev: target block device + * @sector: A sector of the zone to close or ~0ul for all zones. + * @gfp_mask: memory allocation flags (for bio_alloc) + * + * Description: + * Finish one zone or all possible zones. + */ +int blkdev_finish_zone(struct block_device *bdev, + sector_t sector, gfp_t gfp_mask) +{ + return blkdev_issue_zone_action(bdev, sector, REQ_OP_ZONE_FINISH, + gfp_mask); +} diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1c74b19..1165594 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -24,6 +24,7 @@ #include #include #include +#include struct module; struct scsi_ioctl_command; @@ -302,6 +303,113 @@ struct queue_limits { unsigned char zoned; }; +#ifdef CONFIG_BLK_DEV_ZONED + +enum blk_zone_type { + BLK_ZONE_TYPE_UNKNOWN, + BLK_ZONE_TYPE_CONVENTIONAL, + BLK_ZONE_TYPE_SEQWRITE_REQ, + BLK_ZONE_TYPE_SEQWRITE_PREF, +}; + +enum blk_zone_cond { + BLK_ZONE_COND_NO_WP, + BLK_ZONE_COND_EMPTY, + BLK_ZONE_COND_IMP_OPEN, + BLK_ZONE_COND_EXP_OPEN, + BLK_ZONE_COND_CLOSED, + BLK_ZONE_COND_READONLY = 0xd, + BLK_ZONE_COND_FULL, + BLK_ZONE_COND_OFFLINE, +}; + +enum blk_zone_flags { + BLK_ZONE_LOCKED, + BLK_ZONE_WRITE_LOCKED, + BLK_ZONE_IN_UPDATE, +}; + +/** + * Zone descriptor. On 64-bits architectures, + * this will align on sizeof(long), i.e. 64 B, + * and use 64 B. + */ +struct blk_zone { + struct rb_node node; + unsigned long flags; + sector_t len; + sector_t start; + sector_t wp; + unsigned int type : 4; + unsigned int cond : 4; + unsigned int non_seq : 1; + unsigned int reset : 1; +}; + +#define blk_zone_is_seq_req(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_REQ) +#define blk_zone_is_seq_pref(z) ((z)->type == BLK_ZONE_TYPE_SEQWRITE_PREF) +#define blk_zone_is_seq(z) (blk_zone_is_seq_req(z) || blk_zone_is_seq_pref(z)) +#define blk_zone_is_conv(z) ((z)->type == BLK_ZONE_TYPE_CONVENTIONAL) + +#define blk_zone_is_readonly(z) ((z)->cond == BLK_ZONE_COND_READONLY) +#define blk_zone_is_offline(z) ((z)->cond == BLK_ZONE_COND_OFFLINE) +#define blk_zone_is_full(z) ((z)->cond == BLK_ZONE_COND_FULL) +#define blk_zone_is_empty(z) ((z)->cond == BLK_ZONE_COND_EMPTY) +#define blk_zone_is_open(z) ((z)->cond == BLK_ZONE_COND_EXP_OPEN) + +static inline void blk_lock_zone(struct blk_zone *zone) +{ + bit_spin_lock(BLK_ZONE_LOCKED, &zone->flags); +} + +static inline int blk_trylock_zone(struct blk_zone *zone) +{ + return bit_spin_trylock(BLK_ZONE_LOCKED, &zone->flags); +} + +static inline void blk_unlock_zone(struct blk_zone *zone) +{ + bit_spin_unlock(BLK_ZONE_LOCKED, &zone->flags); +} + +static inline int blk_try_write_lock_zone(struct blk_zone *zone) +{ + return !test_and_set_bit(BLK_ZONE_WRITE_LOCKED, &zone->flags); +} + +static inline void blk_write_unlock_zone(struct blk_zone *zone) +{ + clear_bit_unlock(BLK_ZONE_WRITE_LOCKED, &zone->flags); + smp_mb__after_atomic(); +} + +extern void blk_init_zones(struct request_queue *); +extern void blk_drop_zones(struct request_queue *); +extern struct blk_zone *blk_insert_zone(struct request_queue *, + struct blk_zone *); +extern struct blk_zone *blk_lookup_zone(struct request_queue *, sector_t); + +extern int blkdev_update_zones(struct block_device *, gfp_t); +extern void blk_wait_for_zone_update(struct blk_zone *); +#define blk_zone_in_update(z) test_bit(BLK_ZONE_IN_UPDATE, &(z)->flags) +static inline void blk_clear_zone_update(struct blk_zone *zone) +{ + clear_bit_unlock(BLK_ZONE_IN_UPDATE, &zone->flags); + smp_mb__after_atomic(); + wake_up_bit(&zone->flags, BLK_ZONE_IN_UPDATE); +} + +extern struct blk_zone *blkdev_report_zone(struct block_device *, + sector_t, bool, gfp_t); +extern int blkdev_reset_zone(struct block_device *, sector_t, gfp_t); +extern int blkdev_open_zone(struct block_device *, sector_t, gfp_t); +extern int blkdev_close_zone(struct block_device *, sector_t, gfp_t); +extern int blkdev_finish_zone(struct block_device *, sector_t, gfp_t); +#else /* CONFIG_BLK_DEV_ZONED */ +static inline void blk_init_zones(struct request_queue *q) { }; +static inline void blk_drop_zones(struct request_queue *q) { }; +#endif /* CONFIG_BLK_DEV_ZONED */ + struct request_queue { /* * Together with queue_head for cacheline sharing @@ -404,6 +512,11 @@ struct request_queue { unsigned int nr_pending; #endif +#ifdef CONFIG_BLK_DEV_ZONED + spinlock_t zones_lock; + struct rb_root zones; +#endif + /* * queue settings */