1 /* $OpenBSD: task.c,v 1.10 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 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 #include <sys/uio.h> 22 23 #include <scsi/iscsi.h> 24 25 #include <errno.h> 26 #include <event.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <unistd.h> 31 32 #include "iscsid.h" 33 #include "log.h" 34 35 /* 36 * Task handling, PDU are attached to tasks and task are scheduled across 37 * all connections of a session. 38 */ 39 40 void 41 task_init(struct task *t, struct session *s, int immediate, void *carg, 42 void (*c)(struct connection *, void *, struct pdu *), 43 void (*f)(void *)) 44 { 45 TAILQ_INIT(&t->sendq); 46 TAILQ_INIT(&t->recvq); 47 t->callback = c; 48 t->failback = f; 49 t->callarg = carg; 50 /* skip reserved and maybe bad ITT values */ 51 if (s->itt == 0xffffffff || s->itt == 0) 52 s->itt = 1; 53 t->itt = s->itt++; /* XXX we could do better here */ 54 t->cmdseqnum = s->cmdseqnum; 55 if (!immediate) 56 s->cmdseqnum++; 57 } 58 59 void 60 taskq_cleanup(struct taskq *tq) 61 { 62 struct task *t; 63 64 while ((t = TAILQ_FIRST(tq))) { 65 TAILQ_REMOVE(tq, t, entry); 66 if (t->failback) 67 t->failback(t->callarg); 68 conn_task_cleanup(NULL, t); 69 free(t); 70 } 71 } 72 73 void 74 task_pdu_add(struct task *t, struct pdu *p) 75 { 76 struct iscsi_pdu *ipdu; 77 78 /* fixup the pdu by setting the itt and seqnum if needed */ 79 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 80 ipdu->itt = ntohl(t->itt); 81 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 82 case ISCSI_OP_I_NOP: 83 case ISCSI_OP_SCSI_REQUEST: 84 case ISCSI_OP_TASK_REQUEST: 85 case ISCSI_OP_LOGIN_REQUEST: 86 case ISCSI_OP_TEXT_REQUEST: 87 case ISCSI_OP_LOGOUT_REQUEST: 88 ipdu->cmdsn = ntohl(t->cmdseqnum); 89 break; 90 } 91 92 TAILQ_INSERT_TAIL(&t->sendq, p, entry); 93 } 94 95 void 96 task_pdu_cb(struct connection *c, struct pdu *p) 97 { 98 struct task *t; 99 struct iscsi_pdu *ipdu; 100 u_int32_t itt; 101 102 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 103 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 104 case ISCSI_OP_T_NOP: 105 itt = ntohl(ipdu->itt); 106 if (itt == 0xffffffff) { 107 /* target issued a ping, must answer back immediately */ 108 c->expstatsn = ntohl(ipdu->cmdsn) + 1; 109 initiator_nop_in_imm(c, p); 110 break; 111 } 112 /* FALLTHROUGH */ 113 case ISCSI_OP_LOGIN_RESPONSE: 114 case ISCSI_OP_TEXT_RESPONSE: 115 case ISCSI_OP_LOGOUT_RESPONSE: 116 case ISCSI_OP_SCSI_RESPONSE: 117 case ISCSI_OP_R2T: 118 case ISCSI_OP_DATA_IN: 119 itt = ntohl(ipdu->itt); 120 c->expstatsn = ntohl(ipdu->cmdsn) + 1; 121 122 /* XXX for now search the task on the connection queue 123 later on this should be moved to a per session RB tree but 124 now I do the quick ugly thing. */ 125 TAILQ_FOREACH(t, &c->tasks, entry) { 126 if (itt == t->itt) 127 break; 128 } 129 if (t) 130 t->callback(c, t->callarg, p); 131 else { 132 log_debug("no task for PDU found"); 133 log_pdu(p, 1); 134 pdu_free(p); 135 } 136 break; 137 default: 138 log_warnx("not handled yet. fix me"); 139 log_pdu(p, 1); 140 pdu_free(p); 141 } 142 } 143