aac_disk.c revision 138635
174462Salfred/*-
274462Salfred * Copyright (c) 2000 Michael Smith
374462Salfred * Copyright (c) 2001 Scott Long
474462Salfred * Copyright (c) 2000 BSDi
574462Salfred * Copyright (c) 2001 Adaptec, Inc.
674462Salfred * All rights reserved.
774462Salfred *
874462Salfred * Redistribution and use in source and binary forms, with or without
974462Salfred * modification, are permitted provided that the following conditions
1074462Salfred * are met:
1174462Salfred * 1. Redistributions of source code must retain the above copyright
1274462Salfred *    notice, this list of conditions and the following disclaimer.
1374462Salfred * 2. Redistributions in binary form must reproduce the above copyright
1474462Salfred *    notice, this list of conditions and the following disclaimer in the
1574462Salfred *    documentation and/or other materials provided with the distribution.
1674462Salfred *
1774462Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1874462Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1974462Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2074462Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2174462Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2274462Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2374462Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2474462Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2574462Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2674462Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2774462Salfred * SUCH DAMAGE.
2874462Salfred */
2974462Salfred
3074462Salfred#include <sys/cdefs.h>
3174462Salfred__FBSDID("$FreeBSD: head/sys/dev/aac/aac_disk.c 138635 2004-12-09 22:20:25Z scottl $");
3274462Salfred
3374462Salfred#include "opt_aac.h"
3474462Salfred
3574462Salfred#include <sys/param.h>
3674462Salfred#include <sys/systm.h>
3774462Salfred#include <sys/kernel.h>
3874462Salfred#include <sys/module.h>
3974462Salfred#include <sys/sysctl.h>
4074462Salfred
4174462Salfred#include <sys/bus.h>
4274462Salfred#include <sys/conf.h>
4374462Salfred#include <sys/disk.h>
4474462Salfred
4574462Salfred#include <vm/vm.h>
4674462Salfred#include <vm/pmap.h>
4774462Salfred
4874462Salfred#include <machine/md_var.h>
4974462Salfred#include <machine/bus.h>
5074462Salfred#include <sys/rman.h>
5174462Salfred
5274462Salfred#include <dev/aac/aacreg.h>
5374462Salfred#include <sys/aac_ioctl.h>
5474462Salfred#include <dev/aac/aacvar.h>
5574462Salfred
5674462Salfred/*
5774462Salfred * Interface to parent.
5874462Salfred */
5975094Siedowsestatic int aac_disk_probe(device_t dev);
6074462Salfredstatic int aac_disk_attach(device_t dev);
6174462Salfredstatic int aac_disk_detach(device_t dev);
6274462Salfred
6374462Salfred/*
6474462Salfred * Interface to the device switch.
6574462Salfred */
6674462Salfredstatic	disk_open_t	aac_disk_open;
6774462Salfredstatic	disk_close_t	aac_disk_close;
6890868Smikestatic	disk_strategy_t	aac_disk_strategy;
6974462Salfredstatic	dumper_t	aac_disk_dump;
7074462Salfred
7174462Salfredstatic devclass_t	aac_disk_devclass;
7274462Salfred
7374462Salfredstatic device_method_t aac_disk_methods[] = {
7474462Salfred	DEVMETHOD(device_probe,	aac_disk_probe),
7574462Salfred	DEVMETHOD(device_attach,	aac_disk_attach),
7674462Salfred	DEVMETHOD(device_detach,	aac_disk_detach),
7774462Salfred	{ 0, 0 }
7874462Salfred};
7974462Salfred
8074462Salfredstatic driver_t aac_disk_driver = {
8174462Salfred	"aacd",
8274462Salfred	aac_disk_methods,
8374462Salfred	sizeof(struct aac_disk)
8474462Salfred};
8574660Salfred
8674660Salfred#define AAC_MAXIO	65536
8774660Salfred
8874660SalfredDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0);
8974660Salfred
9074462Salfred/* sysctl tunables */
9174462Salfredstatic unsigned int aac_iosize_max = AAC_MAXIO;	/* due to limits of the card */
9292905SobrienTUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max);
9392905Sobrien
9492905SobrienSYSCTL_DECL(_hw_aac);
9592905SobrienSYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RDTUN, &aac_iosize_max, 0,
9692905Sobrien	    "Max I/O size per transfer to an array");
9792905Sobrien
9892905Sobrien/*
9992905Sobrien * Handle open from generic layer.
10092905Sobrien *
10174462Salfred * This is called by the diskslice code on first open in order to get the
10274462Salfred * basic device geometry paramters.
10374462Salfred */
10474462Salfredstatic int
10574462Salfredaac_disk_open(struct disk *dp)
10674462Salfred{
10774462Salfred	struct aac_disk	*sc;
10874462Salfred
10974462Salfred	debug_called(4);
11074462Salfred
11174462Salfred	sc = (struct aac_disk *)dp->d_drv1;
11274462Salfred
11374462Salfred	if (sc == NULL) {
11474462Salfred		printf("aac_disk_open: No Softc\n");
11574462Salfred		return (ENXIO);
11674462Salfred	}
11774462Salfred
11874462Salfred	/* check that the controller is up and running */
11974462Salfred	if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) {
12074462Salfred		printf("Controller Suspended controller state = 0x%x\n",
12174462Salfred		       sc->ad_controller->aac_state);
12274462Salfred		return(ENXIO);
12374462Salfred	}
12474462Salfred
12574462Salfred	sc->ad_flags |= AAC_DISK_OPEN;
12674462Salfred	return (0);
12774462Salfred}
12874462Salfred
12974462Salfred/*
13074462Salfred * Handle last close of the disk device.
13174462Salfred */
13274462Salfredstatic int
13374462Salfredaac_disk_close(struct disk *dp)
13474462Salfred{
13574462Salfred	struct aac_disk	*sc;
13675144Siedowse
13774462Salfred	debug_called(4);
13874462Salfred
13974462Salfred	sc = (struct aac_disk *)dp->d_drv1;
14074462Salfred
14174462Salfred	if (sc == NULL)
14274462Salfred		return (ENXIO);
14374462Salfred
14474462Salfred	sc->ad_flags &= ~AAC_DISK_OPEN;
14574462Salfred	return (0);
14674462Salfred}
14774462Salfred
14874462Salfred/*
14974462Salfred * Handle an I/O request.
15074462Salfred */
15174462Salfredstatic void
15274462Salfredaac_disk_strategy(struct bio *bp)
15374462Salfred{
15474462Salfred	struct aac_disk	*sc;
15574462Salfred
15674462Salfred	debug_called(4);
15774462Salfred
15874462Salfred	sc = (struct aac_disk *)bp->bio_disk->d_drv1;
15974462Salfred
16074462Salfred	/* bogus disk? */
16174462Salfred	if (sc == NULL) {
16274462Salfred		bp->bio_flags |= BIO_ERROR;
16374462Salfred		bp->bio_error = EINVAL;
16474462Salfred		biodone(bp);
16574462Salfred		return;
16674462Salfred	}
16774462Salfred
16874462Salfred	/* do-nothing operation? */
16974462Salfred	if (bp->bio_bcount == 0) {
17074462Salfred		bp->bio_resid = bp->bio_bcount;
17174462Salfred		biodone(bp);
17274462Salfred		return;
17374462Salfred	}
17474462Salfred
17574462Salfred	/* perform accounting */
17674462Salfred
17774462Salfred	/* pass the bio to the controller - it can work out who we are */
17874462Salfred	mtx_lock(&sc->ad_controller->aac_io_lock);
17974462Salfred	aac_submit_bio(bp);
18074462Salfred	mtx_unlock(&sc->ad_controller->aac_io_lock);
18174462Salfred
18274462Salfred	return;
18374462Salfred}
18474462Salfred
18574462Salfred/*
18674462Salfred * Map the S/G elements for doing a dump.
18774462Salfred *
18874462Salfred * XXX This does not handle >4GB of RAM.  Fixing it is possible except on
18974462Salfred *     adapters that cannot do 64bit s/g lists.
19074462Salfred */
19174462Salfredstatic void
19274462Salfredaac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
19374462Salfred{
19474462Salfred	struct aac_fib *fib;
19574462Salfred	struct aac_blockwrite *bw;
19674462Salfred	struct aac_sg_table *sg;
19774462Salfred	int i;
19874462Salfred
19974462Salfred	fib = (struct aac_fib *)arg;
20074462Salfred	bw = (struct aac_blockwrite *)&fib->data[0];
20174462Salfred	sg = &bw->SgMap;
20274462Salfred
20374462Salfred	if (sg != NULL) {
20474462Salfred		sg->SgCount = nsegs;
20574462Salfred		for (i = 0; i < nsegs; i++) {
20674462Salfred			if (segs[i].ds_addr >= BUS_SPACE_MAXADDR_32BIT)
20774462Salfred				return;
20874462Salfred			sg->SgEntry[i].SgAddress = segs[i].ds_addr;
20974462Salfred			sg->SgEntry[i].SgByteCount = segs[i].ds_len;
21074462Salfred		}
21174462Salfred		fib->Header.Size = nsegs * sizeof(struct aac_sg_entry);
21274462Salfred	}
21374462Salfred}
21474462Salfred
21574462Salfred/*
21674462Salfred * Dump memory out to an array
21774462Salfred *
21874462Salfred * Send out one command at a time with up to AAC_MAXIO of data.
21974462Salfred */
22074462Salfredstatic int
22174462Salfredaac_disk_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length)
22274462Salfred{
22374462Salfred	struct aac_disk *ad;
22474462Salfred	struct aac_softc *sc;
22574462Salfred	struct aac_fib *fib;
22674462Salfred	struct aac_blockwrite *bw;
22774462Salfred	size_t len;
22874462Salfred	int size;
22974462Salfred	static bus_dmamap_t dump_datamap;
23074462Salfred	static int first = 0;
23174462Salfred	struct disk *dp;
23274462Salfred
23374462Salfred	dp = arg;
23475097Siedowse	ad = dp->d_drv1;
23574462Salfred
23674462Salfred	if (ad == NULL)
23774462Salfred		return (EINVAL);
23874462Salfred
23974462Salfred	sc= ad->ad_controller;
24074462Salfred
24175097Siedowse	if (!first) {
24274462Salfred		first = 1;
24374462Salfred		if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) {
24474462Salfred			printf("bus_dmamap_create failed\n");
24574462Salfred			return (ENOMEM);
24674462Salfred		}
24774462Salfred	}
24874462Salfred
24974462Salfred	/* Skip aac_alloc_sync_fib().  We don't want to mess with sleep locks */
25074462Salfred	fib = &sc->aac_common->ac_sync_fib;
25174462Salfred	bw = (struct aac_blockwrite *)&fib->data[0];
25274462Salfred
25374462Salfred	while (length > 0) {
25474462Salfred		len = (length > AAC_MAXIO) ? AAC_MAXIO : length;
25574462Salfred		bw->Command = VM_CtBlockWrite;
25674462Salfred		bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
25774462Salfred		bw->BlockNumber = offset / AAC_BLOCK_SIZE;
25874462Salfred		bw->ByteCount = len;
25974462Salfred		bw->Stable = CUNSTABLE;
26074462Salfred
26177588Siedowse		/*
26274462Salfred		 * There really isn't any way to recover from errors or
26374462Salfred		 * resource shortages here.  Oh well.  Because of that, don't
26474462Salfred		 * bother trying to send the command from the callback; there
26574462Salfred		 * is too much required context.
26674462Salfred		 */
26774462Salfred		if (bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual,
26874462Salfred		    len, aac_dump_map_sg, fib, 0) != 0)
26974462Salfred			return (EIO);
27074462Salfred
27174462Salfred		bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
27274462Salfred		    BUS_DMASYNC_PREWRITE);
27374462Salfred
27474462Salfred		/* fib->Header.Size is set in aac_dump_map_sg */
27574462Salfred		size = fib->Header.Size + sizeof(struct aac_blockwrite);
27674462Salfred
27774462Salfred		if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) {
27874462Salfred			printf("Error dumping block 0x%jx\n",
27974462Salfred			       (uintmax_t)physical);
28074462Salfred			return (EIO);
28174462Salfred		}
28274462Salfred
28374462Salfred		length -= len;
28474462Salfred		offset += len;
28574462Salfred		virtual = (uint8_t *)virtual + len;
28674462Salfred	}
28774462Salfred
28874462Salfred	return (0);
28974462Salfred}
29074462Salfred
29174462Salfred/*
29274462Salfred * Handle completion of an I/O request.
29374462Salfred */
29474462Salfredvoid
29574462Salfredaac_biodone(struct bio *bp)
29674462Salfred{
29774462Salfred	struct aac_disk	*sc;
29874462Salfred
29974462Salfred	debug_called(4);
30074462Salfred
30174462Salfred	sc = (struct aac_disk *)bp->bio_disk->d_drv1;
30274462Salfred
30374462Salfred	if (bp->bio_flags & BIO_ERROR)
30474462Salfred		disk_err(bp, "hard error", -1, 1);
30574462Salfred
30674462Salfred	biodone(bp);
30774462Salfred}
30874462Salfred
30974462Salfred/*
31074462Salfred * Stub only.
31174462Salfred */
31274462Salfredstatic int
31374462Salfredaac_disk_probe(device_t dev)
31474462Salfred{
31574462Salfred
31674462Salfred	debug_called(2);
31774462Salfred
31874462Salfred	return (0);
31974462Salfred}
32074462Salfred
32174462Salfred/*
32274462Salfred * Attach a unit to the controller.
32374462Salfred */
32474462Salfredstatic int
32574462Salfredaac_disk_attach(device_t dev)
32674462Salfred{
32774462Salfred	struct aac_disk	*sc;
32874462Salfred
32974462Salfred	debug_called(1);
33074462Salfred
33174462Salfred	sc = (struct aac_disk *)device_get_softc(dev);
33274462Salfred
33374462Salfred	/* initialise our softc */
33474462Salfred	sc->ad_controller =
33574462Salfred	    (struct aac_softc *)device_get_softc(device_get_parent(dev));
33674462Salfred	sc->ad_container = device_get_ivars(dev);
33774462Salfred	sc->ad_dev = dev;
33874462Salfred
33974462Salfred	/*
34074462Salfred	 * require that extended translation be enabled - other drivers read the
34174462Salfred	 * disk!
34274462Salfred	 */
34374462Salfred	sc->ad_size = sc->ad_container->co_mntobj.Capacity;
34474462Salfred	if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
34574462Salfred		sc->ad_heads = 255;
34674462Salfred		sc->ad_sectors = 63;
34774462Salfred	} else if (sc->ad_size >= (1 * 1024 * 1024)) {	/* 1GB */
34874462Salfred		sc->ad_heads = 128;
34974462Salfred		sc->ad_sectors = 32;
35074462Salfred	} else {
35174462Salfred		sc->ad_heads = 64;
35274462Salfred		sc->ad_sectors = 32;
35374462Salfred	}
35474462Salfred	sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
35574462Salfred
35674462Salfred	device_printf(dev, "%uMB (%u sectors)\n",
35774462Salfred		      sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
35874462Salfred		      sc->ad_size);
35974462Salfred
36074462Salfred	/* attach a generic disk device to ourselves */
36174462Salfred	sc->unit = device_get_unit(dev);
36274462Salfred	sc->ad_disk = disk_alloc();
36374462Salfred	sc->ad_disk->d_drv1 = sc;
36474462Salfred	sc->ad_disk->d_name = "aacd";
36574462Salfred	sc->ad_disk->d_maxsize = aac_iosize_max;
36674462Salfred	sc->ad_disk->d_open = aac_disk_open;
36774462Salfred	sc->ad_disk->d_close = aac_disk_close;
36874462Salfred	sc->ad_disk->d_strategy = aac_disk_strategy;
36974462Salfred	sc->ad_disk->d_dump = aac_disk_dump;
37074462Salfred	sc->ad_disk->d_sectorsize = AAC_BLOCK_SIZE;
37174462Salfred	sc->ad_disk->d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE;
37274462Salfred	sc->ad_disk->d_fwsectors = sc->ad_sectors;
37374462Salfred	sc->ad_disk->d_fwheads = sc->ad_heads;
37474462Salfred	sc->ad_disk->d_unit = sc->unit;
37574462Salfred	disk_create(sc->ad_disk, DISK_VERSION);
37674462Salfred
37774462Salfred	return (0);
37874462Salfred}
37974462Salfred
38074462Salfred/*
38174462Salfred * Disconnect ourselves from the system.
38274462Salfred */
38374462Salfredstatic int
38474462Salfredaac_disk_detach(device_t dev)
38574462Salfred{
38674462Salfred	struct aac_disk *sc;
38774462Salfred
38874462Salfred	debug_called(2);
38974462Salfred
39074462Salfred	sc = (struct aac_disk *)device_get_softc(dev);
39174462Salfred
39274462Salfred	if (sc->ad_flags & AAC_DISK_OPEN)
39374462Salfred		return(EBUSY);
39474462Salfred
39574462Salfred	disk_destroy(sc->ad_disk);
39674462Salfred
39774462Salfred	return(0);
39874462Salfred}
39974462Salfred