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