1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2026 voidanix <voidanix@FreeBSD.org>
5 */
6
7 /*
8 * GEOM_ZONED presents a plain (non-zoned) provider to the rest of the system
9 * as a host-managed zoned block device. The medium is divided into equal
10 * sequential-write-required zones. This GEOM class tracks a write pointer per
11 * zone and answers BIO_ZONE management commands similarly to a real ZBC/ZAC
12 * drive would.
13 *
14 * Persistence:
15 * - The provider's last sector holds a metadata block, written by the
16 * userland "create" command. The kernel tastes it on every provider
17 * arrival and re-creates the zoned device automatically.
18 * - The sectors just before it hold the per-zone state (condition + write
19 * pointer). That table is read at taste time and rewritten lazily.
20 * Zone-state changes, zone-management commands included, only mark the
21 * table dirty; BIO_FLUSH is what commits them, mirroring a drive whose
22 * zone state is volatile until a cache flush. Changes since the last
23 * flush may be rolled back by an unclean shutdown.
24 */
25
26 #define EXTERR_CATEGORY EXTERR_CAT_GEOMZONED
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bio.h>
31 #include <sys/disk_zone.h>
32 #include <sys/endian.h>
33 #include <sys/exterrvar.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/sbuf.h>
40 #include <sys/sysctl.h>
41
42 #include <geom/geom.h>
43 #include <geom/geom_dbg.h>
44 #include <geom/zoned/g_zoned.h>
45
46 FEATURE(geom_zoned, "GEOM Zoned Storage Medium emulation");
47
48 static MALLOC_DEFINE(M_ZONED, "zoned_data", "GEOM_ZONED Data");
49
50 SYSCTL_DECL(_kern_geom);
51 static SYSCTL_NODE(_kern_geom, OID_AUTO, zoned, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
52 "GEOM_ZONED stuff");
53 static u_int g_zoned_debug = 0;
54 SYSCTL_UINT(_kern_geom_zoned, OID_AUTO, debug, CTLFLAG_RW, &g_zoned_debug, 0,
55 "Debug level");
56
57 #define G_ZONED_EXTERR(bp, error, ...) ({ \
58 (bp)->bio_flags |= BIO_EXTERR; \
59 EXTERROR_KE(&(bp)->bio_exterr, (error), __VA_ARGS__); \
60 })
61
62 static g_access_t g_zoned_access;
63 static g_ctl_req_t g_zoned_config;
64 static g_ctl_destroy_geom_t g_zoned_destroy_geom;
65 static g_dumpconf_t g_zoned_dumpconf;
66 static g_orphan_t g_zoned_orphan;
67 static g_provgone_t g_zoned_providergone;
68 static g_resize_t g_zoned_resize;
69 static g_start_t g_zoned_start;
70 static g_taste_t g_zoned_taste;
71
72 static struct g_class g_zoned_class = {
73 .name = G_ZONED_CLASS_NAME,
74 .version = G_VERSION,
75 .ctlreq = g_zoned_config,
76 .destroy_geom = g_zoned_destroy_geom,
77 .taste = g_zoned_taste,
78 .access = g_zoned_access,
79 .dumpconf = g_zoned_dumpconf,
80 .orphan = g_zoned_orphan,
81 .providergone = g_zoned_providergone,
82 .resize = g_zoned_resize,
83 .spoiled = g_zoned_orphan,
84 .start = g_zoned_start,
85 };
86
87 /*
88 * Index of the zone that contains the given LBA. Caller must have range-checked
89 * the LBA against sc_maxlba.
90 */
91 static __inline uint32_t
g_zoned_zoneno(struct g_zoned_softc * sc,uint64_t lba)92 g_zoned_zoneno(struct g_zoned_softc *sc, uint64_t lba)
93 {
94
95 return ((uint32_t)(lba / sc->sc_zonesecs));
96 }
97
98 static bool
g_zoned_is_conv(const struct g_zoned_softc * sc,uint32_t zno)99 g_zoned_is_conv(const struct g_zoned_softc *sc, uint32_t zno)
100 {
101 const struct g_zoned_convrange *cr;
102 uint32_t i;
103
104 for (i = 0; i < sc->sc_nconv; i++) {
105 cr = &sc->sc_conv[i];
106 if (zno >= cr->cr_first && zno - cr->cr_first < cr->cr_count)
107 return (true);
108 }
109 return (false);
110 }
111
112 /*
113 * Extend the dirty zone range to cover zone zno, so the next BIO_FLUSH
114 * writes its table entry out. Must be called with sc_lock held.
115 */
116 static void
g_zoned_mark_dirty(struct g_zoned_softc * sc,uint32_t zno)117 g_zoned_mark_dirty(struct g_zoned_softc *sc, uint32_t zno)
118 {
119
120 mtx_assert(&sc->sc_lock, MA_OWNED);
121
122 if (!sc->sc_dirty) {
123 sc->sc_dirty = true;
124 sc->sc_dirtylo = sc->sc_dirtyhi = zno;
125 } else {
126 if (zno < sc->sc_dirtylo)
127 sc->sc_dirtylo = zno;
128 if (zno > sc->sc_dirtyhi)
129 sc->sc_dirtyhi = zno;
130 }
131 }
132
133 static bool
g_zoned_cond_is_open(uint8_t cond)134 g_zoned_cond_is_open(uint8_t cond)
135 {
136
137 return (cond == DISK_ZONE_COND_IMPLICIT_OPEN ||
138 cond == DISK_ZONE_COND_EXPLICIT_OPEN);
139 }
140
141 /*
142 * Put a zone into a new condition, maintaining the count of open
143 * zones. Must be called with sc_lock held.
144 */
145 static void
g_zoned_set_cond(struct g_zoned_softc * sc,struct disk_zone_rep_entry * z,uint8_t cond)146 g_zoned_set_cond(struct g_zoned_softc *sc, struct disk_zone_rep_entry *z,
147 uint8_t cond)
148 {
149 bool is_open, want_open;
150
151 mtx_assert(&sc->sc_lock, MA_OWNED);
152
153 is_open = g_zoned_cond_is_open(z->zone_condition);
154 want_open = g_zoned_cond_is_open(cond);
155 if (!is_open && want_open)
156 sc->sc_nopen++;
157 else if (is_open && !want_open)
158 sc->sc_nopen--;
159 z->zone_condition = cond;
160 }
161
162 /*
163 * Make room to open one more zone under the open-zones limit by implicitly
164 * closing the lowest-numbered implicitly-open zone, similarly to a real drive.
165 */
166 static bool
g_zoned_open_room(struct g_zoned_softc * sc)167 g_zoned_open_room(struct g_zoned_softc *sc)
168 {
169 struct disk_zone_rep_entry *z;
170 uint32_t i;
171
172 mtx_assert(&sc->sc_lock, MA_OWNED);
173
174 if (sc->sc_maxopen == 0 || sc->sc_nopen < sc->sc_maxopen)
175 return (true);
176 for (i = 0; i < sc->sc_nzones; i++) {
177 z = &sc->sc_zones[i];
178 if (z->zone_condition == DISK_ZONE_COND_IMPLICIT_OPEN) {
179 g_zoned_set_cond(sc, z, DISK_ZONE_COND_CLOSED);
180 g_zoned_mark_dirty(sc, i);
181 return (true);
182 }
183 }
184 return (false);
185 }
186
187 /*
188 * Put every zone into its pure state, with the type coming from the
189 * metadata-defined conventional ranges.
190 */
191 static void
g_zoned_reset_zones(struct g_zoned_softc * sc)192 g_zoned_reset_zones(struct g_zoned_softc *sc)
193 {
194 struct disk_zone_rep_entry *z;
195 uint32_t i;
196
197 sc->sc_nopen = 0;
198 for (i = 0; i < sc->sc_nzones; i++) {
199 z = &sc->sc_zones[i];
200 z->zone_start_lba = (uint64_t)i * sc->sc_zonesecs;
201 z->zone_length = sc->sc_zonesecs;
202 z->zone_flags = 0;
203 if (g_zoned_is_conv(sc, i)) {
204 z->zone_type = DISK_ZONE_TYPE_CONVENTIONAL;
205 z->zone_condition = DISK_ZONE_COND_NOT_WP;
206 /* Conventional zones have no write pointer. */
207 z->write_pointer_lba = G_ZONED_WP_NONE_LBA;
208 } else {
209 z->zone_type = DISK_ZONE_TYPE_SEQ_REQUIRED;
210 z->zone_condition = DISK_ZONE_COND_EMPTY;
211 z->write_pointer_lba = z->zone_start_lba;
212 }
213 }
214 }
215
216 /*
217 * Mark the whole table (header included) dirty so that the first BIO_FLUSH
218 * writes it out.
219 */
220 static void
g_zoned_dirty_table(struct g_zoned_softc * sc)221 g_zoned_dirty_table(struct g_zoned_softc *sc)
222 {
223
224 sc->sc_dirty = true;
225 sc->sc_hdrdirty = true;
226 sc->sc_dirtylo = 0;
227 sc->sc_dirtyhi = sc->sc_nzones - 1;
228 }
229
230 /*
231 * Read the existing persistent zone table from the provider into the live zone
232 * array, otherwise initialise an empty one if no valid table is present.
233 */
234 static void
g_zoned_load_table(struct g_zoned_softc * sc,struct g_consumer * cp)235 g_zoned_load_table(struct g_zoned_softc *sc, struct g_consumer *cp)
236 {
237 struct g_zoned_table_hdr th;
238 struct disk_zone_rep_entry *z;
239 u_char *buf;
240 off_t off, resid, chunk;
241 uint32_t i, n, zno;
242 int error;
243 bool valid;
244
245 g_topology_assert();
246 g_topology_unlock();
247
248 valid = false;
249 buf = g_read_data(cp, sc->sc_taboff, sc->sc_secsize, &error);
250 if (buf != NULL) {
251 zoned_table_hdr_decode(buf, &th);
252 valid = memcmp(th.th_magic, G_ZONED_TABLE_MAGIC,
253 sizeof(G_ZONED_TABLE_MAGIC)) == 0 &&
254 th.th_version == G_ZONED_VERSION &&
255 th.th_nzones == sc->sc_nzones;
256 g_free(buf);
257 }
258
259 zno = 0;
260 off = sc->sc_taboff + sc->sc_secsize;
261 resid = (off_t)(sc->sc_tabsecs - 1) * sc->sc_secsize;
262 while (valid && resid > 0) {
263 chunk = MIN(resid, (off_t)maxphys);
264 chunk -= chunk % sc->sc_secsize;
265 if (chunk == 0)
266 chunk = sc->sc_secsize;
267 buf = g_read_data(cp, off, chunk, &error);
268 if (buf == NULL) {
269 valid = false;
270 break;
271 }
272 n = MIN((uint32_t)(chunk / G_ZONED_ENTRY_SIZE),
273 sc->sc_nzones - zno);
274 for (i = 0; i < n; i++)
275 g_zoned_entry_decode(buf + i * G_ZONED_ENTRY_SIZE,
276 &sc->sc_zones[zno + i],
277 (uint64_t)(zno + i) * sc->sc_zonesecs);
278 g_free(buf);
279 zno += n;
280 off += chunk;
281 resid -= chunk;
282 }
283
284 g_topology_lock();
285
286 if (!valid) {
287 G_ZONED_DEBUG(1, "No valid zone table on %s; initialising.",
288 cp->provider->name);
289 g_zoned_reset_zones(sc);
290 g_zoned_dirty_table(sc);
291 return;
292 }
293
294 for (i = 0; i < sc->sc_nzones; i++) {
295 z = &sc->sc_zones[i];
296 z->zone_start_lba = (uint64_t)i * sc->sc_zonesecs;
297 z->zone_length = sc->sc_zonesecs;
298 /* The metadata dictates zone types, not the table. */
299 if (g_zoned_is_conv(sc, i)) {
300 z->zone_type = DISK_ZONE_TYPE_CONVENTIONAL;
301 z->zone_condition = DISK_ZONE_COND_NOT_WP;
302 z->write_pointer_lba = G_ZONED_WP_NONE_LBA;
303 continue;
304 }
305 z->zone_type = DISK_ZONE_TYPE_SEQ_REQUIRED;
306 /* Guard against a corrupt write pointer. */
307 if (z->write_pointer_lba < z->zone_start_lba ||
308 z->write_pointer_lba >
309 z->zone_start_lba + z->zone_length) {
310 z->write_pointer_lba = z->zone_start_lba;
311 z->zone_condition = DISK_ZONE_COND_EMPTY;
312 }
313 }
314 sc->sc_nopen = 0;
315 for (i = 0; i < sc->sc_nzones; i++)
316 if (g_zoned_cond_is_open(sc->sc_zones[i].zone_condition))
317 sc->sc_nopen++;
318 G_ZONED_DEBUG(1, "Restored zone table from %s.", cp->provider->name);
319 }
320
321 /*
322 * "Flush the dirty table sectors, then forward the cache flush" thing. We
323 * allocate one of those per BIO_FLUSH that finds dirty state to commit.
324 */
325 struct g_zoned_flush {
326 struct bio *fl_orig; /* Original BIO_FLUSH. */
327 struct g_consumer *fl_cp; /* Where to send the I/O. */
328 struct g_zoned_softc *fl_sc;
329 u_char *fl_buf; /* Snapshot of dirty sectors. */
330 off_t fl_off; /* Disk offset of first sector. */
331 off_t fl_total; /* Bytes to write. */
332 off_t fl_done; /* Bytes written so far. */
333 u_int fl_secsize;
334 /* Dirty state the snapshot took: to put back if the write fails. */
335 uint32_t fl_dirtylo;
336 uint32_t fl_dirtyhi;
337 bool fl_hdrdirty;
338 };
339
340 static void g_zoned_flush_step(struct g_zoned_flush *fc);
341
342 static void
g_zoned_flush_final(struct bio * bp)343 g_zoned_flush_final(struct bio *bp)
344 {
345 struct g_zoned_flush *fc = bp->bio_caller1;
346 struct bio *orig = fc->fl_orig;
347 int error = bp->bio_error;
348
349 g_destroy_bio(bp);
350 g_free(fc->fl_buf);
351 g_free(fc);
352 g_io_deliver(orig, error);
353 }
354
355 static void
g_zoned_flush_write_done(struct bio * bp)356 g_zoned_flush_write_done(struct bio *bp)
357 {
358 struct g_zoned_flush *fc = bp->bio_caller1;
359 int error = bp->bio_error;
360
361 g_destroy_bio(bp);
362 if (error != 0) {
363 struct g_zoned_softc *sc = fc->fl_sc;
364
365 G_ZONED_DEBUG(0, "Zone table write failed (error=%d).", error);
366 /*
367 * Nothing reached the medium, thus the state the snapshot
368 * cleared is still only in memory. Mark it dirty again,
369 * widening whatever has been dirtied since, so that the next
370 * flush tries once more instead of leaving the table stale.
371 */
372 mtx_lock(&sc->sc_lock);
373 g_zoned_mark_dirty(sc, fc->fl_dirtylo);
374 g_zoned_mark_dirty(sc, fc->fl_dirtyhi);
375 sc->sc_hdrdirty |= fc->fl_hdrdirty;
376 mtx_unlock(&sc->sc_lock);
377 g_free(fc->fl_buf);
378 g_io_deliver(fc->fl_orig, error);
379 g_free(fc);
380 return;
381 }
382 g_zoned_flush_step(fc);
383 }
384
385 static void
g_zoned_flush_step(struct g_zoned_flush * fc)386 g_zoned_flush_step(struct g_zoned_flush *fc)
387 {
388 struct bio *cbp;
389 off_t chunk;
390
391 if (fc->fl_done < fc->fl_total) {
392 chunk = fc->fl_total - fc->fl_done;
393 if (chunk > (off_t)maxphys) {
394 chunk = maxphys;
395 chunk -= chunk % fc->fl_secsize;
396 }
397 cbp = g_alloc_bio();
398 cbp->bio_cmd = BIO_WRITE;
399 cbp->bio_offset = fc->fl_off + fc->fl_done;
400 cbp->bio_data = fc->fl_buf + fc->fl_done;
401 cbp->bio_length = chunk;
402 cbp->bio_done = g_zoned_flush_write_done;
403 cbp->bio_caller1 = fc;
404 fc->fl_done += chunk;
405 g_io_request(cbp, fc->fl_cp);
406 return;
407 }
408 /* Table is on its way down; now forward the real cache flush. */
409 cbp = g_alloc_bio();
410 cbp->bio_cmd = BIO_FLUSH;
411 cbp->bio_done = g_zoned_flush_final;
412 cbp->bio_caller1 = fc;
413 g_io_request(cbp, fc->fl_cp);
414 }
415
416 /*
417 * Snapshot the dirty part of the table into a freshly allocated buffer of
418 * whole sectors, encoded in the on-disk format and clear the dirty state.
419 */
420 static u_char *
g_zoned_encode_dirty(struct g_zoned_softc * sc,off_t * offp,off_t * totalp)421 g_zoned_encode_dirty(struct g_zoned_softc *sc, off_t *offp, off_t *totalp)
422 {
423 struct g_zoned_table_hdr th;
424 u_char *buf;
425 off_t total;
426 uint32_t first, i, limit, seclo, sechi, zpersec;
427
428 mtx_assert(&sc->sc_lock, MA_OWNED);
429
430 if (!sc->sc_dirty)
431 return (NULL);
432 /* Table sector 0 holds the header; entries start at sector 1. */
433 zpersec = sc->sc_secsize / G_ZONED_ENTRY_SIZE;
434 seclo = sc->sc_hdrdirty ? 0 : 1 + sc->sc_dirtylo / zpersec;
435 sechi = 1 + sc->sc_dirtyhi / zpersec;
436 total = (off_t)(sechi - seclo + 1) * sc->sc_secsize;
437 buf = g_malloc(total, M_NOWAIT | M_ZERO);
438 if (buf == NULL)
439 return (NULL);
440 if (seclo == 0) {
441 memset(&th, 0, sizeof(th));
442 memcpy(th.th_magic, G_ZONED_TABLE_MAGIC,
443 sizeof(G_ZONED_TABLE_MAGIC));
444 th.th_version = G_ZONED_VERSION;
445 th.th_nzones = sc->sc_nzones;
446 zoned_table_hdr_encode(&th, buf);
447 }
448 /* Encode every entry falling into the sectors being written. */
449 first = (seclo == 0) ? 0 : (seclo - 1) * zpersec;
450 limit = MIN(sc->sc_nzones, sechi * zpersec);
451 for (i = first; i < limit; i++)
452 g_zoned_entry_encode(&sc->sc_zones[i], buf +
453 (seclo == 0 ? sc->sc_secsize : 0) +
454 (off_t)(i - first) * G_ZONED_ENTRY_SIZE);
455 sc->sc_dirty = false;
456 sc->sc_hdrdirty = false;
457 *offp = sc->sc_taboff + (off_t)seclo * sc->sc_secsize;
458 *totalp = total;
459 return (buf);
460 }
461
462 /*
463 * Attempt commiting the dirty zone-table state for BIO_FLUSH. Returns true
464 * if it took ownership of bp i.e. an async chain is running or the flush has
465 * failed, false if the caller should forward the flush normally.
466 */
467 static bool
g_zoned_flush_begin(struct g_zoned_softc * sc,struct g_geom * gp,struct bio * bp)468 g_zoned_flush_begin(struct g_zoned_softc *sc, struct g_geom *gp,
469 struct bio *bp)
470 {
471 struct g_zoned_flush *fc;
472 struct g_consumer *cp;
473 u_char *buf;
474 off_t off, total;
475 uint32_t dirtylo, dirtyhi;
476 bool dirty, hdrdirty;
477
478 cp = LIST_FIRST(&gp->consumer);
479 /* Persistence is not possible without write access; just forward. */
480 if (cp->acw == 0)
481 return (false);
482
483 fc = g_malloc(sizeof(*fc), M_NOWAIT);
484
485 mtx_lock(&sc->sc_lock);
486 dirty = sc->sc_dirty;
487 dirtylo = sc->sc_dirtylo;
488 dirtyhi = sc->sc_dirtyhi;
489 hdrdirty = sc->sc_hdrdirty;
490 /* Leave the state dirty for the next flush if nothing can carry it. */
491 buf = (fc != NULL) ? g_zoned_encode_dirty(sc, &off, &total) : NULL;
492 mtx_unlock(&sc->sc_lock);
493 if (buf == NULL) {
494 g_free(fc);
495 /* Nothing to commit, so the flush stands on its own. */
496 if (!dirty)
497 return (false);
498 /*
499 * The table stays in memory, thus this flush cannot claim it
500 * reached the medium. Say so, rather than forwarding a flush
501 * that commits none of the state the caller is flushing for.
502 */
503 G_ZONED_DEBUG(0, "No memory to commit the zone table;"
504 " failing the flush.");
505 g_io_deliver(bp, G_ZONED_EXTERR(bp, ENOMEM,
506 "No memory to commit the zone table."));
507 return (true);
508 }
509
510 fc->fl_dirtylo = dirtylo;
511 fc->fl_dirtyhi = dirtyhi;
512 fc->fl_hdrdirty = hdrdirty;
513 fc->fl_orig = bp;
514 fc->fl_cp = cp;
515 fc->fl_sc = sc;
516 fc->fl_buf = buf;
517 fc->fl_off = off;
518 fc->fl_total = total;
519 fc->fl_done = 0;
520 fc->fl_secsize = sc->sc_secsize;
521 g_zoned_flush_step(fc);
522 return (true);
523 }
524
525 /*
526 * Best-effort, synchronous commit of the dirty zone-table state, for the
527 * destroy path where no further BIO_FLUSH will arrive. A failure means the
528 * on-disk table stays as of the last flush.
529 */
530 static void
g_zoned_flush_sync(struct g_zoned_softc * sc,struct g_consumer * cp)531 g_zoned_flush_sync(struct g_zoned_softc *sc, struct g_consumer *cp)
532 {
533 u_char *buf;
534 off_t chunk, done, off, total;
535 int error;
536
537 g_topology_assert();
538
539 /* A provider that went away takes the table's location with it. */
540 if (cp == NULL || cp->provider == NULL || cp->provider->error != 0)
541 return;
542 mtx_lock(&sc->sc_lock);
543 buf = g_zoned_encode_dirty(sc, &off, &total);
544 mtx_unlock(&sc->sc_lock);
545 if (buf == NULL)
546 return;
547 g_topology_unlock();
548 error = 0;
549 for (done = 0; done < total; done += chunk) {
550 chunk = MIN(total - done, (off_t)maxphys);
551 chunk -= chunk % sc->sc_secsize;
552 if (chunk == 0)
553 chunk = sc->sc_secsize;
554 error = g_write_data(cp, off + done, buf + done, chunk);
555 if (error != 0)
556 break;
557 }
558 if (error == 0)
559 error = g_io_flush(cp);
560 if (error != 0)
561 G_ZONED_DEBUG(0,
562 "Final zone table write on %s failed (error=%d).",
563 cp->provider->name, error);
564 g_free(buf);
565 g_topology_lock();
566 }
567
568 /*
569 * Test a zone against a REPORT ZONES reporting option (DISK_ZONE_REP_*).
570 */
571 static bool
g_zoned_rep_match(const struct disk_zone_rep_entry * z,uint8_t rep_option)572 g_zoned_rep_match(const struct disk_zone_rep_entry *z, uint8_t rep_option)
573 {
574
575 switch (rep_option) {
576 case DISK_ZONE_REP_ALL:
577 return (true);
578 case DISK_ZONE_REP_EMPTY:
579 return (z->zone_condition == DISK_ZONE_COND_EMPTY);
580 case DISK_ZONE_REP_IMP_OPEN:
581 return (z->zone_condition == DISK_ZONE_COND_IMPLICIT_OPEN);
582 case DISK_ZONE_REP_EXP_OPEN:
583 return (z->zone_condition == DISK_ZONE_COND_EXPLICIT_OPEN);
584 case DISK_ZONE_REP_CLOSED:
585 return (z->zone_condition == DISK_ZONE_COND_CLOSED);
586 case DISK_ZONE_REP_FULL:
587 return (z->zone_condition == DISK_ZONE_COND_FULL);
588 case DISK_ZONE_REP_READONLY:
589 return (z->zone_condition == DISK_ZONE_COND_READONLY);
590 case DISK_ZONE_REP_OFFLINE:
591 return (z->zone_condition == DISK_ZONE_COND_OFFLINE);
592 case DISK_ZONE_REP_RWP:
593 return ((z->zone_flags & DISK_ZONE_FLAG_RESET) != 0);
594 case DISK_ZONE_REP_NON_SEQ:
595 return ((z->zone_flags & DISK_ZONE_FLAG_NON_SEQ) != 0);
596 case DISK_ZONE_REP_NON_WP:
597 return (z->zone_condition == DISK_ZONE_COND_NOT_WP);
598 default:
599 return (false);
600 }
601 }
602
603 static bool
g_zoned_rep_option_valid(uint8_t rep_option)604 g_zoned_rep_option_valid(uint8_t rep_option)
605 {
606
607 return (rep_option <= DISK_ZONE_REP_OFFLINE ||
608 rep_option == DISK_ZONE_REP_RWP ||
609 rep_option == DISK_ZONE_REP_NON_SEQ ||
610 rep_option == DISK_ZONE_REP_NON_WP);
611 }
612
613 /*
614 * Best-effort BIO_DELETE of just-reset zones, so that backing stores can
615 * reclaim the space, similarly to real drives de-allocating a reset zone.
616 */
617 struct g_zoned_punch {
618 struct bio *pu_orig; /* Original BIO_ZONE. */
619 u_int pu_pending; /* Outstanding deletes + 1. */
620 };
621
622 static void
g_zoned_punch_rele(struct g_zoned_punch * pc)623 g_zoned_punch_rele(struct g_zoned_punch *pc)
624 {
625
626 if (atomic_fetchadd_int(&pc->pu_pending, -1) == 1) {
627 g_io_deliver(pc->pu_orig, 0);
628 g_free(pc);
629 }
630 }
631
632 static void
g_zoned_punch_done(struct bio * bp)633 g_zoned_punch_done(struct bio *bp)
634 {
635 struct g_zoned_punch *pc = bp->bio_caller1;
636
637 g_destroy_bio(bp);
638 g_zoned_punch_rele(pc);
639 }
640
641 static void
g_zoned_punch_queue(struct g_zoned_softc * sc,struct bio_queue_head * queue,const struct disk_zone_rep_entry * z)642 g_zoned_punch_queue(struct g_zoned_softc *sc, struct bio_queue_head *queue,
643 const struct disk_zone_rep_entry *z)
644 {
645 struct bio *dbp;
646
647 mtx_assert(&sc->sc_lock, MA_OWNED);
648
649 dbp = g_new_bio();
650 if (dbp == NULL) {
651 G_ZONED_DEBUG(1, "No memory to reclaim zone %ju.",
652 (uint64_t)g_zoned_zoneno(sc, z->zone_start_lba));
653 return;
654 }
655 dbp->bio_cmd = BIO_DELETE;
656 dbp->bio_offset = (off_t)z->zone_start_lba * sc->sc_secsize;
657 dbp->bio_length = sc->sc_zonesize;
658 dbp->bio_data = NULL;
659 bioq_insert_tail(queue, dbp);
660 }
661
662 static void
g_zoned_punch_drain(struct bio_queue_head * queue)663 g_zoned_punch_drain(struct bio_queue_head *queue)
664 {
665 struct bio *dbp;
666
667 while ((dbp = bioq_takefirst(queue)) != NULL)
668 g_destroy_bio(dbp);
669 }
670
671 static void
g_zoned_punch_issue(struct bio * bp,struct g_consumer * cp,struct bio_queue_head * queue)672 g_zoned_punch_issue(struct bio *bp, struct g_consumer *cp,
673 struct bio_queue_head *queue)
674 {
675 struct g_zoned_punch *pc;
676 struct bio *dbp;
677
678 pc = (bioq_first(queue) != NULL) ? g_malloc(sizeof(*pc), M_NOWAIT) :
679 NULL;
680 if (pc == NULL) {
681 g_zoned_punch_drain(queue);
682 g_io_deliver(bp, 0);
683 return;
684 }
685 pc->pu_orig = bp;
686 pc->pu_pending = 1;
687 while ((dbp = bioq_takefirst(queue)) != NULL) {
688 dbp->bio_done = g_zoned_punch_done;
689 dbp->bio_caller1 = pc;
690 atomic_add_int(&pc->pu_pending, 1);
691 g_io_request(dbp, cp);
692 }
693 g_zoned_punch_rele(pc);
694 }
695
696 /*
697 * Emulate BIO_ZONE management commands.
698 */
699 static void
g_zoned_zonecmd(struct bio * bp,struct g_zoned_softc * sc)700 g_zoned_zonecmd(struct bio *bp, struct g_zoned_softc *sc)
701 {
702 struct disk_zone_args *args = &bp->bio_zone;
703 uint32_t i, first, limit;
704
705 switch (args->zone_cmd) {
706 case DISK_ZONE_GET_PARAMS: {
707 struct disk_zone_disk_params *p =
708 &args->zone_params.disk_params;
709
710 p->zone_mode = DISK_ZONE_MODE_HOST_MANAGED;
711 p->flags = DISK_ZONE_RZ_SUP | DISK_ZONE_OPEN_SUP |
712 DISK_ZONE_CLOSE_SUP | DISK_ZONE_FINISH_SUP |
713 DISK_ZONE_RWP_SUP | DISK_ZONE_MAX_SEQ_SET;
714 if (!sc->sc_rdrestrict)
715 p->flags |= DISK_ZONE_DISK_URSWRZ;
716 /* Optimal zone counts are host-aware concepts; leave unset. */
717 p->optimal_seq_zones = 0;
718 p->optimal_nonseq_zones = 0;
719 p->max_seq_zones = (sc->sc_maxopen != 0) ? sc->sc_maxopen :
720 G_ZONED_SEQ_UNLIMITED;
721 g_io_deliver(bp, 0);
722 return;
723 }
724 case DISK_ZONE_REPORT_ZONES: {
725 struct disk_zone_report *rep = &args->zone_params.report;
726 uint32_t filled, zno;
727
728 if (!g_zoned_rep_option_valid(rep->rep_options)) {
729 g_io_deliver(bp, G_ZONED_EXTERR(bp, EINVAL,
730 "Unsupported reporting option. opt=%ju",
731 (uint64_t)rep->rep_options));
732 return;
733 }
734
735 mtx_lock(&sc->sc_lock);
736 sc->sc_zonecmds++;
737 rep->header.same = (sc->sc_convzones == 0 ||
738 sc->sc_convzones == sc->sc_nzones) ?
739 DISK_ZONE_SAME_ALL_SAME : DISK_ZONE_SAME_TYPES_DIFFERENT;
740 rep->header.maximum_lba = sc->sc_maxlba - 1;
741
742 if (rep->starting_id >= sc->sc_maxlba)
743 zno = sc->sc_nzones;
744 else
745 zno = g_zoned_zoneno(sc, rep->starting_id);
746
747 rep->entries_available = 0;
748 filled = 0;
749 for (; zno < sc->sc_nzones; zno++) {
750 if (!g_zoned_rep_match(&sc->sc_zones[zno],
751 rep->rep_options))
752 continue;
753 rep->entries_available++;
754 if (filled < rep->entries_allocated &&
755 rep->entries != NULL)
756 rep->entries[filled++] = sc->sc_zones[zno];
757 }
758 rep->entries_filled = filled;
759 mtx_unlock(&sc->sc_lock);
760 g_io_deliver(bp, 0);
761 return;
762 }
763 case DISK_ZONE_OPEN:
764 case DISK_ZONE_CLOSE:
765 case DISK_ZONE_FINISH:
766 case DISK_ZONE_RWP: {
767 struct disk_zone_rwp *rwp = &args->zone_params.rwp;
768 struct bio_queue_head punchq;
769 struct g_consumer *cp;
770 bool all = (rwp->flags & DISK_ZONE_RWP_FLAG_ALL) != 0;
771 bool punch;
772
773 if (all) {
774 first = 0;
775 limit = sc->sc_nzones;
776 } else {
777 if (rwp->id >= sc->sc_maxlba) {
778 g_io_deliver(bp, G_ZONED_EXTERR(bp, EINVAL,
779 "Zone ID past the last LBA. id=%ju"
780 " maxlba=%ju", (uint64_t)rwp->id,
781 (uint64_t)sc->sc_maxlba));
782 return;
783 }
784 if ((rwp->id % sc->sc_zonesecs) != 0) {
785 g_io_deliver(bp, G_ZONED_EXTERR(bp, EINVAL,
786 "Zone ID is not the lowest LBA of a zone."
787 " id=%ju zonesecs=%ju",
788 (uint64_t)rwp->id,
789 (uint64_t)sc->sc_zonesecs));
790 return;
791 }
792 first = g_zoned_zoneno(sc, rwp->id);
793 limit = first + 1;
794 }
795
796 /*
797 * A reset drops the zone's contents, thus hand them back to the
798 * backing store as long as it is open for writing.
799 */
800 cp = LIST_FIRST(&bp->bio_to->geom->consumer);
801 punch = args->zone_cmd == DISK_ZONE_RWP && cp->acw > 0;
802 bioq_init(&punchq);
803
804 mtx_lock(&sc->sc_lock);
805 sc->sc_zonecmds++;
806
807 /*
808 * "Open all" must fit within the open-zone limit.
809 */
810 if (all && args->zone_cmd == DISK_ZONE_OPEN &&
811 sc->sc_maxopen != 0) {
812 uint32_t nclosed = 0;
813
814 for (i = 0; i < sc->sc_nzones; i++)
815 if (sc->sc_zones[i].zone_condition ==
816 DISK_ZONE_COND_CLOSED)
817 nclosed++;
818 if (sc->sc_nopen + nclosed > sc->sc_maxopen) {
819 mtx_unlock(&sc->sc_lock);
820 g_zoned_punch_drain(&punchq);
821 g_io_deliver(bp, G_ZONED_EXTERR(bp, ENOSPC,
822 "Opening all zones exceeds the open-zone"
823 " limit. need=%ju maxopen=%ju",
824 (uint64_t)(sc->sc_nopen + nclosed),
825 (uint64_t)sc->sc_maxopen));
826 return;
827 }
828 }
829
830 for (i = first; i < limit; i++) {
831 struct disk_zone_rep_entry *z = &sc->sc_zones[i];
832
833 /*
834 * Conventional zones have no write pointer to manage;
835 * "all zones" operations skip them, while explicitly
836 * targeting one is the caller's error. The same goes
837 * for zones taken readonly or offline.
838 */
839 if (z->zone_type == DISK_ZONE_TYPE_CONVENTIONAL ||
840 z->zone_condition == DISK_ZONE_COND_READONLY ||
841 z->zone_condition == DISK_ZONE_COND_OFFLINE) {
842 if (all)
843 continue;
844 mtx_unlock(&sc->sc_lock);
845 g_zoned_punch_drain(&punchq);
846 g_io_deliver(bp, G_ZONED_EXTERR(bp, EINVAL,
847 "Zone has no write pointer to manage."
848 " zone=%ju cond=%ju", (uint64_t)i,
849 (uint64_t)z->zone_condition));
850 return;
851 }
852 switch (args->zone_cmd) {
853 /*
854 * Many operations here are no-op, per ZBC-r06.
855 */
856 case DISK_ZONE_OPEN:
857 if (all) {
858 if (z->zone_condition !=
859 DISK_ZONE_COND_CLOSED)
860 continue;
861 } else if (z->zone_condition ==
862 DISK_ZONE_COND_FULL ||
863 z->zone_condition ==
864 DISK_ZONE_COND_EXPLICIT_OPEN)
865 continue;
866 if (!g_zoned_cond_is_open(z->zone_condition) &&
867 !g_zoned_open_room(sc)) {
868 mtx_unlock(&sc->sc_lock);
869 g_zoned_punch_drain(&punchq);
870 g_io_deliver(bp, G_ZONED_EXTERR(bp,
871 ENOSPC, "Open-zone limit reached."
872 " zone=%ju maxopen=%ju",
873 (uint64_t)i,
874 (uint64_t)sc->sc_maxopen));
875 return;
876 }
877 g_zoned_set_cond(sc, z,
878 DISK_ZONE_COND_EXPLICIT_OPEN);
879 break;
880 case DISK_ZONE_CLOSE:
881 if (!g_zoned_cond_is_open(z->zone_condition))
882 continue;
883 if (z->write_pointer_lba == z->zone_start_lba)
884 g_zoned_set_cond(sc, z,
885 DISK_ZONE_COND_EMPTY);
886 else
887 g_zoned_set_cond(sc, z,
888 DISK_ZONE_COND_CLOSED);
889 break;
890 case DISK_ZONE_FINISH:
891 if (all &&
892 !g_zoned_cond_is_open(z->zone_condition) &&
893 z->zone_condition != DISK_ZONE_COND_CLOSED)
894 continue;
895 if (z->zone_condition == DISK_ZONE_COND_FULL)
896 continue;
897 z->write_pointer_lba = z->zone_start_lba +
898 z->zone_length;
899 g_zoned_set_cond(sc, z, DISK_ZONE_COND_FULL);
900 break;
901 case DISK_ZONE_RWP:
902 /*
903 * Empty zones have nothing to reclaim, but a
904 * reset still clears the "reset recommended"
905 * bit that a fault may have left on it.
906 */
907 if (z->zone_condition == DISK_ZONE_COND_EMPTY) {
908 if ((z->zone_flags &
909 DISK_ZONE_FLAG_RESET) == 0)
910 continue;
911 z->zone_flags &= ~DISK_ZONE_FLAG_RESET;
912 break;
913 }
914 z->write_pointer_lba = z->zone_start_lba;
915 z->zone_flags &= ~DISK_ZONE_FLAG_RESET;
916 g_zoned_set_cond(sc, z, DISK_ZONE_COND_EMPTY);
917 if (punch)
918 g_zoned_punch_queue(sc, &punchq, z);
919 break;
920 }
921 g_zoned_mark_dirty(sc, i);
922 }
923 mtx_unlock(&sc->sc_lock);
924 if (punch) {
925 g_zoned_punch_issue(bp, cp, &punchq);
926 return;
927 }
928 g_io_deliver(bp, 0);
929 return;
930 }
931 default:
932 G_ZONED_LOGREQLVL(1, bp, "Unsupported zone command %u.",
933 args->zone_cmd);
934 g_io_deliver(bp, G_ZONED_EXTERR(bp, EOPNOTSUPP,
935 "Unsupported zone command. cmd=%ju",
936 (uint64_t)args->zone_cmd));
937 return;
938 }
939 }
940
941 /*
942 * Validate a write against the zone model, advancing the write pointer.
943 */
944 static int
g_zoned_write_check(struct g_zoned_softc * sc,struct bio * bp,uint8_t * oldcond)945 g_zoned_write_check(struct g_zoned_softc *sc, struct bio *bp, uint8_t *oldcond)
946 {
947 struct disk_zone_rep_entry *z;
948 uint64_t lba, end;
949 uint32_t i, last, zno;
950
951 mtx_assert(&sc->sc_lock, MA_OWNED);
952
953 *oldcond = DISK_ZONE_COND_NOT_WP;
954
955 lba = bp->bio_offset / sc->sc_secsize;
956 end = (bp->bio_offset + bp->bio_length) / sc->sc_secsize;
957 if (end > sc->sc_maxlba)
958 return (G_ZONED_EXTERR(bp, EIO,
959 "Write past the last LBA. end=%ju maxlba=%ju",
960 (uint64_t)end, (uint64_t)sc->sc_maxlba));
961
962 zno = g_zoned_zoneno(sc, lba);
963 z = &sc->sc_zones[zno];
964
965 if (z->zone_type == DISK_ZONE_TYPE_CONVENTIONAL) {
966 /*
967 * Conventional zones take writes anywhere and requests may
968 * span zone boundaries into other conventional zones.
969 */
970 last = (end > lba) ? g_zoned_zoneno(sc, end - 1) : zno;
971 for (i = zno; i <= last; i++) {
972 if (i > zno && sc->sc_zones[i].zone_type !=
973 DISK_ZONE_TYPE_CONVENTIONAL) {
974 G_ZONED_LOGREQLVL(1, bp, "Write crosses from "
975 "a conventional into a sequential zone.");
976 return (G_ZONED_EXTERR(bp, EIO,
977 "Write crosses from a conventional into a"
978 " sequential zone. lba=%ju zone=%ju",
979 (uint64_t)lba, (uint64_t)i));
980 }
981 if (sc->sc_zones[i].zone_condition ==
982 DISK_ZONE_COND_READONLY ||
983 sc->sc_zones[i].zone_condition ==
984 DISK_ZONE_COND_OFFLINE) {
985 G_ZONED_LOGREQLVL(1, bp,
986 "Write to a readonly/offline zone.");
987 return (G_ZONED_EXTERR(bp, EIO,
988 "Write to a readonly or offline zone."
989 " zone=%ju cond=%ju", (uint64_t)i,
990 (uint64_t)sc->sc_zones[i].zone_condition));
991 }
992 }
993 } else {
994 if (z->zone_condition == DISK_ZONE_COND_READONLY ||
995 z->zone_condition == DISK_ZONE_COND_OFFLINE) {
996 G_ZONED_LOGREQLVL(1, bp,
997 "Write to a readonly/offline zone.");
998 return (G_ZONED_EXTERR(bp, EIO,
999 "Write to a readonly or offline zone."
1000 " zone=%ju cond=%ju", (uint64_t)zno,
1001 (uint64_t)z->zone_condition));
1002 }
1003 if (end > z->zone_start_lba + z->zone_length) {
1004 G_ZONED_LOGREQLVL(1, bp,
1005 "Write crosses a zone boundary.");
1006 return (G_ZONED_EXTERR(bp, EIO,
1007 "Write crosses a zone boundary. end=%ju"
1008 " zoneend=%ju", (uint64_t)end,
1009 (uint64_t)(z->zone_start_lba + z->zone_length)));
1010 }
1011
1012 /*
1013 * Sequential-write-required zones accept writes at the
1014 * WP only.
1015 */
1016 if (z->zone_condition == DISK_ZONE_COND_FULL ||
1017 lba != z->write_pointer_lba) {
1018 G_ZONED_LOGREQLVL(1, bp,
1019 "Out-of-order write to zone %u (lba %ju, wp %ju).",
1020 zno, (uintmax_t)lba,
1021 (uintmax_t)z->write_pointer_lba);
1022 return (G_ZONED_EXTERR(bp, EIO,
1023 "Write pointer violation. lba=%ju wp=%ju",
1024 (uint64_t)lba, (uint64_t)z->write_pointer_lba));
1025 }
1026
1027 /*
1028 * Implicitly opening one more zone(s) must respect the
1029 * open-zone limit; a write that fills the zone outright
1030 * never leaves it open.
1031 */
1032 if (!g_zoned_cond_is_open(z->zone_condition) &&
1033 end < z->zone_start_lba + z->zone_length &&
1034 !g_zoned_open_room(sc)) {
1035 G_ZONED_LOGREQLVL(1, bp,
1036 "Cannot implicitly open zone %u:"
1037 " open-zone limit reached.", zno);
1038 return (G_ZONED_EXTERR(bp, ENOSPC,
1039 "Open-zone limit reached. zone=%ju maxopen=%ju",
1040 (uint64_t)zno, (uint64_t)sc->sc_maxopen));
1041 }
1042
1043 *oldcond = z->zone_condition;
1044 z->write_pointer_lba = end;
1045 if (z->write_pointer_lba >= z->zone_start_lba + z->zone_length)
1046 g_zoned_set_cond(sc, z, DISK_ZONE_COND_FULL);
1047 else if (!g_zoned_cond_is_open(z->zone_condition))
1048 g_zoned_set_cond(sc, z, DISK_ZONE_COND_IMPLICIT_OPEN);
1049 g_zoned_mark_dirty(sc, zno);
1050 }
1051
1052 return (0);
1053 }
1054
1055 /*
1056 * Validate a read against the zone model. A read may never reach a zone of
1057 * a different type than the one it starts in, nor touch an offline zone,
1058 * whatever URSWRZ says; only spanning several sequential zones and reading
1059 * above the write pointer depend on it.
1060 */
1061 static int
g_zoned_read_check(struct g_zoned_softc * sc,struct bio * bp)1062 g_zoned_read_check(struct g_zoned_softc *sc, struct bio *bp)
1063 {
1064 struct disk_zone_rep_entry *z;
1065 uint64_t lba, end;
1066 uint32_t i, last, zno;
1067
1068 mtx_assert(&sc->sc_lock, MA_OWNED);
1069
1070 lba = bp->bio_offset / sc->sc_secsize;
1071 end = (bp->bio_offset + bp->bio_length) / sc->sc_secsize;
1072 if (end > sc->sc_maxlba)
1073 return (G_ZONED_EXTERR(bp, EIO,
1074 "Read past the last LBA. end=%ju maxlba=%ju",
1075 (uint64_t)end, (uint64_t)sc->sc_maxlba));
1076
1077 zno = g_zoned_zoneno(sc, lba);
1078 z = &sc->sc_zones[zno];
1079 last = (end > lba) ? g_zoned_zoneno(sc, end - 1) : zno;
1080
1081 for (i = zno; i <= last; i++) {
1082 if (sc->sc_zones[i].zone_condition == DISK_ZONE_COND_OFFLINE) {
1083 G_ZONED_LOGREQLVL(1, bp, "Read from an offline zone.");
1084 return (G_ZONED_EXTERR(bp, EIO,
1085 "Read from an offline zone. zone=%ju",
1086 (uint64_t)i));
1087 }
1088 if (i > zno && sc->sc_zones[i].zone_type != z->zone_type) {
1089 G_ZONED_LOGREQLVL(1, bp, "Read crosses into a zone of "
1090 "a different type.");
1091 return (G_ZONED_EXTERR(bp, EIO,
1092 "Read crosses into a zone of a different type."
1093 " lba=%ju zone=%ju", (uint64_t)lba,
1094 (uint64_t)i));
1095 }
1096 }
1097
1098 if (!sc->sc_rdrestrict || z->zone_type == DISK_ZONE_TYPE_CONVENTIONAL)
1099 return (0);
1100
1101 if (end > z->zone_start_lba + z->zone_length) {
1102 G_ZONED_LOGREQLVL(1, bp, "Read crosses a zone boundary.");
1103 return (G_ZONED_EXTERR(bp, EIO,
1104 "Read crosses a zone boundary. end=%ju zoneend=%ju",
1105 (uint64_t)end,
1106 (uint64_t)(z->zone_start_lba + z->zone_length)));
1107 }
1108 if (end > z->write_pointer_lba) {
1109 G_ZONED_LOGREQLVL(1, bp,
1110 "Read above the write pointer of zone %u (lba %ju,"
1111 " wp %ju).", zno, (uintmax_t)lba,
1112 (uintmax_t)z->write_pointer_lba);
1113 return (G_ZONED_EXTERR(bp, EIO,
1114 "Read above the write pointer. end=%ju wp=%ju",
1115 (uint64_t)end, (uint64_t)z->write_pointer_lba));
1116 }
1117 return (0);
1118 }
1119
1120 /*
1121 * A write whose data never reaches the medium leaves the zone claiming blocks
1122 * that were never written, so retract the optimistic write-pointer advance, as
1123 * long as no later write has moved the pointer further. The zone rolls back
1124 * to the condition the write found it in.
1125 */
1126 static void
g_zoned_write_undo(struct g_zoned_softc * sc,off_t offset,off_t length,uint8_t oldcond)1127 g_zoned_write_undo(struct g_zoned_softc *sc, off_t offset, off_t length,
1128 uint8_t oldcond)
1129 {
1130 struct disk_zone_rep_entry *z;
1131 uint64_t lba, end;
1132 uint32_t zno;
1133
1134 lba = offset / sc->sc_secsize;
1135 end = (offset + length) / sc->sc_secsize;
1136
1137 mtx_lock(&sc->sc_lock);
1138 zno = g_zoned_zoneno(sc, lba);
1139 z = &sc->sc_zones[zno];
1140 if (z->zone_type != DISK_ZONE_TYPE_CONVENTIONAL &&
1141 z->write_pointer_lba == end) {
1142 z->write_pointer_lba = lba;
1143 g_zoned_set_cond(sc, z, oldcond);
1144 g_zoned_mark_dirty(sc, zno);
1145 }
1146 mtx_unlock(&sc->sc_lock);
1147 }
1148
1149 static void
g_zoned_write_done(struct bio * cbp)1150 g_zoned_write_done(struct bio *cbp)
1151 {
1152 struct g_zoned_softc *sc;
1153 struct bio *pbp;
1154
1155 pbp = cbp->bio_parent;
1156 sc = pbp->bio_to->geom->softc;
1157 if (cbp->bio_error != 0 && sc != NULL)
1158 g_zoned_write_undo(sc, cbp->bio_offset, cbp->bio_length,
1159 (uint8_t)(uintptr_t)cbp->bio_caller1);
1160 g_std_done(cbp);
1161 }
1162
1163 static void
g_zoned_start(struct bio * bp)1164 g_zoned_start(struct bio *bp)
1165 {
1166 struct g_zoned_softc *sc;
1167 struct g_geom *gp;
1168 struct bio *cbp;
1169 int error;
1170 uint8_t oldcond = DISK_ZONE_COND_NOT_WP;
1171
1172 gp = bp->bio_to->geom;
1173 sc = gp->softc;
1174 G_ZONED_LOGREQ(bp, "Request received.");
1175
1176 switch (bp->bio_cmd) {
1177 case BIO_ZONE:
1178 g_zoned_zonecmd(bp, sc);
1179 return;
1180 case BIO_WRITE:
1181 mtx_lock(&sc->sc_lock);
1182 error = g_zoned_write_check(sc, bp, &oldcond);
1183 if (error != 0) {
1184 mtx_unlock(&sc->sc_lock);
1185 g_io_deliver(bp, error);
1186 return;
1187 }
1188 sc->sc_writes++;
1189 sc->sc_wrotebytes += bp->bio_length;
1190 mtx_unlock(&sc->sc_lock);
1191 break;
1192 case BIO_READ:
1193 mtx_lock(&sc->sc_lock);
1194 error = g_zoned_read_check(sc, bp);
1195 if (error != 0) {
1196 mtx_unlock(&sc->sc_lock);
1197 g_io_deliver(bp, error);
1198 return;
1199 }
1200 sc->sc_reads++;
1201 sc->sc_readbytes += bp->bio_length;
1202 mtx_unlock(&sc->sc_lock);
1203 break;
1204 case BIO_FLUSH:
1205 if (g_zoned_flush_begin(sc, gp, bp))
1206 return;
1207 break;
1208 case BIO_GETATTR:
1209 /* BIO_DELETE is refused below, don't advertise it. */
1210 if (g_handleattr_int(bp, "GEOM::candelete", 0))
1211 return;
1212 break;
1213 case BIO_DELETE:
1214 /*
1215 * Zoned device have no unmap; sequential zones are reclaimed by
1216 * resetting the write pointer instead.
1217 */
1218 g_io_deliver(bp, G_ZONED_EXTERR(bp, EOPNOTSUPP,
1219 "Zoned providers have no unmap; reset the write pointer"
1220 " instead."));
1221 return;
1222 default:
1223 break;
1224 }
1225
1226 cbp = g_clone_bio(bp);
1227 if (cbp == NULL) {
1228 if (bp->bio_cmd == BIO_WRITE)
1229 g_zoned_write_undo(sc, bp->bio_offset, bp->bio_length,
1230 oldcond);
1231 g_io_deliver(bp, ENOMEM);
1232 return;
1233 }
1234 if (bp->bio_cmd == BIO_WRITE) {
1235 cbp->bio_done = g_zoned_write_done;
1236 cbp->bio_caller1 = (void *)(uintptr_t)oldcond;
1237 } else
1238 cbp->bio_done = g_std_done;
1239 G_ZONED_LOGREQ(cbp, "Sending request.");
1240 g_io_request(cbp, LIST_FIRST(&gp->consumer));
1241 }
1242
1243 static int
g_zoned_access(struct g_provider * pp __unused,int dr __unused,int dw __unused,int de __unused)1244 g_zoned_access(struct g_provider *pp __unused, int dr __unused,
1245 int dw __unused, int de __unused)
1246 {
1247
1248 return (0);
1249 }
1250
1251 static struct g_geom *
g_zoned_create(struct g_class * mp,const struct g_zoned_metadata * md,struct g_provider * pp)1252 g_zoned_create(struct g_class *mp, const struct g_zoned_metadata *md,
1253 struct g_provider *pp)
1254 {
1255 struct g_zoned_softc *sc;
1256 struct g_geom *gp;
1257 struct g_provider *newpp;
1258 struct g_consumer *cp;
1259 char name[64];
1260 uint64_t nzones, zonesecs;
1261 uint32_t i;
1262 int error;
1263
1264 g_topology_assert();
1265
1266 nzones = g_zoned_nzones(pp->mediasize, md->md_zonesize, pp->sectorsize);
1267 zonesecs = md->md_zonesize / pp->sectorsize;
1268 if (nzones == 0 || zonesecs == 0 || zonesecs > G_ZONED_MAXZONESECS ||
1269 (md->md_zonesize % pp->sectorsize) != 0 ||
1270 md->md_nconv > G_ZONED_MAXCONV ||
1271 (md->md_flags & ~G_ZONED_MD_FLAGSMASK) != 0) {
1272 G_ZONED_DEBUG(0, "Bogus metadata on %s.", pp->name);
1273 return (NULL);
1274 }
1275 for (i = 0; i < md->md_nconv; i++) {
1276 if (md->md_conv[i].cr_count == 0 ||
1277 md->md_conv[i].cr_first >= nzones ||
1278 md->md_conv[i].cr_count >
1279 nzones - md->md_conv[i].cr_first) {
1280 G_ZONED_DEBUG(0,
1281 "Bogus conventional zone range on %s.", pp->name);
1282 return (NULL);
1283 }
1284 }
1285
1286 snprintf(name, sizeof(name), "%s%s", pp->name, G_ZONED_SUFFIX);
1287 LIST_FOREACH(gp, &mp->geom, geom) {
1288 if (strcmp(gp->name, name) == 0) {
1289 G_ZONED_DEBUG(0, "Device %s already exists.", name);
1290 return (NULL);
1291 }
1292 }
1293
1294 gp = g_new_geom(mp, name);
1295 sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
1296
1297 sc->sc_id = md->md_id;
1298 sc->sc_zonesize = md->md_zonesize;
1299 sc->sc_secsize = pp->sectorsize;
1300 sc->sc_zonesecs = zonesecs;
1301 sc->sc_nzones = (uint32_t)nzones;
1302 sc->sc_maxlba = nzones * zonesecs;
1303 sc->sc_nconv = md->md_nconv;
1304 sc->sc_maxopen = md->md_maxopen;
1305 sc->sc_rdrestrict = (md->md_flags & G_ZONED_MD_RESTRICTED_READS) != 0;
1306 bcopy(md->md_conv, sc->sc_conv, sizeof(sc->sc_conv));
1307 /* Count per zone so that overlapping ranges are not double-counted. */
1308 for (i = 0; i < nzones; i++)
1309 if (g_zoned_is_conv(sc, i))
1310 sc->sc_convzones++;
1311 sc->sc_zones = malloc(nzones * sizeof(*sc->sc_zones), M_ZONED,
1312 M_NOWAIT | M_ZERO);
1313 if (sc->sc_zones == NULL) {
1314 G_ZONED_DEBUG(0, "Cannot allocate a %u zone table for %s.",
1315 sc->sc_nzones, pp->name);
1316 g_free(sc);
1317 g_destroy_geom(gp);
1318 return (NULL);
1319 }
1320 sc->sc_tabsecs = 1 +
1321 howmany(nzones * G_ZONED_ENTRY_SIZE, pp->sectorsize);
1322 sc->sc_taboff = (pp->mediasize - pp->sectorsize) -
1323 (off_t)sc->sc_tabsecs * pp->sectorsize;
1324 g_zoned_reset_zones(sc);
1325 mtx_init(&sc->sc_lock, "gzoned lock", NULL, MTX_DEF);
1326 gp->softc = sc;
1327
1328 newpp = g_new_providerf(gp, "%s", gp->name);
1329 newpp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE;
1330 newpp->mediasize = (off_t)nzones * md->md_zonesize;
1331 newpp->sectorsize = pp->sectorsize;
1332 newpp->stripesize = pp->stripesize;
1333 newpp->stripeoffset = pp->stripeoffset;
1334
1335 cp = g_new_consumer(gp);
1336 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
1337 error = g_attach(cp, pp);
1338 if (error != 0) {
1339 G_ZONED_DEBUG(0, "Cannot attach to provider %s.", pp->name);
1340 goto fail;
1341 }
1342 /* Hold the backing provider open as long as the device exists. */
1343 error = g_access(cp, 1, 1, 1);
1344 if (error != 0) {
1345 G_ZONED_DEBUG(0, "Cannot access provider %s.", pp->name);
1346 goto fail;
1347 }
1348
1349 /* Restore the persistent zone state or initialise an empty table. */
1350 g_zoned_load_table(sc, cp);
1351
1352 newpp->flags |= pp->flags & G_PF_ACCEPT_UNMAPPED;
1353 g_error_provider(newpp, 0);
1354 G_ZONED_DEBUG(0, "Device %s created (%u zones of %jd bytes).",
1355 gp->name, sc->sc_nzones, (intmax_t)sc->sc_zonesize);
1356 return (gp);
1357 fail:
1358 if (cp->provider != NULL)
1359 g_detach(cp);
1360 g_destroy_consumer(cp);
1361 g_destroy_provider(newpp);
1362 g_destroy_geom(gp);
1363 return (NULL);
1364 }
1365
1366 static int
g_zoned_read_metadata(struct g_consumer * cp,struct g_zoned_metadata * md)1367 g_zoned_read_metadata(struct g_consumer *cp, struct g_zoned_metadata *md)
1368 {
1369 struct g_provider *pp;
1370 u_char *buf;
1371 int error;
1372
1373 g_topology_assert();
1374
1375 error = g_access(cp, 1, 0, 0);
1376 if (error != 0)
1377 return (error);
1378 pp = cp->provider;
1379 g_topology_unlock();
1380 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
1381 &error);
1382 g_topology_lock();
1383 g_access(cp, -1, 0, 0);
1384 if (buf == NULL)
1385 return (error);
1386 zoned_metadata_decode(buf, md);
1387 g_free(buf);
1388 return (0);
1389 }
1390
1391 static struct g_geom *
g_zoned_taste(struct g_class * mp,struct g_provider * pp,int flags __unused)1392 g_zoned_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1393 {
1394 struct g_zoned_metadata md;
1395 struct g_zoned_softc *sc;
1396 struct g_consumer *cp;
1397 struct g_geom *gp;
1398 int error;
1399
1400 g_topology_assert();
1401
1402 /* Skip providers that are already open for writing. */
1403 if (pp->acw > 0)
1404 return (NULL);
1405
1406 G_ZONED_DEBUG(3, "Tasting %s.", pp->name);
1407
1408 gp = g_new_geom(mp, "zoned:taste");
1409 cp = g_new_consumer(gp);
1410 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
1411 error = g_attach(cp, pp);
1412 if (error == 0) {
1413 error = g_zoned_read_metadata(cp, &md);
1414 g_detach(cp);
1415 }
1416 g_destroy_consumer(cp);
1417 g_destroy_geom(gp);
1418 if (error != 0)
1419 return (NULL);
1420
1421 if (strcmp(md.md_magic, G_ZONED_MAGIC) != 0)
1422 return (NULL);
1423 if (md.md_version > G_ZONED_VERSION) {
1424 printf("geom_zoned.ko module is too old to handle %s.\n",
1425 pp->name);
1426 return (NULL);
1427 }
1428 if (md.md_provsize != (uint64_t)pp->mediasize)
1429 return (NULL);
1430 if (md.md_sectorsize != pp->sectorsize)
1431 return (NULL);
1432
1433 /* Already running? */
1434 LIST_FOREACH(gp, &mp->geom, geom) {
1435 sc = gp->softc;
1436 if (sc != NULL && sc->sc_id == md.md_id)
1437 return (NULL);
1438 }
1439
1440 gp = g_zoned_create(mp, &md, pp);
1441 if (gp == NULL)
1442 G_ZONED_DEBUG(0, "Cannot create zoned device on %s.",
1443 pp->name);
1444 return (gp);
1445 }
1446
1447 static int
g_zoned_destroy(struct g_geom * gp,boolean_t force)1448 g_zoned_destroy(struct g_geom *gp, boolean_t force)
1449 {
1450 struct g_zoned_softc *sc;
1451 struct g_provider *pp;
1452
1453 g_topology_assert();
1454 sc = gp->softc;
1455 if (sc == NULL)
1456 return (ENXIO);
1457 /* An orphaned provider and a stop request can both land here. */
1458 if ((gp->flags & G_GEOM_WITHER) != 0)
1459 return (ENXIO);
1460 pp = LIST_FIRST(&gp->provider);
1461 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
1462 if (force) {
1463 G_ZONED_DEBUG(0,
1464 "Device %s is still open, so it "
1465 "can't be definitely removed.",
1466 pp->name);
1467 } else {
1468 G_ZONED_DEBUG(1, "Device %s is still open (r%dw%de%d).",
1469 pp->name, pp->acr, pp->acw, pp->ace);
1470 return (EBUSY);
1471 }
1472 } else {
1473 G_ZONED_DEBUG(0, "Device %s removed.", gp->name);
1474 }
1475
1476 /* Commit unflushed zone state to survive the stop. */
1477 g_zoned_flush_sync(sc, LIST_FIRST(&gp->consumer));
1478
1479 /*
1480 * Give up the reference taken when the device was created, so that
1481 * withering can reap the consumer.
1482 */
1483 g_wither_geom_close(gp, ENXIO);
1484 return (0);
1485 }
1486
1487 static int
g_zoned_destroy_geom(struct gctl_req * req __unused,struct g_class * mp __unused,struct g_geom * gp)1488 g_zoned_destroy_geom(struct gctl_req *req __unused,
1489 struct g_class *mp __unused, struct g_geom *gp)
1490 {
1491
1492 return (g_zoned_destroy(gp, 0));
1493 }
1494
1495 static void
g_zoned_orphan(struct g_consumer * cp)1496 g_zoned_orphan(struct g_consumer *cp)
1497 {
1498
1499 g_topology_assert();
1500 g_zoned_destroy(cp->geom, 1);
1501 }
1502
1503 /*
1504 * The metadata and zone table live at the tail of the backing provider, which
1505 * has just moved: neither the on-disk state nor the zone layout can stay
1506 * consistent, so tear the device down. Unflushed zone state is discarded rather
1507 * than written to what is no longer the table's location.
1508 */
1509 static void
g_zoned_resize(struct g_consumer * cp)1510 g_zoned_resize(struct g_consumer *cp)
1511 {
1512 struct g_zoned_softc *sc;
1513
1514 g_topology_assert();
1515
1516 sc = cp->geom->softc;
1517 if (sc == NULL)
1518 return;
1519 G_ZONED_DEBUG(0, "Provider %s resized; destroying %s.",
1520 cp->provider->name, cp->geom->name);
1521 mtx_lock(&sc->sc_lock);
1522 sc->sc_dirty = false;
1523 sc->sc_hdrdirty = false;
1524 mtx_unlock(&sc->sc_lock);
1525 g_zoned_destroy(cp->geom, 1);
1526 }
1527
1528 static void
g_zoned_providergone(struct g_provider * pp)1529 g_zoned_providergone(struct g_provider *pp)
1530 {
1531 struct g_geom *gp = pp->geom;
1532 struct g_zoned_softc *sc = gp->softc;
1533
1534 gp->softc = NULL;
1535 free(sc->sc_zones, M_ZONED);
1536 mtx_destroy(&sc->sc_lock);
1537 g_free(sc);
1538 }
1539
1540 static struct g_geom *
g_zoned_find_geom(struct g_class * mp,const char * name)1541 g_zoned_find_geom(struct g_class *mp, const char *name)
1542 {
1543 struct g_geom *gp;
1544
1545 if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
1546 name += strlen(_PATH_DEV);
1547
1548 LIST_FOREACH(gp, &mp->geom, geom) {
1549 if (gp->softc != NULL && strcmp(gp->name, name) == 0)
1550 return (gp);
1551 }
1552 return (NULL);
1553 }
1554
1555 static void
g_zoned_ctl_create(struct gctl_req * req,struct g_class * mp)1556 g_zoned_ctl_create(struct gctl_req *req, struct g_class *mp)
1557 {
1558 struct g_zoned_metadata md;
1559 struct g_provider *pp;
1560 struct g_consumer *cp;
1561 struct g_geom *gp;
1562 const char *name;
1563 u_char *sector, *buf;
1564 int *nargs, error, len;
1565
1566 g_topology_assert();
1567
1568 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1569 if (nargs == NULL) {
1570 gctl_error(req, "No '%s' argument.", "nargs");
1571 return;
1572 }
1573 if (*nargs != 1) {
1574 gctl_error(req, "Missing device.");
1575 return;
1576 }
1577 name = gctl_get_asciiparam(req, "arg0");
1578 if (name == NULL) {
1579 gctl_error(req, "No 'arg0' argument.");
1580 return;
1581 }
1582 if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
1583 name += strlen(_PATH_DEV);
1584 pp = g_provider_by_name(name);
1585 if (pp == NULL) {
1586 gctl_error(req, "No such provider: %s.", name);
1587 return;
1588 }
1589 sector = gctl_get_param(req, "metadata", &len);
1590 if (sector == NULL) {
1591 gctl_error(req, "No '%s' argument.", "metadata");
1592 return;
1593 }
1594 if (len < (int)sizeof(struct g_zoned_metadata) ||
1595 len > (int)pp->sectorsize) {
1596 gctl_error(req, "Invalid '%s' argument.", "metadata");
1597 return;
1598 }
1599
1600 /*
1601 * The geometry was measured in userland, before the request reached
1602 * the topology lock, so make sure the provider still matches it.
1603 */
1604 zoned_metadata_decode(sector, &md);
1605 if (memcmp(md.md_magic, G_ZONED_MAGIC, sizeof(G_ZONED_MAGIC)) != 0 ||
1606 md.md_version != G_ZONED_VERSION ||
1607 md.md_provsize != (uint64_t)pp->mediasize ||
1608 md.md_sectorsize != pp->sectorsize) {
1609 gctl_error(req, "Invalid metadata for %s.", name);
1610 return;
1611 }
1612
1613 gp = g_zoned_create(mp, &md, pp);
1614 if (gp == NULL) {
1615 gctl_error(req, "Cannot create a zoned device on %s.", name);
1616 return;
1617 }
1618
1619 /* The tail sector is ours now; pad the metadata out to fill it. */
1620 cp = LIST_FIRST(&gp->consumer);
1621 buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
1622 memcpy(buf, sector, len);
1623 g_topology_unlock();
1624 error = g_write_data(cp, pp->mediasize - pp->sectorsize, buf,
1625 pp->sectorsize);
1626 if (error == 0)
1627 error = g_io_flush(cp);
1628 g_topology_lock();
1629 g_free(buf);
1630 if (error != 0) {
1631 gctl_error(req, "Cannot store metadata on %s (error=%d).",
1632 name, error);
1633 (void)g_zoned_destroy(gp, 1);
1634 }
1635 }
1636
1637 static void
g_zoned_ctl_destroy(struct gctl_req * req,struct g_class * mp)1638 g_zoned_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1639 {
1640 struct g_geom *gp;
1641 const char *name;
1642 char param[16];
1643 int *force, *nargs, error, i;
1644
1645 g_topology_assert();
1646
1647 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1648 if (nargs == NULL) {
1649 gctl_error(req, "No '%s' argument.", "nargs");
1650 return;
1651 }
1652 if (*nargs <= 0) {
1653 gctl_error(req, "Missing device(s).");
1654 return;
1655 }
1656 force = gctl_get_paraml(req, "force", sizeof(*force));
1657 if (force == NULL) {
1658 gctl_error(req, "No '%s' argument.", "force");
1659 return;
1660 }
1661
1662 for (i = 0; i < *nargs; i++) {
1663 snprintf(param, sizeof(param), "arg%d", i);
1664 name = gctl_get_asciiparam(req, param);
1665 if (name == NULL) {
1666 gctl_error(req, "No 'arg%d' argument.", i);
1667 return;
1668 }
1669 gp = g_zoned_find_geom(mp, name);
1670 if (gp == NULL) {
1671 gctl_error(req, "No such device: %s.", name);
1672 return;
1673 }
1674 error = g_zoned_destroy(gp, *force);
1675 if (error != 0) {
1676 gctl_error(req, "Cannot destroy device %s (error=%d).",
1677 gp->name, error);
1678 return;
1679 }
1680 }
1681 }
1682
1683 /*
1684 * Fault injection: force a zone readonly or offline and flag it as needing a
1685 * write-pointer reset, or clear the fault again. The state persists through the
1686 * zone table.
1687 */
1688 static void
g_zoned_ctl_fault(struct gctl_req * req,struct g_class * mp)1689 g_zoned_ctl_fault(struct gctl_req *req, struct g_class *mp)
1690 {
1691 struct g_zoned_softc *sc;
1692 struct g_geom *gp;
1693 struct disk_zone_rep_entry *z;
1694 const char *name, *state;
1695 intmax_t *zone;
1696 uint32_t zno;
1697
1698 g_topology_assert();
1699
1700 name = gctl_get_asciiparam(req, "arg0");
1701 if (name == NULL) {
1702 gctl_error(req, "Missing device.");
1703 return;
1704 }
1705 zone = gctl_get_paraml(req, "zone", sizeof(*zone));
1706 if (zone == NULL) {
1707 gctl_error(req, "No '%s' argument.", "zone");
1708 return;
1709 }
1710 state = gctl_get_asciiparam(req, "state");
1711 if (state == NULL) {
1712 gctl_error(req, "No '%s' argument.", "state");
1713 return;
1714 }
1715 gp = g_zoned_find_geom(mp, name);
1716 if (gp == NULL) {
1717 gctl_error(req, "No such device: %s.", name);
1718 return;
1719 }
1720 sc = gp->softc;
1721 if (*zone < 0 || (uintmax_t)*zone >= sc->sc_nzones) {
1722 gctl_error(req, "Zone %jd out of range.", *zone);
1723 return;
1724 }
1725 zno = (uint32_t)*zone;
1726
1727 mtx_lock(&sc->sc_lock);
1728 z = &sc->sc_zones[zno];
1729 if (strcmp(state, "ro") == 0 || strcmp(state, "readonly") == 0)
1730 g_zoned_set_cond(sc, z, DISK_ZONE_COND_READONLY);
1731 else if (strcmp(state, "offline") == 0)
1732 g_zoned_set_cond(sc, z, DISK_ZONE_COND_OFFLINE);
1733 else if (strcmp(state, "reset") == 0)
1734 z->zone_flags |= DISK_ZONE_FLAG_RESET;
1735 else if (strcmp(state, "clear") == 0) {
1736 /* Reobtain the normal condition from the write pointer. */
1737 z->zone_flags &= ~DISK_ZONE_FLAG_RESET;
1738 if (z->zone_type == DISK_ZONE_TYPE_CONVENTIONAL)
1739 g_zoned_set_cond(sc, z, DISK_ZONE_COND_NOT_WP);
1740 else if (z->write_pointer_lba == z->zone_start_lba)
1741 g_zoned_set_cond(sc, z, DISK_ZONE_COND_EMPTY);
1742 else if (z->write_pointer_lba ==
1743 z->zone_start_lba + z->zone_length)
1744 g_zoned_set_cond(sc, z, DISK_ZONE_COND_FULL);
1745 else
1746 g_zoned_set_cond(sc, z, DISK_ZONE_COND_CLOSED);
1747 } else {
1748 mtx_unlock(&sc->sc_lock);
1749 gctl_error(req, "Invalid state '%s'.", state);
1750 return;
1751 }
1752 g_zoned_mark_dirty(sc, zno);
1753 mtx_unlock(&sc->sc_lock);
1754 }
1755
1756 static void
g_zoned_config(struct gctl_req * req,struct g_class * mp,const char * verb)1757 g_zoned_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1758 {
1759 uint32_t *version;
1760
1761 g_topology_assert();
1762
1763 version = gctl_get_paraml(req, "version", sizeof(*version));
1764 if (version == NULL) {
1765 gctl_error(req, "No '%s' argument.", "version");
1766 return;
1767 }
1768 if (*version != G_ZONED_VERSION) {
1769 gctl_error(req, "Userland and kernel parts are out of sync.");
1770 return;
1771 }
1772
1773 if (strcmp(verb, "create") == 0) {
1774 g_zoned_ctl_create(req, mp);
1775 return;
1776 }
1777 if (strcmp(verb, "destroy") == 0 || strcmp(verb, "stop") == 0) {
1778 g_zoned_ctl_destroy(req, mp);
1779 return;
1780 }
1781 if (strcmp(verb, "fault") == 0) {
1782 g_zoned_ctl_fault(req, mp);
1783 return;
1784 }
1785 gctl_error(req, "Unknown verb.");
1786 }
1787
1788 static void
g_zoned_dumpconf(struct sbuf * sb,const char * indent,struct g_geom * gp,struct g_consumer * cp,struct g_provider * pp)1789 g_zoned_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1790 struct g_consumer *cp, struct g_provider *pp)
1791 {
1792 struct g_zoned_softc *sc;
1793
1794 if (pp != NULL || cp != NULL)
1795 return;
1796 sc = gp->softc;
1797 sbuf_printf(sb, "%s<ZoneSize>%jd</ZoneSize>\n", indent,
1798 (intmax_t)sc->sc_zonesize);
1799 sbuf_printf(sb, "%s<Zones>%u</Zones>\n", indent, sc->sc_nzones);
1800 sbuf_printf(sb, "%s<ConventionalZones>%u</ConventionalZones>\n",
1801 indent, sc->sc_convzones);
1802 sbuf_printf(sb, "%s<Mode>Host Managed</Mode>\n", indent);
1803 sbuf_printf(sb, "%s<UnrestrictedReads>%s</UnrestrictedReads>\n", indent,
1804 sc->sc_rdrestrict ? "No" : "Yes");
1805 sbuf_printf(sb, "%s<MaxOpenZones>%u</MaxOpenZones>\n", indent,
1806 sc->sc_maxopen);
1807 sbuf_printf(sb, "%s<OpenZones>%u</OpenZones>\n", indent, sc->sc_nopen);
1808 sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
1809 sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
1810 sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
1811 sc->sc_readbytes);
1812 sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
1813 sc->sc_wrotebytes);
1814 sbuf_printf(sb, "%s<ZoneCommands>%ju</ZoneCommands>\n", indent,
1815 sc->sc_zonecmds);
1816 }
1817
1818 DECLARE_GEOM_CLASS(g_zoned_class, g_zoned);
1819 MODULE_VERSION(geom_zoned, 0);
1820