1 /*-
2 * Copyright (c) 2010 by Panasas, Inc.
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 immediately at the beginning of the file, without modification,
10 * this list of conditions, and the following disclaimer.
11 * 2. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
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 FOR
18 * 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 * VHBA device that just reate boatloads of devices.
28 */
29 #include "vhba.h"
30
31 #define MAX_TGT VHBA_MAXTGT
32 #define MAX_LUN 32
33
34 #define DISK_SIZE 32
35 #define DISK_SHIFT 9
36 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT)
37 #define PSEUDO_SPT 64
38 #define PSEUDO_HDS 64
39 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS)
40
41 typedef struct {
42 vhba_softc_t * vhba;
43 uint8_t * disk;
44 size_t disk_size;
45 struct task qt;
46 } vhbalots_t;
47
48 static void vhba_task(void *, int);
49 static void vhbalots_act(vhbalots_t *, struct ccb_scsiio *);
50
51 void
vhba_init(vhba_softc_t * vhba)52 vhba_init(vhba_softc_t *vhba)
53 {
54 static vhbalots_t vhbas;
55 vhbas.vhba = vhba;
56 vhbas.disk_size = DISK_SIZE << 20;
57 vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO);
58 vhba->private = &vhbas;
59 TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas);
60 }
61
62
63 void
vhba_fini(vhba_softc_t * vhba)64 vhba_fini(vhba_softc_t *vhba)
65 {
66 vhbalots_t *vhbas = vhba->private;
67 vhba->private = NULL;
68 free(vhbas->disk, M_DEVBUF);
69 }
70
71 void
vhba_kick(vhba_softc_t * vhba)72 vhba_kick(vhba_softc_t *vhba)
73 {
74 vhbalots_t *vhbas = vhba->private;
75 taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
76 }
77
78 static void
vhba_task(void * arg,int pending)79 vhba_task(void *arg, int pending)
80 {
81 vhbalots_t *vhbas = arg;
82 struct ccb_hdr *ccbh;
83
84 mtx_lock(&vhbas->vhba->lock);
85 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) {
86 TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe);
87 vhbalots_act(vhbas, (struct ccb_scsiio *)ccbh);
88 }
89 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) {
90 TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe);
91 xpt_done((union ccb *)ccbh);
92 }
93 mtx_unlock(&vhbas->vhba->lock);
94 }
95
96 static void
vhbalots_act(vhbalots_t * vhbas,struct ccb_scsiio * csio)97 vhbalots_act(vhbalots_t *vhbas, struct ccb_scsiio *csio)
98 {
99 char junk[128];
100 uint8_t *cdb, *ptr, status;
101 uint32_t data_len;
102 uint64_t off;
103
104 data_len = 0;
105 status = SCSI_STATUS_OK;
106
107 memset(&csio->sense_data, 0, sizeof (csio->sense_data));
108 cdb = csio->cdb_io.cdb_bytes;
109
110 if (csio->ccb_h.target_id >= MAX_TGT) {
111 csio->ccb_h.status = CAM_SEL_TIMEOUT;
112 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
113 return;
114 }
115 if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
116 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
117 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
118 return;
119 }
120
121 switch (cdb[0]) {
122 case MODE_SENSE:
123 case MODE_SENSE_10:
124 {
125 unsigned int nbyte;
126 uint8_t page = cdb[2] & SMS_PAGE_CODE;
127 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK;
128
129 switch (page) {
130 case SMS_FORMAT_DEVICE_PAGE:
131 case SMS_GEOMETRY_PAGE:
132 case SMS_CACHE_PAGE:
133 case SMS_CONTROL_MODE_PAGE:
134 case SMS_ALL_PAGES_PAGE:
135 break;
136 default:
137 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
138 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
139 return;
140 }
141 memset(junk, 0, sizeof (junk));
142 if (cdb[1] & SMS_DBD) {
143 ptr = &junk[4];
144 } else {
145 ptr = junk;
146 ptr[3] = 8;
147 ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
148 ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
149 ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff;
150 ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
151
152 ptr[8] = (DISK_NBLKS >> 24) & 0xff;
153 ptr[9] = (DISK_NBLKS >> 16) & 0xff;
154 ptr[10] = (DISK_NBLKS >> 8) & 0xff;
155 ptr[11] = DISK_NBLKS & 0xff;
156 ptr += 12;
157 }
158
159 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) {
160 ptr[0] = SMS_FORMAT_DEVICE_PAGE;
161 ptr[1] = 24;
162 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
163 /* tracks per zone */
164 /* ptr[2] = 0; */
165 /* ptr[3] = 0; */
166 /* alternate sectors per zone */
167 /* ptr[4] = 0; */
168 /* ptr[5] = 0; */
169 /* alternate tracks per zone */
170 /* ptr[6] = 0; */
171 /* ptr[7] = 0; */
172 /* alternate tracks per logical unit */
173 /* ptr[8] = 0; */
174 /* ptr[9] = 0; */
175 /* sectors per track */
176 ptr[10] = (PSEUDO_SPT >> 8) & 0xff;
177 ptr[11] = PSEUDO_SPT & 0xff;
178 /* data bytes per physical sector */
179 ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff;
180 ptr[13] = (1 << DISK_SHIFT) & 0xff;
181 /* interleave */
182 /* ptr[14] = 0; */
183 /* ptr[15] = 1; */
184 /* track skew factor */
185 /* ptr[16] = 0; */
186 /* ptr[17] = 0; */
187 /* cylinder skew factor */
188 /* ptr[18] = 0; */
189 /* ptr[19] = 0; */
190 /* SSRC, HSEC, RMB, SURF */
191 }
192 ptr += 26;
193 }
194
195 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) {
196 ptr[0] = SMS_GEOMETRY_PAGE;
197 ptr[1] = 24;
198 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
199 uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC;
200 /* number of cylinders */
201 ptr[2] = (cyl >> 24) & 0xff;
202 ptr[3] = (cyl >> 16) & 0xff;
203 ptr[4] = cyl & 0xff;
204 /* number of heads */
205 ptr[5] = PSEUDO_HDS;
206 /* starting cylinder- write precompensation */
207 /* ptr[6] = 0; */
208 /* ptr[7] = 0; */
209 /* ptr[8] = 0; */
210 /* starting cylinder- reduced write current */
211 /* ptr[9] = 0; */
212 /* ptr[10] = 0; */
213 /* ptr[11] = 0; */
214 /* drive step rate */
215 /* ptr[12] = 0; */
216 /* ptr[13] = 0; */
217 /* landing zone cylinder */
218 /* ptr[14] = 0; */
219 /* ptr[15] = 0; */
220 /* ptr[16] = 0; */
221 /* RPL */
222 /* ptr[17] = 0; */
223 /* rotational offset */
224 /* ptr[18] = 0; */
225 /* medium rotation rate - 7200 RPM */
226 ptr[20] = 0x1c;
227 ptr[21] = 0x20;
228 }
229 ptr += 26;
230 }
231
232 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) {
233 ptr[0] = SMS_CACHE_PAGE;
234 ptr[1] = 18;
235 ptr[2] = 1 << 2;
236 ptr += 20;
237 }
238
239 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) {
240 ptr[0] = SMS_CONTROL_MODE_PAGE;
241 ptr[1] = 10;
242 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
243 ptr[3] = 1 << 4; /* unrestricted reordering allowed */
244 ptr[8] = 0x75; /* 30000 ms */
245 ptr[9] = 0x30;
246 }
247 ptr += 12;
248 }
249 nbyte = (char *)ptr - &junk[0];
250 ptr[0] = nbyte - 4;
251
252 if (cdb[0] == MODE_SENSE) {
253 data_len = min(cdb[4], csio->dxfer_len);
254 } else {
255 uint16_t tw = (cdb[7] << 8) | cdb[8];
256 data_len = min(tw, csio->dxfer_len);
257 }
258 data_len = min(data_len, nbyte);
259 if (data_len) {
260 memcpy(csio->data_ptr, junk, data_len);
261 }
262 csio->resid = csio->dxfer_len - data_len;
263 break;
264 }
265 case READ_6:
266 case READ_10:
267 case READ_12:
268 case READ_16:
269 case WRITE_6:
270 case WRITE_10:
271 case WRITE_12:
272 case WRITE_16:
273 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) {
274 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
275 break;
276 }
277 if (data_len) {
278 if ((cdb[0] & 0xf) == 8) {
279 memcpy(csio->data_ptr, &vhbas->disk[off], data_len);
280 } else {
281 memcpy(&vhbas->disk[off], csio->data_ptr, data_len);
282 }
283 csio->resid = csio->dxfer_len - data_len;
284 } else {
285 csio->resid = csio->dxfer_len;
286 }
287 break;
288
289 case READ_CAPACITY:
290 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
291 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0);
292 break;
293 }
294 if (cdb[8] & 0x1) { /* PMI */
295 csio->data_ptr[0] = 0xff;
296 csio->data_ptr[1] = 0xff;
297 csio->data_ptr[2] = 0xff;
298 csio->data_ptr[3] = 0xff;
299 } else {
300 uint64_t last_blk = DISK_NBLKS - 1;
301 if (last_blk < 0xffffffffULL) {
302 csio->data_ptr[0] = (last_blk >> 24) & 0xff;
303 csio->data_ptr[1] = (last_blk >> 16) & 0xff;
304 csio->data_ptr[2] = (last_blk >> 8) & 0xff;
305 csio->data_ptr[3] = (last_blk) & 0xff;
306 } else {
307 csio->data_ptr[0] = 0xff;
308 csio->data_ptr[1] = 0xff;
309 csio->data_ptr[2] = 0xff;
310 csio->data_ptr[3] = 0xff;
311 }
312 }
313 csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
314 csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
315 csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff;
316 csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
317 break;
318 default:
319 vhba_default_cmd(csio, MAX_LUN, NULL);
320 break;
321 }
322 csio->ccb_h.status &= ~CAM_STATUS_MASK;
323 if (csio->scsi_status != SCSI_STATUS_OK) {
324 csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
325 if (csio->scsi_status == SCSI_STATUS_CHECK_COND) {
326 csio->ccb_h.status |= CAM_AUTOSNS_VALID;
327 }
328 } else {
329 csio->scsi_status = SCSI_STATUS_OK;
330 csio->ccb_h.status |= CAM_REQ_CMP;
331 }
332 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
333 }
334 DEV_MODULE(vhba_lots, vhba_modprobe, NULL);
335