aac_disk.c revision 103675
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 103675 2002-09-20 12:52:03Z phk $
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
11195350Sscottl#define AAC_MAXIO	65536
11295350Sscottl
11365793SmsmithDRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0);
11465793Smsmith
11581154Sscottl/* sysctl tunables */
11695350Sscottlstatic unsigned int aac_iosize_max = AAC_MAXIO;	/* due to limits of the card */
11781154SscottlTUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max);
11881154Sscottl
11981154SscottlSYSCTL_DECL(_hw_aac);
12081154SscottlSYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0,
12182527Sscottl	    "Max I/O size per transfer to an array");
12281154Sscottl
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/*
21695350Sscottl * Map the S/G elements for doing a dump.
21795350Sscottl */
21895350Sscottlstatic void
21995350Sscottlaac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
22095350Sscottl{
22195350Sscottl	struct aac_fib *fib;
22295350Sscottl	struct aac_blockwrite *bw;
22395350Sscottl	struct aac_sg_table *sg;
22495350Sscottl	int i;
22595350Sscottl
22695350Sscottl	fib = (struct aac_fib *)arg;
22795350Sscottl	bw = (struct aac_blockwrite *)&fib->data[0];
22895350Sscottl	sg = &bw->SgMap;
22995350Sscottl
23095350Sscottl	if (sg != NULL) {
23195350Sscottl		sg->SgCount = nsegs;
23295350Sscottl		for (i = 0; i < nsegs; i++) {
23395350Sscottl			sg->SgEntry[i].SgAddress = segs[i].ds_addr;
23495350Sscottl			sg->SgEntry[i].SgByteCount = segs[i].ds_len;
23595350Sscottl		}
23695350Sscottl		fib->Header.Size = nsegs * sizeof(struct aac_sg_entry);
23795350Sscottl	}
23895350Sscottl}
23995350Sscottl
24095350Sscottl/*
24182527Sscottl * Dump memory out to an array
24282527Sscottl *
24395350Sscottl * Send out one command at a time with up to AAC_MAXIO of data.
24482527Sscottl */
24582527Sscottlstatic int
24693495Sphkaac_disk_dump(dev_t dev, void *virtual, vm_offset_t physical, off_t offset, size_t length)
24782527Sscottl{
24883114Sscottl	struct aac_disk *ad;
24983114Sscottl	struct aac_softc *sc;
25095350Sscottl	struct aac_fib *fib;
25195350Sscottl	struct aac_blockwrite *bw;
25295350Sscottl	size_t len;
25395350Sscottl	int size;
25495350Sscottl	static bus_dmamap_t dump_datamap;
25595350Sscottl	static int first = 0;
25682527Sscottl
25783114Sscottl	ad = dev->si_drv1;
25882527Sscottl
25983114Sscottl	if (ad == NULL)
26095350Sscottl		return (EINVAL);
26182527Sscottl
26283114Sscottl	sc= ad->ad_controller;
26382527Sscottl
26495350Sscottl	if (!first) {
26595350Sscottl		first = 1;
26695350Sscottl		if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) {
26795350Sscottl			printf("bus_dmamap_create failed\n");
26895350Sscottl			return (ENOMEM);
26995350Sscottl		}
27095350Sscottl	}
27182527Sscottl
27295536Sscottl	aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE);
27395350Sscottl	bw = (struct aac_blockwrite *)&fib->data[0];
27482527Sscottl
27595350Sscottl	while (length > 0) {
27695350Sscottl		len = (length > AAC_MAXIO) ? AAC_MAXIO : length;
27795350Sscottl		bw->Command = VM_CtBlockWrite;
27895350Sscottl		bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
27995350Sscottl		bw->BlockNumber = offset / AAC_BLOCK_SIZE;
28095350Sscottl		bw->ByteCount = len;
28195350Sscottl		bw->Stable = CUNSTABLE;
28295350Sscottl		bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual,
28395350Sscottl		    len, aac_dump_map_sg, fib, 0);
28495350Sscottl		bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
28595350Sscottl		    BUS_DMASYNC_PREWRITE);
28682527Sscottl
28795350Sscottl		/* fib->Header.Size is set in aac_dump_map_sg */
28895350Sscottl		size = fib->Header.Size + sizeof(struct aac_blockwrite);
28983114Sscottl
29095350Sscottl		if (aac_sync_fib(sc, ContainerCommand, 0, fib, size)) {
29195350Sscottl			printf("Error dumping block 0x%x\n", physical);
29295350Sscottl			return (EIO);
29383114Sscottl		}
29495350Sscottl		length -= len;
29595350Sscottl		offset += len;
29683114Sscottl	}
29782527Sscottl
29883114Sscottl	return (0);
29982527Sscottl}
30082527Sscottl
30183114Sscottl/*
30265793Smsmith * Handle completion of an I/O request.
30365793Smsmith */
30465793Smsmithvoid
30570393Smsmithaac_biodone(struct bio *bp)
30665793Smsmith{
30783114Sscottl	struct aac_disk	*sc;
30865793Smsmith
30983114Sscottl	debug_called(4);
31065793Smsmith
31183114Sscottl	sc = (struct aac_disk *)bp->bio_dev->si_drv1;
31283114Sscottl
31383114Sscottl	devstat_end_transaction_bio(&sc->ad_stats, bp);
31483114Sscottl	if (bp->bio_flags & BIO_ERROR) {
315103675Sphk#if __FreeBSD_version > 500039
316103675Sphk		disk_err(bp, "hard error", -1, 1);
317103675Sphk#elif __FreeBSD_version > 500005
318103675Sphk		int blkno;
31983114Sscottl		blkno = (sc->ad_label.d_nsectors) ? 0 : -1;
32083114Sscottl		diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label);
32182527Sscottl#else
322103675Sphk		int blkno;
323103675Sphk		blkno = (sc->ad_label.d_nsectors) ? 0 : -1;
32483114Sscottl		diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label);
32582527Sscottl#endif
32683114Sscottl	}
32783114Sscottl	biodone(bp);
32865793Smsmith}
32965793Smsmith
33083114Sscottl/*
33165793Smsmith * Stub only.
33265793Smsmith */
33365793Smsmithstatic int
33465793Smsmithaac_disk_probe(device_t dev)
33565793Smsmith{
33665793Smsmith
33783114Sscottl	debug_called(2);
33865793Smsmith
33983114Sscottl	return (0);
34065793Smsmith}
34165793Smsmith
34283114Sscottl/*
34365793Smsmith * Attach a unit to the controller.
34465793Smsmith */
34565793Smsmithstatic int
34665793Smsmithaac_disk_attach(device_t dev)
34765793Smsmith{
34883114Sscottl	struct aac_disk	*sc;
34983114Sscottl
35083114Sscottl	debug_called(1);
35165793Smsmith
35283114Sscottl	sc = (struct aac_disk *)device_get_softc(dev);
35365793Smsmith
35483114Sscottl	/* initialise our softc */
35583114Sscottl	sc->ad_controller =
35683114Sscottl	    (struct aac_softc *)device_get_softc(device_get_parent(dev));
35783114Sscottl	sc->ad_container = device_get_ivars(dev);
35883114Sscottl	sc->ad_dev = dev;
35965793Smsmith
36083114Sscottl	/*
36183114Sscottl	 * require that extended translation be enabled - other drivers read the
36283114Sscottl	 * disk!
36383114Sscottl	 */
36483114Sscottl	sc->ad_size = sc->ad_container->co_mntobj.Capacity;
36583114Sscottl	if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
36683114Sscottl		sc->ad_heads = 255;
36783114Sscottl		sc->ad_sectors = 63;
36883114Sscottl	} else if (sc->ad_size >= (1 * 1024 * 1024)) {	/* 1GB */
36983114Sscottl		sc->ad_heads = 128;
37083114Sscottl		sc->ad_sectors = 32;
37183114Sscottl	} else {
37283114Sscottl		sc->ad_heads = 64;
37383114Sscottl		sc->ad_sectors = 32;
37483114Sscottl	}
37583114Sscottl	sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
37665793Smsmith
37783114Sscottl	device_printf(dev, "%uMB (%u sectors)\n",
37883114Sscottl		      sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
37983114Sscottl		      sc->ad_size);
38065793Smsmith
38183114Sscottl	devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev),
38283114Sscottl			  AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS,
38383114Sscottl			  DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
38483114Sscottl			  DEVSTAT_PRIORITY_ARRAY);
38583114Sscottl
38683114Sscottl	/* attach a generic disk device to ourselves */
38783114Sscottl	sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0,
38883114Sscottl				   &aac_disk_cdevsw, &aac_disk_disk_cdevsw);
38983114Sscottl	sc->ad_dev_t->si_drv1 = sc;
39065793Smsmith#ifdef FREEBSD_4
39183114Sscottl	disks_registered++;
39265793Smsmith#endif
39365793Smsmith
39483114Sscottl	sc->ad_dev_t->si_iosize_max = aac_iosize_max;
39583114Sscottl	sc->unit = device_get_unit(dev);
39681082Sscottl
39783114Sscottl	return (0);
39865793Smsmith}
39965793Smsmith
40083114Sscottl/*
40165793Smsmith * Disconnect ourselves from the system.
40265793Smsmith */
40365793Smsmithstatic int
40465793Smsmithaac_disk_detach(device_t dev)
40565793Smsmith{
40683114Sscottl	struct aac_disk *sc;
40765793Smsmith
40883114Sscottl	debug_called(2);
40965793Smsmith
41083114Sscottl	sc = (struct aac_disk *)device_get_softc(dev);
41165793Smsmith
41283114Sscottl	if (sc->ad_flags & AAC_DISK_OPEN)
41383114Sscottl		return(EBUSY);
41483114Sscottl
41583114Sscottl	devstat_remove_entry(&sc->ad_stats);
41683114Sscottl	disk_destroy(sc->ad_dev_t);
41765793Smsmith#ifdef FREEBSD_4
41883114Sscottl	if (--disks_registered == 0)
41983114Sscottl		cdevsw_remove(&aac_disk_cdevsw);
42065793Smsmith#endif
42165793Smsmith
42283114Sscottl	return(0);
42365793Smsmith}
424