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