aac_disk.c revision 89112
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 * $FreeBSD: head/sys/dev/aac/aac_disk.c 89112 2002-01-09 03:32:40Z msmith $ 3065793Smsmith */ 3165793Smsmith 3281151Sscottl#include "opt_aac.h" 3381151Sscottl 3465793Smsmith#include <sys/param.h> 3565793Smsmith#include <sys/systm.h> 3665793Smsmith#include <sys/kernel.h> 3781154Sscottl#include <sys/sysctl.h> 3865793Smsmith 3965793Smsmith#include <dev/aac/aac_compat.h> 4065793Smsmith#include <sys/bus.h> 4165793Smsmith#include <sys/conf.h> 4265793Smsmith#include <sys/devicestat.h> 4365793Smsmith#include <sys/disk.h> 4465793Smsmith 4582527Sscottl#include <vm/vm.h> 4682527Sscottl#include <vm/pmap.h> 4782527Sscottl 4882527Sscottl#include <machine/md_var.h> 4965793Smsmith#include <machine/bus.h> 5065793Smsmith#include <sys/rman.h> 5165793Smsmith 5265793Smsmith#include <dev/aac/aacreg.h> 5370393Smsmith#include <dev/aac/aac_ioctl.h> 5465793Smsmith#include <dev/aac/aacvar.h> 5565793Smsmith 5665793Smsmith/* 5765793Smsmith * Interface to parent. 5865793Smsmith */ 5965793Smsmithstatic int aac_disk_probe(device_t dev); 6065793Smsmithstatic int aac_disk_attach(device_t dev); 6165793Smsmithstatic int aac_disk_detach(device_t dev); 6265793Smsmith 6365793Smsmith/* 6465793Smsmith * Interface to the device switch. 6565793Smsmith */ 6665793Smsmithstatic d_open_t aac_disk_open; 6765793Smsmithstatic d_close_t aac_disk_close; 6865793Smsmithstatic d_strategy_t aac_disk_strategy; 6982527Sscottlstatic d_dump_t aac_disk_dump; 7065793Smsmith 7165793Smsmith#define AAC_DISK_CDEV_MAJOR 151 7265793Smsmith 7365793Smsmithstatic struct cdevsw aac_disk_cdevsw = { 7483114Sscottl /* open */ aac_disk_open, 7583114Sscottl /* close */ aac_disk_close, 7683114Sscottl /* read */ physread, 7783114Sscottl /* write */ physwrite, 7883114Sscottl /* ioctl */ noioctl, 7983114Sscottl /* poll */ nopoll, 8083114Sscottl /* mmap */ nommap, 8183114Sscottl /* strategy */ aac_disk_strategy, 8283114Sscottl /* name */ "aacd", 8383114Sscottl /* maj */ AAC_DISK_CDEV_MAJOR, 8483114Sscottl /* dump */ aac_disk_dump, 8583114Sscottl /* psize */ nopsize, 8683114Sscottl /* flags */ D_DISK, 8782527Sscottl#if __FreeBSD_version < 500005 8883114Sscottl /* bmaj */ -1 8982527Sscottl#endif 9065793Smsmith}; 9165793Smsmith 9289112Smsmithstatic devclass_t aac_disk_devclass; 9365793Smsmithstatic struct cdevsw aac_disk_disk_cdevsw; 9465793Smsmith#ifdef FREEBSD_4 9565793Smsmithstatic int disks_registered = 0; 9665793Smsmith#endif 9765793Smsmith 9865793Smsmithstatic device_method_t aac_disk_methods[] = { 9983114Sscottl DEVMETHOD(device_probe, aac_disk_probe), 10083114Sscottl DEVMETHOD(device_attach, aac_disk_attach), 10183114Sscottl DEVMETHOD(device_detach, aac_disk_detach), 10283114Sscottl { 0, 0 } 10365793Smsmith}; 10465793Smsmith 10565793Smsmithstatic driver_t aac_disk_driver = { 10683114Sscottl "aacd", 10783114Sscottl aac_disk_methods, 10883114Sscottl sizeof(struct aac_disk) 10965793Smsmith}; 11065793Smsmith 11165793SmsmithDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0); 11265793Smsmith 11381154Sscottl/* sysctl tunables */ 11481154Sscottlstatic unsigned int aac_iosize_max = 65536; /* due to limits of the card */ 11581154SscottlTUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max); 11681154Sscottl 11781154SscottlSYSCTL_DECL(_hw_aac); 11881154SscottlSYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0, 11982527Sscottl "Max I/O size per transfer to an array"); 12081154Sscottl 12182527Sscottl#define AAC_MAXIO 65536 12282527Sscottl 12383114Sscottl/* 12465793Smsmith * Handle open from generic layer. 12565793Smsmith * 12665793Smsmith * This is called by the diskslice code on first open in order to get the 12765793Smsmith * basic device geometry paramters. 12865793Smsmith */ 12965793Smsmithstatic int 13087310Sscottlaac_disk_open(dev_t dev, int flags, int fmt, d_thread_t *td) 13165793Smsmith{ 13283114Sscottl struct aac_disk *sc; 13383114Sscottl struct disklabel *label; 13465793Smsmith 13583114Sscottl debug_called(4); 13683114Sscottl 13783114Sscottl sc = (struct aac_disk *)dev->si_drv1; 13865793Smsmith 13983114Sscottl if (sc == NULL) 14083114Sscottl return (ENXIO); 14165793Smsmith 14283114Sscottl /* check that the controller is up and running */ 14383114Sscottl if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) 14483114Sscottl return(ENXIO); 14565793Smsmith 14683114Sscottl /* build synthetic label */ 14783114Sscottl label = &sc->ad_disk.d_label; 14883114Sscottl bzero(label, sizeof(*label)); 14983114Sscottl label->d_type = DTYPE_ESDI; 15083114Sscottl label->d_secsize = AAC_BLOCK_SIZE; 15183114Sscottl label->d_nsectors = sc->ad_sectors; 15283114Sscottl label->d_ntracks = sc->ad_heads; 15383114Sscottl label->d_ncylinders = sc->ad_cylinders; 15483114Sscottl label->d_secpercyl = sc->ad_sectors * sc->ad_heads; 15583114Sscottl label->d_secperunit = sc->ad_size; 15665793Smsmith 15783114Sscottl sc->ad_flags |= AAC_DISK_OPEN; 15883114Sscottl return (0); 15965793Smsmith} 16065793Smsmith 16183114Sscottl/* 16265793Smsmith * Handle last close of the disk device. 16365793Smsmith */ 16465793Smsmithstatic int 16587310Sscottlaac_disk_close(dev_t dev, int flags, int fmt, d_thread_t *td) 16665793Smsmith{ 16783114Sscottl struct aac_disk *sc; 16865793Smsmith 16983114Sscottl debug_called(4); 17083114Sscottl 17183114Sscottl sc = (struct aac_disk *)dev->si_drv1; 17265793Smsmith 17383114Sscottl if (sc == NULL) 17483114Sscottl return (ENXIO); 17565793Smsmith 17683114Sscottl sc->ad_flags &= ~AAC_DISK_OPEN; 17783114Sscottl return (0); 17865793Smsmith} 17965793Smsmith 18083114Sscottl/* 18165793Smsmith * Handle an I/O request. 18265793Smsmith */ 18365793Smsmithstatic void 18465793Smsmithaac_disk_strategy(struct bio *bp) 18565793Smsmith{ 18683114Sscottl struct aac_disk *sc; 18765793Smsmith 18883114Sscottl debug_called(4); 18965793Smsmith 19083114Sscottl sc = (struct aac_disk *)bp->bio_dev->si_drv1; 19165793Smsmith 19283114Sscottl /* bogus disk? */ 19383114Sscottl if (sc == NULL) { 19483114Sscottl bp->bio_flags |= BIO_ERROR; 19583114Sscottl bp->bio_error = EINVAL; 19683114Sscottl biodone(bp); 19783114Sscottl return; 19883114Sscottl } 19982527Sscottl 20083114Sscottl /* do-nothing operation? */ 20183114Sscottl if (bp->bio_bcount == 0) { 20283114Sscottl bp->bio_resid = bp->bio_bcount; 20383114Sscottl biodone(bp); 20483114Sscottl return; 20583114Sscottl } 20665793Smsmith 20783114Sscottl /* perform accounting */ 20883114Sscottl devstat_start_transaction(&sc->ad_stats); 20983114Sscottl 21083114Sscottl /* pass the bio to the controller - it can work out who we are */ 21183114Sscottl aac_submit_bio(bp); 21283114Sscottl return; 21365793Smsmith} 21465793Smsmith 21583114Sscottl/* 21682527Sscottl * Dump memory out to an array 21782527Sscottl * 21882527Sscottl * This queues blocks of memory of size AAC_MAXIO to the controller and waits 21982527Sscottl * for the controller to complete the requests. 22082527Sscottl */ 22182527Sscottlstatic int 22282527Sscottlaac_disk_dump(dev_t dev) 22382527Sscottl{ 22483114Sscottl struct aac_disk *ad; 22583114Sscottl struct aac_softc *sc; 22683114Sscottl vm_offset_t addr; 22783114Sscottl long blkcnt; 22883114Sscottl unsigned int count, blkno, secsize; 22983114Sscottl int dumppages; 23083114Sscottl int i, error; 23182527Sscottl 23283114Sscottl ad = dev->si_drv1; 23383114Sscottl addr = 0; 23483114Sscottl dumppages = AAC_MAXIO / PAGE_SIZE; 23582527Sscottl 23683114Sscottl if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize))) 23783114Sscottl return (error); 23882527Sscottl 23983114Sscottl if (ad == NULL) 24083114Sscottl return (ENXIO); 24182527Sscottl 24283114Sscottl sc= ad->ad_controller; 24382527Sscottl 24483114Sscottl blkcnt = howmany(PAGE_SIZE, secsize); 24582527Sscottl 24683114Sscottl while (count > 0) { 24783114Sscottl caddr_t va = NULL; 24882527Sscottl 24983114Sscottl if ((count / blkcnt) < dumppages) 25083114Sscottl dumppages = count / blkcnt; 25182527Sscottl 25283114Sscottl for (i = 0; i < dumppages; ++i) { 25383114Sscottl vm_offset_t a = addr + (i * PAGE_SIZE); 25483114Sscottl if (is_physical_memory(a)) { 25587183Sscottl va = pmap_kenter_temporary(trunc_page(a), i); 25683114Sscottl } else { 25787183Sscottl va = pmap_kenter_temporary(trunc_page(0), i); 25883114Sscottl } 25983114Sscottl } 26083114Sscottl 26182527Sscottlretry: 26283114Sscottl /* 26383114Sscottl * Queue the block to the controller. If the queue is full, 26483114Sscottl * EBUSY will be returned. 26583114Sscottl */ 26683114Sscottl error = aac_dump_enqueue(ad, blkno, va, dumppages); 26783114Sscottl if (error && (error != EBUSY)) 26883114Sscottl return (error); 26982527Sscottl 27083114Sscottl if (!error) { 27187310Sscottl if (dumpstatus(addr, (off_t)(count * DEV_BSIZE)) < 0) 27283114Sscottl return (EINTR); 27382527Sscottl 27483114Sscottl blkno += blkcnt * dumppages; 27583114Sscottl count -= blkcnt * dumppages; 27683114Sscottl addr += PAGE_SIZE * dumppages; 27783114Sscottl if (count > 0) 27883114Sscottl continue; 27983114Sscottl } 28082527Sscottl 28183114Sscottl /* 28283114Sscottl * Either the queue was full on the last attemp, or we have no 28383114Sscottl * more data to dump. Let the queue drain out and retry the 28483114Sscottl * block if the queue was full. 28583114Sscottl */ 28683114Sscottl aac_dump_complete(sc); 28782527Sscottl 28883114Sscottl if (error == EBUSY) 28983114Sscottl goto retry; 29083114Sscottl } 29182527Sscottl 29283114Sscottl return (0); 29382527Sscottl} 29482527Sscottl 29583114Sscottl/* 29665793Smsmith * Handle completion of an I/O request. 29765793Smsmith */ 29865793Smsmithvoid 29970393Smsmithaac_biodone(struct bio *bp) 30065793Smsmith{ 30183114Sscottl struct aac_disk *sc; 30283114Sscottl int blkno; 30365793Smsmith 30483114Sscottl debug_called(4); 30565793Smsmith 30683114Sscottl sc = (struct aac_disk *)bp->bio_dev->si_drv1; 30783114Sscottl 30883114Sscottl devstat_end_transaction_bio(&sc->ad_stats, bp); 30983114Sscottl if (bp->bio_flags & BIO_ERROR) { 31083114Sscottl blkno = (sc->ad_label.d_nsectors) ? 0 : -1; 31182527Sscottl#if __FreeBSD_version > 500005 31283114Sscottl diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label); 31382527Sscottl#else 31483114Sscottl diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label); 31582527Sscottl#endif 31683114Sscottl } 31783114Sscottl biodone(bp); 31865793Smsmith} 31965793Smsmith 32083114Sscottl/* 32165793Smsmith * Stub only. 32265793Smsmith */ 32365793Smsmithstatic int 32465793Smsmithaac_disk_probe(device_t dev) 32565793Smsmith{ 32665793Smsmith 32783114Sscottl debug_called(2); 32865793Smsmith 32983114Sscottl return (0); 33065793Smsmith} 33165793Smsmith 33283114Sscottl/* 33365793Smsmith * Attach a unit to the controller. 33465793Smsmith */ 33565793Smsmithstatic int 33665793Smsmithaac_disk_attach(device_t dev) 33765793Smsmith{ 33883114Sscottl struct aac_disk *sc; 33983114Sscottl 34083114Sscottl debug_called(1); 34165793Smsmith 34283114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 34365793Smsmith 34483114Sscottl /* initialise our softc */ 34583114Sscottl sc->ad_controller = 34683114Sscottl (struct aac_softc *)device_get_softc(device_get_parent(dev)); 34783114Sscottl sc->ad_container = device_get_ivars(dev); 34883114Sscottl sc->ad_dev = dev; 34965793Smsmith 35083114Sscottl /* 35183114Sscottl * require that extended translation be enabled - other drivers read the 35283114Sscottl * disk! 35383114Sscottl */ 35483114Sscottl sc->ad_size = sc->ad_container->co_mntobj.Capacity; 35583114Sscottl if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */ 35683114Sscottl sc->ad_heads = 255; 35783114Sscottl sc->ad_sectors = 63; 35883114Sscottl } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */ 35983114Sscottl sc->ad_heads = 128; 36083114Sscottl sc->ad_sectors = 32; 36183114Sscottl } else { 36283114Sscottl sc->ad_heads = 64; 36383114Sscottl sc->ad_sectors = 32; 36483114Sscottl } 36583114Sscottl sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors)); 36665793Smsmith 36783114Sscottl device_printf(dev, "%uMB (%u sectors)\n", 36883114Sscottl sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), 36983114Sscottl sc->ad_size); 37065793Smsmith 37183114Sscottl devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev), 37283114Sscottl AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS, 37383114Sscottl DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 37483114Sscottl DEVSTAT_PRIORITY_ARRAY); 37583114Sscottl 37683114Sscottl /* attach a generic disk device to ourselves */ 37783114Sscottl sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0, 37883114Sscottl &aac_disk_cdevsw, &aac_disk_disk_cdevsw); 37983114Sscottl sc->ad_dev_t->si_drv1 = sc; 38065793Smsmith#ifdef FREEBSD_4 38183114Sscottl disks_registered++; 38265793Smsmith#endif 38365793Smsmith 38483114Sscottl sc->ad_dev_t->si_iosize_max = aac_iosize_max; 38583114Sscottl sc->unit = device_get_unit(dev); 38681082Sscottl 38783114Sscottl return (0); 38865793Smsmith} 38965793Smsmith 39083114Sscottl/* 39165793Smsmith * Disconnect ourselves from the system. 39265793Smsmith */ 39365793Smsmithstatic int 39465793Smsmithaac_disk_detach(device_t dev) 39565793Smsmith{ 39683114Sscottl struct aac_disk *sc; 39765793Smsmith 39883114Sscottl debug_called(2); 39965793Smsmith 40083114Sscottl sc = (struct aac_disk *)device_get_softc(dev); 40165793Smsmith 40283114Sscottl if (sc->ad_flags & AAC_DISK_OPEN) 40383114Sscottl return(EBUSY); 40483114Sscottl 40583114Sscottl devstat_remove_entry(&sc->ad_stats); 40683114Sscottl disk_destroy(sc->ad_dev_t); 40765793Smsmith#ifdef FREEBSD_4 40883114Sscottl if (--disks_registered == 0) 40983114Sscottl cdevsw_remove(&aac_disk_cdevsw); 41065793Smsmith#endif 41165793Smsmith 41283114Sscottl return(0); 41365793Smsmith} 414