aac_disk.c revision 82527
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 82527 2001-08-29 23:34:05Z 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 = (struct aac_disk *)dev->si_drv1;
133    struct disklabel	*label;
134
135    debug_called(4);
136
137    if (sc == NULL)
138	return (ENXIO);
139
140    /* check that the controller is up and running */
141    if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND)
142	return(ENXIO);
143
144    /* build synthetic label */
145    label = &sc->ad_disk.d_label;
146    bzero(label, sizeof(*label));
147    label->d_type = DTYPE_ESDI;
148    label->d_secsize    = AAC_BLOCK_SIZE;
149    label->d_nsectors   = sc->ad_sectors;
150    label->d_ntracks    = sc->ad_heads;
151    label->d_ncylinders = sc->ad_cylinders;
152    label->d_secpercyl  = sc->ad_sectors * sc->ad_heads;
153    label->d_secperunit = sc->ad_size;
154
155    sc->ad_flags |= AAC_DISK_OPEN;
156    return (0);
157}
158
159/******************************************************************************
160 * Handle last close of the disk device.
161 */
162static int
163aac_disk_close(dev_t dev, int flags, int fmt, struct proc *p)
164{
165    struct aac_disk	*sc = (struct aac_disk *)dev->si_drv1;
166
167    debug_called(4);
168
169    if (sc == NULL)
170	return (ENXIO);
171
172    sc->ad_flags &= ~AAC_DISK_OPEN;
173    return (0);
174}
175
176/******************************************************************************
177 * Handle an I/O request.
178 */
179static void
180aac_disk_strategy(struct bio *bp)
181{
182    struct aac_disk	*sc = (struct aac_disk *)bp->bio_dev->si_drv1;
183
184    debug_called(4);
185
186    /* bogus disk? */
187    if (sc == NULL) {
188	bp->bio_flags |= BIO_ERROR;
189	bp->bio_error = EINVAL;
190	biodone(bp);
191	return;
192    }
193
194    /* do-nothing operation? */
195    if (bp->bio_bcount == 0) {
196	bp->bio_resid = bp->bio_bcount;
197	biodone(bp);
198	return;
199    }
200
201    /* perform accounting */
202    devstat_start_transaction(&sc->ad_stats);
203
204    /* pass the bio to the controller - it can work out who we are */
205    aac_submit_bio(bp);
206    return;
207}
208
209/******************************************************************************
210 * Dump memory out to an array
211 *
212 * This queues blocks of memory of size AAC_MAXIO to the controller and waits
213 * for the controller to complete the requests.
214 */
215static int
216aac_disk_dump(dev_t dev)
217{
218    struct aac_disk	*ad = dev->si_drv1;
219    struct aac_softc	*sc;
220    vm_offset_t		addr = 0;
221    long		blkcnt;
222    unsigned int	count, blkno, secsize;
223    int			dumppages = AAC_MAXIO / PAGE_SIZE;
224    int			i, error;
225
226    if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize)))
227	return (error);
228
229    if (ad == NULL)
230	return (ENXIO);
231
232    sc= ad->ad_controller;
233
234    blkcnt = howmany(PAGE_SIZE, secsize);
235
236    while (count > 0) {
237	caddr_t va = NULL;
238
239	if ((count / blkcnt) < dumppages)
240	    dumppages = count / blkcnt;
241
242	for (i = 0; i < dumppages; ++i) {
243	    vm_offset_t a = addr + (i * PAGE_SIZE);
244	    if (is_physical_memory(a)) {
245		va = pmap_kenter_temporary(trunc_page(a), i);
246	    } else {
247		va = pmap_kenter_temporary(trunc_page(0), i);
248	    }
249	}
250
251retry:
252	/*
253	 * Queue the block to the controller.  If the queue is full, EBUSY
254	 * will be returned.
255	 */
256	error = aac_dump_enqueue(ad, blkno, va, dumppages);
257	if (error && (error != EBUSY))
258	    return (error);
259
260	if (!error) {
261	    if (dumpstatus(addr, (long)(count * DEV_BSIZE)) < 0)
262		return (EINTR);
263
264	    blkno += blkcnt * dumppages;
265	    count -= blkcnt * dumppages;
266	    addr += PAGE_SIZE * dumppages;
267	    if (count > 0)
268		continue;
269	}
270
271	/*
272	 * Either the queue was full on the last attemp, or we have no more
273	 * data to dump.  Let the queue drain out and retry the block if
274	 * the queue was full.
275	 */
276	aac_dump_complete(sc);
277
278	if (error == EBUSY)
279	    goto retry;
280    }
281
282    return (0);
283}
284
285/******************************************************************************
286 * Handle completion of an I/O request.
287 */
288void
289aac_biodone(struct bio *bp)
290{
291    struct aac_disk	*sc = (struct aac_disk *)bp->bio_dev->si_drv1;
292
293    debug_called(4);
294
295    devstat_end_transaction_bio(&sc->ad_stats, bp);
296    if (bp->bio_flags & BIO_ERROR) {
297#if __FreeBSD_version > 500005
298	diskerr(bp, (char *)bp->bio_driver1, 0, &sc->ad_label);
299#else
300	diskerr(bp, (char *)bp->bio_driver1, 0, -1, &sc->ad_label);
301#endif
302    }
303    biodone(bp);
304}
305
306/******************************************************************************
307 * Stub only.
308 */
309static int
310aac_disk_probe(device_t dev)
311{
312
313    debug_called(2);
314
315    return (0);
316}
317
318/******************************************************************************
319 * Attach a unit to the controller.
320 */
321static int
322aac_disk_attach(device_t dev)
323{
324    struct aac_disk	*sc = (struct aac_disk *)device_get_softc(dev);
325
326    debug_called(1);
327
328    /* initialise our softc */
329    sc->ad_controller =
330	(struct aac_softc *)device_get_softc(device_get_parent(dev));
331    sc->ad_container = device_get_ivars(dev);
332    sc->ad_dev = dev;
333
334    /*
335     * require that extended translation be enabled - other drivers read the
336     * disk!
337     */
338    sc->ad_size = sc->ad_container->co_mntobj.Capacity;
339    if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
340	sc->ad_heads = 255;
341	sc->ad_sectors = 63;
342    } else if (sc->ad_size >= (1 * 1024 * 1024)) {	/* 1GB */
343	sc->ad_heads = 128;
344	sc->ad_sectors = 32;
345    } else {
346	sc->ad_heads = 64;
347	sc->ad_sectors = 32;
348    }
349    sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
350
351    device_printf(dev, "%uMB (%u sectors)\n",
352		  sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE), sc->ad_size);
353
354    devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev),
355		      AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS,
356		      DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
357		      DEVSTAT_PRIORITY_ARRAY);
358
359    /* attach a generic disk device to ourselves */
360    sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk, 0,
361			       &aac_disk_cdevsw, &aac_disk_disk_cdevsw);
362    sc->ad_dev_t->si_drv1 = sc;
363#ifdef FREEBSD_4
364    disks_registered++;
365#endif
366
367    sc->ad_dev_t->si_iosize_max = aac_iosize_max;
368    sc->unit = device_get_unit(dev);
369
370    return (0);
371}
372
373/******************************************************************************
374 * Disconnect ourselves from the system.
375 */
376static int
377aac_disk_detach(device_t dev)
378{
379    struct aac_disk *sc = (struct aac_disk *)device_get_softc(dev);
380
381    debug_called(2);
382
383    if (sc->ad_flags & AAC_DISK_OPEN)
384	return(EBUSY);
385
386    devstat_remove_entry(&sc->ad_stats);
387    disk_destroy(sc->ad_dev_t);
388#ifdef FREEBSD_4
389    if (--disks_registered == 0)
390	cdevsw_remove(&aac_disk_cdevsw);
391#endif
392
393    return(0);
394}
395
396