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