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