1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5 * All rights reserved. 6 * Copyright (c) 2025 Mateusz Piotrowski <0mp@FreeBSD.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/bio.h> 32 #include <sys/kernel.h> 33 #include <sys/limits.h> 34 #include <sys/malloc.h> 35 #include <sys/queue.h> 36 #include <sys/sysctl.h> 37 #include <sys/systm.h> 38 #include <sys/uio.h> 39 #include <sys/types.h> 40 41 #include <geom/geom.h> 42 43 #define G_ZERO_CLASS_NAME "ZERO" 44 45 static int g_zero_byte_sysctl(SYSCTL_HANDLER_ARGS); 46 47 SYSCTL_DECL(_kern_geom); 48 static SYSCTL_NODE(_kern_geom, OID_AUTO, zero, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 49 "GEOM_ZERO stuff"); 50 static int g_zero_clear = 1; 51 SYSCTL_INT(_kern_geom_zero, OID_AUTO, clear, 52 CTLFLAG_RWTUN, &g_zero_clear, 0, 53 "Clear read data buffer"); 54 static int g_zero_byte = 0; 55 static uint8_t g_zero_buffer[PAGE_SIZE]; 56 SYSCTL_PROC(_kern_geom_zero, OID_AUTO, byte, 57 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &g_zero_byte, 0, 58 g_zero_byte_sysctl, "I", 59 "Byte (octet) value to clear the buffers with"); 60 61 static struct g_provider *gpp; 62 63 static int 64 g_zero_byte_sysctl(SYSCTL_HANDLER_ARGS) 65 { 66 int error; 67 68 // XXX: Confirm that this is called on module load as well. 69 // XXX: Shouldn't we lock here to avoid changing the byte value if the 70 // driver is in the process of handling I/O? 71 error = sysctl_handle_int(oidp, &g_zero_byte, 0, req); 72 if (error != 0 || req->newptr == NULL) 73 return (error); 74 memset(g_zero_buffer, g_zero_byte, PAGE_SIZE); 75 return (0); 76 } 77 78 static void 79 g_zero_fill_pages(struct bio *bp) 80 { 81 struct iovec aiovec; 82 struct uio auio; 83 size_t length; 84 vm_offset_t offset; 85 86 aiovec.iov_base = g_zero_buffer; 87 aiovec.iov_len = PAGE_SIZE; 88 auio.uio_iov = &aiovec; 89 auio.uio_iovcnt = 1; 90 auio.uio_offset = 0; 91 auio.uio_segflg = UIO_SYSSPACE; 92 auio.uio_rw = UIO_WRITE; 93 auio.uio_td = curthread; 94 95 /* 96 * To handle the unmapped I/O request, we need to fill the pages in the 97 * bp->bio_ma array with the g_zero_byte value. However, instead of 98 * setting every byte individually, we use uiomove_fromphys() to fill a 99 * page at a time with g_zero_buffer. 100 */ 101 bp->bio_resid = bp->bio_length; 102 offset = bp->bio_ma_offset & PAGE_MASK; 103 for (int i = 0; i < bp->bio_ma_n && bp->bio_resid > 0; i++) { 104 length = MIN(PAGE_SIZE - offset, bp->bio_resid); 105 auio.uio_resid = length; 106 107 (void)uiomove_fromphys(&bp->bio_ma[i], offset, length, &auio); 108 109 offset = 0; 110 bp->bio_resid -= length; 111 } 112 } 113 114 115 static void 116 g_zero_start(struct bio *bp) 117 { 118 int error; 119 120 switch (bp->bio_cmd) { 121 case BIO_READ: 122 if (g_zero_clear) { 123 if ((bp->bio_flags & BIO_UNMAPPED) != 0) 124 g_zero_fill_pages(bp); 125 else 126 memset(bp->bio_data, g_zero_byte, 127 bp->bio_length); 128 } 129 /* FALLTHROUGH */ 130 case BIO_DELETE: 131 case BIO_WRITE: 132 bp->bio_completed = bp->bio_length; 133 error = 0; 134 break; 135 case BIO_GETATTR: 136 default: 137 error = EOPNOTSUPP; 138 break; 139 } 140 g_io_deliver(bp, error); 141 } 142 143 static void 144 g_zero_init(struct g_class *mp) 145 { 146 struct g_geom *gp; 147 struct g_provider *pp; 148 149 g_topology_assert(); 150 gp = g_new_geom(mp, "gzero"); 151 gp->start = g_zero_start; 152 gp->access = g_std_access; 153 gpp = pp = g_new_providerf(gp, "%s", gp->name); 154 pp->flags |= G_PF_ACCEPT_UNMAPPED | G_PF_DIRECT_SEND | 155 G_PF_DIRECT_RECEIVE; 156 pp->mediasize = 1152921504606846976LLU; 157 pp->sectorsize = 512; 158 g_error_provider(pp, 0); 159 } 160 161 static int 162 g_zero_destroy_geom(struct gctl_req *req __unused, struct g_class *mp __unused, 163 struct g_geom *gp) 164 { 165 struct g_provider *pp; 166 167 g_topology_assert(); 168 if (gp == NULL) 169 return (0); 170 pp = LIST_FIRST(&gp->provider); 171 if (pp == NULL) 172 return (0); 173 if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) 174 return (EBUSY); 175 gpp = NULL; 176 g_wither_geom(gp, ENXIO); 177 return (0); 178 } 179 180 static struct g_class g_zero_class = { 181 .name = G_ZERO_CLASS_NAME, 182 .version = G_VERSION, 183 .init = g_zero_init, 184 .destroy_geom = g_zero_destroy_geom 185 }; 186 187 DECLARE_GEOM_CLASS(g_zero_class, g_zero); 188 MODULE_VERSION(geom_zero, 0); 189