]>
Commit | Line | Data |
---|---|---|
ca4b2a01 MB |
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 | ||
8 | static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect) | |
9 | { | |
10 | return sect >> ilog2(dev->zone_size_sects); | |
11 | } | |
12 | ||
13 | int 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("null_blk: 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 | for (i = 0; i < dev->nr_zones; i++) { | |
33 | struct blk_zone *zone = &dev->zones[i]; | |
34 | ||
35 | zone->start = zone->wp = sector; | |
36 | zone->len = dev->zone_size_sects; | |
37 | zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ; | |
38 | zone->cond = BLK_ZONE_COND_EMPTY; | |
39 | ||
40 | sector += dev->zone_size_sects; | |
41 | } | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | void null_zone_exit(struct nullb_device *dev) | |
47 | { | |
48 | kvfree(dev->zones); | |
49 | } | |
50 | ||
e76239a3 CH |
51 | int null_zone_report(struct gendisk *disk, sector_t sector, |
52 | struct blk_zone *zones, unsigned int *nr_zones, | |
53 | gfp_t gfp_mask) | |
ca4b2a01 | 54 | { |
e76239a3 CH |
55 | struct nullb *nullb = disk->private_data; |
56 | struct nullb_device *dev = nullb->dev; | |
57 | unsigned int zno, nrz = 0; | |
ca4b2a01 | 58 | |
e76239a3 CH |
59 | if (!dev->zoned) |
60 | /* Not a zoned null device */ | |
61 | return -EOPNOTSUPP; | |
ca4b2a01 | 62 | |
e76239a3 CH |
63 | zno = null_zone_no(dev, sector); |
64 | if (zno < dev->nr_zones) { | |
65 | nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno); | |
66 | memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone)); | |
ca4b2a01 | 67 | } |
ca4b2a01 | 68 | |
e76239a3 | 69 | *nr_zones = nrz; |
ca4b2a01 | 70 | |
e76239a3 | 71 | return 0; |
ca4b2a01 MB |
72 | } |
73 | ||
b228ba1c JA |
74 | void null_zone_write(struct nullb_cmd *cmd, sector_t sector, |
75 | unsigned int nr_sectors) | |
ca4b2a01 MB |
76 | { |
77 | struct nullb_device *dev = cmd->nq->dev; | |
ca4b2a01 MB |
78 | unsigned int zno = null_zone_no(dev, sector); |
79 | struct blk_zone *zone = &dev->zones[zno]; | |
80 | ||
81 | switch (zone->cond) { | |
82 | case BLK_ZONE_COND_FULL: | |
83 | /* Cannot write to a full zone */ | |
84 | cmd->error = BLK_STS_IOERR; | |
85 | break; | |
86 | case BLK_ZONE_COND_EMPTY: | |
87 | case BLK_ZONE_COND_IMP_OPEN: | |
88 | /* Writes must be at the write pointer position */ | |
b228ba1c | 89 | if (sector != zone->wp) { |
ca4b2a01 MB |
90 | cmd->error = BLK_STS_IOERR; |
91 | break; | |
92 | } | |
93 | ||
94 | if (zone->cond == BLK_ZONE_COND_EMPTY) | |
95 | zone->cond = BLK_ZONE_COND_IMP_OPEN; | |
96 | ||
b228ba1c | 97 | zone->wp += nr_sectors; |
ca4b2a01 MB |
98 | if (zone->wp == zone->start + zone->len) |
99 | zone->cond = BLK_ZONE_COND_FULL; | |
100 | break; | |
101 | default: | |
102 | /* Invalid zone condition */ | |
103 | cmd->error = BLK_STS_IOERR; | |
104 | break; | |
105 | } | |
106 | } | |
107 | ||
b228ba1c | 108 | void null_zone_reset(struct nullb_cmd *cmd, sector_t sector) |
ca4b2a01 MB |
109 | { |
110 | struct nullb_device *dev = cmd->nq->dev; | |
b228ba1c | 111 | unsigned int zno = null_zone_no(dev, sector); |
ca4b2a01 MB |
112 | struct blk_zone *zone = &dev->zones[zno]; |
113 | ||
114 | zone->cond = BLK_ZONE_COND_EMPTY; | |
115 | zone->wp = zone->start; | |
116 | } |