aac_disk.c revision 119418
165793Smsmith/*- 265793Smsmith * Copyright (c) 2000 Michael Smith 381082Sscottl * Copyright (c) 2001 Scott Long 465793Smsmith * Copyright (c) 2000 BSDi 581082Sscottl * Copyright (c) 2001 Adaptec, Inc. 665793Smsmith * All rights reserved. 765793Smsmith * 865793Smsmith * Redistribution and use in source and binary forms, with or without 965793Smsmith * modification, are permitted provided that the following conditions 1065793Smsmith * are met: 1165793Smsmith * 1. Redistributions of source code must retain the above copyright 1265793Smsmith * notice, this list of conditions and the following disclaimer. 1365793Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1465793Smsmith * notice, this list of conditions and the following disclaimer in the 1565793Smsmith * documentation and/or other materials provided with the distribution. 1665793Smsmith * 1765793Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1865793Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1965793Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2065793Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2165793Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2265793Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2365793Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2465793Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2565793Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2665793Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2765793Smsmith * SUCH DAMAGE. 2865793Smsmith */ 2965793Smsmith 30119418Sobrien#include <sys/cdefs.h> 31119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/aac/aac_disk.c 119418 2003-08-24 17:55:58Z obrien $"); 32119418Sobrien 3381151Sscottl#include "opt_aac.h" 3481151Sscottl 3565793Smsmith#include <sys/param.h> 3665793Smsmith#include <sys/systm.h> 3765793Smsmith#include <sys/kernel.h> 3881154Sscottl#include <sys/sysctl.h> 3965793Smsmith 4065793Smsmith#include <sys/bus.h> 4165793Smsmith#include <sys/conf.h> 4265793Smsmith#include <sys/disk.h> 4365793Smsmith 4482527Sscottl#include <vm/vm.h> 4582527Sscottl#include <vm/pmap.h> 4682527Sscottl 4782527Sscottl#include <machine/md_var.h> 4865793Smsmith#include <machine/bus.h> 4965793Smsmith#include <sys/rman.h> 5065793Smsmith 5165793Smsmith#include <dev/aac/aacreg.h> 5270393Smsmith#include <dev/aac/aac_ioctl.h> 5365793Smsmith#include <dev/aac/aacvar.h> 5465793Smsmith 5565793Smsmith/* 5665793Smsmith * Interface to parent. 5765793Smsmith */ 5865793Smsmithstatic int aac_disk_probe(device_t dev); 5965793Smsmithstatic int aac_disk_attach(device_t dev); 6065793Smsmithstatic int aac_disk_detach(device_t dev); 6165793Smsmith 6265793Smsmith/* 6365793Smsmith * Interface to the device switch. 6465793Smsmith */ 65111525Sscottlstatic disk_open_t aac_disk_open; 66111525Sscottlstatic disk_close_t aac_disk_close; 67111525Sscottlstatic disk_strategy_t aac_disk_strategy; 68111220Sphkstatic dumper_t aac_disk_dump; 6965793Smsmith 7089112Smsmithstatic devclass_t aac_disk_devclass; 7165793Smsmith 7265793Smsmithstatic device_method_t aac_disk_methods[] = { 7383114Sscottl DEVMETHOD(device_probe, aac_disk_probe), 7483114Sscottl DEVMETHOD(device_attach, aac_disk_attach), 7583114Sscottl DEVMETHOD(device_detach, aac_disk_detach), 7683114Sscottl { 0, 0 } 7765793Smsmith}; 7865793Smsmith 7965793Smsmithstatic driver_t aac_disk_driver = { 8083114Sscottl "aacd", 8183114Sscottl aac_disk_methods, 8283114Sscottl sizeof(struct aac_disk) 8365793Smsmith}; 8465793Smsmith 8595350Sscottl#define AAC_MAXIO 65536 8695350Sscottl 8765793SmsmithDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0); 8865793Smsmith 8981154Sscottl/* sysctl tunables */ 9095350Sscottlstatic unsigned int aac_iosize_max = AAC_MAXIO; /* due to limits of the card */ 9181154SscottlTUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max); 9281154Sscottl 9381154SscottlSYSCTL_DECL(_hw_aac); 9481154SscottlSYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0, 9582527Sscottl "Max I/O size per transfer to an array"); 9681154Sscottl 9783114Sscottl/* 9865793Smsmith * Handle open from generic layer. 9965793Smsmith * 10065793Smsmith * This is called by the diskslice code on first open in order to get the 10165793Smsmith * basic device geometry paramters. 10265793Smsmith */ 10365793Smsmithstatic int 104111525Sscottlaac_disk_open(struct disk *dp) 10565793Smsmith{ 10683114Sscottl struct aac_disk *sc; 10765793Smsmith 10883114Sscottl debug_called(4); 10983114Sscottl 110111525Sscottl sc = (struct aac_disk *)dp->d_drv1; 11165793Smsmith 112109088Sscottl if (sc == NULL) { 113109088Sscottl printf("aac_disk_open: No Softc\n"); 11483114Sscottl return (ENXIO); 115109088Sscottl } 11665793Smsmith 11783114Sscottl /* check that the controller is up and running */ 118109088Sscottl if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) { 119109088Sscottl printf("Controller Suspended controller state = 0x%x\n", 120109088Sscottl sc->ad_controller->aac_state); 12183114Sscottl return(ENXIO); 122109088Sscottl } 12365793Smsmith 12483114Sscottl sc->ad_flags |= AAC_DISK_OPEN; 12583114Sscottl return (0); 12665793Smsmith} 12765793Smsmith 12883114Sscottl/* 12965793Smsmith * Handle last close of the disk device. 13065793Smsmith */ 13165793Smsmithstatic int 132111525Sscottlaac_disk_close(struct disk *dp) 13365793Smsmith{ 13483114Sscottl struct aac_disk *sc; 13565793Smsmith 13683114Sscottl debug_called(4); 13783114Sscottl 138111525Sscottl sc = (struct aac_disk *)dp->d_drv1; 13965793Smsmith 14083114Sscottl if (sc == NULL) 14183114Sscottl return (ENXIO); 14265793Smsmith 14383114Sscottl sc->ad_flags &= ~AAC_DISK_OPEN; 14483114Sscottl return (0); 14565793Smsmith} 14665793Smsmith 14783114Sscottl/* 14865793Smsmith * Handle an I/O request. 14965793Smsmith */ 15065793Smsmithstatic void 15165793Smsmithaac_disk_strategy(struct bio *bp) 15265793Smsmith{ 15383114Sscottl struct aac_disk *sc; 15465793Smsmith 15583114Sscottl debug_called(4); 15665793Smsmith 157111525Sscottl sc = (struct aac_disk *)bp->bio_disk->d_drv1; 15865793Smsmith 15983114Sscottl /* bogus disk? */ 16083114Sscottl if (sc == NULL) { 16183114Sscottl bp->bio_flags |= BIO_ERROR; 16283114Sscottl bp->bio_error = EINVAL; 16383114Sscottl biodone(bp); 16483114Sscottl return; 16583114Sscottl } 16682527Sscottl 16783114Sscottl /* do-nothing operation? */ 16883114Sscottl if (bp->bio_bcount == 0) { 16983114Sscottl bp->bio_resid = bp->bio_bcount; 17083114Sscottl biodone(bp); 17183114Sscottl return; 17283114Sscottl } 17365793Smsmith 17483114Sscottl /* perform accounting */ 17583114Sscottl 17683114Sscottl /* pass the bio to the controller - it can work out who we are */ 177111532Sscottl AAC_LOCK_ACQUIRE(&sc->ad_controller->aac_io_lock); 17883114Sscottl aac_submit_bio(bp); 179111532Sscottl AAC_LOCK_RELEASE(&sc->ad_controller->aac_io_lock); 180111532Sscottl 18183114Sscottl return; 18265793Smsmith} 18365793Smsmith 18483114Sscottl/* 18595350Sscottl * Map the S/G elements for doing a dump. 186116553Sscottl * 187116553Sscottl * XXX This does not handle >4GB of RAM. Fixing it is possible except on 188116553Sscottl * adapters that cannot do 64bit s/g lists. 18995350Sscottl */ 19095350Sscottlstatic void 19195350Sscottlaac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 19295350Sscottl{ 19395350Sscottl struct aac_fib *fib; 19495350Sscottl struct aac_blockwrite *bw; 19595350Sscottl struct aac_sg_table *sg; 19695350Sscottl int i; 19795350Sscottl 19895350Sscottl fib = (struct aac_fib *)arg; 19995350Sscottl bw = (struct aac_blockwrite *)&fib->data[0]; 20095350Sscottl sg = &bw->SgMap; 20195350Sscottl 20295350Sscottl if (sg != NULL) { 20395350Sscottl sg->SgCount = nsegs; 20495350Sscottl for (i = 0; i < nsegs; i++) { 205116553Sscottl if (segs[i].ds_addr >= BUS_SPACE_MAXADDR_32BIT) 206116553Sscottl return; 20795350Sscottl sg->SgEntry[i].SgAddress = segs[i].ds_addr; 20895350Sscottl sg->SgEntry[i].SgByteCount = segs[i].ds_len; 20995350Sscottl } 21095350Sscottl fib->Header.Size = nsegs * sizeof(struct aac_sg_entry); 21195350Sscottl } 21295350Sscottl} 21395350Sscottl 21495350Sscottl/* 21582527Sscottl * Dump memory out to an array 21682527Sscottl * 21795350Sscottl * Send out one command at a time with up to AAC_MAXIO of data. 21882527Sscottl */ 21982527Sscottlstatic int 220111220Sphkaac_disk_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) 22182527Sscottl{ 22283114Sscottl struct aac_disk *ad; 22383114Sscottl struct aac_softc *sc; 22495350Sscottl struct aac_fib *fib; 22595350Sscottl struct aac_blockwrite *bw; 22695350Sscottl size_t len; 22795350Sscottl int size; 22895350Sscottl static bus_dmamap_t dump_datamap; 22995350Sscottl static int first = 0; 230111220Sphk struct disk *dp; 23182527Sscottl 232111220Sphk dp = arg; 233111525Sscottl ad = dp->d_drv1; 23482527Sscottl 23583114Sscottl if (ad == NULL) 23695350Sscottl return (EINVAL); 23782527Sscottl 23883114Sscottl sc= ad->ad_controller; 23982527Sscottl 24095350Sscottl if (!first) { 24195350Sscottl first = 1; 24295350Sscottl if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) { 24395350Sscottl printf("bus_dmamap_create failed\n"); 24495350Sscottl return (ENOMEM); 24595350Sscottl } 24695350Sscottl } 24782527Sscottl 24895536Sscottl aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE); 24995350Sscottl bw = (struct aac_blockwrite *)&fib->data[0]; 25082527Sscottl 25195350Sscottl while (length > 0) { 25295350Sscottl len = (length > AAC_MAXIO) ? AAC_MAXIO : length; 25395350Sscottl bw->Command = VM_CtBlockWrite; 25495350Sscottl bw->ContainerId = ad->ad_container->co_mntobj.ObjectId; 25595350Sscottl bw->BlockNumber = offset / AAC_BLOCK_SIZE; 25695350Sscottl bw->ByteCount = len; 25795350Sscottl bw->Stable = CUNSTABLE; 258116553Sscottl 259116553Sscottl /* 260116553Sscottl * There really isn't any way to recover from errors or 261116553Sscottl * resource shortages here. Oh well. Because of that, don't 262116553Sscottl * bother trying to send the command from the callback; there 263116553Sscottl * is too much required context. 264116553Sscottl */ 265116553Sscottl if (bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual, 266116553Sscottl len, aac_dump_map_sg, fib, 0) != 0) 267116553Sscottl return (EIO); 268116553Sscottl 26995350Sscottl bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap, 27095350Sscottl BUS_DMASYNC_PREWRITE); 27182527Sscottl 27295350Sscottl /* fib->Header.Size is set in aac_dump_map_sg */ 27395350Sscottl size = fib->Header.Size + sizeof(struct aac_blockwrite); 27483114Sscottl 27595350Sscottl if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) { 276119146Sscottl printf("Error dumping block 0x%jx\n", 277119171Smux (uintmax_t)physical); 27895350Sscottl return (EIO); 27983114Sscottl } 280116553Sscottl 28195350Sscottl length -= len; 28295350Sscottl offset += len; 283116553Sscottl (vm_offset_t)virtual += len; 28483114Sscottl } 28582527Sscottl 28683114Sscottl return (0); 28782527Sscottl} 28882527Sscottl 28983114Sscottl/* 29065793Smsmith * Handle completion of an I/O request. 29165793Smsmith */ 29265793Smsmithvoid 29370393Smsmithaac_biodone(struct bio *bp) 29465793Smsmith{ 29583114Sscottl struct aac_disk *sc; 29665793Smsmith 29783114Sscottl debug_called(4); 29865793Smsmith 299111525Sscottl sc = (struct aac_disk *)bp->bio_disk->d_drv1; 30083114Sscottl 301111691Sscottl if (bp->bio_flags & BIO_ERROR) 302103675Sphk disk_err(bp, "hard error", -1, 1); 303111691Sscottl 30483114Sscottl biodone(bp); 30565793Smsmith} 30665793Smsmith 30783114Sscottl/* 30865793Smsmith * Stub only. 30965793Smsmith */ 31065793Smsmithstatic int 31165793Smsmithaac_disk_probe(device_t dev) 31265793Smsmith{ 31365793Smsmith 31483114Sscottl debug_called(2); 31565793Smsmith 31683114Sscottl return (0); 31765793Smsmith} 31865793Smsmith 31983114Sscottl/* 32065793Smsmith * Attach a unit to the controller. 32165793Smsmith */ 32265793Smsmithstatic int 32365793Smsmithaac_disk_attach(device_t dev) 32465793Smsmith{ 32583114Sscottl struct aac_disk *sc; 32683114Sscottl 32783114Sscottl debug_called(1); 32865793Smsmith 32983114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 33065793Smsmith 33183114Sscottl /* initialise our softc */ 33283114Sscottl sc->ad_controller = 33383114Sscottl (struct aac_softc *)device_get_softc(device_get_parent(dev)); 33483114Sscottl sc->ad_container = device_get_ivars(dev); 33583114Sscottl sc->ad_dev = dev; 33665793Smsmith 33783114Sscottl /* 33883114Sscottl * require that extended translation be enabled - other drivers read the 33983114Sscottl * disk! 34083114Sscottl */ 34183114Sscottl sc->ad_size = sc->ad_container->co_mntobj.Capacity; 34283114Sscottl if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 34383114Sscottl sc->ad_heads = 255; 34483114Sscottl sc->ad_sectors = 63; 34583114Sscottl } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 34683114Sscottl sc->ad_heads = 128; 34783114Sscottl sc->ad_sectors = 32; 34883114Sscottl } else { 34983114Sscottl sc->ad_heads = 64; 35083114Sscottl sc->ad_sectors = 32; 35183114Sscottl } 35283114Sscottl sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 35365793Smsmith 35483114Sscottl device_printf(dev, "%uMB (%u sectors)\n", 35583114Sscottl sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 35683114Sscottl sc->ad_size); 35765793Smsmith 35883114Sscottl /* attach a generic disk device to ourselves */ 35983114Sscottl sc->unit = device_get_unit(dev); 360111525Sscottl sc->ad_disk.d_drv1 = sc; 361111525Sscottl sc->ad_disk.d_name = "aacd"; 362111525Sscottl sc->ad_disk.d_maxsize = aac_iosize_max; 363111525Sscottl sc->ad_disk.d_open = aac_disk_open; 364111525Sscottl sc->ad_disk.d_close = aac_disk_close; 365111525Sscottl sc->ad_disk.d_strategy = aac_disk_strategy; 366111525Sscottl sc->ad_disk.d_dump = aac_disk_dump; 367111525Sscottl sc->ad_disk.d_sectorsize = AAC_BLOCK_SIZE; 368111525Sscottl sc->ad_disk.d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE; 369111525Sscottl sc->ad_disk.d_fwsectors = sc->ad_sectors; 370111525Sscottl sc->ad_disk.d_fwheads = sc->ad_heads; 371111532Sscottl disk_create(sc->unit, &sc->ad_disk, DISKFLAG_NOGIANT, NULL, NULL); 37281082Sscottl 37383114Sscottl return (0); 37465793Smsmith} 37565793Smsmith 37683114Sscottl/* 37765793Smsmith * Disconnect ourselves from the system. 37865793Smsmith */ 37965793Smsmithstatic int 38065793Smsmithaac_disk_detach(device_t dev) 38165793Smsmith{ 38283114Sscottl struct aac_disk *sc; 38365793Smsmith 38483114Sscottl debug_called(2); 38565793Smsmith 38683114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 38765793Smsmith 38883114Sscottl if (sc->ad_flags & AAC_DISK_OPEN) 38983114Sscottl return(EBUSY); 39083114Sscottl 391111216Sphk disk_destroy(&sc->ad_disk); 39265793Smsmith 39383114Sscottl return(0); 39465793Smsmith} 395