aac_disk.c revision 111220
1296417Sdim/*- 2284677Sdim * Copyright (c) 2000 Michael Smith 3284677Sdim * Copyright (c) 2001 Scott Long 4284677Sdim * Copyright (c) 2000 BSDi 5284677Sdim * Copyright (c) 2001 Adaptec, Inc. 6284677Sdim * All rights reserved. 7284677Sdim * 8284677Sdim * Redistribution and use in source and binary forms, with or without 9284677Sdim * modification, are permitted provided that the following conditions 10284677Sdim * are met: 11284677Sdim * 1. Redistributions of source code must retain the above copyright 12284677Sdim * notice, this list of conditions and the following disclaimer. 13284677Sdim * 2. Redistributions in binary form must reproduce the above copyright 14284677Sdim * notice, this list of conditions and the following disclaimer in the 15296417Sdim * documentation and/or other materials provided with the distribution. 16296417Sdim * 17296417Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18284677Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19284677Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20284677Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21296417Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22284677Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23285181Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24284677Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25284677Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26284677Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27284677Sdim * SUCH DAMAGE. 28284677Sdim * 29284677Sdim * $FreeBSD: head/sys/dev/aac/aac_disk.c 111220 2003-02-21 19:00:48Z phk $ 30284677Sdim */ 31284677Sdim 32284677Sdim#include "opt_aac.h" 33284677Sdim 34284677Sdim#include <sys/param.h> 35284677Sdim#include <sys/systm.h> 36284677Sdim#include <sys/kernel.h> 37284677Sdim#include <sys/sysctl.h> 38284677Sdim 39284677Sdim#include <dev/aac/aac_compat.h> 40284677Sdim#include <sys/bus.h> 41284677Sdim#include <sys/conf.h> 42284677Sdim#include <sys/devicestat.h> 43284677Sdim#include <sys/disk.h> 44284677Sdim 45284677Sdim#include <vm/vm.h> 46284677Sdim#include <vm/pmap.h> 47284677Sdim 48284677Sdim#include <machine/md_var.h> 49284677Sdim#include <machine/bus.h> 50284677Sdim#include <sys/rman.h> 51285181Sdim 52285181Sdim#include <dev/aac/aacreg.h> 53285181Sdim#include <dev/aac/aac_ioctl.h> 54285181Sdim#include <dev/aac/aacvar.h> 55285181Sdim 56296417Sdim/* 57296417Sdim * Interface to parent. 58285181Sdim */ 59285181Sdimstatic int aac_disk_probe(device_t dev); 60284677Sdimstatic int aac_disk_attach(device_t dev); 61284677Sdimstatic int aac_disk_detach(device_t dev); 62284677Sdim 63284677Sdim/* 64284677Sdim * Interface to the device switch. 65284677Sdim */ 66284677Sdimstatic d_open_t aac_disk_open; 67284677Sdimstatic d_close_t aac_disk_close; 68284677Sdimstatic d_strategy_t aac_disk_strategy; 69284677Sdimstatic dumper_t aac_disk_dump; 70284677Sdim 71284677Sdim#define AAC_DISK_CDEV_MAJOR 151 72284677Sdim 73284677Sdimstatic struct cdevsw aac_disk_cdevsw = { 74296417Sdim /* open */ aac_disk_open, 75284677Sdim /* close */ aac_disk_close, 76284677Sdim /* read */ physread, 77284677Sdim /* write */ physwrite, 78284677Sdim /* ioctl */ noioctl, 79286684Sdim /* poll */ nopoll, 80296417Sdim /* mmap */ nommap, 81284677Sdim /* strategy */ aac_disk_strategy, 82284677Sdim /* name */ "aacd", 83284677Sdim /* maj */ AAC_DISK_CDEV_MAJOR, 84284677Sdim /* dump */ aac_disk_dump, 85284677Sdim /* psize */ nopsize, 86284677Sdim /* flags */ D_DISK, 87284677Sdim#if __FreeBSD_version < 500005 88284677Sdim /* bmaj */ -1 89284677Sdim#endif 90284677Sdim}; 91284677Sdim 92296417Sdimstatic devclass_t aac_disk_devclass; 93287521Sdimstatic struct cdevsw aac_disk_disk_cdevsw; 94296417Sdim#ifdef FREEBSD_4 95284677Sdimstatic int disks_registered = 0; 96296417Sdim#endif 97284677Sdim 98284677Sdimstatic device_method_t aac_disk_methods[] = { 99284677Sdim DEVMETHOD(device_probe, aac_disk_probe), 100284677Sdim DEVMETHOD(device_attach, aac_disk_attach), 101284677Sdim DEVMETHOD(device_detach, aac_disk_detach), 102284677Sdim { 0, 0 } 103284677Sdim}; 104284677Sdim 105284677Sdimstatic driver_t aac_disk_driver = { 106284677Sdim "aacd", 107284677Sdim aac_disk_methods, 108284677Sdim sizeof(struct aac_disk) 109296417Sdim}; 110284677Sdim 111284677Sdim#define AAC_MAXIO 65536 112284677Sdim 113284677SdimDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0); 114284677Sdim 115284677Sdim/* sysctl tunables */ 116284677Sdimstatic unsigned int aac_iosize_max = AAC_MAXIO; /* due to limits of the card */ 117284677SdimTUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max); 118284677Sdim 119284677SdimSYSCTL_DECL(_hw_aac); 120284677SdimSYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0, 121284677Sdim "Max I/O size per transfer to an array"); 122284677Sdim 123284677Sdim/* 124284677Sdim * Handle open from generic layer. 125284677Sdim * 126284677Sdim * This is called by the diskslice code on first open in order to get the 127284677Sdim * basic device geometry paramters. 128284677Sdim */ 129284677Sdimstatic int 130284677Sdimaac_disk_open(dev_t dev, int flags, int fmt, d_thread_t *td) 131284677Sdim{ 132284677Sdim struct aac_disk *sc; 133284677Sdim 134284677Sdim debug_called(4); 135284677Sdim 136284677Sdim sc = (struct aac_disk *)dev->si_drv1; 137284677Sdim 138284677Sdim if (sc == NULL) { 139284677Sdim printf("aac_disk_open: No Softc\n"); 140284677Sdim return (ENXIO); 141284677Sdim } 142284677Sdim 143284677Sdim /* check that the controller is up and running */ 144284677Sdim if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) { 145284677Sdim printf("Controller Suspended controller state = 0x%x\n", 146284677Sdim sc->ad_controller->aac_state); 147284677Sdim return(ENXIO); 148284677Sdim } 149284677Sdim 150284677Sdim sc->ad_disk.d_sectorsize = AAC_BLOCK_SIZE; 151284677Sdim sc->ad_disk.d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE; 152284677Sdim sc->ad_disk.d_fwsectors = sc->ad_sectors; 153284677Sdim sc->ad_disk.d_fwheads = sc->ad_heads; 154284677Sdim 155284677Sdim sc->ad_flags |= AAC_DISK_OPEN; 156284677Sdim return (0); 157284677Sdim} 158284677Sdim 159284677Sdim/* 160284677Sdim * Handle last close of the disk device. 161284677Sdim */ 162284677Sdimstatic int 163284677Sdimaac_disk_close(dev_t dev, int flags, int fmt, d_thread_t *td) 164284677Sdim{ 165284677Sdim struct aac_disk *sc; 166296417Sdim 167296417Sdim debug_called(4); 168296417Sdim 169296417Sdim sc = (struct aac_disk *)dev->si_drv1; 170284677Sdim 171284677Sdim if (sc == NULL) 172284677Sdim return (ENXIO); 173284677Sdim 174284677Sdim sc->ad_flags &= ~AAC_DISK_OPEN; 175284677Sdim return (0); 176284677Sdim} 177284677Sdim 178284677Sdim/* 179284677Sdim * Handle an I/O request. 180284677Sdim */ 181284677Sdimstatic void 182284677Sdimaac_disk_strategy(struct bio *bp) 183284677Sdim{ 184284677Sdim struct aac_disk *sc; 185284677Sdim 186284677Sdim debug_called(4); 187284677Sdim 188284677Sdim sc = (struct aac_disk *)bp->bio_dev->si_drv1; 189284677Sdim 190284677Sdim /* bogus disk? */ 191284677Sdim if (sc == NULL) { 192284677Sdim bp->bio_flags |= BIO_ERROR; 193284677Sdim bp->bio_error = EINVAL; 194284677Sdim biodone(bp); 195284677Sdim return; 196284677Sdim } 197284677Sdim 198284677Sdim /* do-nothing operation? */ 199284677Sdim if (bp->bio_bcount == 0) { 200284677Sdim bp->bio_resid = bp->bio_bcount; 201284677Sdim biodone(bp); 202284677Sdim return; 203284677Sdim } 204284677Sdim 205284677Sdim /* perform accounting */ 206284677Sdim devstat_start_transaction(&sc->ad_stats); 207284677Sdim 208284677Sdim /* pass the bio to the controller - it can work out who we are */ 209284677Sdim aac_submit_bio(bp); 210284677Sdim return; 211284677Sdim} 212284677Sdim 213284677Sdim/* 214284677Sdim * Map the S/G elements for doing a dump. 215284677Sdim */ 216284677Sdimstatic void 217284677Sdimaac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 218284677Sdim{ 219284677Sdim struct aac_fib *fib; 220284677Sdim struct aac_blockwrite *bw; 221284677Sdim struct aac_sg_table *sg; 222284677Sdim int i; 223284677Sdim 224284677Sdim fib = (struct aac_fib *)arg; 225284677Sdim bw = (struct aac_blockwrite *)&fib->data[0]; 226284677Sdim sg = &bw->SgMap; 227284677Sdim 228284677Sdim if (sg != NULL) { 229284677Sdim sg->SgCount = nsegs; 230284677Sdim for (i = 0; i < nsegs; i++) { 231284677Sdim sg->SgEntry[i].SgAddress = segs[i].ds_addr; 232284677Sdim sg->SgEntry[i].SgByteCount = segs[i].ds_len; 233286684Sdim } 234286684Sdim fib->Header.Size = nsegs * sizeof(struct aac_sg_entry); 235286684Sdim } 236286684Sdim} 237284677Sdim 238284677Sdim/* 239284677Sdim * Dump memory out to an array 240284677Sdim * 241284677Sdim * Send out one command at a time with up to AAC_MAXIO of data. 242284677Sdim */ 243284677Sdimstatic int 244284677Sdimaac_disk_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) 245284677Sdim{ 246284677Sdim struct aac_disk *ad; 247284677Sdim struct aac_softc *sc; 248284677Sdim struct aac_fib *fib; 249284677Sdim struct aac_blockwrite *bw; 250284677Sdim size_t len; 251284677Sdim int size; 252284677Sdim static bus_dmamap_t dump_datamap; 253284677Sdim static int first = 0; 254284677Sdim struct disk *dp; 255284677Sdim 256284677Sdim dp = arg; 257284677Sdim ad = dp->d_dev->si_drv1; 258284677Sdim 259284677Sdim if (ad == NULL) 260284677Sdim return (EINVAL); 261284677Sdim 262285181Sdim sc= ad->ad_controller; 263285181Sdim 264284677Sdim if (!first) { 265284677Sdim first = 1; 266284677Sdim if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) { 267284677Sdim printf("bus_dmamap_create failed\n"); 268284677Sdim return (ENOMEM); 269284677Sdim } 270284677Sdim } 271284677Sdim 272284677Sdim aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE); 273284677Sdim bw = (struct aac_blockwrite *)&fib->data[0]; 274284677Sdim 275284677Sdim while (length > 0) { 276284677Sdim len = (length > AAC_MAXIO) ? AAC_MAXIO : length; 277284677Sdim bw->Command = VM_CtBlockWrite; 278284677Sdim bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; 279284677Sdim bw->BlockNumber = offset / AAC_BLOCK_SIZE; 280284677Sdim bw->ByteCount = len; 281287521Sdim bw->Stable = CUNSTABLE; 282287521Sdim bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual, 283287521Sdim len, aac_dump_map_sg, fib, 0); 284287521Sdim bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap, 285296417Sdim BUS_DMASYNC_PREWRITE); 286296417Sdim 287296417Sdim /* fib->Header.Size is set in aac_dump_map_sg */ 288296417Sdim size = fib->Header.Size + sizeof(struct aac_blockwrite); 289284677Sdim 290284677Sdim if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) { 291284677Sdim printf("Error dumping block 0x%x\n", physical); 292284677Sdim return (EIO); 293284677Sdim } 294284677Sdim length -= len; 295284677Sdim offset += len; 296284677Sdim } 297284677Sdim 298284677Sdim return (0); 299284677Sdim} 300296417Sdim 301296417Sdim/* 302296417Sdim * Handle completion of an I/O request. 303296417Sdim */ 304284677Sdimvoid 305284677Sdimaac_biodone(struct bio *bp) 306284677Sdim{ 307284677Sdim struct aac_disk *sc; 308284677Sdim 309284677Sdim debug_called(4); 310284677Sdim 311284677Sdim sc = (struct aac_disk *)bp->bio_dev->si_drv1; 312284677Sdim 313284677Sdim devstat_end_transaction_bio(&sc->ad_stats, bp); 314284677Sdim if (bp->bio_flags & BIO_ERROR) { 315285181Sdim#if __FreeBSD_version > 500039 316285181Sdim disk_err(bp, "hard error", -1, 1); 317285181Sdim#elif __FreeBSD_version > 500005 318285181Sdim int blkno; 319285181Sdim blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 320285181Sdim diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label); 321285181Sdim#else 322296417Sdim int blkno; 323296417Sdim blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 324296417Sdim diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label); 325284677Sdim#endif 326284677Sdim } 327284677Sdim biodone(bp); 328284677Sdim} 329284677Sdim 330/* 331 * Stub only. 332 */ 333static int 334aac_disk_probe(device_t dev) 335{ 336 337 debug_called(2); 338 339 return (0); 340} 341 342/* 343 * Attach a unit to the controller. 344 */ 345static int 346aac_disk_attach(device_t dev) 347{ 348 struct aac_disk *sc; 349 350 debug_called(1); 351 352 sc = (struct aac_disk *)device_get_softc(dev); 353 354 /* initialise our softc */ 355 sc->ad_controller = 356 (struct aac_softc *)device_get_softc(device_get_parent(dev)); 357 sc->ad_container = device_get_ivars(dev); 358 sc->ad_dev = dev; 359 360 /* 361 * require that extended translation be enabled - other drivers read the 362 * disk! 363 */ 364 sc->ad_size = sc->ad_container->co_mntobj.Capacity; 365 if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 366 sc->ad_heads = 255; 367 sc->ad_sectors = 63; 368 } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 369 sc->ad_heads = 128; 370 sc->ad_sectors = 32; 371 } else { 372 sc->ad_heads = 64; 373 sc->ad_sectors = 32; 374 } 375 sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 376 377 device_printf(dev, "%uMB (%u sectors)\n", 378 sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 379 sc->ad_size); 380 381 devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev), 382 AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS, 383 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 384 DEVSTAT_PRIORITY_ARRAY); 385 386 /* attach a generic disk device to ourselves */ 387 sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0, 388 &aac_disk_cdevsw, &aac_disk_disk_cdevsw); 389 sc->ad_dev_t->si_drv1 = sc; 390#ifdef FREEBSD_4 391 disks_registered++; 392#endif 393 394 sc->ad_dev_t->si_iosize_max = aac_iosize_max; 395 sc->unit = device_get_unit(dev); 396 397 return (0); 398} 399 400/* 401 * Disconnect ourselves from the system. 402 */ 403static int 404aac_disk_detach(device_t dev) 405{ 406 struct aac_disk *sc; 407 408 debug_called(2); 409 410 sc = (struct aac_disk *)device_get_softc(dev); 411 412 if (sc->ad_flags & AAC_DISK_OPEN) 413 return(EBUSY); 414 415 devstat_remove_entry(&sc->ad_stats); 416 disk_destroy(&sc->ad_disk); 417#ifdef FREEBSD_4 418 if (--disks_registered == 0) 419 cdevsw_remove(&aac_disk_cdevsw); 420#endif 421 422 return(0); 423} 424