1 /* $OpenBSD: task.c,v 1.9 2011/04/28 18:32:01 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 else { 69 conn_task_cleanup(NULL, t); 70 free(t); 71 } 72 } 73 } 74 75 void 76 task_pdu_add(struct task *t, struct pdu *p) 77 { 78 struct iscsi_pdu *ipdu; 79 80 /* fixup the pdu by setting the itt and seqnum if needed */ 81 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 82 ipdu->itt = ntohl(t->itt); 83 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 84 case ISCSI_OP_I_NOP: 85 case ISCSI_OP_SCSI_REQUEST: 86 case ISCSI_OP_TASK_REQUEST: 87 case ISCSI_OP_LOGIN_REQUEST: 88 case ISCSI_OP_TEXT_REQUEST: 89 case ISCSI_OP_LOGOUT_REQUEST: 90 ipdu->cmdsn = ntohl(t->cmdseqnum); 91 break; 92 } 93 94 TAILQ_INSERT_TAIL(&t->sendq, p, entry); 95 } 96 97 void 98 task_pdu_cb(struct connection *c, struct pdu *p) 99 { 100 struct task *t; 101 struct iscsi_pdu *ipdu; 102 u_int32_t itt; 103 104 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 105 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 106 case ISCSI_OP_T_NOP: 107 itt = ntohl(ipdu->itt); 108 if (itt == 0xffffffff) { 109 /* target issued a ping, must answer back immediately */ 110 c->expstatsn = ntohl(ipdu->cmdsn) + 1; 111 initiator_nop_in_imm(c, p); 112 break; 113 } 114 /* FALLTHROUGH */ 115 case ISCSI_OP_LOGIN_RESPONSE: 116 case ISCSI_OP_TEXT_RESPONSE: 117 case ISCSI_OP_LOGOUT_RESPONSE: 118 case ISCSI_OP_SCSI_RESPONSE: 119 case ISCSI_OP_R2T: 120 case ISCSI_OP_DATA_IN: 121 itt = ntohl(ipdu->itt); 122 c->expstatsn = ntohl(ipdu->cmdsn) + 1; 123 124 /* XXX for now search the task on the connection queue 125 later on this should be moved to a per session RB tree but 126 now I do the quick ugly thing. */ 127 TAILQ_FOREACH(t, &c->tasks, entry) { 128 if (itt == t->itt) 129 break; 130 } 131 if (t) 132 t->callback(c, t->callarg, p); 133 else { 134 log_debug("no task for PDU found"); 135 log_pdu(p, 1); 136 pdu_free(p); 137 } 138 break; 139 default: 140 log_warnx("not handled yet. fix me"); 141 log_pdu(p, 1); 142 pdu_free(p); 143 } 144 } 145