Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2#include <linux/vmalloc.h>
  3#include "null_blk.h"
  4
  5/* zone_size in MBs to sectors. */
  6#define ZONE_SIZE_SHIFT		11
  7
  8static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
  9{
 10	return sect >> ilog2(dev->zone_size_sects);
 11}
 12
 13int null_zone_init(struct nullb_device *dev)
 14{
 15	sector_t dev_size = (sector_t)dev->size * 1024 * 1024;
 16	sector_t sector = 0;
 17	unsigned int i;
 18
 19	if (!is_power_of_2(dev->zone_size)) {
 20		pr_err("zone_size must be power-of-two\n");
 21		return -EINVAL;
 22	}
 23
 24	dev->zone_size_sects = dev->zone_size << ZONE_SIZE_SHIFT;
 25	dev->nr_zones = dev_size >>
 26				(SECTOR_SHIFT + ilog2(dev->zone_size_sects));
 27	dev->zones = kvmalloc_array(dev->nr_zones, sizeof(struct blk_zone),
 28			GFP_KERNEL | __GFP_ZERO);
 29	if (!dev->zones)
 30		return -ENOMEM;
 31
 32	if (dev->zone_nr_conv >= dev->nr_zones) {
 33		dev->zone_nr_conv = dev->nr_zones - 1;
 34		pr_info("changed the number of conventional zones to %u",
 35			dev->zone_nr_conv);
 36	}
 37
 38	for (i = 0; i <  dev->zone_nr_conv; i++) {
 39		struct blk_zone *zone = &dev->zones[i];
 40
 41		zone->start = sector;
 42		zone->len = dev->zone_size_sects;
 43		zone->wp = zone->start + zone->len;
 44		zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
 45		zone->cond = BLK_ZONE_COND_NOT_WP;
 46
 47		sector += dev->zone_size_sects;
 48	}
 49
 50	for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
 51		struct blk_zone *zone = &dev->zones[i];
 52
 53		zone->start = zone->wp = sector;
 54		zone->len = dev->zone_size_sects;
 55		zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
 56		zone->cond = BLK_ZONE_COND_EMPTY;
 57
 58		sector += dev->zone_size_sects;
 59	}
 60
 61	return 0;
 62}
 63
 64void null_zone_exit(struct nullb_device *dev)
 65{
 66	kvfree(dev->zones);
 67}
 68
 69int null_zone_report(struct gendisk *disk, sector_t sector,
 70		     struct blk_zone *zones, unsigned int *nr_zones)
 71{
 72	struct nullb *nullb = disk->private_data;
 73	struct nullb_device *dev = nullb->dev;
 74	unsigned int zno, nrz = 0;
 75
 76	zno = null_zone_no(dev, sector);
 77	if (zno < dev->nr_zones) {
 78		nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno);
 79		memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone));
 80	}
 81
 82	*nr_zones = nrz;
 83
 84	return 0;
 85}
 86
 87static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 88		     unsigned int nr_sectors)
 89{
 90	struct nullb_device *dev = cmd->nq->dev;
 91	unsigned int zno = null_zone_no(dev, sector);
 92	struct blk_zone *zone = &dev->zones[zno];
 93
 94	switch (zone->cond) {
 95	case BLK_ZONE_COND_FULL:
 96		/* Cannot write to a full zone */
 97		cmd->error = BLK_STS_IOERR;
 98		return BLK_STS_IOERR;
 99	case BLK_ZONE_COND_EMPTY:
100	case BLK_ZONE_COND_IMP_OPEN:
101		/* Writes must be at the write pointer position */
102		if (sector != zone->wp)
103			return BLK_STS_IOERR;
104
105		if (zone->cond == BLK_ZONE_COND_EMPTY)
106			zone->cond = BLK_ZONE_COND_IMP_OPEN;
107
108		zone->wp += nr_sectors;
109		if (zone->wp == zone->start + zone->len)
110			zone->cond = BLK_ZONE_COND_FULL;
111		break;
112	case BLK_ZONE_COND_NOT_WP:
113		break;
114	default:
115		/* Invalid zone condition */
116		return BLK_STS_IOERR;
117	}
118	return BLK_STS_OK;
119}
120
121static blk_status_t null_zone_reset(struct nullb_cmd *cmd, sector_t sector)
122{
123	struct nullb_device *dev = cmd->nq->dev;
124	unsigned int zno = null_zone_no(dev, sector);
125	struct blk_zone *zone = &dev->zones[zno];
126	size_t i;
127
128	switch (req_op(cmd->rq)) {
129	case REQ_OP_ZONE_RESET_ALL:
130		for (i = 0; i < dev->nr_zones; i++) {
131			if (zone[i].type == BLK_ZONE_TYPE_CONVENTIONAL)
132				continue;
133			zone[i].cond = BLK_ZONE_COND_EMPTY;
134			zone[i].wp = zone[i].start;
135		}
136		break;
137	case REQ_OP_ZONE_RESET:
138		if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
139			return BLK_STS_IOERR;
140
141		zone->cond = BLK_ZONE_COND_EMPTY;
142		zone->wp = zone->start;
143		break;
144	default:
145		return BLK_STS_NOTSUPP;
146	}
147	return BLK_STS_OK;
148}
149
150blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op,
151			       sector_t sector, sector_t nr_sectors)
152{
153	switch (op) {
154	case REQ_OP_WRITE:
155		return null_zone_write(cmd, sector, nr_sectors);
156	case REQ_OP_ZONE_RESET:
157	case REQ_OP_ZONE_RESET_ALL:
158		return null_zone_reset(cmd, sector);
159	default:
160		return BLK_STS_OK;
161	}
162}