From owner-svn-src-all@FreeBSD.ORG Thu Jul 4 17:53:03 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 4AAFC64C; Thu, 4 Jul 2013 17:53:03 +0000 (UTC) (envelope-from bryanv@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 2BFDB15D1; Thu, 4 Jul 2013 17:53:03 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r64Hr3H5086786; Thu, 4 Jul 2013 17:53:03 GMT (envelope-from bryanv@svn.freebsd.org) Received: (from bryanv@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r64Hr25J086782; Thu, 4 Jul 2013 17:53:02 GMT (envelope-from bryanv@svn.freebsd.org) Message-Id: <201307041753.r64Hr25J086782@svn.freebsd.org> From: Bryan Venteicher Date: Thu, 4 Jul 2013 17:53:02 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r252703 - in head: share/man/man4 sys/dev/virtio/block X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 04 Jul 2013 17:53:03 -0000 Author: bryanv Date: Thu Jul 4 17:53:02 2013 New Revision: 252703 URL: http://svnweb.freebsd.org/changeset/base/252703 Log: Merge several virtio_blk changes from projects/virtio The notable changes of this commit are support for disk resizing and chases updates to the spec regarding write caching. Contains projects/virtio commits: r245713: virtio_blk: Replace __FUNCTION__ with __func__ r245714: virtio_blk: Use more consistent mutex name r245715: virtio_blk: Print device name too if failed to reinit during dump r245716: virtio_blk: Remove an unuseful ASSERT r245723: virtio_blk: Record the vendor and device information r245724: virtio_blk: Add resize support r245726: virtio_blk: More verbose ASSERT messages r245730: virtio_blk: Tweak resize announcement message r246061: virtio_blk: Do not always read entire config r246062: virtio_blk: Use topology to set the stripe size/offset r246307: virtio_blk: Correct stripe offset calculation r246063: virtio_blk: Add support for write cache enable feature r246303: virtio_blk: Expand a comment r252529: virtio_blk: Improve write cache handling r252681: virtio_blk: Remove unneeded curly braces MFC after: 1 month Modified: head/share/man/man4/virtio_blk.4 head/sys/dev/virtio/block/virtio_blk.c head/sys/dev/virtio/block/virtio_blk.h Modified: head/share/man/man4/virtio_blk.4 ============================================================================== --- head/share/man/man4/virtio_blk.4 Thu Jul 4 17:50:11 2013 (r252702) +++ head/share/man/man4/virtio_blk.4 Thu Jul 4 17:53:02 2013 (r252703) @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 22, 2012 +.Dd July 2, 2013 .Dt VIRTIO_BLK 4 .Os .Sh NAME @@ -53,11 +53,33 @@ Tunables can be set at the .Xr loader 8 prompt before booting the kernel or stored in .Xr loader.conf 5 . -.Bl -tag -width "xxxxxx" +.Bl -tag -width indent .It Va hw.vtblk.no_ident -This tunable disables retrieving the device identification string -from the hypervisor. +.It Va hw.vtblk. Ns Ar X Ns Va .no_ident +.Pp +These tunables disable retrieving the device identification string +from the hypervisor either globally or per-device. The default value is 0. +.It Va hw.vtblk.writecache_mode +.It Va hw.vtblk. Ns Ar X Ns Va .writecache_mode +.Pp +These tunables determine the write cache mode globally or per-device. +The mode can changed only if the ConfigWCE feature is negotiated. +Set to 0 for writethrough mode, 1 for writeback mode, and -1 to leave +it as-is. +The default value is to leave as-is. +.El +.Sh SYSCTL VARIABLES +The following variables are available as +.Xr sysctl 8 +variables. +.Bl -tag -width indent +.It Va dev.vtblk. Ns Ar X Ns Va .writecache_mode +.Pp +The write cache mode of the device can be either writethrough (0) or +writeback (1). +If the ConfigWCE feature is negotiated, the write cache mode can +be toggled between writethrough and writeback. .El .Sh SEE ALSO .Xr virtio 4 Modified: head/sys/dev/virtio/block/virtio_blk.c ============================================================================== --- head/sys/dev/virtio/block/virtio_blk.c Thu Jul 4 17:50:11 2013 (r252702) +++ head/sys/dev/virtio/block/virtio_blk.c Thu Jul 4 17:53:02 2013 (r252703) @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -61,6 +62,12 @@ struct vtblk_request { TAILQ_ENTRY(vtblk_request) vbr_link; }; +enum vtblk_cache_mode { + VTBLK_CACHE_WRITETHROUGH, + VTBLK_CACHE_WRITEBACK, + VTBLK_CACHE_MAX +}; + struct vtblk_softc { device_t vtblk_dev; struct mtx vtblk_mtx; @@ -72,6 +79,7 @@ struct vtblk_softc { #define VTBLK_FLAG_SUSPEND 0x0008 #define VTBLK_FLAG_DUMPING 0x0010 #define VTBLK_FLAG_BARRIER 0x0020 +#define VTBLK_FLAG_WC_CONFIG 0x0040 struct virtqueue *vtblk_vq; struct sglist *vtblk_sglist; @@ -86,6 +94,7 @@ struct vtblk_softc { int vtblk_max_nsegs; int vtblk_request_count; + enum vtblk_cache_mode vtblk_write_cache; struct vtblk_request vtblk_dump_request; }; @@ -98,8 +107,9 @@ static struct virtio_feature_desc vtblk_ { VIRTIO_BLK_F_RO, "ReadOnly" }, { VIRTIO_BLK_F_BLK_SIZE, "BlockSize" }, { VIRTIO_BLK_F_SCSI, "SCSICmds" }, - { VIRTIO_BLK_F_FLUSH, "FlushCmd" }, + { VIRTIO_BLK_F_WCE, "WriteCache" }, { VIRTIO_BLK_F_TOPOLOGY, "Topology" }, + { VIRTIO_BLK_F_CONFIG_WCE, "ConfigWCE" }, { 0, NULL } }; @@ -112,6 +122,7 @@ static int vtblk_detach(device_t); static int vtblk_suspend(device_t); static int vtblk_resume(device_t); static int vtblk_shutdown(device_t); +static int vtblk_config_change(device_t); static int vtblk_open(struct disk *); static int vtblk_close(struct disk *); @@ -124,6 +135,11 @@ static void vtblk_negotiate_features(str static int vtblk_maximum_segments(struct vtblk_softc *, struct virtio_blk_config *); static int vtblk_alloc_virtqueue(struct vtblk_softc *); +static void vtblk_resize_disk(struct vtblk_softc *, uint64_t); +static void vtblk_set_write_cache(struct vtblk_softc *, int); +static int vtblk_write_cache_enabled(struct vtblk_softc *sc, + struct virtio_blk_config *); +static int vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS); static void vtblk_alloc_disk(struct vtblk_softc *, struct virtio_blk_config *); static void vtblk_create_disk(struct vtblk_softc *); @@ -138,6 +154,8 @@ static void vtblk_vq_intr(void *); static void vtblk_stop(struct vtblk_softc *); +static void vtblk_read_config(struct vtblk_softc *, + struct virtio_blk_config *); static void vtblk_get_ident(struct vtblk_softc *); static void vtblk_prepare_dump(struct vtblk_softc *); static int vtblk_write_dump(struct vtblk_softc *, void *, off_t, size_t); @@ -162,9 +180,14 @@ static void vtblk_enqueue_ready(struct v static int vtblk_request_error(struct vtblk_request *); static void vtblk_finish_bio(struct bio *, int); +static void vtblk_setup_sysctl(struct vtblk_softc *); +static int vtblk_tunable_int(struct vtblk_softc *, const char *, int); + /* Tunables. */ static int vtblk_no_ident = 0; TUNABLE_INT("hw.vtblk.no_ident", &vtblk_no_ident); +static int vtblk_writecache_mode = -1; +TUNABLE_INT("hw.vtblk.writecache_mode", &vtblk_writecache_mode); /* Features desired/implemented by this driver. */ #define VTBLK_FEATURES \ @@ -174,13 +197,14 @@ TUNABLE_INT("hw.vtblk.no_ident", &vtblk_ VIRTIO_BLK_F_GEOMETRY | \ VIRTIO_BLK_F_RO | \ VIRTIO_BLK_F_BLK_SIZE | \ - VIRTIO_BLK_F_FLUSH | \ + VIRTIO_BLK_F_WCE | \ + VIRTIO_BLK_F_CONFIG_WCE | \ VIRTIO_RING_F_INDIRECT_DESC) #define VTBLK_MTX(_sc) &(_sc)->vtblk_mtx #define VTBLK_LOCK_INIT(_sc, _name) \ mtx_init(VTBLK_MTX((_sc)), (_name), \ - "VTBLK Lock", MTX_DEF) + "VirtIO Block Lock", MTX_DEF) #define VTBLK_LOCK(_sc) mtx_lock(VTBLK_MTX((_sc))) #define VTBLK_UNLOCK(_sc) mtx_unlock(VTBLK_MTX((_sc))) #define VTBLK_LOCK_DESTROY(_sc) mtx_destroy(VTBLK_MTX((_sc))) @@ -206,6 +230,9 @@ static device_method_t vtblk_methods[] = DEVMETHOD(device_resume, vtblk_resume), DEVMETHOD(device_shutdown, vtblk_shutdown), + /* VirtIO methods. */ + DEVMETHOD(virtio_config_change, vtblk_config_change), + DEVMETHOD_END }; @@ -279,10 +306,13 @@ vtblk_attach(device_t dev) sc->vtblk_flags |= VTBLK_FLAG_READONLY; if (virtio_with_feature(dev, VIRTIO_BLK_F_BARRIER)) sc->vtblk_flags |= VTBLK_FLAG_BARRIER; + if (virtio_with_feature(dev, VIRTIO_BLK_F_CONFIG_WCE)) + sc->vtblk_flags |= VTBLK_FLAG_WC_CONFIG; + + vtblk_setup_sysctl(sc); /* Get local copy of config. */ - virtio_read_device_config(dev, 0, &blkcfg, - sizeof(struct virtio_blk_config)); + vtblk_read_config(sc, &blkcfg); /* * With the current sglist(9) implementation, it is not easy @@ -418,6 +448,26 @@ vtblk_shutdown(device_t dev) } static int +vtblk_config_change(device_t dev) +{ + struct vtblk_softc *sc; + struct virtio_blk_config blkcfg; + uint64_t capacity; + + sc = device_get_softc(dev); + + vtblk_read_config(sc, &blkcfg); + + /* Capacity is always in 512-byte units. */ + capacity = blkcfg.capacity * 512; + + if (sc->vtblk_disk->d_mediasize != capacity) + vtblk_resize_disk(sc, capacity); + + return (0); +} + +static int vtblk_open(struct disk *dp) { struct vtblk_softc *sc; @@ -518,8 +568,8 @@ vtblk_strategy(struct bio *bp) max_nsegs = sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS; KASSERT(nsegs <= max_nsegs, - ("bio %p spanned too many segments: %d, max: %d", - bp, nsegs, max_nsegs)); + ("%s: bio %p spanned too many segments: %d, max: %d", + __func__, bp, nsegs, max_nsegs)); } #endif @@ -583,6 +633,84 @@ vtblk_alloc_virtqueue(struct vtblk_softc } static void +vtblk_resize_disk(struct vtblk_softc *sc, uint64_t new_capacity) +{ + device_t dev; + struct disk *dp; + int error; + + dev = sc->vtblk_dev; + dp = sc->vtblk_disk; + + dp->d_mediasize = new_capacity; + if (bootverbose) { + device_printf(dev, "resized to %juMB (%ju %u byte sectors)\n", + (uintmax_t) dp->d_mediasize >> 20, + (uintmax_t) dp->d_mediasize / dp->d_sectorsize, + dp->d_sectorsize); + } + + error = disk_resize(dp, M_NOWAIT); + if (error) { + device_printf(dev, + "disk_resize(9) failed, error: %d\n", error); + } +} + +static void +vtblk_set_write_cache(struct vtblk_softc *sc, int wc) +{ + + /* Set either writeback (1) or writethrough (0) mode. */ + virtio_write_dev_config_1(sc->vtblk_dev, + offsetof(struct virtio_blk_config, writeback), wc); +} + +static int +vtblk_write_cache_enabled(struct vtblk_softc *sc, + struct virtio_blk_config *blkcfg) +{ + int wc; + + if (sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) { + wc = vtblk_tunable_int(sc, "writecache_mode", + vtblk_writecache_mode); + if (wc >= 0 && wc < VTBLK_CACHE_MAX) + vtblk_set_write_cache(sc, wc); + else + wc = blkcfg->writeback; + } else + wc = virtio_with_feature(sc->vtblk_dev, VIRTIO_BLK_F_WCE); + + return (wc); +} + +static int +vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct vtblk_softc *sc; + int wc, error; + + sc = oidp->oid_arg1; + wc = sc->vtblk_write_cache; + + error = sysctl_handle_int(oidp, &wc, 0, req); + if (error || req->newptr == NULL) + return (error); + if ((sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) == 0) + return (EPERM); + if (wc < 0 || wc >= VTBLK_CACHE_MAX) + return (EINVAL); + + VTBLK_LOCK(sc); + sc->vtblk_write_cache = wc; + vtblk_set_write_cache(sc, sc->vtblk_write_cache); + VTBLK_UNLOCK(sc); + + return (0); +} + +static void vtblk_alloc_disk(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg) { device_t dev; @@ -598,6 +726,11 @@ vtblk_alloc_disk(struct vtblk_softc *sc, dp->d_name = VTBLK_DISK_NAME; dp->d_unit = device_get_unit(dev); dp->d_drv1 = sc; + dp->d_flags = DISKFLAG_CANFLUSHCACHE; + dp->d_hba_vendor = virtio_get_vendor(dev); + dp->d_hba_device = virtio_get_device(dev); + dp->d_hba_subvendor = virtio_get_subvendor(dev); + dp->d_hba_subdevice = virtio_get_subdevice(dev); if ((sc->vtblk_flags & VTBLK_FLAG_READONLY) == 0) dp->d_dump = vtblk_dump; @@ -633,8 +766,18 @@ vtblk_alloc_disk(struct vtblk_softc *sc, dp->d_fwheads = blkcfg->geometry.heads; } - if (virtio_with_feature(dev, VIRTIO_BLK_F_FLUSH)) - dp->d_flags |= DISKFLAG_CANFLUSHCACHE; + if (virtio_with_feature(dev, VIRTIO_BLK_F_TOPOLOGY)) { + dp->d_stripesize = dp->d_sectorsize * + (1 << blkcfg->topology.physical_block_exp); + dp->d_stripeoffset = (dp->d_stripesize - + blkcfg->topology.alignment_offset * dp->d_sectorsize) % + dp->d_stripesize; + } + + if (vtblk_write_cache_enabled(sc, blkcfg) != 0) + sc->vtblk_write_cache = VTBLK_CACHE_WRITEBACK; + else + sc->vtblk_write_cache = VTBLK_CACHE_WRITETHROUGH; } static void @@ -742,8 +885,7 @@ vtblk_bio_request(struct vtblk_softc *sc req->vbr_hdr.sector = bp->bio_offset / 512; break; default: - panic("%s: bio with unhandled cmd: %d", __FUNCTION__, - bp->bio_cmd); + panic("%s: bio with unhandled cmd: %d", __func__, bp->bio_cmd); } return (req); @@ -786,14 +928,13 @@ vtblk_execute_request(struct vtblk_softc } sglist_reset(sg); - sglist_append(sg, &req->vbr_hdr, sizeof(struct virtio_blk_outhdr)); if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { error = sglist_append(sg, bp->bio_data, bp->bio_bcount); if (error || sg->sg_nseg == sg->sg_maxseg) panic("%s: data buffer too big bio:%p error:%d", - __FUNCTION__, bp, error); + __func__, bp, error); /* BIO_READ means the host writes into our buffer. */ if (bp->bio_cmd == BIO_READ) @@ -851,6 +992,37 @@ vtblk_stop(struct vtblk_softc *sc) virtio_stop(sc->vtblk_dev); } +#define VTBLK_GET_CONFIG(_dev, _feature, _field, _cfg) \ + if (virtio_with_feature(_dev, _feature)) { \ + virtio_read_device_config(_dev, \ + offsetof(struct virtio_blk_config, _field), \ + &(_cfg)->_field, sizeof((_cfg)->_field)); \ + } + +static void +vtblk_read_config(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg) +{ + device_t dev; + + dev = sc->vtblk_dev; + + bzero(blkcfg, sizeof(struct virtio_blk_config)); + + /* The capacity is always available. */ + virtio_read_device_config(dev, offsetof(struct virtio_blk_config, + capacity), &blkcfg->capacity, sizeof(blkcfg->capacity)); + + /* Read the configuration if the feature was negotiated. */ + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SIZE_MAX, size_max, blkcfg); + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SEG_MAX, seg_max, blkcfg); + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_GEOMETRY, geometry, blkcfg); + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_BLK_SIZE, blk_size, blkcfg); + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_TOPOLOGY, topology, blkcfg); + VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_CONFIG_WCE, writeback, blkcfg); +} + +#undef VTBLK_GET_CONFIG + static void vtblk_get_ident(struct vtblk_softc *sc) { @@ -862,7 +1034,7 @@ vtblk_get_ident(struct vtblk_softc *sc) dp = sc->vtblk_disk; len = MIN(VIRTIO_BLK_ID_BYTES, DISK_IDENT_SIZE); - if (vtblk_no_ident != 0) + if (vtblk_tunable_int(sc, "no_ident", vtblk_no_ident) != 0) return; req = vtblk_dequeue_request(sc); @@ -912,8 +1084,10 @@ vtblk_prepare_dump(struct vtblk_softc *s */ vtblk_drain_vq(sc, 1); - if (virtio_reinit(dev, sc->vtblk_features) != 0) - panic("cannot reinit VirtIO block device during dump"); + if (virtio_reinit(dev, sc->vtblk_features) != 0) { + panic("%s: cannot reinit VirtIO block device during dump", + device_get_nameunit(dev)); + } virtqueue_disable_intr(vq); virtio_reinit_complete(dev); @@ -966,7 +1140,6 @@ static int vtblk_poll_request(struct vtblk_softc *sc, struct vtblk_request *req) { struct virtqueue *vq; - struct vtblk_request *r; int error; vq = sc->vtblk_vq; @@ -979,14 +1152,12 @@ vtblk_poll_request(struct vtblk_softc *s return (error); virtqueue_notify(vq); - - r = virtqueue_poll(vq, NULL); - KASSERT(r == req, ("unexpected request response: %p/%p", r, req)); + virtqueue_poll(vq, NULL); error = vtblk_request_error(req); if (error && bootverbose) { device_printf(sc->vtblk_dev, - "%s: IO error: %d\n", __FUNCTION__, error); + "%s: IO error: %d\n", __func__, error); } return (error); @@ -1117,7 +1288,7 @@ vtblk_free_requests(struct vtblk_softc * struct vtblk_request *req; KASSERT(TAILQ_EMPTY(&sc->vtblk_req_ready), - ("ready requests left on queue")); + ("%s: ready requests left on queue", __func__)); while ((req = vtblk_dequeue_request(sc)) != NULL) { sc->vtblk_request_count--; @@ -1125,7 +1296,7 @@ vtblk_free_requests(struct vtblk_softc * } KASSERT(sc->vtblk_request_count == 0, - ("leaked requests: %d", sc->vtblk_request_count)); + ("%s: leaked %d requests", __func__, sc->vtblk_request_count)); } static struct vtblk_request * @@ -1199,3 +1370,33 @@ vtblk_finish_bio(struct bio *bp, int err biodone(bp); } + +static void +vtblk_setup_sysctl(struct vtblk_softc *sc) +{ + device_t dev; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid_list *child; + + dev = sc->vtblk_dev; + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "writecache_mode", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, vtblk_write_cache_sysctl, + "I", "Write cache mode (writethrough (0) or writeback (1))"); +} + +static int +vtblk_tunable_int(struct vtblk_softc *sc, const char *knob, int def) +{ + char path[64]; + + snprintf(path, sizeof(path), + "hw.vtblk.%d.%s", device_get_unit(sc->vtblk_dev), knob); + TUNABLE_INT_FETCH(path, &def); + + return (def); +} Modified: head/sys/dev/virtio/block/virtio_blk.h ============================================================================== --- head/sys/dev/virtio/block/virtio_blk.h Thu Jul 4 17:50:11 2013 (r252702) +++ head/sys/dev/virtio/block/virtio_blk.h Thu Jul 4 17:53:02 2013 (r252703) @@ -39,8 +39,9 @@ #define VIRTIO_BLK_F_RO 0x0020 /* Disk is read-only */ #define VIRTIO_BLK_F_BLK_SIZE 0x0040 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 0x0080 /* Supports scsi command passthru */ -#define VIRTIO_BLK_F_FLUSH 0x0200 /* Cache flush command support */ +#define VIRTIO_BLK_F_WCE 0x0200 /* Writeback mode enabled after reset */ #define VIRTIO_BLK_F_TOPOLOGY 0x0400 /* Topology information is available */ +#define VIRTIO_BLK_F_CONFIG_WCE 0x0800 /* Writeback mode available in config */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ @@ -51,15 +52,27 @@ struct virtio_blk_config { uint32_t size_max; /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ uint32_t seg_max; - /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */ + /* Geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */ struct virtio_blk_geometry { uint16_t cylinders; uint8_t heads; uint8_t sectors; } geometry; - /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + /* Block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ uint32_t blk_size; + + /* Topology of the device (if VIRTIO_BLK_F_TOPOLOGY) */ + struct virtio_blk_topology { + uint8_t physical_block_exp; + uint8_t alignment_offset; + uint16_t min_io_size; + uint16_t opt_io_size; + } topology; + + /* Writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ + uint8_t writeback; + } __packed; /*