1 /* $OpenBSD: vscsi.c,v 1.14 2014/05/10 11:28:02 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/param.h> /* for nitems */ 21 #include <sys/ioctl.h> 22 #include <sys/queue.h> 23 #include <sys/socket.h> 24 #include <sys/uio.h> 25 26 #include <scsi/iscsi.h> 27 #include <scsi/scsi_all.h> 28 #include <dev/vscsivar.h> 29 30 #include <event.h> 31 #include <fcntl.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #include "iscsid.h" 36 #include "log.h" 37 38 struct vscsi { 39 struct event ev; 40 int fd; 41 struct vscsi_stats stats; 42 } v; 43 44 struct scsi_task { 45 struct task task; 46 int tag; 47 u_int target; 48 u_int lun; 49 size_t datalen; 50 }; 51 52 void vscsi_callback(struct connection *, void *, struct pdu *); 53 void vscsi_fail(void *arg); 54 void vscsi_dataout(struct connection *, struct scsi_task *, u_int32_t, 55 size_t, size_t); 56 57 void 58 vscsi_open(char *dev) 59 { 60 if ((v.fd = open(dev, O_RDWR)) == -1) 61 fatal("vscsi_open"); 62 63 event_set(&v.ev, v.fd, EV_READ|EV_PERSIST, vscsi_dispatch, NULL); 64 event_add(&v.ev, NULL); 65 } 66 67 void 68 vscsi_dispatch(int fd, short event, void *arg) 69 { 70 struct vscsi_ioc_i2t i2t; 71 struct iscsi_pdu_scsi_request *sreq; 72 struct session *s; 73 struct scsi_task *t; 74 struct pdu *p; 75 76 if (!(event & EV_READ)) { 77 log_debug("spurious read call"); 78 return; 79 } 80 81 if (ioctl(v.fd, VSCSI_I2T, &i2t) == -1) 82 fatal("vscsi_dispatch"); 83 84 v.stats.cnt_i2t++; 85 if (i2t.direction < (int)nitems(v.stats.cnt_i2t_dir)) 86 v.stats.cnt_i2t_dir[i2t.direction]++; 87 88 s = initiator_t2s(i2t.target); 89 if (s == NULL) 90 fatalx("vscsi_dispatch: unknown target"); 91 92 if (!(t = calloc(1, sizeof(*t)))) 93 fatal("vscsi_dispatch"); 94 95 t->tag = i2t.tag; 96 t->target = i2t.target; 97 t->lun = i2t.lun; 98 99 if (!(p = pdu_new())) 100 fatal("vscsi_dispatch"); 101 if (!(sreq = pdu_gethdr(p))) 102 fatal("vscsi_dispatch"); 103 104 sreq->opcode = ISCSI_OP_SCSI_REQUEST; 105 /* XXX use untagged commands, dlg sais so */ 106 sreq->flags = ISCSI_SCSI_F_F | ISCSI_SCSI_ATTR_UNTAGGED; 107 if (i2t.direction == VSCSI_DIR_WRITE) 108 sreq->flags |= ISCSI_SCSI_F_W; 109 if (i2t.direction == VSCSI_DIR_READ) 110 sreq->flags |= ISCSI_SCSI_F_R; 111 sreq->bytes = htonl(i2t.datalen); 112 113 /* LUN handling: currently we only do single level LUNs < 256 */ 114 if (t->lun >= 256) 115 fatal("vscsi_dispatch: I'm sorry, Dave. " 116 "I'm afraid I can't do that."); 117 sreq->lun[1] = t->lun; 118 119 memcpy(sreq->cdb, &i2t.cmd, i2t.cmdlen); 120 121 /* include immediate data of up to FirstBurstLength bytes if allowed */ 122 if (i2t.direction == VSCSI_DIR_WRITE && s->active.ImmediateData) { 123 struct connection *c; 124 char *buf; 125 u_int32_t t32; 126 size_t size; 127 128 size = i2t.datalen > s->active.FirstBurstLength ? 129 s->active.FirstBurstLength : i2t.datalen; 130 131 /* XXX assumes all connections have same settings */ 132 c = TAILQ_FIRST(&s->connections); 133 if (c && size > c->active.MaxRecvDataSegmentLength) 134 size = c->active.MaxRecvDataSegmentLength; 135 136 if (!(buf = pdu_alloc(size))) 137 fatal("vscsi_dispatch"); 138 t32 = htonl(size); 139 memcpy(&sreq->ahslen, &t32, sizeof(t32)); 140 vscsi_data(VSCSI_DATA_WRITE, i2t.tag, buf, size); 141 pdu_addbuf(p, buf, size, PDU_DATA); 142 } 143 144 task_init(&t->task, s, 0, t, vscsi_callback, vscsi_fail); 145 task_pdu_add(&t->task, p); 146 session_task_issue(s, &t->task); 147 } 148 149 /* read / write data to vscsi */ 150 void 151 vscsi_data(unsigned long req, int tag, void *buf, size_t len) 152 { 153 struct vscsi_ioc_data data; 154 155 if (req == VSCSI_DATA_READ) { 156 v.stats.cnt_read++; 157 v.stats.bytes_rd += len; 158 } else if (req == VSCSI_DATA_WRITE) { 159 v.stats.cnt_write++; 160 v.stats.bytes_wr += len; 161 } 162 data.tag = tag; 163 data.data = buf; 164 data.datalen = len; 165 166 if (ioctl(v.fd, req, &data) == -1) 167 fatal("vscsi_data"); 168 } 169 170 void 171 vscsi_status(int tag, int status, void *buf, size_t len) 172 { 173 struct vscsi_ioc_t2i t2i; 174 175 v.stats.cnt_t2i++; 176 if (status < (int)nitems(v.stats.cnt_t2i_status)) 177 v.stats.cnt_t2i_status[status]++; 178 179 bzero(&t2i, sizeof(t2i)); 180 t2i.tag = tag; 181 t2i.status = status; 182 if (buf) { 183 if (len > sizeof(t2i.sense)) 184 len = sizeof(t2i.sense); 185 memcpy(&t2i.sense, buf, len); 186 } 187 188 if (ioctl(v.fd, VSCSI_T2I, &t2i) == -1) 189 fatal("vscsi_status"); 190 } 191 192 void 193 vscsi_event(unsigned long req, u_int target, u_int lun) 194 { 195 struct vscsi_ioc_devevent devev; 196 197 if (req == VSCSI_REQPROBE) 198 v.stats.cnt_probe++; 199 else if (req == VSCSI_REQDETACH) 200 v.stats.cnt_detach++; 201 202 devev.target = target; 203 devev.lun = lun; 204 205 if (ioctl(v.fd, req, &devev) == -1) 206 fatal("vscsi_event"); 207 } 208 209 void 210 vscsi_callback(struct connection *c, void *arg, struct pdu *p) 211 { 212 struct scsi_task *t = arg; 213 struct iscsi_pdu_scsi_response *sresp; 214 struct iscsi_pdu_rt2 *r2t; 215 int status = VSCSI_STAT_DONE; 216 u_char *buf = NULL; 217 size_t size, off, n; 218 int tag; 219 220 sresp = pdu_getbuf(p, NULL, PDU_HEADER); 221 switch (ISCSI_PDU_OPCODE(sresp->opcode)) { 222 case ISCSI_OP_SCSI_RESPONSE: 223 conn_task_cleanup(c, &t->task); 224 tag = t->tag; 225 free(t); 226 227 if (!(sresp->flags & 0x80) || (sresp->flags & 0x06) == 0x06 || 228 (sresp->flags & 0x18) == 0x18) { 229 log_debug("vscsi_callback: bad scsi response"); 230 conn_fail(c); 231 break; 232 } 233 size = 0; 234 /* XXX handle the various serial numbers */ 235 if (sresp->response) { 236 status = VSCSI_STAT_ERR; 237 goto send_status; 238 } 239 switch (sresp->status) { 240 case ISCSI_SCSI_STAT_GOOD: 241 break; 242 case ISCSI_SCSI_STAT_CHCK_COND: 243 status = VSCSI_STAT_SENSE; 244 /* stupid encoding of sense data in the data segment */ 245 buf = pdu_getbuf(p, &n, PDU_DATA); 246 if (buf) { 247 size = buf[0] << 8 | buf[1]; 248 buf += 2; 249 } 250 break; 251 default: 252 status = VSCSI_STAT_ERR; 253 break; 254 } 255 send_status: 256 vscsi_status(tag, status, buf, size); 257 break; 258 case ISCSI_OP_DATA_IN: 259 buf = pdu_getbuf(p, &n, PDU_DATA); 260 size = sresp->datalen[0] << 16 | sresp->datalen[1] << 8 | 261 sresp->datalen[2]; 262 if (size > n) 263 fatal("This does not work as it should"); 264 vscsi_data(VSCSI_DATA_READ, t->tag, buf, size); 265 if (sresp->flags & 1) { /* XXX magic */ 266 conn_task_cleanup(c, &t->task); 267 vscsi_status(t->tag, status, NULL, 0); 268 free(t); 269 } 270 break; 271 case ISCSI_OP_R2T: 272 conn_task_cleanup(c, &t->task); 273 r2t = (struct iscsi_pdu_rt2 *)sresp; 274 off = ntohl(r2t->buffer_offs); 275 size = ntohl(r2t->desired_datalen); 276 277 vscsi_dataout(c, t, r2t->ttt, size, off); 278 break; 279 default: 280 log_debug("scsi task: tag %d, target %d lun %d", t->tag, 281 t->target, t->lun); 282 log_pdu(p, 1); 283 } 284 pdu_free(p); 285 } 286 287 void 288 vscsi_fail(void *arg) 289 { 290 struct scsi_task *t = arg; 291 292 log_debug("vscsi_fail: task failed, reseting it"); 293 vscsi_status(t->tag, VSCSI_STAT_RESET, NULL, 0); 294 } 295 296 void 297 vscsi_dataout(struct connection *c, struct scsi_task *t, u_int32_t ttt, 298 size_t len, size_t buffer_off) 299 { 300 struct pdu *p; 301 struct iscsi_pdu_data_out *dout; 302 u_char *buf = NULL; 303 size_t size, off; 304 u_int32_t t32, dsn = 0; 305 306 for (off = 0; off < len; off += size) { 307 size = len - off > c->active.MaxRecvDataSegmentLength ? 308 c->active.MaxRecvDataSegmentLength : len - off; 309 310 /* XXX also respect the MaxBurstLength */ 311 312 if (!(p = pdu_new())) 313 fatal("vscsi_r2t"); 314 if (!(dout = pdu_gethdr(p))) 315 fatal("vscsi_r2t"); 316 317 dout->opcode = ISCSI_OP_DATA_OUT; 318 if (off + size == len) 319 dout->flags = 0x80; /* XXX magic value: F flag*/ 320 /* LUN handling: currently we only do single level LUNs < 256 */ 321 dout->lun[1] = t->lun; 322 dout->ttt = ttt; 323 dout->datasn = htonl(dsn++); 324 t32 = htonl(size); 325 memcpy(&dout->ahslen, &t32, sizeof(t32)); 326 327 dout->buffer_offs = htonl(buffer_off + off); 328 if (!(buf = pdu_alloc(size))) 329 fatal("vscsi_r2t"); 330 331 vscsi_data(VSCSI_DATA_WRITE, t->tag, buf, size); 332 pdu_addbuf(p, buf, size, PDU_DATA); 333 task_pdu_add(&t->task, p); 334 } 335 conn_task_issue(c, &t->task); 336 } 337 338 struct vscsi_stats * 339 vscsi_stats(void) 340 { 341 return &v.stats; 342 } 343