1 /* $OpenBSD: vscsi.c,v 1.8 2011/05/04 21:00:04 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/queue.h> 22 #include <sys/socket.h> 23 #include <sys/uio.h> 24 25 #include <scsi/iscsi.h> 26 #include <scsi/scsi_all.h> 27 #include <dev/vscsivar.h> 28 29 #include <event.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #include "iscsid.h" 35 #include "log.h" 36 37 struct vscsi { 38 struct event ev; 39 int fd; 40 } v; 41 42 struct scsi_task { 43 struct task task; 44 int tag; 45 u_int target; 46 u_int lun; 47 size_t datalen; 48 }; 49 50 void vscsi_callback(struct connection *, void *, struct pdu *); 51 void vscsi_fail(void *arg); 52 void vscsi_dataout(struct connection *, struct scsi_task *, u_int32_t, size_t); 53 54 void 55 vscsi_open(char *dev) 56 { 57 if ((v.fd = open(dev, O_RDWR)) == -1) 58 fatal("vscsi_open"); 59 60 event_set(&v.ev, v.fd, EV_READ|EV_PERSIST, vscsi_dispatch, NULL); 61 event_add(&v.ev, NULL); 62 } 63 64 void 65 vscsi_dispatch(int fd, short event, void *arg) 66 { 67 struct vscsi_ioc_i2t i2t; 68 struct iscsi_pdu_scsi_request *sreq; 69 struct session *s; 70 struct scsi_task *t; 71 struct pdu *p; 72 #if 0 73 char *buf; 74 u_int32_t t32; 75 #endif 76 77 if (!(event & EV_READ)) { 78 log_debug("spurious read call"); 79 return; 80 } 81 82 if (ioctl(v.fd, VSCSI_I2T, &i2t) == -1) 83 fatal("vscsi_dispatch"); 84 85 s = initiator_t2s(i2t.target); 86 if (s == NULL) 87 fatalx("vscsi_dispatch: unknown target"); 88 89 if (!(t = calloc(1, sizeof(*t)))) 90 fatal("vscsi_dispatch"); 91 92 t->tag = i2t.tag; 93 t->target = i2t.target; 94 t->lun = i2t.lun; 95 96 if (!(p = pdu_new())) 97 fatal("vscsi_dispatch"); 98 if (!(sreq = pdu_gethdr(p))) 99 fatal("vscsi_dispatch"); 100 101 sreq->opcode = ISCSI_OP_SCSI_REQUEST; 102 /* XXX use untagged commands, dlg sais so */ 103 sreq->flags = ISCSI_SCSI_F_F | ISCSI_SCSI_ATTR_UNTAGGED; 104 if (i2t.direction == VSCSI_DIR_WRITE) 105 sreq->flags |= ISCSI_SCSI_F_W; 106 if (i2t.direction == VSCSI_DIR_READ) 107 sreq->flags |= ISCSI_SCSI_F_R; 108 sreq->bytes = htonl(i2t.datalen); 109 110 /* LUN handling: currently we only do single level LUNs < 256 */ 111 if (t->lun >= 256) 112 fatal("vscsi_dispatch: I'm sorry, Dave. " 113 "I'm afraid I can't do that."); 114 sreq->lun[1] = t->lun; 115 116 bcopy(&i2t.cmd, sreq->cdb, i2t.cmdlen); 117 118 #if 0 119 if (i2t.direction == VSCSI_DIR_WRITE) { 120 if (!(buf = pdu_alloc(i2t.datalen))) 121 fatal("vscsi_dispatch"); 122 t32 = htonl(i2t.datalen); 123 bcopy(&t32, &sreq->ahslen, sizeof(t32)); 124 vscsi_data(VSCSI_DATA_WRITE, i2t.tag, buf, i2t.datalen); 125 pdu_addbuf(p, buf, i2t.datalen, PDU_DATA); 126 } 127 #endif 128 129 task_init(&t->task, s, 0, t, vscsi_callback, vscsi_fail); 130 task_pdu_add(&t->task, p); 131 session_task_issue(s, &t->task); 132 } 133 134 void 135 vscsi_data(unsigned long req, int tag, void *buf, size_t len) 136 { 137 struct vscsi_ioc_data data; 138 139 data.tag = tag; 140 data.data = buf; 141 data.datalen = len; 142 143 if (ioctl(v.fd, req, &data) == -1) 144 fatal("vscsi_data"); 145 } 146 147 void 148 vscsi_status(int tag, int status, void *buf, size_t len) 149 { 150 struct vscsi_ioc_t2i t2i; 151 152 bzero(&t2i, sizeof(t2i)); 153 t2i.tag = tag; 154 t2i.status = status; 155 if (buf) { 156 if (len > sizeof(t2i.sense)) 157 len = sizeof(t2i.sense); 158 bcopy(buf, &t2i.sense, len); 159 } 160 161 if (ioctl(v.fd, VSCSI_T2I, &t2i) == -1) 162 fatal("vscsi_status"); 163 } 164 165 void 166 vscsi_event(unsigned long req, u_int target, u_int lun) 167 { 168 struct vscsi_ioc_devevent devev; 169 170 devev.target = target; 171 devev.lun = lun; 172 173 if (ioctl(v.fd, req, &devev) == -1) 174 fatal("vscsi_event"); 175 } 176 177 void 178 vscsi_callback(struct connection *c, void *arg, struct pdu *p) 179 { 180 struct scsi_task *t = arg; 181 struct iscsi_pdu_scsi_response *sresp; 182 struct iscsi_pdu_rt2 *r2t; 183 int status = VSCSI_STAT_DONE; 184 u_char *buf = NULL; 185 size_t size = 0, n; 186 int tag; 187 188 sresp = pdu_getbuf(p, NULL, PDU_HEADER); 189 switch (ISCSI_PDU_OPCODE(sresp->opcode)) { 190 case ISCSI_OP_SCSI_RESPONSE: 191 conn_task_cleanup(c, &t->task); 192 tag = t->tag; 193 free(t); 194 195 if (!(sresp->flags & 0x80) || (sresp->flags & 0x06) == 0x06 || 196 (sresp->flags & 0x18) == 0x18) { 197 log_debug("vscsi_callback: bad scsi response"); 198 conn_fail(c); 199 break; 200 } 201 /* XXX handle the various serial numbers */ 202 if (sresp->response) { 203 status = VSCSI_STAT_ERR; 204 goto send_status; 205 } 206 switch (sresp->status) { 207 case ISCSI_SCSI_STAT_GOOD: 208 break; 209 case ISCSI_SCSI_STAT_CHCK_COND: 210 status = VSCSI_STAT_SENSE; 211 /* stupid encoding of sense data in the data segment */ 212 buf = pdu_getbuf(p, &n, PDU_DATA); 213 if (buf) { 214 size = buf[0] << 8 | buf[1]; 215 buf += 2; 216 } 217 break; 218 default: 219 status = VSCSI_STAT_ERR; 220 break; 221 } 222 send_status: 223 vscsi_status(tag, status, buf, size); 224 break; 225 case ISCSI_OP_DATA_IN: 226 buf = pdu_getbuf(p, &n, PDU_DATA); 227 size = sresp->datalen[0] << 16 | sresp->datalen[1] << 8 | 228 sresp->datalen[2]; 229 if (size > n) 230 fatal("This does not work as it should"); 231 vscsi_data(VSCSI_DATA_READ, t->tag, buf, size); 232 if (sresp->flags & 1) { /* XXX magic */ 233 conn_task_cleanup(c, &t->task); 234 vscsi_status(t->tag, status, NULL, 0); 235 free(t); 236 } 237 break; 238 case ISCSI_OP_R2T: 239 conn_task_cleanup(c, &t->task); 240 r2t = (struct iscsi_pdu_rt2 *)sresp; 241 if (ntohl(r2t->buffer_offs)) 242 fatalx("vscsi: r2t bummer failure"); 243 size = ntohl(r2t->desired_datalen); 244 245 vscsi_dataout(c, t, r2t->ttt, size); 246 break; 247 default: 248 log_debug("scsi task: tag %d, target %d lun %d", t->tag, 249 t->target, t->lun); 250 log_pdu(p, 1); 251 } 252 pdu_free(p); 253 } 254 255 void 256 vscsi_fail(void *arg) 257 { 258 struct scsi_task *t = arg; 259 int tag; 260 261 conn_task_cleanup(NULL, &t->task); 262 tag = t->tag; 263 free(t); 264 vscsi_status(tag, VSCSI_STAT_RESET, NULL, 0); 265 } 266 267 void 268 vscsi_dataout(struct connection *c, struct scsi_task *t, u_int32_t ttt, 269 size_t len) 270 { 271 struct pdu *p; 272 struct iscsi_pdu_data_out *dout; 273 u_char *buf = NULL; 274 size_t size, off; 275 u_int32_t t32, dsn = 0; 276 277 for (off = 0; off < len; off += size) { 278 size = len - off > c->active.MaxRecvDataSegmentLength ? 279 c->active.MaxRecvDataSegmentLength : len - off; 280 281 if (!(p = pdu_new())) 282 fatal("vscsi_r2t"); 283 if (!(dout = pdu_gethdr(p))) 284 fatal("vscsi_r2t"); 285 286 dout->opcode = ISCSI_OP_DATA_OUT; 287 if (off + size == len) 288 dout->flags = 0x80; /* XXX magic value: F flag*/ 289 /* LUN handling: currently we only do single level LUNs < 256 */ 290 dout->lun[1] = t->lun; 291 dout->ttt = ttt; 292 dout->datasn = htonl(dsn++); 293 t32 = htonl(size); 294 bcopy(&t32, &dout->ahslen, sizeof(t32)); 295 296 dout->buffer_offs = htonl(off); 297 if (!(buf = pdu_alloc(size))) 298 fatal("vscsi_r2t"); 299 300 vscsi_data(VSCSI_DATA_WRITE, t->tag, buf, size); 301 pdu_addbuf(p, buf, size, PDU_DATA); 302 task_pdu_add(&t->task, p); 303 } 304 conn_task_issue(c, &t->task); 305 } 306