aac_disk.c revision 83114
1/*-
2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2001 Scott Long
4 * Copyright (c) 2000 BSDi
5 * Copyright (c) 2001 Adaptec, Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *	$FreeBSD: head/sys/dev/aac/aac_disk.c 83114 2001-09-05 20:43:02Z scottl $
30 */
31
32#include "opt_aac.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/sysctl.h>
38
39#include <dev/aac/aac_compat.h>
40#include <sys/bus.h>
41#include <sys/conf.h>
42#include <sys/devicestat.h>
43#include <sys/disk.h>
44
45#include <vm/vm.h>
46#include <vm/pmap.h>
47
48#include <machine/md_var.h>
49#include <machine/bus.h>
50#include <sys/rman.h>
51
52#include <dev/aac/aacreg.h>
53#include <dev/aac/aac_ioctl.h>
54#include <dev/aac/aacvar.h>
55
56/*
57 * Interface to parent.
58 */
59static int aac_disk_probe(device_t dev);
60static int aac_disk_attach(device_t dev);
61static int aac_disk_detach(device_t dev);
62
63/*
64 * Interface to the device switch.
65 */
66static	d_open_t	aac_disk_open;
67static	d_close_t	aac_disk_close;
68static	d_strategy_t	aac_disk_strategy;
69static	d_dump_t	aac_disk_dump;
70
71#define AAC_DISK_CDEV_MAJOR	151
72
73static struct cdevsw aac_disk_cdevsw = {
74	/* open */		aac_disk_open,
75	/* close */		aac_disk_close,
76	/* read */		physread,
77	/* write */		physwrite,
78	/* ioctl */		noioctl,
79	/* poll */		nopoll,
80	/* mmap */		nommap,
81	/* strategy */		aac_disk_strategy,
82	/* name */ 		"aacd",
83	/* maj */		AAC_DISK_CDEV_MAJOR,
84	/* dump */		aac_disk_dump,
85	/* psize */ 		nopsize,
86	/* flags */		D_DISK,
87#if __FreeBSD_version < 500005
88	/* bmaj */		-1
89#endif
90};
91
92devclass_t		aac_disk_devclass;
93static struct cdevsw	aac_disk_disk_cdevsw;
94#ifdef FREEBSD_4
95static int		disks_registered = 0;
96#endif
97
98static device_method_t aac_disk_methods[] = {
99	DEVMETHOD(device_probe,	aac_disk_probe),
100	DEVMETHOD(device_attach,	aac_disk_attach),
101	DEVMETHOD(device_detach,	aac_disk_detach),
102	{ 0, 0 }
103};
104
105static driver_t aac_disk_driver = {
106	"aacd",
107	aac_disk_methods,
108	sizeof(struct aac_disk)
109};
110
111DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, 0, 0);
112
113/* sysctl tunables */
114static unsigned int aac_iosize_max = 65536;	/* due to limits of the card */
115TUNABLE_INT("hw.aac.iosize_max", &aac_iosize_max);
116
117SYSCTL_DECL(_hw_aac);
118SYSCTL_UINT(_hw_aac, OID_AUTO, iosize_max, CTLFLAG_RD, &aac_iosize_max, 0,
119	    "Max I/O size per transfer to an array");
120
121#define AAC_MAXIO	65536
122
123/*
124 * Handle open from generic layer.
125 *
126 * This is called by the diskslice code on first open in order to get the
127 * basic device geometry paramters.
128 */
129static int
130aac_disk_open(dev_t dev, int flags, int fmt, struct proc *p)
131{
132	struct aac_disk	*sc;
133	struct disklabel *label;
134
135	debug_called(4);
136
137	sc = (struct aac_disk *)dev->si_drv1;
138
139	if (sc == NULL)
140		return (ENXIO);
141
142	/* check that the controller is up and running */
143	if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND)
144		return(ENXIO);
145
146	/* build synthetic label */
147	label = &sc->ad_disk.d_label;
148	bzero(label, sizeof(*label));
149	label->d_type = DTYPE_ESDI;
150	label->d_secsize	= AAC_BLOCK_SIZE;
151	label->d_nsectors   = sc->ad_sectors;
152	label->d_ntracks	= sc->ad_heads;
153	label->d_ncylinders = sc->ad_cylinders;
154	label->d_secpercyl  = sc->ad_sectors * sc->ad_heads;
155	label->d_secperunit = sc->ad_size;
156
157	sc->ad_flags |= AAC_DISK_OPEN;
158	return (0);
159}
160
161/*
162 * Handle last close of the disk device.
163 */
164static int
165aac_disk_close(dev_t dev, int flags, int fmt, struct proc *p)
166{
167	struct aac_disk	*sc;
168
169	debug_called(4);
170
171	sc = (struct aac_disk *)dev->si_drv1;
172
173	if (sc == NULL)
174		return (ENXIO);
175
176	sc->ad_flags &= ~AAC_DISK_OPEN;
177	return (0);
178}
179
180/*
181 * Handle an I/O request.
182 */
183static void
184aac_disk_strategy(struct bio *bp)
185{
186	struct aac_disk	*sc;
187
188	debug_called(4);
189
190	sc = (struct aac_disk *)bp->bio_dev->si_drv1;
191
192	/* bogus disk? */
193	if (sc == NULL) {
194		bp->bio_flags |= BIO_ERROR;
195		bp->bio_error = EINVAL;
196		biodone(bp);
197		return;
198	}
199
200	/* do-nothing operation? */
201	if (bp->bio_bcount == 0) {
202		bp->bio_resid = bp->bio_bcount;
203		biodone(bp);
204		return;
205	}
206
207	/* perform accounting */
208	devstat_start_transaction(&sc->ad_stats);
209
210	/* pass the bio to the controller - it can work out who we are */
211	aac_submit_bio(bp);
212	return;
213}
214
215/*
216 * Dump memory out to an array
217 *
218 * This queues blocks of memory of size AAC_MAXIO to the controller and waits
219 * for the controller to complete the requests.
220 */
221static int
222aac_disk_dump(dev_t dev)
223{
224	struct aac_disk *ad;
225	struct aac_softc *sc;
226	vm_offset_t addr;
227	long blkcnt;
228	unsigned int count, blkno, secsize;
229	int dumppages;
230	int i, error;
231
232	ad = dev->si_drv1;
233	addr = 0;
234	dumppages = AAC_MAXIO / PAGE_SIZE;
235
236	if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize)))
237		return (error);
238
239	if (ad == NULL)
240		return (ENXIO);
241
242	sc= ad->ad_controller;
243
244	blkcnt = howmany(PAGE_SIZE, secsize);
245
246	while (count > 0) {
247		caddr_t va = NULL;
248
249		if ((count / blkcnt) < dumppages)
250			dumppages = count / blkcnt;
251
252		for (i = 0; i < dumppages; ++i) {
253			vm_offset_t a = addr + (i * PAGE_SIZE);
254			if (is_physical_memory(a)) {
255			va = pmap_kenter_temporary(trunc_page(a), i);
256			} else {
257			va = pmap_kenter_temporary(trunc_page(0), i);
258			}
259		}
260
261retry:
262		/*
263		 * Queue the block to the controller.  If the queue is full,
264		 * EBUSY will be returned.
265		 */
266		error = aac_dump_enqueue(ad, blkno, va, dumppages);
267		if (error && (error != EBUSY))
268			return (error);
269
270		if (!error) {
271			if (dumpstatus(addr, (long)(count * DEV_BSIZE)) < 0)
272			return (EINTR);
273
274			blkno += blkcnt * dumppages;
275			count -= blkcnt * dumppages;
276			addr += PAGE_SIZE * dumppages;
277			if (count > 0)
278			continue;
279		}
280
281		/*
282		 * Either the queue was full on the last attemp, or we have no
283		 * more data to dump.  Let the queue drain out and retry the
284		 * block if the queue was full.
285		 */
286		aac_dump_complete(sc);
287
288		if (error == EBUSY)
289			goto retry;
290	}
291
292	return (0);
293}
294
295/*
296 * Handle completion of an I/O request.
297 */
298void
299aac_biodone(struct bio *bp)
300{
301	struct aac_disk	*sc;
302	int blkno;
303
304	debug_called(4);
305
306	sc = (struct aac_disk *)bp->bio_dev->si_drv1;
307
308	devstat_end_transaction_bio(&sc->ad_stats, bp);
309	if (bp->bio_flags & BIO_ERROR) {
310		blkno = (sc->ad_label.d_nsectors) ? 0 : -1;
311#if __FreeBSD_version > 500005
312		diskerr(bp, (char *)bp->bio_driver1, blkno, &sc->ad_label);
313#else
314		diskerr(bp, (char *)bp->bio_driver1, 0, blkno, &sc->ad_label);
315#endif
316	}
317	biodone(bp);
318}
319
320/*
321 * Stub only.
322 */
323static int
324aac_disk_probe(device_t dev)
325{
326
327	debug_called(2);
328
329	return (0);
330}
331
332/*
333 * Attach a unit to the controller.
334 */
335static int
336aac_disk_attach(device_t dev)
337{
338	struct aac_disk	*sc;
339
340	debug_called(1);
341
342	sc = (struct aac_disk *)device_get_softc(dev);
343
344	/* initialise our softc */
345	sc->ad_controller =
346	    (struct aac_softc *)device_get_softc(device_get_parent(dev));
347	sc->ad_container = device_get_ivars(dev);
348	sc->ad_dev = dev;
349
350	/*
351	 * require that extended translation be enabled - other drivers read the
352	 * disk!
353	 */
354	sc->ad_size = sc->ad_container->co_mntobj.Capacity;
355	if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
356		sc->ad_heads = 255;
357		sc->ad_sectors = 63;
358	} else if (sc->ad_size >= (1 * 1024 * 1024)) {	/* 1GB */
359		sc->ad_heads = 128;
360		sc->ad_sectors = 32;
361	} else {
362		sc->ad_heads = 64;
363		sc->ad_sectors = 32;
364	}
365	sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
366
367	device_printf(dev, "%uMB (%u sectors)\n",
368		      sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
369		      sc->ad_size);
370
371	devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev),
372			  AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS,
373			  DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
374			  DEVSTAT_PRIORITY_ARRAY);
375
376	/* attach a generic disk device to ourselves */
377	sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0,
378				   &aac_disk_cdevsw, &aac_disk_disk_cdevsw);
379	sc->ad_dev_t->si_drv1 = sc;
380#ifdef FREEBSD_4
381	disks_registered++;
382#endif
383
384	sc->ad_dev_t->si_iosize_max = aac_iosize_max;
385	sc->unit = device_get_unit(dev);
386
387	return (0);
388}
389
390/*
391 * Disconnect ourselves from the system.
392 */
393static int
394aac_disk_detach(device_t dev)
395{
396	struct aac_disk *sc;
397
398	debug_called(2);
399
400	sc = (struct aac_disk *)device_get_softc(dev);
401
402	if (sc->ad_flags & AAC_DISK_OPEN)
403		return(EBUSY);
404
405	devstat_remove_entry(&sc->ad_stats);
406	disk_destroy(sc->ad_dev_t);
407#ifdef FREEBSD_4
408	if (--disks_registered == 0)
409		cdevsw_remove(&aac_disk_cdevsw);
410#endif
411
412	return(0);
413}
414