nrelease - fix/improve livecd
[dragonfly.git] / sys / dev / raid / aac / aac_disk.c
... / ...
CommitLineData
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 251115 2013-05-30 00:11:22Z marius $
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/module.h>
38
39#include <sys/bus.h>
40#include <sys/conf.h>
41#include <sys/disk.h>
42#include <sys/dtype.h>
43
44#include <vm/vm.h>
45#include <vm/pmap.h>
46
47#include <machine/md_var.h>
48#include <sys/rman.h>
49
50#include <dev/raid/aac/aacreg.h>
51#include <dev/raid/aac/aac_ioctl.h>
52#include <dev/raid/aac/aacvar.h>
53
54/*
55 * Interface to parent.
56 */
57static int aac_disk_probe(device_t dev);
58static int aac_disk_attach(device_t dev);
59static int aac_disk_detach(device_t dev);
60
61/*
62 * Interface to the device switch.
63 */
64static d_open_t aac_disk_open;
65static d_close_t aac_disk_close;
66static d_strategy_t aac_disk_strategy;
67static d_dump_t aac_disk_dump;
68
69static struct dev_ops aac_disk_ops = {
70 { "aacd", 0, D_DISK },
71 .d_open = aac_disk_open,
72 .d_close = aac_disk_close,
73 .d_read = physread,
74 .d_write = physwrite,
75 .d_strategy = aac_disk_strategy,
76 .d_dump = aac_disk_dump,
77};
78
79static devclass_t aac_disk_devclass;
80
81static device_method_t aac_disk_methods[] = {
82 DEVMETHOD(device_probe, aac_disk_probe),
83 DEVMETHOD(device_attach, aac_disk_attach),
84 DEVMETHOD(device_detach, aac_disk_detach),
85 DEVMETHOD_END
86};
87
88static driver_t aac_disk_driver = {
89 "aacd",
90 aac_disk_methods,
91 sizeof(struct aac_disk)
92};
93
94DRIVER_MODULE(aacd, aac, aac_disk_driver, aac_disk_devclass, NULL, NULL);
95
96/*
97 * Handle open from generic layer.
98 *
99 * This is called by the diskslice code on first open in order to get the
100 * basic device geometry paramters.
101 */
102static int
103aac_disk_open(struct dev_open_args *ap)
104{
105 struct aac_disk *sc;
106
107 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
108
109 sc = ap->a_head.a_dev->si_drv1;
110
111 if (sc == NULL) {
112 kprintf("aac_disk_open: No Softc\n");
113 return (ENXIO);
114 }
115
116 /* check that the controller is up and running */
117 if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) {
118 device_printf(sc->ad_controller->aac_dev,
119 "Controller Suspended controller state = 0x%x\n",
120 sc->ad_controller->aac_state);
121 return(ENXIO);
122 }
123
124 sc->ad_flags |= AAC_DISK_OPEN;
125 return (0);
126}
127
128/*
129 * Handle last close of the disk device.
130 */
131static int
132aac_disk_close(struct dev_close_args *ap)
133{
134 struct aac_disk *sc;
135
136 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
137
138 sc = ap->a_head.a_dev->si_drv1;
139
140 if (sc == NULL)
141 return (ENXIO);
142
143 sc->ad_flags &= ~AAC_DISK_OPEN;
144 return (0);
145}
146
147/*
148 * Handle an I/O request.
149 */
150static int
151aac_disk_strategy(struct dev_strategy_args *ap)
152{
153 struct bio *bio = ap->a_bio;
154 struct buf *bp = bio->bio_buf;
155 struct aac_disk *sc;
156
157 sc = ap->a_head.a_dev->si_drv1;
158 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
159
160 /* bogus disk? */
161 if (sc == NULL) {
162 bp->b_flags |= B_ERROR;
163 bp->b_error = EINVAL;
164 biodone(bio);
165 return (0);
166 }
167
168 /* do-nothing operation? */
169 if (bp->b_bcount == 0) {
170 bp->b_resid = bp->b_bcount;
171 biodone(bio);
172 return (0);
173 }
174
175 /* perform accounting */
176
177 /* pass the bio to the controller - it can work out who we are */
178 lockmgr(&sc->ad_controller->aac_io_lock, LK_EXCLUSIVE);
179 devstat_start_transaction(&sc->ad_stats);
180 aac_submit_bio(sc, bio);
181 lockmgr(&sc->ad_controller->aac_io_lock, LK_RELEASE);
182
183 return (0);
184}
185
186/*
187 * Map the S/G elements for doing a dump.
188 */
189static void
190aac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
191{
192 struct aac_fib *fib;
193 struct aac_blockwrite *bw;
194 struct aac_sg_table *sg;
195 int i;
196
197 fib = (struct aac_fib *)arg;
198 bw = (struct aac_blockwrite *)&fib->data[0];
199 sg = &bw->SgMap;
200
201 if (sg != NULL) {
202 sg->SgCount = nsegs;
203 for (i = 0; i < nsegs; i++) {
204 if (segs[i].ds_addr >= BUS_SPACE_MAXADDR_32BIT)
205 return;
206 sg->SgEntry[i].SgAddress = segs[i].ds_addr;
207 sg->SgEntry[i].SgByteCount = segs[i].ds_len;
208 }
209 fib->Header.Size = nsegs * sizeof(struct aac_sg_entry);
210 }
211}
212
213/*
214 * Map the S/G elements for doing a dump on 64-bit capable devices.
215 */
216static void
217aac_dump_map_sg64(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
218{
219 struct aac_fib *fib;
220 struct aac_blockwrite64 *bw;
221 struct aac_sg_table64 *sg;
222 int i;
223
224 fib = (struct aac_fib *)arg;
225 bw = (struct aac_blockwrite64 *)&fib->data[0];
226 sg = &bw->SgMap64;
227
228 if (sg != NULL) {
229 sg->SgCount = nsegs;
230 for (i = 0; i < nsegs; i++) {
231 sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
232 sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
233 }
234 fib->Header.Size = nsegs * sizeof(struct aac_sg_entry64);
235 }
236}
237
238/*
239 * Dump memory out to an array
240 *
241 * Send out one command at a time with up to maxio of data.
242 */
243static int
244aac_disk_dump(struct dev_dump_args *ap)
245{
246 cdev_t dev = ap->a_head.a_dev;
247 size_t length = ap->a_length;
248 off_t offset = ap->a_offset;
249 void *virtual = ap->a_virtual;
250 vm_offset_t physical = ap->a_physical;
251 struct aac_disk *ad;
252 struct aac_softc *sc;
253 struct aac_fib *fib;
254 size_t len, maxio;
255 int size;
256 static bus_dmamap_t dump_datamap;
257 static int first = 0;
258 bus_dmamap_callback_t *callback;
259 u_int32_t command;
260
261 ad = dev->si_drv1;
262
263 if (ad == NULL)
264 return (EINVAL);
265
266 sc= ad->ad_controller;
267
268 if (!first) {
269 first = 1;
270 if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) {
271 device_printf(sc->aac_dev,
272 "bus_dmamap_create failed\n");
273 return (ENOMEM);
274 }
275 }
276
277 /* Skip aac_alloc_sync_fib(). We don't want to mess with sleep locks */
278 fib = &sc->aac_common->ac_sync_fib;
279
280 while (length > 0) {
281 maxio = sc->aac_max_sectors << 9;
282 len = (length > maxio) ? maxio : length;
283 if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
284 struct aac_blockwrite *bw;
285 bw = (struct aac_blockwrite *)&fib->data[0];
286 bw->Command = VM_CtBlockWrite;
287 bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
288 bw->BlockNumber = offset / AAC_BLOCK_SIZE;
289 bw->ByteCount = len;
290 bw->Stable = CUNSTABLE;
291 command = ContainerCommand;
292 callback = aac_dump_map_sg;
293 size = sizeof(struct aac_blockwrite);
294 } else {
295 struct aac_blockwrite64 *bw;
296 bw = (struct aac_blockwrite64 *)&fib->data[0];
297 bw->Command = VM_CtHostWrite64;
298 bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
299 bw->BlockNumber = offset / AAC_BLOCK_SIZE;
300 bw->SectorCount = len / AAC_BLOCK_SIZE;
301 bw->Pad = 0;
302 bw->Flags = 0;
303 command = ContainerCommand64;
304 callback = aac_dump_map_sg64;
305 size = sizeof(struct aac_blockwrite64);
306 }
307
308 /*
309 * There really isn't any way to recover from errors or
310 * resource shortages here. Oh well. Because of that, don't
311 * bother trying to send the command from the callback; there
312 * is too much required context.
313 */
314 if (bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual,
315 len, callback, fib, BUS_DMA_NOWAIT) != 0)
316 return (ENOMEM);
317
318 bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
319 BUS_DMASYNC_PREWRITE);
320
321 /* fib->Header.Size is set in aac_dump_map_sg */
322 size += fib->Header.Size;
323
324 if (aac_sync_fib(sc, command, 0, fib, size)) {
325 device_printf(sc->aac_dev,
326 "Error dumping block 0x%jx\n",
327 (uintmax_t)physical);
328 return (EIO);
329 }
330
331 bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
332 BUS_DMASYNC_POSTWRITE);
333
334 bus_dmamap_unload(sc->aac_buffer_dmat, dump_datamap);
335
336 length -= len;
337 offset += len;
338 virtual = (uint8_t *)virtual + len;
339 }
340
341 return (0);
342}
343
344/*
345 * Handle completion of an I/O request.
346 */
347void
348aac_biodone(struct bio *bio, const char *code)
349{
350 struct buf *bp = bio->bio_buf;
351 struct aac_disk *sc;
352
353 sc = (struct aac_disk *)bio->bio_driver_info;
354 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
355
356 devstat_end_transaction_buf(&sc->ad_stats, bp);
357 if (bp->b_flags & B_ERROR) {
358 bp->b_resid = bp->b_bcount;
359 diskerr(bio, sc->ad_dev_t, code, 0, 0);
360 }
361
362 biodone(bio);
363}
364
365/*
366 * Stub only.
367 */
368static int
369aac_disk_probe(device_t dev)
370{
371
372 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
373
374 return (0);
375}
376
377/*
378 * Attach a unit to the controller.
379 */
380static int
381aac_disk_attach(device_t dev)
382{
383 struct disk_info info;
384 struct aac_disk *sc;
385
386 sc = (struct aac_disk *)device_get_softc(dev);
387 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
388
389 /* initialise our softc */
390 sc->ad_controller =
391 (struct aac_softc *)device_get_softc(device_get_parent(dev));
392 sc->ad_container = device_get_ivars(dev);
393 sc->ad_dev = dev;
394
395 /*
396 * require that extended translation be enabled - other drivers read the
397 * disk!
398 */
399 sc->ad_size = sc->ad_container->co_mntobj.Capacity;
400 if (sc->ad_controller->flags & AAC_FLAGS_LBA_64BIT)
401 sc->ad_size += (u_int64_t)
402 sc->ad_container->co_mntobj.CapacityHigh << 32;
403 if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */
404 sc->ad_heads = 255;
405 sc->ad_sectors = 63;
406 } else if (sc->ad_size >= (1 * 1024 * 1024)) { /* 1GB */
407 sc->ad_heads = 128;
408 sc->ad_sectors = 32;
409 } else {
410 sc->ad_heads = 64;
411 sc->ad_sectors = 32;
412 }
413 sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
414
415 device_printf(dev, "%juMB (%ju sectors)\n",
416 (intmax_t)sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
417 (intmax_t)sc->ad_size);
418 devstat_add_entry(&sc->ad_stats, "aacd", device_get_unit(dev),
419 AAC_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS,
420 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
421 DEVSTAT_PRIORITY_ARRAY);
422
423 /* attach a generic disk device to ourselves */
424 sc->ad_dev_t = disk_create(device_get_unit(dev), &sc->ad_disk,
425 &aac_disk_ops);
426 sc->ad_dev_t->si_drv1 = sc;
427
428 sc->ad_dev_t->si_iosize_max = sc->ad_controller->aac_max_sectors << 9;
429 sc->unit = device_get_unit(dev);
430
431 /*
432 * Set disk info, as it appears that all needed data is available already.
433 * Setting the disk info will also cause the probing to start.
434 */
435 bzero(&info, sizeof(info));
436 info.d_media_blksize= AAC_BLOCK_SIZE; /* mandatory */
437 info.d_media_blocks = sc->ad_size;
438
439 info.d_type = DTYPE_ESDI; /* optional */
440 info.d_secpertrack = sc->ad_sectors;
441 info.d_nheads = sc->ad_heads;
442 info.d_ncylinders = sc->ad_cylinders;
443 info.d_secpercyl = sc->ad_sectors * sc->ad_heads;
444
445 disk_setdiskinfo(&sc->ad_disk, &info);
446
447 return (0);
448}
449
450/*
451 * Disconnect ourselves from the system.
452 */
453static int
454aac_disk_detach(device_t dev)
455{
456 struct aac_disk *sc;
457
458 sc = (struct aac_disk *)device_get_softc(dev);
459 fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
460
461 if (sc->ad_flags & AAC_DISK_OPEN)
462 return(EBUSY);
463
464 devstat_remove_entry(&sc->ad_stats);
465 disk_destroy(&sc->ad_disk);
466
467 return(0);
468}