summaryrefslogtreecommitdiffstats
path: root/sys/dev/aac/aac_disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/aac/aac_disk.c')
-rw-r--r--sys/dev/aac/aac_disk.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/sys/dev/aac/aac_disk.c b/sys/dev/aac/aac_disk.c
new file mode 100644
index 0000000..68692aa
--- /dev/null
+++ b/sys/dev/aac/aac_disk.c
@@ -0,0 +1,416 @@
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2001 Scott Long
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2001 Adaptec, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_aac.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <dev/aac/aac_compat.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/devicestat.h>
+#include <sys/disk.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/md_var.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <dev/aac/aacreg.h>
+#include <dev/aac/aac_ioctl.h>
+#include <dev/aac/aacvar.h>
+
+/*
+ * Interface to parent.
+ */
+static int aac_disk_probe(device_t dev);
+static int aac_disk_attach(device_t dev);
+static int aac_disk_detach(device_t dev);
+
+/*
+ * Interface to the device switch.
+ */
+static d_open_t aac_disk_open;
+static d_close_t aac_disk_close;
+static d_strategy_t aac_disk_strategy;
+static d_dump_t aac_disk_dump;
+
+#define AAC_DISK_CDEV_MAJOR 151
+
+static struct cdevsw aac_disk_cdevsw = {
+ /* open */ aac_disk_open,
+ /* close */ aac_disk_close,
+ /* read */ physread,
+ /* write */ physwrite,
+ /* ioctl */ noioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ aac_disk_strategy,
+ /* name */ "aacd",
+ /* maj */ AAC_DISK_CDEV_MAJOR,
+ /* dump */ aac_disk_dump,
+ /* psize */ nopsize,
+ /* flags */ D_DISK,
+#if __FreeBSD_version < 500005
+ /* bmaj */ -1
+#endif
+};
+
+static devclass_t aac_disk_devclass;
+static struct cdevsw aac_disk_disk_cdevsw;
+#ifdef FREEBSD_4
+static int disks_registered = 0;
+#endif
+
+static device_method_t aac_disk_methods[] = {
+ DEVMETHOD(device_probe, aac_disk_probe),
+ DEVMETHOD(device_attach, aac_disk_attach),
+ DEVMETHOD(device_detach, aac_disk_detach),
+ { 0, 0 }
+};
+
+static driver_t aac_disk_driver = {
+ "aacd",
+ aac_disk_methods,
+ sizeof(struct aac_disk)
+};
+
+#define AAC_MAXIO 65536
+
+DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0);
+
+/* sysctl tunables */
+static unsigned int aac_iosize_max = AAC_MAXIO; /* due to limits of the card */
+TUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max);
+
+SYSCTL_DECL(_hw_aac);
+SYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0,
+ "Max I/O size per transfer to an array");
+
+/*
+ * Handle open from generic layer.
+ *
+ * This is called by the diskslice code on first open in order to get the
+ * basic device geometry paramters.
+ */
+static int
+aac_disk_open(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+ struct aac_disk *sc;
+
+ debug_called(4);
+
+ sc = (struct aac_disk *)dev->si_drv1;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* check that the controller is up and running */
+ if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND)
+ return(ENXIO);
+
+ sc->ad_disk.d_sectorsize = AAC_BLOCK_SIZE;
+ sc->ad_disk.d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE;
+ sc->ad_disk.d_fwsectors = sc->ad_sectors;
+ sc->ad_disk.d_fwheads = sc->ad_heads;
+
+ sc->ad_flags |= AAC_DISK_OPEN;
+ return (0);
+}
+
+/*
+ * Handle last close of the disk device.
+ */
+static int
+aac_disk_close(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+ struct aac_disk *sc;
+
+ debug_called(4);
+
+ sc = (struct aac_disk *)dev->si_drv1;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ sc->ad_flags &= ~AAC_DISK_OPEN;
+ return (0);
+}
+
+/*
+ * Handle an I/O request.
+ */
+static void
+aac_disk_strategy(struct bio *bp)
+{
+ struct aac_disk *sc;
+
+ debug_called(4);
+
+ sc = (struct aac_disk *)bp->bio_dev->si_drv1;
+
+ /* bogus disk? */
+ if (sc == NULL) {
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EINVAL;
+ biodone(bp);
+ return;
+ }
+
+ /* do-nothing operation? */
+ if (bp->bio_bcount == 0) {
+ bp->bio_resid = bp->bio_bcount;
+ biodone(bp);
+ return;
+ }
+
+ /* perform accounting */
+ devstat_start_transaction(&sc->ad_stats);
+
+ /* pass the bio to the controller - it can work out who we are */
+ aac_submit_bio(bp);
+ return;
+}
+
+/*
+ * Map the S/G elements for doing a dump.
+ */
+static void
+aac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct aac_fib *fib;
+ struct aac_blockwrite *bw;
+ struct aac_sg_table *sg;
+ int i;
+
+ fib = (struct aac_fib *)arg;
+ bw = (struct aac_blockwrite *)&fib->data[0];
+ sg = &bw->SgMap;
+
+ if (sg != NULL) {
+ sg->SgCount = nsegs;
+ for (i = 0; i < nsegs; i++) {
+ sg->SgEntry[i].SgAddress = segs[i].ds_addr;
+ sg->SgEntry[i].SgByteCount = segs[i].ds_len;
+ }
+ fib->Header.Size = nsegs * sizeof(struct aac_sg_entry);
+ }
+}
+
+/*
+ * Dump memory out to an array
+ *
+ * Send out one command at a time with up to AAC_MAXIO of data.
+ */
+static int
+aac_disk_dump(dev_t dev, void *virtual, vm_offset_t physical, off_t offset, size_t length)
+{
+ struct aac_disk *ad;
+ struct aac_softc *sc;
+ struct aac_fib *fib;
+ struct aac_blockwrite *bw;
+ size_t len;
+ int size;
+ static bus_dmamap_t dump_datamap;
+ static int first = 0;
+
+ ad = dev->si_drv1;
+
+ if (ad == NULL)
+ return (EINVAL);
+
+ sc= ad->ad_controller;
+
+ if (!first) {
+ first = 1;
+ if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) {
+ printf("bus_dmamap_create failed\n");
+ return (ENOMEM);
+ }
+ }
+
+ aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
+ bw = (struct aac_blockwrite *)&fib->data[0];
+
+ while (length > 0) {
+ len = (length > AAC_MAXIO) ? AAC_MAXIO : length;
+ bw->Command = VM_CtBlockWrite;
+ bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
+ bw->BlockNumber = offset / AAC_BLOCK_SIZE;
+ bw->ByteCount = len;
+ bw->Stable = CUNSTABLE;
+ bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual,
+ len, aac_dump_map_sg, fib, 0);
+ bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
+ BUS_DMASYNC_PREWRITE);
+
+ /* fib->Header.Size is set in aac_dump_map_sg */
+ size = fib->Header.Size + sizeof(struct aac_blockwrite);
+
+ if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) {
+ printf("Error dumping block 0x%x\n", physical);
+ return (EIO);
+ }
+ length -= len;
+ offset += len;
+ }
+
+ return (0);
+}
+
+/*
+ * Handle completion of an I/O request.
+ */
+void
+aac_biodone(struct bio *bp)
+{
+ struct aac_disk *sc;
+
+ debug_called(4);
+
+ sc = (struct aac_disk *)bp->bio_dev->si_drv1;
+
+ devstat_end_transaction_bio(&sc->ad_stats, bp);
+ if (bp->bio_flags & BIO_ERROR) {
+#if __FreeBSD_version > 500039
+ disk_err(bp, "hard error", -1, 1);
+#elif __FreeBSD_version > 500005
+ int blkno;
+ blkno = (sc->ad_label.d_nsectors) ? 0 : -1;
+ diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label);
+#else
+ int blkno;
+ blkno = (sc->ad_label.d_nsectors) ? 0 : -1;
+ diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label);
+#endif
+ }
+ biodone(bp);
+}
+
+/*
+ * Stub only.
+ */
+static int
+aac_disk_probe(device_t dev)
+{
+
+ debug_called(2);
+
+ return (0);
+}
+
+/*
+ * Attach a unit to the controller.
+ */
+static int
+aac_disk_attach(device_t dev)
+{
+ struct aac_disk *sc;
+
+ debug_called(1);
+
+ sc = (struct aac_disk *)device_get_softc(dev);
+
+ /* initialise our softc */
+ sc->ad_controller =
+ (struct aac_softc *)device_get_softc(device_get_parent(dev));
+ sc->ad_container = device_get_ivars(dev);
+ sc->ad_dev = dev;
+
+ /*
+ * require that extended translation be enabled - other drivers read the
+ * disk!
+ */
+ sc->ad_size = sc->ad_container->co_mntobj.Capacity;
+ if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */
+ sc->ad_heads = 255;
+ sc->ad_sectors = 63;
+ } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */
+ sc->ad_heads = 128;
+ sc->ad_sectors = 32;
+ } else {
+ sc->ad_heads = 64;
+ sc->ad_sectors = 32;
+ }
+ sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
+
+ device_printf(dev, "%uMB (%u sectors)\n",
+ sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
+ sc->ad_size);
+
+ devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev),
+ AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
+ DEVSTAT_PRIORITY_ARRAY);
+
+ /* attach a generic disk device to ourselves */
+ sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0,
+ &aac_disk_cdevsw, &aac_disk_disk_cdevsw);
+ sc->ad_dev_t->si_drv1 = sc;
+#ifdef FREEBSD_4
+ disks_registered++;
+#endif
+
+ sc->ad_dev_t->si_iosize_max = aac_iosize_max;
+ sc->unit = device_get_unit(dev);
+
+ return (0);
+}
+
+/*
+ * Disconnect ourselves from the system.
+ */
+static int
+aac_disk_detach(device_t dev)
+{
+ struct aac_disk *sc;
+
+ debug_called(2);
+
+ sc = (struct aac_disk *)device_get_softc(dev);
+
+ if (sc->ad_flags & AAC_DISK_OPEN)
+ return(EBUSY);
+
+ devstat_remove_entry(&sc->ad_stats);
+ disk_destroy(sc->ad_dev_t);
+#ifdef FREEBSD_4
+ if (--disks_registered == 0)
+ cdevsw_remove(&aac_disk_cdevsw);
+#endif
+
+ return(0);
+}
OpenPOWER on IntegriCloud