18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Western Digital Corporation or its affiliates. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 78c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 88c2ecf20Sopenharmony_ci#include "nvme.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ciint nvme_revalidate_zones(struct nvme_ns *ns) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci struct request_queue *q = ns->queue; 138c2ecf20Sopenharmony_ci int ret; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci ret = blk_revalidate_disk_zones(ns->disk, NULL); 168c2ecf20Sopenharmony_ci if (!ret) 178c2ecf20Sopenharmony_ci blk_queue_max_zone_append_sectors(q, ns->ctrl->max_zone_append); 188c2ecf20Sopenharmony_ci return ret; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int nvme_set_max_append(struct nvme_ctrl *ctrl) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct nvme_command c = { }; 248c2ecf20Sopenharmony_ci struct nvme_id_ctrl_zns *id; 258c2ecf20Sopenharmony_ci int status; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci id = kzalloc(sizeof(*id), GFP_KERNEL); 288c2ecf20Sopenharmony_ci if (!id) 298c2ecf20Sopenharmony_ci return -ENOMEM; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci c.identify.opcode = nvme_admin_identify; 328c2ecf20Sopenharmony_ci c.identify.cns = NVME_ID_CNS_CS_CTRL; 338c2ecf20Sopenharmony_ci c.identify.csi = NVME_CSI_ZNS; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci status = nvme_submit_sync_cmd(ctrl->admin_q, &c, id, sizeof(*id)); 368c2ecf20Sopenharmony_ci if (status) { 378c2ecf20Sopenharmony_ci kfree(id); 388c2ecf20Sopenharmony_ci return status; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (id->zasl) 428c2ecf20Sopenharmony_ci ctrl->max_zone_append = 1 << (id->zasl + 3); 438c2ecf20Sopenharmony_ci else 448c2ecf20Sopenharmony_ci ctrl->max_zone_append = ctrl->max_hw_sectors; 458c2ecf20Sopenharmony_ci kfree(id); 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciint nvme_update_zone_info(struct nvme_ns *ns, unsigned lbaf) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct nvme_effects_log *log = ns->head->effects; 528c2ecf20Sopenharmony_ci struct request_queue *q = ns->queue; 538c2ecf20Sopenharmony_ci struct nvme_command c = { }; 548c2ecf20Sopenharmony_ci struct nvme_id_ns_zns *id; 558c2ecf20Sopenharmony_ci int status; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Driver requires zone append support */ 588c2ecf20Sopenharmony_ci if (!(le32_to_cpu(log->iocs[nvme_cmd_zone_append]) & 598c2ecf20Sopenharmony_ci NVME_CMD_EFFECTS_CSUPP)) { 608c2ecf20Sopenharmony_ci dev_warn(ns->ctrl->device, 618c2ecf20Sopenharmony_ci "append not supported for zoned namespace:%d\n", 628c2ecf20Sopenharmony_ci ns->head->ns_id); 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Lazily query controller append limit for the first zoned namespace */ 678c2ecf20Sopenharmony_ci if (!ns->ctrl->max_zone_append) { 688c2ecf20Sopenharmony_ci status = nvme_set_max_append(ns->ctrl); 698c2ecf20Sopenharmony_ci if (status) 708c2ecf20Sopenharmony_ci return status; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci id = kzalloc(sizeof(*id), GFP_KERNEL); 748c2ecf20Sopenharmony_ci if (!id) 758c2ecf20Sopenharmony_ci return -ENOMEM; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci c.identify.opcode = nvme_admin_identify; 788c2ecf20Sopenharmony_ci c.identify.nsid = cpu_to_le32(ns->head->ns_id); 798c2ecf20Sopenharmony_ci c.identify.cns = NVME_ID_CNS_CS_NS; 808c2ecf20Sopenharmony_ci c.identify.csi = NVME_CSI_ZNS; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci status = nvme_submit_sync_cmd(ns->ctrl->admin_q, &c, id, sizeof(*id)); 838c2ecf20Sopenharmony_ci if (status) 848c2ecf20Sopenharmony_ci goto free_data; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * We currently do not handle devices requiring any of the zoned 888c2ecf20Sopenharmony_ci * operation characteristics. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (id->zoc) { 918c2ecf20Sopenharmony_ci dev_warn(ns->ctrl->device, 928c2ecf20Sopenharmony_ci "zone operations:%x not supported for namespace:%u\n", 938c2ecf20Sopenharmony_ci le16_to_cpu(id->zoc), ns->head->ns_id); 948c2ecf20Sopenharmony_ci status = -EINVAL; 958c2ecf20Sopenharmony_ci goto free_data; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ns->zsze = nvme_lba_to_sect(ns, le64_to_cpu(id->lbafe[lbaf].zsze)); 998c2ecf20Sopenharmony_ci if (!is_power_of_2(ns->zsze)) { 1008c2ecf20Sopenharmony_ci dev_warn(ns->ctrl->device, 1018c2ecf20Sopenharmony_ci "invalid zone size:%llu for namespace:%u\n", 1028c2ecf20Sopenharmony_ci ns->zsze, ns->head->ns_id); 1038c2ecf20Sopenharmony_ci status = -EINVAL; 1048c2ecf20Sopenharmony_ci goto free_data; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci q->limits.zoned = BLK_ZONED_HM; 1088c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q); 1098c2ecf20Sopenharmony_ci blk_queue_max_open_zones(q, le32_to_cpu(id->mor) + 1); 1108c2ecf20Sopenharmony_ci blk_queue_max_active_zones(q, le32_to_cpu(id->mar) + 1); 1118c2ecf20Sopenharmony_cifree_data: 1128c2ecf20Sopenharmony_ci kfree(id); 1138c2ecf20Sopenharmony_ci return status; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void *nvme_zns_alloc_report_buffer(struct nvme_ns *ns, 1178c2ecf20Sopenharmony_ci unsigned int nr_zones, size_t *buflen) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct request_queue *q = ns->disk->queue; 1208c2ecf20Sopenharmony_ci size_t bufsize; 1218c2ecf20Sopenharmony_ci void *buf; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci const size_t min_bufsize = sizeof(struct nvme_zone_report) + 1248c2ecf20Sopenharmony_ci sizeof(struct nvme_zone_descriptor); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci nr_zones = min_t(unsigned int, nr_zones, 1278c2ecf20Sopenharmony_ci get_capacity(ns->disk) >> ilog2(ns->zsze)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci bufsize = sizeof(struct nvme_zone_report) + 1308c2ecf20Sopenharmony_ci nr_zones * sizeof(struct nvme_zone_descriptor); 1318c2ecf20Sopenharmony_ci bufsize = min_t(size_t, bufsize, 1328c2ecf20Sopenharmony_ci queue_max_hw_sectors(q) << SECTOR_SHIFT); 1338c2ecf20Sopenharmony_ci bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci while (bufsize >= min_bufsize) { 1368c2ecf20Sopenharmony_ci buf = __vmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY); 1378c2ecf20Sopenharmony_ci if (buf) { 1388c2ecf20Sopenharmony_ci *buflen = bufsize; 1398c2ecf20Sopenharmony_ci return buf; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci bufsize >>= 1; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci return NULL; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int nvme_zone_parse_entry(struct nvme_ns *ns, 1478c2ecf20Sopenharmony_ci struct nvme_zone_descriptor *entry, 1488c2ecf20Sopenharmony_ci unsigned int idx, report_zones_cb cb, 1498c2ecf20Sopenharmony_ci void *data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct blk_zone zone = { }; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if ((entry->zt & 0xf) != NVME_ZONE_TYPE_SEQWRITE_REQ) { 1548c2ecf20Sopenharmony_ci dev_err(ns->ctrl->device, "invalid zone type %#x\n", 1558c2ecf20Sopenharmony_ci entry->zt); 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci zone.type = BLK_ZONE_TYPE_SEQWRITE_REQ; 1608c2ecf20Sopenharmony_ci zone.cond = entry->zs >> 4; 1618c2ecf20Sopenharmony_ci zone.len = ns->zsze; 1628c2ecf20Sopenharmony_ci zone.capacity = nvme_lba_to_sect(ns, le64_to_cpu(entry->zcap)); 1638c2ecf20Sopenharmony_ci zone.start = nvme_lba_to_sect(ns, le64_to_cpu(entry->zslba)); 1648c2ecf20Sopenharmony_ci zone.wp = nvme_lba_to_sect(ns, le64_to_cpu(entry->wp)); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return cb(&zone, idx, data); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector, 1708c2ecf20Sopenharmony_ci unsigned int nr_zones, report_zones_cb cb, void *data) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct nvme_zone_report *report; 1738c2ecf20Sopenharmony_ci struct nvme_command c = { }; 1748c2ecf20Sopenharmony_ci int ret, zone_idx = 0; 1758c2ecf20Sopenharmony_ci unsigned int nz, i; 1768c2ecf20Sopenharmony_ci size_t buflen; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci report = nvme_zns_alloc_report_buffer(ns, nr_zones, &buflen); 1798c2ecf20Sopenharmony_ci if (!report) 1808c2ecf20Sopenharmony_ci return -ENOMEM; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci c.zmr.opcode = nvme_cmd_zone_mgmt_recv; 1838c2ecf20Sopenharmony_ci c.zmr.nsid = cpu_to_le32(ns->head->ns_id); 1848c2ecf20Sopenharmony_ci c.zmr.numd = cpu_to_le32(nvme_bytes_to_numd(buflen)); 1858c2ecf20Sopenharmony_ci c.zmr.zra = NVME_ZRA_ZONE_REPORT; 1868c2ecf20Sopenharmony_ci c.zmr.zrasf = NVME_ZRASF_ZONE_REPORT_ALL; 1878c2ecf20Sopenharmony_ci c.zmr.pr = NVME_REPORT_ZONE_PARTIAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci sector &= ~(ns->zsze - 1); 1908c2ecf20Sopenharmony_ci while (zone_idx < nr_zones && sector < get_capacity(ns->disk)) { 1918c2ecf20Sopenharmony_ci memset(report, 0, buflen); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci c.zmr.slba = cpu_to_le64(nvme_sect_to_lba(ns, sector)); 1948c2ecf20Sopenharmony_ci ret = nvme_submit_sync_cmd(ns->queue, &c, report, buflen); 1958c2ecf20Sopenharmony_ci if (ret) { 1968c2ecf20Sopenharmony_ci if (ret > 0) 1978c2ecf20Sopenharmony_ci ret = -EIO; 1988c2ecf20Sopenharmony_ci goto out_free; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci nz = min((unsigned int)le64_to_cpu(report->nr_zones), nr_zones); 2028c2ecf20Sopenharmony_ci if (!nz) 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (i = 0; i < nz && zone_idx < nr_zones; i++) { 2068c2ecf20Sopenharmony_ci ret = nvme_zone_parse_entry(ns, &report->entries[i], 2078c2ecf20Sopenharmony_ci zone_idx, cb, data); 2088c2ecf20Sopenharmony_ci if (ret) 2098c2ecf20Sopenharmony_ci goto out_free; 2108c2ecf20Sopenharmony_ci zone_idx++; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci sector += ns->zsze * nz; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (zone_idx > 0) 2178c2ecf20Sopenharmony_ci ret = zone_idx; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci ret = -EINVAL; 2208c2ecf20Sopenharmony_ciout_free: 2218c2ecf20Sopenharmony_ci kvfree(report); 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciint nvme_report_zones(struct gendisk *disk, sector_t sector, 2268c2ecf20Sopenharmony_ci unsigned int nr_zones, report_zones_cb cb, void *data) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct nvme_ns_head *head = NULL; 2298c2ecf20Sopenharmony_ci struct nvme_ns *ns; 2308c2ecf20Sopenharmony_ci int srcu_idx, ret; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ns = nvme_get_ns_from_disk(disk, &head, &srcu_idx); 2338c2ecf20Sopenharmony_ci if (unlikely(!ns)) 2348c2ecf20Sopenharmony_ci return -EWOULDBLOCK; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (ns->head->ids.csi == NVME_CSI_ZNS) 2378c2ecf20Sopenharmony_ci ret = nvme_ns_report_zones(ns, sector, nr_zones, cb, data); 2388c2ecf20Sopenharmony_ci else 2398c2ecf20Sopenharmony_ci ret = -EINVAL; 2408c2ecf20Sopenharmony_ci nvme_put_ns_from_disk(head, srcu_idx); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciblk_status_t nvme_setup_zone_mgmt_send(struct nvme_ns *ns, struct request *req, 2468c2ecf20Sopenharmony_ci struct nvme_command *c, enum nvme_zone_mgmt_action action) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci c->zms.opcode = nvme_cmd_zone_mgmt_send; 2498c2ecf20Sopenharmony_ci c->zms.nsid = cpu_to_le32(ns->head->ns_id); 2508c2ecf20Sopenharmony_ci c->zms.slba = cpu_to_le64(nvme_sect_to_lba(ns, blk_rq_pos(req))); 2518c2ecf20Sopenharmony_ci c->zms.zsa = action; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (req_op(req) == REQ_OP_ZONE_RESET_ALL) 2548c2ecf20Sopenharmony_ci c->zms.select_all = 1; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return BLK_STS_OK; 2578c2ecf20Sopenharmony_ci} 258