FreeBSD/Linux Kernel Cross Reference
sys/dev/mfi/mfi_disk.c
1 /*-
2 * Copyright (c) 2006 IronPort Systems
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_mfi.h"
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/select.h>
36 #include <sys/module.h>
37 #include <sys/malloc.h>
38 #include <sys/uio.h>
39
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 <dev/mfi/mfi_compat.h>
49 #include <machine/md_var.h>
50 #include <machine/bus.h>
51 #include <sys/rman.h>
52
53 #include <dev/mfi/mfireg.h>
54 #include <dev/mfi/mfi_ioctl.h>
55 #include <dev/mfi/mfivar.h>
56
57 static int mfi_disk_probe(device_t dev);
58 static int mfi_disk_attach(device_t dev);
59 static int mfi_disk_detach(device_t dev);
60
61 static d_open_t mfi_disk_open;
62 static d_close_t mfi_disk_close;
63 static d_strategy_t mfi_disk_strategy;
64 static d_dump_t mfi_disk_dump;
65
66 static devclass_t mfi_disk_devclass;
67
68 struct mfi_disk {
69 device_t ld_dev;
70 dev_t ld_dev_t;
71 int ld_id;
72 int ld_unit;
73 struct mfi_softc *ld_controller;
74 struct mfi_ld *ld_ld;
75 struct disk ld_disk;
76 struct devstat ld_stats;
77 int ld_flags;
78 #define MFID_OPEN (1<<0) /* drive is open */
79 };
80
81 #define MFID_CDEV_MAJOR 178
82
83 static struct cdevsw mfid_cdevsw = {
84 mfi_disk_open,
85 mfi_disk_close,
86 physread,
87 physwrite,
88 noioctl,
89 nopoll,
90 nommap,
91 mfi_disk_strategy,
92 "mfid",
93 MFID_CDEV_MAJOR,
94 mfi_disk_dump,
95 nopsize,
96 D_DISK,
97 -1
98 };
99 static struct cdevsw mfiddisk_cdevsw;
100 static int mfi_disks_registered;
101
102
103 static device_method_t mfi_disk_methods[] = {
104 DEVMETHOD(device_probe, mfi_disk_probe),
105 DEVMETHOD(device_attach, mfi_disk_attach),
106 DEVMETHOD(device_detach, mfi_disk_detach),
107 { 0, 0 }
108 };
109
110 static driver_t mfi_disk_driver = {
111 "mfid",
112 mfi_disk_methods,
113 sizeof(struct mfi_disk)
114 };
115
116 DRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, 0, 0);
117
118 static char *
119 mfi_disk_describe_state(uint8_t state)
120 {
121
122 switch (state) {
123 case MFI_LD_STATE_OFFLINE:
124 return ("offline");
125
126 case MFI_LD_STATE_PARTIALLY_DEGRADED:
127 return ("partially degraded");
128
129 case MFI_LD_STATE_DEGRADED:
130 return ("degraded");
131
132 case MFI_LD_STATE_OPTIMAL:
133 return ("optimal");
134 }
135 return ("unknown");
136 }
137
138 static int
139 mfi_disk_probe(device_t dev)
140 {
141
142 return (0);
143 }
144
145 static int
146 mfi_disk_attach(device_t dev)
147 {
148 struct mfi_disk *sc;
149 struct mfi_ld *ld;
150 struct disklabel *label;
151 uint64_t sectors;
152 uint32_t secsize;
153
154 sc = device_get_softc(dev);
155 ld = device_get_ivars(dev);
156
157 sc->ld_dev = dev;
158 sc->ld_id = ld->ld_id;
159 sc->ld_unit = device_get_unit(dev);
160 sc->ld_ld = device_get_ivars(dev);
161 sc->ld_controller = device_get_softc(device_get_parent(dev));
162
163 sectors = ld->ld_info->size;
164 secsize = MFI_SECTOR_LEN;
165 TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, ld, ld_link);
166
167 device_printf(dev, "%lluMB (%llu sectors) RAID %d (%s)\n",
168 sectors / (1024 * 1024 / secsize), sectors,
169 ld->ld_info->ld_config.params.primary_raid_level,
170 mfi_disk_describe_state(ld->ld_info->ld_config.params.state));
171
172 devstat_add_entry(&sc->ld_stats, "mfid", sc->ld_unit, secsize,
173 DEVSTAT_NO_ORDERED_TAGS,
174 DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
175 DEVSTAT_PRIORITY_ARRAY);
176
177 sc->ld_dev_t =
178 disk_create(sc->ld_unit, &sc->ld_disk, 0, &mfid_cdevsw,
179 &mfiddisk_cdevsw);
180 sc->ld_dev_t->si_drv1 = sc;
181 mfi_disks_registered++;
182 sc->ld_dev_t->si_iosize_max = sc->ld_controller->mfi_max_io * secsize;
183
184 label = &sc->ld_disk.d_label;
185 bzero(label, sizeof(*label));
186 label->d_type = DTYPE_SCSI;
187 label->d_secsize = secsize;
188 if ((sectors * secsize) > (1 * 1024 * 1024)) {
189 label->d_ntracks = 255;
190 label->d_nsectors = 63;
191 } else {
192 label->d_ntracks = 64;
193 label->d_nsectors = 32;
194 }
195 label->d_ncylinders = sectors / (label->d_ntracks * label->d_nsectors);
196 label->d_secpercyl = label->d_ntracks * label->d_nsectors;
197 label->d_secperunit = sectors;
198
199 return (0);
200 }
201
202 static int
203 mfi_disk_detach(device_t dev)
204 {
205 struct mfi_disk *sc;
206
207 sc = device_get_softc(dev);
208
209 if (sc->ld_flags & MFID_OPEN)
210 return (EBUSY);
211
212 devstat_remove_entry(&sc->ld_stats);
213 if (--mfi_disks_registered == 0)
214 cdevsw_remove(&mfiddisk_cdevsw);
215 disk_destroy(sc->ld_dev_t);
216 return (0);
217 }
218
219 static int
220 mfi_disk_open(dev_t dev , int flags, int cmd, d_thread_t *td)
221 {
222 struct mfi_disk *sc;
223
224 sc = (struct mfi_disk *)dev->si_drv1;
225 sc->ld_flags |= MFID_OPEN;
226
227 return (0);
228 }
229
230 static int
231 mfi_disk_close(dev_t dev , int flags, int cmd, d_thread_t *td)
232 {
233 struct mfi_disk *sc;
234
235 sc = (struct mfi_disk *)dev->si_drv1;
236 if (sc == NULL)
237 return (ENXIO);
238 sc->ld_flags &= ~MFID_OPEN;
239
240 return (0);
241 }
242
243 static void
244 mfi_disk_strategy(struct bio *bio)
245 {
246 struct mfi_disk *sc;
247 struct mfi_softc *controller;
248
249 sc = bio->bio_disk->d_drv1;
250
251 if (sc == NULL) {
252 bio->bio_error = EINVAL;
253 bio->bio_flags |= BIO_ERROR;
254 bio->bio_resid = bio->bio_bcount;
255 biodone(bio);
256 return;
257 }
258
259 controller = sc->ld_controller;
260 bio->bio_driver1 = (void *)(uintptr_t)sc->ld_id;
261 devstat_start_transaction(&sc->ld_stats);
262 mfi_enqueue_bio(controller, bio);
263 mfi_startio(controller);
264 return;
265 }
266
267 void
268 mfi_disk_complete(struct bio *bio)
269 {
270 struct mfi_disk *sc;
271 struct mfi_frame_header *hdr;
272
273 sc = bio->bio_disk->d_drv1;
274 hdr = bio->bio_driver1;
275
276 if (bio->bio_flags & BIO_ERROR) {
277 if (bio->bio_error == 0)
278 bio->bio_error = EIO;
279 diskerr(bio, "hard error", -1, 1, NULL);
280 } else {
281 bio->bio_resid = 0;
282 }
283 devstat_end_transaction_bio(&sc->ld_stats, bio);
284 biodone(bio);
285 }
286
287 static int
288 mfi_disk_dump(dev_t dev)
289 {
290 struct mfi_disk *sc;
291 struct mfi_softc *parent_sc;
292 u_int count, blkno;
293 u_int32_t secsize;
294 vm_paddr_t addr = 0;
295 long blkcnt;
296 int dumppages = MAXDUMPPGS;
297 int error;
298 int i;
299
300 if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize)))
301 return (error);
302
303 sc = dev->d_drv1;
304 parent_sc = sc->ld_controller;
305
306 if (!sc || !parent_sc)
307 return (ENXIO);
308
309 blkcnt = howmany(PAGE_SIZE, secsize);
310
311 while (count > 0) {
312 caddr_t va = NULL;
313
314 if ((count / blkcnt) < dumppages)
315 dumppages = count / blkcnt;
316
317 for (i = 0; i < dumppages; ++i) {
318 vm_paddr_t a = addr + (i * PAGE_SIZE);
319 if (is_physical_memory(a))
320 va = pmap_kenter_temporary(trunc_page(a), i);
321 else
322 va = pmap_kenter_temporary(trunc_page(0), i);
323 }
324 if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, blkno, va,
325 PAGE_SIZE * dumppages)) != 0) {
326 return (error);
327 }
328
329 if (dumpstatus(addr, (off_t)count * DEV_BSIZE) < 0)
330 return (EINTR);
331
332 blkno += blkcnt * dumppages;
333 count -= blkcnt * dumppages;
334 addr += PAGE_SIZE * dumppages;
335 }
336 return (0);
337 }
Cache object: e589bc51a478c24d8ec7b2b5a836c892
|