1 /*- 2 * Copyright (c) 2000 Michael Smith 3 * Copyright (c) 2001 Scott Long 4 * Copyright (c) 2000 BSDi 5 * Copyright (c) 2001 Adaptec, Inc. 6 * All rights reserved. 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 AUTHOR 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 AUTHOR 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 * $FreeBSD: src/sys/dev/aac/aac_disk.c,v 1.3.2.8 2003/01/11 18:39:39 scottl Exp $ 30 * $DragonFly: src/sys/dev/raid/aac/aac_disk.c,v 1.10 2005/08/08 01:25:31 hmp Exp $ 31 */ 32 33 #include "opt_aac.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/sysctl.h> 39 40 #include "aac_compat.h" 41 #include <sys/bus.h> 42 #include <sys/conf.h> 43 #include <sys/devicestat.h> 44 #include <sys/disk.h> 45 46 #include <vm/vm.h> 47 #include <vm/pmap.h> 48 49 #include <machine/md_var.h> 50 #include <machine/bus.h> 51 #include <sys/rman.h> 52 53 #include "aacreg.h" 54 #include "aac_ioctl.h" 55 #include "aacvar.h" 56 57 /* 58 * Interface to parent. 59 */ 60 static int aac_disk_probe(device_t dev); 61 static int aac_disk_attach(device_t dev); 62 static int aac_disk_detach(device_t dev); 63 64 /* 65 * Interface to the device switch. 66 */ 67 static d_open_t aac_disk_open; 68 static d_close_t aac_disk_close; 69 static d_strategy_t aac_disk_strategy; 70 static d_dump_t aac_disk_dump; 71 72 #define AAC_DISK_CDEV_MAJOR 151 73 74 static struct cdevsw aac_disk_cdevsw = { 75 /* name */ "aacd", 76 /* maj */ AAC_DISK_CDEV_MAJOR, 77 /* flags */ D_DISK, 78 /* port */ NULL, 79 /* clone */ NULL, 80 81 /* open */ aac_disk_open, 82 /* close */ aac_disk_close, 83 /* read */ physread, 84 /* write */ physwrite, 85 /* ioctl */ noioctl, 86 /* poll */ nopoll, 87 /* mmap */ nommap, 88 /* strategy */ aac_disk_strategy, 89 /* dump */ aac_disk_dump, 90 /* psize */ nopsize 91 }; 92 93 devclass_t aac_disk_devclass; 94 95 static device_method_t aac_disk_methods[] = { 96 DEVMETHOD(device_probe, aac_disk_probe), 97 DEVMETHOD(device_attach, aac_disk_attach), 98 DEVMETHOD(device_detach, aac_disk_detach), 99 { 0, 0 } 100 }; 101 102 static driver_t aac_disk_driver = { 103 "aacd", 104 aac_disk_methods, 105 sizeof(struct aac_disk) 106 }; 107 108 #define AAC_MAXIO 65536 109 110 DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0); 111 112 /* sysctl tunables */ 113 static unsigned int aac_iosize_max = AAC_MAXIO; /* due to limits of the card */ 114 TUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max); 115 116 SYSCTL_DECL(_hw_aac); 117 SYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0, 118 "Max I/O size per transfer to an array"); 119 120 /* 121 * Handle open from generic layer. 122 * 123 * This is called by the diskslice code on first open in order to get the 124 * basic device geometry paramters. 125 */ 126 static int 127 aac_disk_open(dev_t dev, int flags, int fmt, d_thread_t *td) 128 { 129 struct aac_disk *sc; 130 struct disklabel *label; 131 132 debug_called(0); 133 134 sc = (struct aac_disk *)dev->si_drv1; 135 136 if (sc == NULL) { 137 printf("aac_disk_open: No Softc\n"); 138 return (ENXIO); 139 } 140 141 /* check that the controller is up and running */ 142 if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) { 143 printf("Controller Suspended controller state = 0x%x\n", 144 sc->ad_controller->aac_state); 145 return(ENXIO); 146 } 147 148 /* build synthetic label */ 149 label = &sc->ad_disk.d_label; 150 bzero(label, sizeof(*label)); 151 label->d_type = DTYPE_ESDI; 152 label->d_secsize = AAC_BLOCK_SIZE; 153 label->d_nsectors = sc->ad_sectors; 154 label->d_ntracks = sc->ad_heads; 155 label->d_ncylinders = sc->ad_cylinders; 156 label->d_secpercyl = sc->ad_sectors * sc->ad_heads; 157 label->d_secperunit = sc->ad_size; 158 159 sc->ad_flags |= AAC_DISK_OPEN; 160 return (0); 161 } 162 163 /* 164 * Handle last close of the disk device. 165 */ 166 static int 167 aac_disk_close(dev_t dev, int flags, int fmt, d_thread_t *td) 168 { 169 struct aac_disk *sc; 170 171 debug_called(0); 172 173 sc = (struct aac_disk *)dev->si_drv1; 174 175 if (sc == NULL) 176 return (ENXIO); 177 178 sc->ad_flags &= ~AAC_DISK_OPEN; 179 return (0); 180 } 181 182 /* 183 * Handle an I/O request. 184 */ 185 static void 186 aac_disk_strategy(struct buf *bp) 187 { 188 struct aac_disk *sc; 189 190 debug_called(4); 191 192 sc = (struct aac_disk *)bp->b_dev->si_drv1; 193 194 /* bogus disk? */ 195 if (sc == NULL) { 196 bp->b_flags |= B_ERROR; 197 bp->b_error = EINVAL; 198 biodone(bp); 199 return; 200 } 201 202 /* do-nothing operation? */ 203 if (bp->b_bcount == 0) { 204 bp->b_resid = bp->b_bcount; 205 biodone(bp); 206 return; 207 } 208 209 /* perform accounting */ 210 devstat_start_transaction(&sc->ad_stats); 211 212 /* pass the bio to the controller - it can work out who we are */ 213 aac_submit_bio(bp); 214 return; 215 } 216 217 /* 218 * Dump memory out to an array 219 * 220 * This queues blocks of memory of size AAC_MAXIO to the controller and waits 221 * for the controller to complete the requests. 222 */ 223 static int 224 aac_disk_dump(dev_t dev, u_int count, u_int blkno, u_int secsize) 225 { 226 struct aac_disk *ad; 227 struct aac_softc *sc; 228 vm_offset_t addr; 229 long blkcnt; 230 int dumppages; 231 int i, error; 232 233 ad = dev->si_drv1; 234 addr = 0; 235 dumppages = AAC_MAXIO / PAGE_SIZE; 236 237 if (ad == NULL) 238 return (ENXIO); 239 240 sc= ad->ad_controller; 241 242 blkcnt = howmany(PAGE_SIZE, secsize); 243 244 while (count > 0) { 245 caddr_t va = NULL; 246 247 if ((count / blkcnt) < dumppages) 248 dumppages = count / blkcnt; 249 250 for (i = 0; i < dumppages; ++i) { 251 vm_offset_t a = addr + (i * PAGE_SIZE); 252 if (is_physical_memory(a)) { 253 va = pmap_kenter_temporary(trunc_page(a), i); 254 } else { 255 va = pmap_kenter_temporary(trunc_page(0), i); 256 } 257 } 258 259 retry: 260 /* 261 * Queue the block to the controller. If the queue is full, 262 * EBUSY will be returned. 263 */ 264 error = aac_dump_enqueue(ad, blkno, va, dumppages); 265 if (error && (error != EBUSY)) 266 return (error); 267 268 if (!error) { 269 if (dumpstatus(addr, (off_t)(count * DEV_BSIZE)) < 0) 270 return (EINTR); 271 272 blkno += blkcnt * dumppages; 273 count -= blkcnt * dumppages; 274 addr += PAGE_SIZE * dumppages; 275 if (count > 0) 276 continue; 277 } 278 279 /* 280 * Either the queue was full on the last attemp, or we have no 281 * more data to dump. Let the queue drain out and retry the 282 * block if the queue was full. 283 */ 284 aac_dump_complete(sc); 285 286 if (error == EBUSY) 287 goto retry; 288 } 289 290 return (0); 291 } 292 293 /* 294 * Handle completion of an I/O request. 295 */ 296 void 297 aac_biodone(struct buf *bp) 298 { 299 struct aac_disk *sc; 300 int blkno; 301 302 debug_called(4); 303 304 sc = (struct aac_disk *)bp->b_dev->si_drv1; 305 306 devstat_end_transaction_bio(&sc->ad_stats, bp); 307 if (bp->b_flags & BIO_ERROR) { 308 blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 309 #if defined(__FreeBSD__) && __FreeBSD_version > 500005 310 diskerr(bp, bp->b_dev, 311 (char *)bp->bio_driver1, blkno, &sc->ad_label); 312 #else 313 diskerr(bp, bp->b_dev, 314 (char *)bp->b_driver1, 0, blkno, &sc->ad_label); 315 #endif 316 } 317 biodone(bp); 318 } 319 320 /* 321 * Stub only. 322 */ 323 static int 324 aac_disk_probe(device_t dev) 325 { 326 327 debug_called(2); 328 329 return (0); 330 } 331 332 /* 333 * Attach a unit to the controller. 334 */ 335 static int 336 aac_disk_attach(device_t dev) 337 { 338 struct aac_disk *sc; 339 340 debug_called(0); 341 342 sc = (struct aac_disk *)device_get_softc(dev); 343 344 /* initialise our softc */ 345 sc->ad_controller = 346 (struct aac_softc *)device_get_softc(device_get_parent(dev)); 347 sc->ad_container = device_get_ivars(dev); 348 sc->ad_dev = dev; 349 350 /* 351 * require that extended translation be enabled - other drivers read the 352 * disk! 353 */ 354 sc->ad_size = sc->ad_container->co_mntobj.Capacity; 355 if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 356 sc->ad_heads = 255; 357 sc->ad_sectors = 63; 358 } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 359 sc->ad_heads = 128; 360 sc->ad_sectors = 32; 361 } else { 362 sc->ad_heads = 64; 363 sc->ad_sectors = 32; 364 } 365 sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 366 367 device_printf(dev, "%uMB (%u sectors)\n", 368 sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 369 sc->ad_size); 370 371 devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev), 372 AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS, 373 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 374 DEVSTAT_PRIORITY_ARRAY); 375 376 /* attach a generic disk device to ourselves */ 377 sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0, 378 &aac_disk_cdevsw); 379 sc->ad_dev_t->si_drv1 = sc; 380 381 sc->ad_dev_t->si_iosize_max = aac_iosize_max; 382 sc->unit = device_get_unit(dev); 383 384 return (0); 385 } 386 387 /* 388 * Disconnect ourselves from the system. 389 */ 390 static int 391 aac_disk_detach(device_t dev) 392 { 393 struct aac_disk *sc; 394 395 debug_called(2); 396 397 sc = (struct aac_disk *)device_get_softc(dev); 398 399 if (sc->ad_flags & AAC_DISK_OPEN) 400 return(EBUSY); 401 402 devstat_remove_entry(&sc->ad_stats); 403 disk_destroy(&sc->ad_disk); 404 return(0); 405 } 406