xref: /openbsd/usr.sbin/iscsid/task.c (revision bb7fde4e)
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
task_init(struct task * t,struct session * s,int immediate,void * carg,void (* c)(struct connection *,void *,struct pdu *),void (* f)(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
taskq_cleanup(struct taskq * tq)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
task_pdu_add(struct task * t,struct pdu * p)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
task_pdu_cb(struct connection * c,struct pdu * p)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