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