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