13a5aa68fSMatthew Dillon /*-
23a5aa68fSMatthew Dillon * Copyright (c) 2012 The DragonFly Project. All rights reserved.
33a5aa68fSMatthew Dillon *
43a5aa68fSMatthew Dillon * This code is derived from software contributed to The DragonFly Project
53a5aa68fSMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
63a5aa68fSMatthew Dillon *
73a5aa68fSMatthew Dillon * Redistribution and use in source and binary forms, with or without
83a5aa68fSMatthew Dillon * modification, are permitted provided that the following conditions
93a5aa68fSMatthew Dillon * are met:
103a5aa68fSMatthew Dillon *
113a5aa68fSMatthew Dillon * 1. Redistributions of source code must retain the above copyright
123a5aa68fSMatthew Dillon * notice, this list of conditions and the following disclaimer.
133a5aa68fSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
143a5aa68fSMatthew Dillon * notice, this list of conditions and the following disclaimer in
153a5aa68fSMatthew Dillon * the documentation and/or other materials provided with the
163a5aa68fSMatthew Dillon * distribution.
173a5aa68fSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
183a5aa68fSMatthew Dillon * contributors may be used to endorse or promote products derived
193a5aa68fSMatthew Dillon * from this software without specific, prior written permission.
203a5aa68fSMatthew Dillon *
213a5aa68fSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
223a5aa68fSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
233a5aa68fSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
243a5aa68fSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
253a5aa68fSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
263a5aa68fSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
273a5aa68fSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
283a5aa68fSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
293a5aa68fSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
303a5aa68fSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
313a5aa68fSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323a5aa68fSMatthew Dillon * SUCH DAMAGE.
333a5aa68fSMatthew Dillon */
348d6d37b8SMatthew Dillon /*
358d6d37b8SMatthew Dillon * TODO: txcmd CREATE state is deferred by tx msgq, need to calculate
368d6d37b8SMatthew Dillon * a streaming response. See subr_diskiocom()'s diskiodone().
378d6d37b8SMatthew Dillon */
383a5aa68fSMatthew Dillon #include <sys/param.h>
393a5aa68fSMatthew Dillon #include <sys/types.h>
403a5aa68fSMatthew Dillon #include <sys/kernel.h>
413a5aa68fSMatthew Dillon #include <sys/conf.h>
423a5aa68fSMatthew Dillon #include <sys/systm.h>
433a5aa68fSMatthew Dillon #include <sys/queue.h>
443a5aa68fSMatthew Dillon #include <sys/tree.h>
453a5aa68fSMatthew Dillon #include <sys/malloc.h>
463a5aa68fSMatthew Dillon #include <sys/mount.h>
473a5aa68fSMatthew Dillon #include <sys/socket.h>
483a5aa68fSMatthew Dillon #include <sys/vnode.h>
495ab1caedSMatthew Dillon #include <sys/sysctl.h>
503a5aa68fSMatthew Dillon #include <sys/file.h>
513a5aa68fSMatthew Dillon #include <sys/proc.h>
52*2b3f93eaSMatthew Dillon #include <sys/caps.h>
533a5aa68fSMatthew Dillon #include <sys/thread.h>
543a5aa68fSMatthew Dillon #include <sys/globaldata.h>
553a5aa68fSMatthew Dillon #include <sys/limits.h>
563a5aa68fSMatthew Dillon
573a5aa68fSMatthew Dillon #include <sys/dmsg.h>
583a5aa68fSMatthew Dillon
593a5aa68fSMatthew Dillon RB_GENERATE(kdmsg_state_tree, kdmsg_state, rbnode, kdmsg_state_cmp);
603a5aa68fSMatthew Dillon
615ab1caedSMatthew Dillon SYSCTL_NODE(, OID_AUTO, kdmsg, CTLFLAG_RW, 0, "kdmsg");
625ab1caedSMatthew Dillon static int kdmsg_debug = 1;
635ab1caedSMatthew Dillon SYSCTL_INT(_kdmsg, OID_AUTO, debug, CTLFLAG_RW, &kdmsg_debug, 0,
645ab1caedSMatthew Dillon "Set debug level for kernel dmsg layer");
655ab1caedSMatthew Dillon
665ab1caedSMatthew Dillon #define kd_printf(level, ctl, ...) \
675ab1caedSMatthew Dillon if (kdmsg_debug >= (level)) kprintf("kdmsg: " ctl, __VA_ARGS__)
685ab1caedSMatthew Dillon
695ab1caedSMatthew Dillon #define kdio_printf(iocom, level, ctl, ...) \
705ab1caedSMatthew Dillon if (kdmsg_debug >= (level)) kprintf("kdmsg: " ctl, __VA_ARGS__)
715ab1caedSMatthew Dillon
728d6d37b8SMatthew Dillon static int kdmsg_msg_receive_handling(kdmsg_msg_t *msg);
738d6d37b8SMatthew Dillon static int kdmsg_state_msgrx(kdmsg_msg_t *msg);
748d6d37b8SMatthew Dillon static int kdmsg_state_msgtx(kdmsg_msg_t *msg);
755ab1caedSMatthew Dillon static void kdmsg_msg_write_locked(kdmsg_iocom_t *iocom, kdmsg_msg_t *msg);
768d6d37b8SMatthew Dillon static void kdmsg_state_cleanuprx(kdmsg_msg_t *msg);
778d6d37b8SMatthew Dillon static void kdmsg_state_cleanuptx(kdmsg_msg_t *msg);
780a9eefcaSMatthew Dillon static void kdmsg_subq_delete(kdmsg_state_t *state);
79a06d536bSMatthew Dillon static void kdmsg_simulate_failure(kdmsg_state_t *state, int meto, int error);
808d6d37b8SMatthew Dillon static void kdmsg_state_abort(kdmsg_state_t *state);
810a9eefcaSMatthew Dillon static void kdmsg_state_dying(kdmsg_state_t *state);
828d6d37b8SMatthew Dillon static void kdmsg_state_free(kdmsg_state_t *state);
8393c84330SMatthew Dillon static void kdmsg_drain_msg(kdmsg_msg_t *msg);
848d6d37b8SMatthew Dillon
85a06d536bSMatthew Dillon #ifdef KDMSG_DEBUG
86a06d536bSMatthew Dillon #define KDMSG_DEBUG_ARGS , const char *file, int line
870a9eefcaSMatthew Dillon #define kdmsg_state_hold(state) _kdmsg_state_hold(state, __FILE__, __LINE__)
88a06d536bSMatthew Dillon #define kdmsg_state_drop(state) _kdmsg_state_drop(state, __FILE__, __LINE__)
89a06d536bSMatthew Dillon #else
90895bb45aSSascha Wildner #define KDMSG_DEBUG 0
91a06d536bSMatthew Dillon #define KDMSG_DEBUG_ARGS
920a9eefcaSMatthew Dillon #define kdmsg_state_hold(state) _kdmsg_state_hold(state)
93a06d536bSMatthew Dillon #define kdmsg_state_drop(state) _kdmsg_state_drop(state)
94a06d536bSMatthew Dillon #endif
950a9eefcaSMatthew Dillon static void _kdmsg_state_hold(kdmsg_state_t *state KDMSG_DEBUG_ARGS);
96a06d536bSMatthew Dillon static void _kdmsg_state_drop(kdmsg_state_t *state KDMSG_DEBUG_ARGS);
97a06d536bSMatthew Dillon
983a5aa68fSMatthew Dillon static void kdmsg_iocom_thread_rd(void *arg);
993a5aa68fSMatthew Dillon static void kdmsg_iocom_thread_wr(void *arg);
10003d99ea4SMatthew Dillon static int kdmsg_autorxmsg(kdmsg_msg_t *msg);
1013a5aa68fSMatthew Dillon
1021b8eded1SMatthew Dillon /*static struct lwkt_token kdmsg_token = LWKT_TOKEN_INITIALIZER(kdmsg_token);*/
1038d6d37b8SMatthew Dillon
1043a5aa68fSMatthew Dillon /*
1053a5aa68fSMatthew Dillon * Initialize the roll-up communications structure for a network
1063a5aa68fSMatthew Dillon * messaging session. This function does not install the socket.
1073a5aa68fSMatthew Dillon */
1083a5aa68fSMatthew Dillon void
kdmsg_iocom_init(kdmsg_iocom_t * iocom,void * handle,uint32_t flags,struct malloc_type * mmsg,int (* rcvmsg)(kdmsg_msg_t * msg))10903d99ea4SMatthew Dillon kdmsg_iocom_init(kdmsg_iocom_t *iocom, void *handle, uint32_t flags,
1103a5aa68fSMatthew Dillon struct malloc_type *mmsg,
11103d99ea4SMatthew Dillon int (*rcvmsg)(kdmsg_msg_t *msg))
1123a5aa68fSMatthew Dillon {
1133a5aa68fSMatthew Dillon bzero(iocom, sizeof(*iocom));
1143a5aa68fSMatthew Dillon iocom->handle = handle;
1153a5aa68fSMatthew Dillon iocom->mmsg = mmsg;
11603d99ea4SMatthew Dillon iocom->rcvmsg = rcvmsg;
11703d99ea4SMatthew Dillon iocom->flags = flags;
1183a5aa68fSMatthew Dillon lockinit(&iocom->msglk, "h2msg", 0, 0);
1193a5aa68fSMatthew Dillon TAILQ_INIT(&iocom->msgq);
1203a5aa68fSMatthew Dillon RB_INIT(&iocom->staterd_tree);
1213a5aa68fSMatthew Dillon RB_INIT(&iocom->statewr_tree);
1221b8eded1SMatthew Dillon
1231b8eded1SMatthew Dillon iocom->state0.iocom = iocom;
1241b8eded1SMatthew Dillon iocom->state0.parent = &iocom->state0;
1251b8eded1SMatthew Dillon TAILQ_INIT(&iocom->state0.subq);
1263a5aa68fSMatthew Dillon }
1273a5aa68fSMatthew Dillon
1283a5aa68fSMatthew Dillon /*
1293a5aa68fSMatthew Dillon * [Re]connect using the passed file pointer. The caller must ref the
1303a5aa68fSMatthew Dillon * fp for us. We own that ref now.
1313a5aa68fSMatthew Dillon */
1323a5aa68fSMatthew Dillon void
kdmsg_iocom_reconnect(kdmsg_iocom_t * iocom,struct file * fp,const char * subsysname)1333a5aa68fSMatthew Dillon kdmsg_iocom_reconnect(kdmsg_iocom_t *iocom, struct file *fp,
1343a5aa68fSMatthew Dillon const char *subsysname)
1353a5aa68fSMatthew Dillon {
1363a5aa68fSMatthew Dillon /*
1373a5aa68fSMatthew Dillon * Destroy the current connection
1383a5aa68fSMatthew Dillon */
1398d6d37b8SMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
14045c1a24bSMatthew Dillon atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX);
1413a5aa68fSMatthew Dillon while (iocom->msgrd_td || iocom->msgwr_td) {
1423a5aa68fSMatthew Dillon wakeup(&iocom->msg_ctl);
1438d6d37b8SMatthew Dillon lksleep(iocom, &iocom->msglk, 0, "clstrkl", hz);
1443a5aa68fSMatthew Dillon }
1453a5aa68fSMatthew Dillon
1463a5aa68fSMatthew Dillon /*
1473a5aa68fSMatthew Dillon * Drop communications descriptor
1483a5aa68fSMatthew Dillon */
1493a5aa68fSMatthew Dillon if (iocom->msg_fp) {
1503a5aa68fSMatthew Dillon fdrop(iocom->msg_fp);
1513a5aa68fSMatthew Dillon iocom->msg_fp = NULL;
1523a5aa68fSMatthew Dillon }
1533a5aa68fSMatthew Dillon
1543a5aa68fSMatthew Dillon /*
1553a5aa68fSMatthew Dillon * Setup new communications descriptor
1563a5aa68fSMatthew Dillon */
1573a5aa68fSMatthew Dillon iocom->msg_ctl = 0;
1583a5aa68fSMatthew Dillon iocom->msg_fp = fp;
1593a5aa68fSMatthew Dillon iocom->msg_seq = 0;
1608d6d37b8SMatthew Dillon iocom->flags &= ~KDMSG_IOCOMF_EXITNOACC;
1613a5aa68fSMatthew Dillon
1623a5aa68fSMatthew Dillon lwkt_create(kdmsg_iocom_thread_rd, iocom, &iocom->msgrd_td,
1633a5aa68fSMatthew Dillon NULL, 0, -1, "%s-msgrd", subsysname);
1643a5aa68fSMatthew Dillon lwkt_create(kdmsg_iocom_thread_wr, iocom, &iocom->msgwr_td,
1653a5aa68fSMatthew Dillon NULL, 0, -1, "%s-msgwr", subsysname);
1668d6d37b8SMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
1673a5aa68fSMatthew Dillon }
1683a5aa68fSMatthew Dillon
1693a5aa68fSMatthew Dillon /*
17003d99ea4SMatthew Dillon * Caller sets up iocom->auto_lnk_conn and iocom->auto_lnk_span, then calls
17103d99ea4SMatthew Dillon * this function to handle the state machine for LNK_CONN and LNK_SPAN.
17203d99ea4SMatthew Dillon */
17303d99ea4SMatthew Dillon static int kdmsg_lnk_conn_reply(kdmsg_state_t *state, kdmsg_msg_t *msg);
17403d99ea4SMatthew Dillon static int kdmsg_lnk_span_reply(kdmsg_state_t *state, kdmsg_msg_t *msg);
17503d99ea4SMatthew Dillon
17603d99ea4SMatthew Dillon void
kdmsg_iocom_autoinitiate(kdmsg_iocom_t * iocom,void (* auto_callback)(kdmsg_msg_t * msg))17703d99ea4SMatthew Dillon kdmsg_iocom_autoinitiate(kdmsg_iocom_t *iocom,
17803d99ea4SMatthew Dillon void (*auto_callback)(kdmsg_msg_t *msg))
17903d99ea4SMatthew Dillon {
18003d99ea4SMatthew Dillon kdmsg_msg_t *msg;
18103d99ea4SMatthew Dillon
18203d99ea4SMatthew Dillon iocom->auto_callback = auto_callback;
18303d99ea4SMatthew Dillon
1841b8eded1SMatthew Dillon msg = kdmsg_msg_alloc(&iocom->state0,
18503d99ea4SMatthew Dillon DMSG_LNK_CONN | DMSGF_CREATE,
18603d99ea4SMatthew Dillon kdmsg_lnk_conn_reply, NULL);
18703d99ea4SMatthew Dillon iocom->auto_lnk_conn.head = msg->any.head;
18803d99ea4SMatthew Dillon msg->any.lnk_conn = iocom->auto_lnk_conn;
18903d99ea4SMatthew Dillon iocom->conn_state = msg->state;
1900a9eefcaSMatthew Dillon kdmsg_state_hold(msg->state); /* iocom->conn_state */
19103d99ea4SMatthew Dillon kdmsg_msg_write(msg);
19203d99ea4SMatthew Dillon }
19303d99ea4SMatthew Dillon
19403d99ea4SMatthew Dillon static
19503d99ea4SMatthew Dillon int
kdmsg_lnk_conn_reply(kdmsg_state_t * state,kdmsg_msg_t * msg)19603d99ea4SMatthew Dillon kdmsg_lnk_conn_reply(kdmsg_state_t *state, kdmsg_msg_t *msg)
19703d99ea4SMatthew Dillon {
19803d99ea4SMatthew Dillon kdmsg_iocom_t *iocom = state->iocom;
19903d99ea4SMatthew Dillon kdmsg_msg_t *rmsg;
20003d99ea4SMatthew Dillon
2018e226bc8SMatthew Dillon /*
2028e226bc8SMatthew Dillon * Upon receipt of the LNK_CONN acknowledgement initiate an
2038e226bc8SMatthew Dillon * automatic SPAN if we were asked to. Used by e.g. xdisk, but
2048e226bc8SMatthew Dillon * not used by HAMMER2 which must manage more than one transmitted
2058e226bc8SMatthew Dillon * SPAN.
2068e226bc8SMatthew Dillon */
2078e226bc8SMatthew Dillon if ((msg->any.head.cmd & DMSGF_CREATE) &&
2088e226bc8SMatthew Dillon (iocom->flags & KDMSG_IOCOMF_AUTOTXSPAN)) {
2091b8eded1SMatthew Dillon rmsg = kdmsg_msg_alloc(&iocom->state0,
21003d99ea4SMatthew Dillon DMSG_LNK_SPAN | DMSGF_CREATE,
21103d99ea4SMatthew Dillon kdmsg_lnk_span_reply, NULL);
21203d99ea4SMatthew Dillon iocom->auto_lnk_span.head = rmsg->any.head;
21303d99ea4SMatthew Dillon rmsg->any.lnk_span = iocom->auto_lnk_span;
21403d99ea4SMatthew Dillon kdmsg_msg_write(rmsg);
21503d99ea4SMatthew Dillon }
2168d6d37b8SMatthew Dillon
2178d6d37b8SMatthew Dillon /*
2188d6d37b8SMatthew Dillon * Process shim after the CONN is acknowledged and before the CONN
2198d6d37b8SMatthew Dillon * transaction is deleted. For deletions this gives device drivers
2208d6d37b8SMatthew Dillon * the ability to interlock new operations on the circuit before
2218d6d37b8SMatthew Dillon * it becomes illegal and panics.
2228d6d37b8SMatthew Dillon */
2238d6d37b8SMatthew Dillon if (iocom->auto_callback)
2248d6d37b8SMatthew Dillon iocom->auto_callback(msg);
2258d6d37b8SMatthew Dillon
22603d99ea4SMatthew Dillon if ((state->txcmd & DMSGF_DELETE) == 0 &&
22703d99ea4SMatthew Dillon (msg->any.head.cmd & DMSGF_DELETE)) {
228a06d536bSMatthew Dillon /*
229a06d536bSMatthew Dillon * iocom->conn_state has a state ref, drop it when clearing.
230a06d536bSMatthew Dillon */
231a06d536bSMatthew Dillon if (iocom->conn_state)
232a06d536bSMatthew Dillon kdmsg_state_drop(iocom->conn_state);
23303d99ea4SMatthew Dillon iocom->conn_state = NULL;
23403d99ea4SMatthew Dillon kdmsg_msg_reply(msg, 0);
23503d99ea4SMatthew Dillon }
23603d99ea4SMatthew Dillon
23703d99ea4SMatthew Dillon return (0);
23803d99ea4SMatthew Dillon }
23903d99ea4SMatthew Dillon
24003d99ea4SMatthew Dillon static
24103d99ea4SMatthew Dillon int
kdmsg_lnk_span_reply(kdmsg_state_t * state,kdmsg_msg_t * msg)24203d99ea4SMatthew Dillon kdmsg_lnk_span_reply(kdmsg_state_t *state, kdmsg_msg_t *msg)
24303d99ea4SMatthew Dillon {
2448d6d37b8SMatthew Dillon /*
2458d6d37b8SMatthew Dillon * Be sure to process shim before terminating the SPAN
2468d6d37b8SMatthew Dillon * transaction. Gives device drivers the ability to
2478d6d37b8SMatthew Dillon * interlock new operations on the circuit before it
2488d6d37b8SMatthew Dillon * becomes illegal and panics.
2498d6d37b8SMatthew Dillon */
2508d6d37b8SMatthew Dillon if (state->iocom->auto_callback)
2518d6d37b8SMatthew Dillon state->iocom->auto_callback(msg);
25203d99ea4SMatthew Dillon
25303d99ea4SMatthew Dillon if ((state->txcmd & DMSGF_DELETE) == 0 &&
25403d99ea4SMatthew Dillon (msg->any.head.cmd & DMSGF_DELETE)) {
25503d99ea4SMatthew Dillon kdmsg_msg_reply(msg, 0);
25603d99ea4SMatthew Dillon }
25703d99ea4SMatthew Dillon return (0);
25803d99ea4SMatthew Dillon }
25903d99ea4SMatthew Dillon
26003d99ea4SMatthew Dillon /*
261185ace93SMatthew Dillon * Disconnect and clean up
262185ace93SMatthew Dillon */
263185ace93SMatthew Dillon void
kdmsg_iocom_uninit(kdmsg_iocom_t * iocom)264185ace93SMatthew Dillon kdmsg_iocom_uninit(kdmsg_iocom_t *iocom)
265185ace93SMatthew Dillon {
266085cb1ddSMatthew Dillon kdmsg_state_t *state;
2675ab1caedSMatthew Dillon kdmsg_msg_t *msg;
2685ab1caedSMatthew Dillon int retries;
269085cb1ddSMatthew Dillon
270185ace93SMatthew Dillon /*
2715ab1caedSMatthew Dillon * Ask the cluster controller to go away by setting
2725ab1caedSMatthew Dillon * KILLRX. Send a PING to get a response to unstick reading
2735ab1caedSMatthew Dillon * from the pipe.
2745ab1caedSMatthew Dillon *
2755ab1caedSMatthew Dillon * After 10 seconds shitcan the pipe and do an unclean shutdown.
276185ace93SMatthew Dillon */
2778d6d37b8SMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
278185ace93SMatthew Dillon
2795ab1caedSMatthew Dillon atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX);
2805ab1caedSMatthew Dillon msg = kdmsg_msg_alloc(&iocom->state0, DMSG_LNK_PING, NULL, NULL);
2815ab1caedSMatthew Dillon kdmsg_msg_write_locked(iocom, msg);
2825ab1caedSMatthew Dillon
2835ab1caedSMatthew Dillon retries = 10;
284185ace93SMatthew Dillon while (iocom->msgrd_td || iocom->msgwr_td) {
285185ace93SMatthew Dillon wakeup(&iocom->msg_ctl);
2868d6d37b8SMatthew Dillon lksleep(iocom, &iocom->msglk, 0, "clstrkl", hz);
2875ab1caedSMatthew Dillon if (--retries == 0 && iocom->msg_fp) {
2885ab1caedSMatthew Dillon kdio_printf(iocom, 0, "%s\n",
2895ab1caedSMatthew Dillon "iocom_uninit: "
2905ab1caedSMatthew Dillon "shitcanning unresponsive pipe");
2915ab1caedSMatthew Dillon fp_shutdown(iocom->msg_fp, SHUT_RDWR);
2925ab1caedSMatthew Dillon /* retries allowed to go negative, keep looping */
2935ab1caedSMatthew Dillon }
294185ace93SMatthew Dillon }
295185ace93SMatthew Dillon
296185ace93SMatthew Dillon /*
297085cb1ddSMatthew Dillon * Cleanup caches
298085cb1ddSMatthew Dillon */
299085cb1ddSMatthew Dillon if ((state = iocom->freerd_state) != NULL) {
300085cb1ddSMatthew Dillon iocom->freerd_state = NULL;
301a06d536bSMatthew Dillon kdmsg_state_drop(state);
302085cb1ddSMatthew Dillon }
303085cb1ddSMatthew Dillon
304085cb1ddSMatthew Dillon if ((state = iocom->freewr_state) != NULL) {
305085cb1ddSMatthew Dillon iocom->freewr_state = NULL;
306a06d536bSMatthew Dillon kdmsg_state_drop(state);
307085cb1ddSMatthew Dillon }
308085cb1ddSMatthew Dillon
309085cb1ddSMatthew Dillon /*
310185ace93SMatthew Dillon * Drop communications descriptor
311185ace93SMatthew Dillon */
312185ace93SMatthew Dillon if (iocom->msg_fp) {
313185ace93SMatthew Dillon fdrop(iocom->msg_fp);
314185ace93SMatthew Dillon iocom->msg_fp = NULL;
315185ace93SMatthew Dillon }
3168d6d37b8SMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
317185ace93SMatthew Dillon }
318185ace93SMatthew Dillon
319185ace93SMatthew Dillon /*
3203a5aa68fSMatthew Dillon * Cluster controller thread. Perform messaging functions. We have one
3213a5aa68fSMatthew Dillon * thread for the reader and one for the writer. The writer handles
3223a5aa68fSMatthew Dillon * shutdown requests (which should break the reader thread).
3233a5aa68fSMatthew Dillon */
3243a5aa68fSMatthew Dillon static
3253a5aa68fSMatthew Dillon void
kdmsg_iocom_thread_rd(void * arg)3263a5aa68fSMatthew Dillon kdmsg_iocom_thread_rd(void *arg)
3273a5aa68fSMatthew Dillon {
3283a5aa68fSMatthew Dillon kdmsg_iocom_t *iocom = arg;
3293a5aa68fSMatthew Dillon dmsg_hdr_t hdr;
3308d6d37b8SMatthew Dillon kdmsg_msg_t *msg = NULL;
3313a5aa68fSMatthew Dillon size_t hbytes;
3328d6d37b8SMatthew Dillon size_t abytes;
3333a5aa68fSMatthew Dillon int error = 0;
3343a5aa68fSMatthew Dillon
33545c1a24bSMatthew Dillon while ((iocom->msg_ctl & KDMSG_CLUSTERCTL_KILLRX) == 0) {
3363a5aa68fSMatthew Dillon /*
3373a5aa68fSMatthew Dillon * Retrieve the message from the pipe or socket.
3383a5aa68fSMatthew Dillon */
3393a5aa68fSMatthew Dillon error = fp_read(iocom->msg_fp, &hdr, sizeof(hdr),
3403a5aa68fSMatthew Dillon NULL, 1, UIO_SYSSPACE);
3413a5aa68fSMatthew Dillon if (error)
3423a5aa68fSMatthew Dillon break;
3433a5aa68fSMatthew Dillon if (hdr.magic != DMSG_HDR_MAGIC) {
3445ab1caedSMatthew Dillon kdio_printf(iocom, 1, "bad magic: %04x\n", hdr.magic);
3453a5aa68fSMatthew Dillon error = EINVAL;
3463a5aa68fSMatthew Dillon break;
3473a5aa68fSMatthew Dillon }
3483a5aa68fSMatthew Dillon hbytes = (hdr.cmd & DMSGF_SIZE) * DMSG_ALIGN;
3491a2a529dSMatthew Dillon if (hbytes < sizeof(hdr) || hbytes > DMSG_HDR_MAX) {
3505ab1caedSMatthew Dillon kdio_printf(iocom, 1, "bad header size %zd\n", hbytes);
3513a5aa68fSMatthew Dillon error = EINVAL;
3523a5aa68fSMatthew Dillon break;
3533a5aa68fSMatthew Dillon }
3541b8eded1SMatthew Dillon
3553a5aa68fSMatthew Dillon /* XXX messy: mask cmd to avoid allocating state */
3561b8eded1SMatthew Dillon msg = kdmsg_msg_alloc(&iocom->state0,
3573a5aa68fSMatthew Dillon hdr.cmd & DMSGF_BASECMDMASK,
3583a5aa68fSMatthew Dillon NULL, NULL);
3593a5aa68fSMatthew Dillon msg->any.head = hdr;
3603a5aa68fSMatthew Dillon msg->hdr_size = hbytes;
3613a5aa68fSMatthew Dillon if (hbytes > sizeof(hdr)) {
3623a5aa68fSMatthew Dillon error = fp_read(iocom->msg_fp, &msg->any.head + 1,
3633a5aa68fSMatthew Dillon hbytes - sizeof(hdr),
3643a5aa68fSMatthew Dillon NULL, 1, UIO_SYSSPACE);
3653a5aa68fSMatthew Dillon if (error) {
3665ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
3675ab1caedSMatthew Dillon "short msg received");
3683a5aa68fSMatthew Dillon error = EINVAL;
3693a5aa68fSMatthew Dillon break;
3703a5aa68fSMatthew Dillon }
3713a5aa68fSMatthew Dillon }
3728d6d37b8SMatthew Dillon msg->aux_size = hdr.aux_bytes;
3733a5aa68fSMatthew Dillon if (msg->aux_size > DMSG_AUX_MAX) {
3745ab1caedSMatthew Dillon kdio_printf(iocom, 1,
3755ab1caedSMatthew Dillon "illegal msg payload size %zd\n",
3763a5aa68fSMatthew Dillon msg->aux_size);
3773a5aa68fSMatthew Dillon error = EINVAL;
3783a5aa68fSMatthew Dillon break;
3793a5aa68fSMatthew Dillon }
3803a5aa68fSMatthew Dillon if (msg->aux_size) {
3818d6d37b8SMatthew Dillon abytes = DMSG_DOALIGN(msg->aux_size);
3828d6d37b8SMatthew Dillon msg->aux_data = kmalloc(abytes, iocom->mmsg, M_WAITOK);
38303d99ea4SMatthew Dillon msg->flags |= KDMSG_FLAG_AUXALLOC;
3843a5aa68fSMatthew Dillon error = fp_read(iocom->msg_fp, msg->aux_data,
3858d6d37b8SMatthew Dillon abytes, NULL, 1, UIO_SYSSPACE);
3863a5aa68fSMatthew Dillon if (error) {
3875ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
3885ab1caedSMatthew Dillon "short msg payload received");
3893a5aa68fSMatthew Dillon break;
3903a5aa68fSMatthew Dillon }
3913a5aa68fSMatthew Dillon }
3923a5aa68fSMatthew Dillon
3938d6d37b8SMatthew Dillon error = kdmsg_msg_receive_handling(msg);
3943a5aa68fSMatthew Dillon msg = NULL;
3953a5aa68fSMatthew Dillon }
3963a5aa68fSMatthew Dillon
397f156ae94SMatthew Dillon #if 0
3985ab1caedSMatthew Dillon kdio_printf(iocom, 1, "read thread terminating error=%d\n", error);
399f156ae94SMatthew Dillon #endif
4003a5aa68fSMatthew Dillon
4013a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
4028d6d37b8SMatthew Dillon if (msg)
4033a5aa68fSMatthew Dillon kdmsg_msg_free(msg);
4043a5aa68fSMatthew Dillon
4053a5aa68fSMatthew Dillon /*
40645c1a24bSMatthew Dillon * Shutdown the socket and set KILLRX for consistency in case the
40745c1a24bSMatthew Dillon * shutdown was not commanded. Signal the transmit side to shutdown
40845c1a24bSMatthew Dillon * by setting KILLTX and waking it up.
4093a5aa68fSMatthew Dillon */
4103a5aa68fSMatthew Dillon fp_shutdown(iocom->msg_fp, SHUT_RDWR);
41145c1a24bSMatthew Dillon atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX |
41245c1a24bSMatthew Dillon KDMSG_CLUSTERCTL_KILLTX);
4133a5aa68fSMatthew Dillon iocom->msgrd_td = NULL;
41445c1a24bSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
41545c1a24bSMatthew Dillon wakeup(&iocom->msg_ctl);
4163a5aa68fSMatthew Dillon
4173a5aa68fSMatthew Dillon /*
41845c1a24bSMatthew Dillon * iocom can be ripped out at any time once the lock is
41945c1a24bSMatthew Dillon * released with msgrd_td set to NULL. The wakeup()s are safe but
42045c1a24bSMatthew Dillon * that is all.
4213a5aa68fSMatthew Dillon */
4223a5aa68fSMatthew Dillon wakeup(iocom);
4233a5aa68fSMatthew Dillon lwkt_exit();
4243a5aa68fSMatthew Dillon }
4253a5aa68fSMatthew Dillon
4263a5aa68fSMatthew Dillon static
4273a5aa68fSMatthew Dillon void
kdmsg_iocom_thread_wr(void * arg)4283a5aa68fSMatthew Dillon kdmsg_iocom_thread_wr(void *arg)
4293a5aa68fSMatthew Dillon {
4303a5aa68fSMatthew Dillon kdmsg_iocom_t *iocom = arg;
4313a5aa68fSMatthew Dillon kdmsg_msg_t *msg;
4323a5aa68fSMatthew Dillon ssize_t res;
4338d6d37b8SMatthew Dillon size_t abytes;
4343a5aa68fSMatthew Dillon int error = 0;
43545c1a24bSMatthew Dillon int save_ticks;
43645c1a24bSMatthew Dillon int didwarn;
4373a5aa68fSMatthew Dillon
4383a5aa68fSMatthew Dillon /*
4393a5aa68fSMatthew Dillon * Transmit loop
4403a5aa68fSMatthew Dillon */
4413a5aa68fSMatthew Dillon msg = NULL;
4423a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
4433a5aa68fSMatthew Dillon
44445c1a24bSMatthew Dillon while ((iocom->msg_ctl & KDMSG_CLUSTERCTL_KILLTX) == 0 && error == 0) {
4453a5aa68fSMatthew Dillon /*
4463a5aa68fSMatthew Dillon * Sleep if no messages pending. Interlock with flag while
4473a5aa68fSMatthew Dillon * holding msglk.
4483a5aa68fSMatthew Dillon */
4493a5aa68fSMatthew Dillon if (TAILQ_EMPTY(&iocom->msgq)) {
4503a5aa68fSMatthew Dillon atomic_set_int(&iocom->msg_ctl,
4513a5aa68fSMatthew Dillon KDMSG_CLUSTERCTL_SLEEPING);
4523a5aa68fSMatthew Dillon lksleep(&iocom->msg_ctl, &iocom->msglk, 0, "msgwr", hz);
4533a5aa68fSMatthew Dillon atomic_clear_int(&iocom->msg_ctl,
4543a5aa68fSMatthew Dillon KDMSG_CLUSTERCTL_SLEEPING);
4553a5aa68fSMatthew Dillon }
4563a5aa68fSMatthew Dillon
4573a5aa68fSMatthew Dillon while ((msg = TAILQ_FIRST(&iocom->msgq)) != NULL) {
4583a5aa68fSMatthew Dillon /*
4593a5aa68fSMatthew Dillon * Remove msg from the transmit queue and do
4603a5aa68fSMatthew Dillon * persist and half-closed state handling.
4613a5aa68fSMatthew Dillon */
4623a5aa68fSMatthew Dillon TAILQ_REMOVE(&iocom->msgq, msg, qentry);
4633a5aa68fSMatthew Dillon
4643a5aa68fSMatthew Dillon error = kdmsg_state_msgtx(msg);
4653a5aa68fSMatthew Dillon if (error == EALREADY) {
4663a5aa68fSMatthew Dillon error = 0;
4673a5aa68fSMatthew Dillon kdmsg_msg_free(msg);
4683a5aa68fSMatthew Dillon continue;
4693a5aa68fSMatthew Dillon }
4703a5aa68fSMatthew Dillon if (error) {
4713a5aa68fSMatthew Dillon kdmsg_msg_free(msg);
4723a5aa68fSMatthew Dillon break;
4733a5aa68fSMatthew Dillon }
4743a5aa68fSMatthew Dillon
4753a5aa68fSMatthew Dillon /*
4763a5aa68fSMatthew Dillon * Dump the message to the pipe or socket.
4778d6d37b8SMatthew Dillon *
4788d6d37b8SMatthew Dillon * We have to clean up the message as if the transmit
4798d6d37b8SMatthew Dillon * succeeded even if it failed.
4803a5aa68fSMatthew Dillon */
481a06d536bSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
4823a5aa68fSMatthew Dillon error = fp_write(iocom->msg_fp, &msg->any,
4833a5aa68fSMatthew Dillon msg->hdr_size, &res, UIO_SYSSPACE);
4843a5aa68fSMatthew Dillon if (error || res != msg->hdr_size) {
4853a5aa68fSMatthew Dillon if (error == 0)
4863a5aa68fSMatthew Dillon error = EINVAL;
4873a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
488a06d536bSMatthew Dillon kdmsg_state_cleanuptx(msg);
4893a5aa68fSMatthew Dillon break;
4903a5aa68fSMatthew Dillon }
4913a5aa68fSMatthew Dillon if (msg->aux_size) {
4928d6d37b8SMatthew Dillon abytes = DMSG_DOALIGN(msg->aux_size);
4933a5aa68fSMatthew Dillon error = fp_write(iocom->msg_fp,
4948d6d37b8SMatthew Dillon msg->aux_data, abytes,
4953a5aa68fSMatthew Dillon &res, UIO_SYSSPACE);
4968d6d37b8SMatthew Dillon if (error || res != abytes) {
4973a5aa68fSMatthew Dillon if (error == 0)
4983a5aa68fSMatthew Dillon error = EINVAL;
4993a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
500a06d536bSMatthew Dillon kdmsg_state_cleanuptx(msg);
5013a5aa68fSMatthew Dillon break;
5023a5aa68fSMatthew Dillon }
5033a5aa68fSMatthew Dillon }
5043a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
505a06d536bSMatthew Dillon kdmsg_state_cleanuptx(msg);
5063a5aa68fSMatthew Dillon }
5073a5aa68fSMatthew Dillon }
5083a5aa68fSMatthew Dillon
509f156ae94SMatthew Dillon #if 0
5105ab1caedSMatthew Dillon kdio_printf(iocom, 1, "write thread terminating error=%d\n", error);
511f156ae94SMatthew Dillon #endif
5123a5aa68fSMatthew Dillon
5133a5aa68fSMatthew Dillon /*
51445c1a24bSMatthew Dillon * Shutdown the socket and set KILLTX for consistency in case the
51545c1a24bSMatthew Dillon * shutdown was not commanded. Signal the receive side to shutdown
51645c1a24bSMatthew Dillon * by setting KILLRX and waking it up.
5173a5aa68fSMatthew Dillon */
5183a5aa68fSMatthew Dillon fp_shutdown(iocom->msg_fp, SHUT_RDWR);
51945c1a24bSMatthew Dillon atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX |
52045c1a24bSMatthew Dillon KDMSG_CLUSTERCTL_KILLTX);
52145c1a24bSMatthew Dillon wakeup(&iocom->msg_ctl);
5223a5aa68fSMatthew Dillon
5233a5aa68fSMatthew Dillon /*
52445c1a24bSMatthew Dillon * The transmit thread is responsible for final cleanups, wait
52545c1a24bSMatthew Dillon * for the receive side to terminate to prevent new received
52645c1a24bSMatthew Dillon * states from interfering with our cleanup.
52745c1a24bSMatthew Dillon *
52845c1a24bSMatthew Dillon * Do not set msgwr_td to NULL until we actually exit.
5293a5aa68fSMatthew Dillon */
5303a5aa68fSMatthew Dillon while (iocom->msgrd_td) {
5313a5aa68fSMatthew Dillon wakeup(&iocom->msg_ctl);
53245c1a24bSMatthew Dillon lksleep(iocom, &iocom->msglk, 0, "clstrkt", hz);
5333a5aa68fSMatthew Dillon }
5343a5aa68fSMatthew Dillon
5353a5aa68fSMatthew Dillon /*
536a06d536bSMatthew Dillon * We can no longer receive new messages. We must drain the transmit
537a06d536bSMatthew Dillon * message queue and simulate received messages to close anay remaining
538a06d536bSMatthew Dillon * states.
539a06d536bSMatthew Dillon *
54045c1a24bSMatthew Dillon * Loop until all the states are gone and there are no messages
54145c1a24bSMatthew Dillon * pending transmit.
5423a5aa68fSMatthew Dillon */
54345c1a24bSMatthew Dillon save_ticks = ticks;
54445c1a24bSMatthew Dillon didwarn = 0;
54593c84330SMatthew Dillon iocom->flags |= KDMSG_IOCOMF_EXITNOACC;
54645c1a24bSMatthew Dillon
54745c1a24bSMatthew Dillon while (TAILQ_FIRST(&iocom->msgq) ||
54845c1a24bSMatthew Dillon RB_ROOT(&iocom->staterd_tree) ||
54993c84330SMatthew Dillon RB_ROOT(&iocom->statewr_tree) ||
55093c84330SMatthew Dillon iocom->conn_state) {
55145c1a24bSMatthew Dillon /*
5520a9eefcaSMatthew Dillon * Simulate failure for all sub-states of state0.
55345c1a24bSMatthew Dillon */
55445c1a24bSMatthew Dillon kdmsg_drain_msgq(iocom);
555a06d536bSMatthew Dillon kdmsg_simulate_failure(&iocom->state0, 0, DMSG_ERR_LOSTLINK);
55645c1a24bSMatthew Dillon
557a06d536bSMatthew Dillon lksleep(iocom, &iocom->msglk, 0, "clstrtk", hz / 2);
5583a5aa68fSMatthew Dillon
55945c1a24bSMatthew Dillon if ((int)(ticks - save_ticks) > hz*2 && didwarn == 0) {
56045c1a24bSMatthew Dillon didwarn = 1;
5615ab1caedSMatthew Dillon kdio_printf(iocom, 0,
5625ab1caedSMatthew Dillon "Warning, write thread on %p "
5635ab1caedSMatthew Dillon "still terminating\n",
5645ab1caedSMatthew Dillon iocom);
56545c1a24bSMatthew Dillon }
566a06d536bSMatthew Dillon if ((int)(ticks - save_ticks) > hz*15 && didwarn == 1) {
567a06d536bSMatthew Dillon didwarn = 2;
5685ab1caedSMatthew Dillon kdio_printf(iocom, 0,
5695ab1caedSMatthew Dillon "Warning, write thread on %p "
5705ab1caedSMatthew Dillon "still terminating\n",
5715ab1caedSMatthew Dillon iocom);
572a06d536bSMatthew Dillon }
57345c1a24bSMatthew Dillon if ((int)(ticks - save_ticks) > hz*60) {
5745ab1caedSMatthew Dillon kdio_printf(iocom, 0,
5755ab1caedSMatthew Dillon "Can't terminate: msgq %p "
5765ab1caedSMatthew Dillon "rd_tree %p wr_tree %p\n",
577a06d536bSMatthew Dillon TAILQ_FIRST(&iocom->msgq),
578a06d536bSMatthew Dillon RB_ROOT(&iocom->staterd_tree),
579a06d536bSMatthew Dillon RB_ROOT(&iocom->statewr_tree));
5805ab1caedSMatthew Dillon lksleep(iocom, &iocom->msglk, 0, "clstrtk", hz * 10);
58145c1a24bSMatthew Dillon }
58245c1a24bSMatthew Dillon }
58345c1a24bSMatthew Dillon
58445c1a24bSMatthew Dillon /*
58545c1a24bSMatthew Dillon * Exit handling is done by the write thread.
58645c1a24bSMatthew Dillon */
5873a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
5883a5aa68fSMatthew Dillon
5893a5aa68fSMatthew Dillon /*
5903a5aa68fSMatthew Dillon * The state trees had better be empty now
5913a5aa68fSMatthew Dillon */
5923a5aa68fSMatthew Dillon KKASSERT(RB_EMPTY(&iocom->staterd_tree));
5933a5aa68fSMatthew Dillon KKASSERT(RB_EMPTY(&iocom->statewr_tree));
5943a5aa68fSMatthew Dillon KKASSERT(iocom->conn_state == NULL);
5953a5aa68fSMatthew Dillon
596ddfbb283SMatthew Dillon if (iocom->exit_func) {
5973a5aa68fSMatthew Dillon /*
598ddfbb283SMatthew Dillon * iocom is invalid after we call the exit function.
599ddfbb283SMatthew Dillon */
600ddfbb283SMatthew Dillon iocom->msgwr_td = NULL;
601ddfbb283SMatthew Dillon iocom->exit_func(iocom);
602ddfbb283SMatthew Dillon } else {
603ddfbb283SMatthew Dillon /*
604ddfbb283SMatthew Dillon * iocom can be ripped out from under us once msgwr_td is
605ddfbb283SMatthew Dillon * set to NULL. The wakeup is safe.
6063a5aa68fSMatthew Dillon */
6073a5aa68fSMatthew Dillon iocom->msgwr_td = NULL;
6083a5aa68fSMatthew Dillon wakeup(iocom);
609ddfbb283SMatthew Dillon }
6103a5aa68fSMatthew Dillon lwkt_exit();
6113a5aa68fSMatthew Dillon }
6123a5aa68fSMatthew Dillon
6133a5aa68fSMatthew Dillon /*
6143a5aa68fSMatthew Dillon * This cleans out the pending transmit message queue, adjusting any
6153a5aa68fSMatthew Dillon * persistent states properly in the process.
6163a5aa68fSMatthew Dillon *
617a06d536bSMatthew Dillon * Called with iocom locked.
6183a5aa68fSMatthew Dillon */
6193a5aa68fSMatthew Dillon void
kdmsg_drain_msgq(kdmsg_iocom_t * iocom)6203a5aa68fSMatthew Dillon kdmsg_drain_msgq(kdmsg_iocom_t *iocom)
6213a5aa68fSMatthew Dillon {
6223a5aa68fSMatthew Dillon kdmsg_msg_t *msg;
6233a5aa68fSMatthew Dillon
6243a5aa68fSMatthew Dillon /*
6253a5aa68fSMatthew Dillon * Clean out our pending transmit queue, executing the
62693c84330SMatthew Dillon * appropriate state adjustments as if the messages were
62793c84330SMatthew Dillon * sent.
6283a5aa68fSMatthew Dillon */
6293a5aa68fSMatthew Dillon while ((msg = TAILQ_FIRST(&iocom->msgq)) != NULL) {
6303a5aa68fSMatthew Dillon TAILQ_REMOVE(&iocom->msgq, msg, qentry);
63193c84330SMatthew Dillon kdmsg_drain_msg(msg);
63293c84330SMatthew Dillon }
63393c84330SMatthew Dillon }
63493c84330SMatthew Dillon
63593c84330SMatthew Dillon /*
63693c84330SMatthew Dillon * Drain one message by simulating transmission and also simulating a
63793c84330SMatthew Dillon * receive failure.
63893c84330SMatthew Dillon */
63993c84330SMatthew Dillon static void
kdmsg_drain_msg(kdmsg_msg_t * msg)64093c84330SMatthew Dillon kdmsg_drain_msg(kdmsg_msg_t *msg)
64193c84330SMatthew Dillon {
64293c84330SMatthew Dillon if (kdmsg_state_msgtx(msg)) {
6433a5aa68fSMatthew Dillon kdmsg_msg_free(msg);
64493c84330SMatthew Dillon } else {
64593c84330SMatthew Dillon if (msg->state) {
64693c84330SMatthew Dillon kdmsg_simulate_failure(msg->state,
64793c84330SMatthew Dillon 0, DMSG_ERR_LOSTLINK);
64893c84330SMatthew Dillon }
6493a5aa68fSMatthew Dillon kdmsg_state_cleanuptx(msg);
6503a5aa68fSMatthew Dillon }
6513a5aa68fSMatthew Dillon }
6523a5aa68fSMatthew Dillon
6533a5aa68fSMatthew Dillon /*
6548d6d37b8SMatthew Dillon * Do all processing required to handle a freshly received message
6558d6d37b8SMatthew Dillon * after its low level header has been validated.
656a06d536bSMatthew Dillon *
657a06d536bSMatthew Dillon * iocom is not locked.
6588d6d37b8SMatthew Dillon */
6598d6d37b8SMatthew Dillon static
6608d6d37b8SMatthew Dillon int
kdmsg_msg_receive_handling(kdmsg_msg_t * msg)6618d6d37b8SMatthew Dillon kdmsg_msg_receive_handling(kdmsg_msg_t *msg)
6628d6d37b8SMatthew Dillon {
6631b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
6648d6d37b8SMatthew Dillon int error;
6658d6d37b8SMatthew Dillon
6668d6d37b8SMatthew Dillon /*
6678d6d37b8SMatthew Dillon * State machine tracking, state assignment for msg,
6688d6d37b8SMatthew Dillon * returns error and discard status. Errors are fatal
6698d6d37b8SMatthew Dillon * to the connection except for EALREADY which forces
6708d6d37b8SMatthew Dillon * a discard without execution.
6718d6d37b8SMatthew Dillon */
6728d6d37b8SMatthew Dillon error = kdmsg_state_msgrx(msg);
6730a9eefcaSMatthew Dillon if (msg->state->flags & KDMSG_STATE_ABORTING) {
6745ab1caedSMatthew Dillon kdio_printf(iocom, 5,
6755ab1caedSMatthew Dillon "kdmsg_state_abort(b): state %p rxcmd=%08x "
6765ab1caedSMatthew Dillon "txcmd=%08x msgrx error %d\n",
6775ab1caedSMatthew Dillon msg->state, msg->state->rxcmd,
6785ab1caedSMatthew Dillon msg->state->txcmd, error);
6790a9eefcaSMatthew Dillon }
6808d6d37b8SMatthew Dillon if (error) {
6818d6d37b8SMatthew Dillon /*
6828d6d37b8SMatthew Dillon * Raw protocol or connection error
6838d6d37b8SMatthew Dillon */
6840a9eefcaSMatthew Dillon if (msg->state->flags & KDMSG_STATE_ABORTING)
6855ab1caedSMatthew Dillon kdio_printf(iocom, 5,
6865ab1caedSMatthew Dillon "X1 state %p error %d\n",
6875ab1caedSMatthew Dillon msg->state, error);
6888d6d37b8SMatthew Dillon kdmsg_msg_free(msg);
6898d6d37b8SMatthew Dillon if (error == EALREADY)
6908d6d37b8SMatthew Dillon error = 0;
6918d6d37b8SMatthew Dillon } else if (msg->state && msg->state->func) {
6928d6d37b8SMatthew Dillon /*
6938d6d37b8SMatthew Dillon * Message related to state which already has a
6948d6d37b8SMatthew Dillon * handling function installed for it.
6958d6d37b8SMatthew Dillon */
6960a9eefcaSMatthew Dillon if (msg->state->flags & KDMSG_STATE_ABORTING)
6975ab1caedSMatthew Dillon kdio_printf(iocom, 5,
6985ab1caedSMatthew Dillon "X2 state %p func %p\n",
6995ab1caedSMatthew Dillon msg->state, msg->state->func);
7008d6d37b8SMatthew Dillon error = msg->state->func(msg->state, msg);
7018d6d37b8SMatthew Dillon kdmsg_state_cleanuprx(msg);
7028d6d37b8SMatthew Dillon } else if (iocom->flags & KDMSG_IOCOMF_AUTOANY) {
7030a9eefcaSMatthew Dillon if (msg->state->flags & KDMSG_STATE_ABORTING)
7045ab1caedSMatthew Dillon kdio_printf(iocom, 5,
7055ab1caedSMatthew Dillon "X3 state %p\n", msg->state);
7068d6d37b8SMatthew Dillon error = kdmsg_autorxmsg(msg);
7078d6d37b8SMatthew Dillon kdmsg_state_cleanuprx(msg);
7088d6d37b8SMatthew Dillon } else {
7090a9eefcaSMatthew Dillon if (msg->state->flags & KDMSG_STATE_ABORTING)
7105ab1caedSMatthew Dillon kdio_printf(iocom, 5,
7115ab1caedSMatthew Dillon "X4 state %p\n", msg->state);
7128d6d37b8SMatthew Dillon error = iocom->rcvmsg(msg);
7138d6d37b8SMatthew Dillon kdmsg_state_cleanuprx(msg);
7148d6d37b8SMatthew Dillon }
7158d6d37b8SMatthew Dillon return error;
7168d6d37b8SMatthew Dillon }
7178d6d37b8SMatthew Dillon
7188d6d37b8SMatthew Dillon /*
7190a9eefcaSMatthew Dillon * Process state tracking for a message after reception and dequeueing,
7200a9eefcaSMatthew Dillon * prior to execution of the state callback. The state is updated and
7210a9eefcaSMatthew Dillon * will be removed from the RBTREE if completely closed, but the state->parent
7220a9eefcaSMatthew Dillon * and subq linkage is not cleaned up until after the callback (see
7230a9eefcaSMatthew Dillon * cleanuprx()).
7243a5aa68fSMatthew Dillon *
7250a9eefcaSMatthew Dillon * msglk is not held.
7263a5aa68fSMatthew Dillon *
7270a9eefcaSMatthew Dillon * NOTE: A message transaction can consist of several messages in either
7280a9eefcaSMatthew Dillon * direction.
7293a5aa68fSMatthew Dillon *
7300a9eefcaSMatthew Dillon * NOTE: The msgid is unique to the initiator, not necessarily unique for
7310a9eefcaSMatthew Dillon * us or for any relay or for the return direction for that matter.
7320a9eefcaSMatthew Dillon * That is, two sides sending a new message can use the same msgid
7330a9eefcaSMatthew Dillon * without colliding.
7343a5aa68fSMatthew Dillon *
7353a5aa68fSMatthew Dillon * --
7363a5aa68fSMatthew Dillon *
7373a5aa68fSMatthew Dillon * ABORT sequences work by setting the ABORT flag along with normal message
7383a5aa68fSMatthew Dillon * state. However, ABORTs can also be sent on half-closed messages, that is
7393a5aa68fSMatthew Dillon * even if the command or reply side has already sent a DELETE, as long as
7403a5aa68fSMatthew Dillon * the message has not been fully closed it can still send an ABORT+DELETE
7413a5aa68fSMatthew Dillon * to terminate the half-closed message state.
7423a5aa68fSMatthew Dillon *
7433a5aa68fSMatthew Dillon * Since ABORT+DELETEs can race we silently discard ABORT's for message
7443a5aa68fSMatthew Dillon * state which has already been fully closed. REPLY+ABORT+DELETEs can
7453a5aa68fSMatthew Dillon * also race, and in this situation the other side might have already
7463a5aa68fSMatthew Dillon * initiated a new unrelated command with the same message id. Since
7473a5aa68fSMatthew Dillon * the abort has not set the CREATE flag the situation can be detected
7483a5aa68fSMatthew Dillon * and the message will also be discarded.
7493a5aa68fSMatthew Dillon *
7503a5aa68fSMatthew Dillon * Non-blocking requests can be initiated with ABORT+CREATE[+DELETE].
7513a5aa68fSMatthew Dillon * The ABORT request is essentially integrated into the command instead
7523a5aa68fSMatthew Dillon * of being sent later on. In this situation the command implementation
7533a5aa68fSMatthew Dillon * detects that CREATE and ABORT are both set (vs ABORT alone) and can
7543a5aa68fSMatthew Dillon * special-case non-blocking operation for the command.
7553a5aa68fSMatthew Dillon *
7563a5aa68fSMatthew Dillon * NOTE! Messages with ABORT set without CREATE or DELETE are considered
7573a5aa68fSMatthew Dillon * to be mid-stream aborts for command/reply sequences. ABORTs on
7583a5aa68fSMatthew Dillon * one-way messages are not supported.
7593a5aa68fSMatthew Dillon *
7603a5aa68fSMatthew Dillon * NOTE! If a command sequence does not support aborts the ABORT flag is
7613a5aa68fSMatthew Dillon * simply ignored.
7623a5aa68fSMatthew Dillon *
7633a5aa68fSMatthew Dillon * --
7643a5aa68fSMatthew Dillon *
7653a5aa68fSMatthew Dillon * One-off messages (no reply expected) are sent with neither CREATE or DELETE
7663a5aa68fSMatthew Dillon * set. One-off messages cannot be aborted and typically aren't processed
7673a5aa68fSMatthew Dillon * by these routines. The REPLY bit can be used to distinguish whether a
7683a5aa68fSMatthew Dillon * one-off message is a command or reply. For example, one-off replies
7693a5aa68fSMatthew Dillon * will typically just contain status updates.
7703a5aa68fSMatthew Dillon */
7718d6d37b8SMatthew Dillon static
7723a5aa68fSMatthew Dillon int
kdmsg_state_msgrx(kdmsg_msg_t * msg)7733a5aa68fSMatthew Dillon kdmsg_state_msgrx(kdmsg_msg_t *msg)
7743a5aa68fSMatthew Dillon {
7751b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
7763a5aa68fSMatthew Dillon kdmsg_state_t *state;
7771b8eded1SMatthew Dillon kdmsg_state_t *pstate;
7781b8eded1SMatthew Dillon kdmsg_state_t sdummy;
7793a5aa68fSMatthew Dillon int error;
7803a5aa68fSMatthew Dillon
78157e09377SMatthew Dillon bzero(&sdummy, sizeof(sdummy)); /* avoid gcc warnings */
78257e09377SMatthew Dillon
7833a5aa68fSMatthew Dillon /*
7843a5aa68fSMatthew Dillon * Make sure a state structure is ready to go in case we need a new
7853a5aa68fSMatthew Dillon * one. This is the only routine which uses freerd_state so no
7863a5aa68fSMatthew Dillon * races are possible.
7873a5aa68fSMatthew Dillon */
7883a5aa68fSMatthew Dillon if ((state = iocom->freerd_state) == NULL) {
7893a5aa68fSMatthew Dillon state = kmalloc(sizeof(*state), iocom->mmsg, M_WAITOK | M_ZERO);
7903a5aa68fSMatthew Dillon state->flags = KDMSG_STATE_DYNAMIC;
7911b8eded1SMatthew Dillon state->iocom = iocom;
792a06d536bSMatthew Dillon state->refs = 1;
7931b8eded1SMatthew Dillon TAILQ_INIT(&state->subq);
7943a5aa68fSMatthew Dillon iocom->freerd_state = state;
7953a5aa68fSMatthew Dillon }
796a06d536bSMatthew Dillon state = NULL; /* safety */
7973a5aa68fSMatthew Dillon
7983a5aa68fSMatthew Dillon /*
7993a5aa68fSMatthew Dillon * Lock RB tree and locate existing persistent state, if any.
8003a5aa68fSMatthew Dillon *
8013a5aa68fSMatthew Dillon * If received msg is a command state is on staterd_tree.
8023a5aa68fSMatthew Dillon * If received msg is a reply state is on statewr_tree.
8033a5aa68fSMatthew Dillon */
8043a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
8053a5aa68fSMatthew Dillon
80645c1a24bSMatthew Dillon again:
807a06d536bSMatthew Dillon if (msg->state == &iocom->state0) {
8081b8eded1SMatthew Dillon sdummy.msgid = msg->any.head.msgid;
8091b8eded1SMatthew Dillon sdummy.iocom = iocom;
8101b8eded1SMatthew Dillon if (msg->any.head.cmd & DMSGF_REVTRANS) {
8111b8eded1SMatthew Dillon state = RB_FIND(kdmsg_state_tree, &iocom->statewr_tree,
8121b8eded1SMatthew Dillon &sdummy);
8131b8eded1SMatthew Dillon } else {
8141b8eded1SMatthew Dillon state = RB_FIND(kdmsg_state_tree, &iocom->staterd_tree,
8151b8eded1SMatthew Dillon &sdummy);
8161b8eded1SMatthew Dillon }
817a06d536bSMatthew Dillon
818a06d536bSMatthew Dillon /*
819a06d536bSMatthew Dillon * Set message state unconditionally. If this is a CREATE
820a06d536bSMatthew Dillon * message this state will become the parent state and new
821a06d536bSMatthew Dillon * state will be allocated for the message state.
822a06d536bSMatthew Dillon */
8231b8eded1SMatthew Dillon if (state == NULL)
8241b8eded1SMatthew Dillon state = &iocom->state0;
82545c1a24bSMatthew Dillon if (state->flags & KDMSG_STATE_INTERLOCK) {
82645c1a24bSMatthew Dillon state->flags |= KDMSG_STATE_SIGNAL;
82745c1a24bSMatthew Dillon lksleep(state, &iocom->msglk, 0, "dmrace", hz);
82845c1a24bSMatthew Dillon goto again;
82945c1a24bSMatthew Dillon }
8300a9eefcaSMatthew Dillon kdmsg_state_hold(state);
831a06d536bSMatthew Dillon kdmsg_state_drop(msg->state); /* iocom->state0 */
8323a5aa68fSMatthew Dillon msg->state = state;
833a06d536bSMatthew Dillon } else {
834a06d536bSMatthew Dillon state = msg->state;
835a06d536bSMatthew Dillon }
8363a5aa68fSMatthew Dillon
8373a5aa68fSMatthew Dillon /*
8381b8eded1SMatthew Dillon * Short-cut one-off or mid-stream messages.
8393a5aa68fSMatthew Dillon */
8403a5aa68fSMatthew Dillon if ((msg->any.head.cmd & (DMSGF_CREATE | DMSGF_DELETE |
8413a5aa68fSMatthew Dillon DMSGF_ABORT)) == 0) {
8421b8eded1SMatthew Dillon error = 0;
8438e226bc8SMatthew Dillon goto done;
8443a5aa68fSMatthew Dillon }
8453a5aa68fSMatthew Dillon
8463a5aa68fSMatthew Dillon /*
8473a5aa68fSMatthew Dillon * Switch on CREATE, DELETE, REPLY, and also handle ABORT from
8483a5aa68fSMatthew Dillon * inside the case statements.
8493a5aa68fSMatthew Dillon */
8503a5aa68fSMatthew Dillon switch(msg->any.head.cmd & (DMSGF_CREATE|DMSGF_DELETE|DMSGF_REPLY)) {
8513a5aa68fSMatthew Dillon case DMSGF_CREATE:
8523a5aa68fSMatthew Dillon case DMSGF_CREATE | DMSGF_DELETE:
8533a5aa68fSMatthew Dillon /*
8543a5aa68fSMatthew Dillon * New persistant command received.
8553a5aa68fSMatthew Dillon */
8561b8eded1SMatthew Dillon if (state != &iocom->state0) {
8575ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
8585ab1caedSMatthew Dillon "duplicate transaction");
8593a5aa68fSMatthew Dillon error = EINVAL;
8603a5aa68fSMatthew Dillon break;
8613a5aa68fSMatthew Dillon }
8621b8eded1SMatthew Dillon
8631b8eded1SMatthew Dillon /*
8641b8eded1SMatthew Dillon * Lookup the circuit. The circuit is an open transaction.
8651b8eded1SMatthew Dillon * the REVCIRC bit in the message tells us which side
8661b8eded1SMatthew Dillon * initiated the transaction representing the circuit.
8671b8eded1SMatthew Dillon */
8681b8eded1SMatthew Dillon if (msg->any.head.circuit) {
8691b8eded1SMatthew Dillon sdummy.msgid = msg->any.head.circuit;
8701b8eded1SMatthew Dillon
8711b8eded1SMatthew Dillon if (msg->any.head.cmd & DMSGF_REVCIRC) {
8721b8eded1SMatthew Dillon pstate = RB_FIND(kdmsg_state_tree,
8731b8eded1SMatthew Dillon &iocom->statewr_tree,
8741b8eded1SMatthew Dillon &sdummy);
8751b8eded1SMatthew Dillon } else {
8761b8eded1SMatthew Dillon pstate = RB_FIND(kdmsg_state_tree,
8771b8eded1SMatthew Dillon &iocom->staterd_tree,
8781b8eded1SMatthew Dillon &sdummy);
8791b8eded1SMatthew Dillon }
8801b8eded1SMatthew Dillon if (pstate == NULL) {
8815ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
8825ab1caedSMatthew Dillon "missing parent in "
8835ab1caedSMatthew Dillon "stacked trans");
8841b8eded1SMatthew Dillon error = EINVAL;
8851b8eded1SMatthew Dillon break;
8861b8eded1SMatthew Dillon }
8871b8eded1SMatthew Dillon } else {
8881b8eded1SMatthew Dillon pstate = &iocom->state0;
8891b8eded1SMatthew Dillon }
8901b8eded1SMatthew Dillon
8911b8eded1SMatthew Dillon /*
892a06d536bSMatthew Dillon * Allocate new state.
893a06d536bSMatthew Dillon *
894a06d536bSMatthew Dillon * msg->state becomes the owner of the ref we inherit from
895a06d536bSMatthew Dillon * freerd_stae.
8961b8eded1SMatthew Dillon */
897a06d536bSMatthew Dillon kdmsg_state_drop(state);
8983a5aa68fSMatthew Dillon state = iocom->freerd_state;
8993a5aa68fSMatthew Dillon iocom->freerd_state = NULL;
9001b8eded1SMatthew Dillon
901a06d536bSMatthew Dillon msg->state = state; /* inherits freerd ref */
9021b8eded1SMatthew Dillon state->parent = pstate;
9031b8eded1SMatthew Dillon KKASSERT(state->iocom == iocom);
904a06d536bSMatthew Dillon state->flags |= KDMSG_STATE_RBINSERTED |
905a06d536bSMatthew Dillon KDMSG_STATE_SUBINSERTED |
9061b8eded1SMatthew Dillon KDMSG_STATE_OPPOSITE;
9070a9eefcaSMatthew Dillon if (TAILQ_EMPTY(&pstate->subq))
9080a9eefcaSMatthew Dillon kdmsg_state_hold(pstate);/* states on pstate->subq */
9090a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* state on pstate->subq */
9100a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* state on rbtree */
91103d99ea4SMatthew Dillon state->icmd = msg->any.head.cmd & DMSGF_BASECMDMASK;
9123a5aa68fSMatthew Dillon state->rxcmd = msg->any.head.cmd & ~DMSGF_DELETE;
9133a5aa68fSMatthew Dillon state->txcmd = DMSGF_REPLY;
91403d99ea4SMatthew Dillon state->msgid = msg->any.head.msgid;
9150a9eefcaSMatthew Dillon state->flags &= ~KDMSG_STATE_NEW;
9163a5aa68fSMatthew Dillon RB_INSERT(kdmsg_state_tree, &iocom->staterd_tree, state);
9171b8eded1SMatthew Dillon TAILQ_INSERT_TAIL(&pstate->subq, state, entry);
9183a5aa68fSMatthew Dillon error = 0;
9193a5aa68fSMatthew Dillon break;
9203a5aa68fSMatthew Dillon case DMSGF_DELETE:
9213a5aa68fSMatthew Dillon /*
9223a5aa68fSMatthew Dillon * Persistent state is expected but might not exist if an
9233a5aa68fSMatthew Dillon * ABORT+DELETE races the close.
9243a5aa68fSMatthew Dillon */
9251b8eded1SMatthew Dillon if (state == &iocom->state0) {
9263a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
9275ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
9285ab1caedSMatthew Dillon "msgrx: "
9295ab1caedSMatthew Dillon "state already A");
9303a5aa68fSMatthew Dillon error = EALREADY;
9313a5aa68fSMatthew Dillon } else {
9325ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
9335ab1caedSMatthew Dillon "msgrx: no state for DELETE");
9343a5aa68fSMatthew Dillon error = EINVAL;
9353a5aa68fSMatthew Dillon }
9363a5aa68fSMatthew Dillon break;
9373a5aa68fSMatthew Dillon }
9383a5aa68fSMatthew Dillon
9393a5aa68fSMatthew Dillon /*
9403a5aa68fSMatthew Dillon * Handle another ABORT+DELETE case if the msgid has already
9413a5aa68fSMatthew Dillon * been reused.
9423a5aa68fSMatthew Dillon */
9433a5aa68fSMatthew Dillon if ((state->rxcmd & DMSGF_CREATE) == 0) {
9443a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
9455ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
9465ab1caedSMatthew Dillon "msgrx: state already B");
9473a5aa68fSMatthew Dillon error = EALREADY;
9483a5aa68fSMatthew Dillon } else {
9495ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
9505ab1caedSMatthew Dillon "msgrx: state reused for DELETE");
9513a5aa68fSMatthew Dillon error = EINVAL;
9523a5aa68fSMatthew Dillon }
9533a5aa68fSMatthew Dillon break;
9543a5aa68fSMatthew Dillon }
9553a5aa68fSMatthew Dillon error = 0;
9563a5aa68fSMatthew Dillon break;
9573a5aa68fSMatthew Dillon default:
9583a5aa68fSMatthew Dillon /*
9593a5aa68fSMatthew Dillon * Check for mid-stream ABORT command received, otherwise
9603a5aa68fSMatthew Dillon * allow.
9613a5aa68fSMatthew Dillon */
9623a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
9631b8eded1SMatthew Dillon if (state == &iocom->state0 ||
9643a5aa68fSMatthew Dillon (state->rxcmd & DMSGF_CREATE) == 0) {
9653a5aa68fSMatthew Dillon error = EALREADY;
9663a5aa68fSMatthew Dillon break;
9673a5aa68fSMatthew Dillon }
9683a5aa68fSMatthew Dillon }
9693a5aa68fSMatthew Dillon error = 0;
9703a5aa68fSMatthew Dillon break;
9713a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_CREATE:
9723a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_CREATE | DMSGF_DELETE:
9733a5aa68fSMatthew Dillon /*
9743a5aa68fSMatthew Dillon * When receiving a reply with CREATE set the original
9753a5aa68fSMatthew Dillon * persistent state message should already exist.
9763a5aa68fSMatthew Dillon */
9771b8eded1SMatthew Dillon if (state == &iocom->state0) {
9785ab1caedSMatthew Dillon kdio_printf(iocom, 1,
9795ab1caedSMatthew Dillon "msgrx: no state match for "
9803a5aa68fSMatthew Dillon "REPLY cmd=%08x msgid=%016jx\n",
9813a5aa68fSMatthew Dillon msg->any.head.cmd,
9823a5aa68fSMatthew Dillon (intmax_t)msg->any.head.msgid);
9833a5aa68fSMatthew Dillon error = EINVAL;
9843a5aa68fSMatthew Dillon break;
9853a5aa68fSMatthew Dillon }
9863a5aa68fSMatthew Dillon state->rxcmd = msg->any.head.cmd & ~DMSGF_DELETE;
9873a5aa68fSMatthew Dillon error = 0;
9883a5aa68fSMatthew Dillon break;
9893a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_DELETE:
9903a5aa68fSMatthew Dillon /*
9913a5aa68fSMatthew Dillon * Received REPLY+ABORT+DELETE in case where msgid has
9923a5aa68fSMatthew Dillon * already been fully closed, ignore the message.
9933a5aa68fSMatthew Dillon */
9941b8eded1SMatthew Dillon if (state == &iocom->state0) {
9953a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
9963a5aa68fSMatthew Dillon error = EALREADY;
9973a5aa68fSMatthew Dillon } else {
9985ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
9995ab1caedSMatthew Dillon "msgrx: no state match "
10005ab1caedSMatthew Dillon "for REPLY|DELETE");
10013a5aa68fSMatthew Dillon error = EINVAL;
10023a5aa68fSMatthew Dillon }
10033a5aa68fSMatthew Dillon break;
10043a5aa68fSMatthew Dillon }
10053a5aa68fSMatthew Dillon
10063a5aa68fSMatthew Dillon /*
10073a5aa68fSMatthew Dillon * Received REPLY+ABORT+DELETE in case where msgid has
10083a5aa68fSMatthew Dillon * already been reused for an unrelated message,
10093a5aa68fSMatthew Dillon * ignore the message.
10103a5aa68fSMatthew Dillon */
10113a5aa68fSMatthew Dillon if ((state->rxcmd & DMSGF_CREATE) == 0) {
10123a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
10133a5aa68fSMatthew Dillon error = EALREADY;
10143a5aa68fSMatthew Dillon } else {
10155ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
10165ab1caedSMatthew Dillon "msgrx: state reused "
10175ab1caedSMatthew Dillon "for REPLY|DELETE");
10183a5aa68fSMatthew Dillon error = EINVAL;
10193a5aa68fSMatthew Dillon }
10203a5aa68fSMatthew Dillon break;
10213a5aa68fSMatthew Dillon }
10223a5aa68fSMatthew Dillon error = 0;
10233a5aa68fSMatthew Dillon break;
10243a5aa68fSMatthew Dillon case DMSGF_REPLY:
10253a5aa68fSMatthew Dillon /*
10263a5aa68fSMatthew Dillon * Check for mid-stream ABORT reply received to sent command.
10273a5aa68fSMatthew Dillon */
10283a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
10291b8eded1SMatthew Dillon if (state == &iocom->state0 ||
10303a5aa68fSMatthew Dillon (state->rxcmd & DMSGF_CREATE) == 0) {
10313a5aa68fSMatthew Dillon error = EALREADY;
10323a5aa68fSMatthew Dillon break;
10333a5aa68fSMatthew Dillon }
10343a5aa68fSMatthew Dillon }
10353a5aa68fSMatthew Dillon error = 0;
10363a5aa68fSMatthew Dillon break;
10373a5aa68fSMatthew Dillon }
10388e226bc8SMatthew Dillon
10398e226bc8SMatthew Dillon /*
10408e226bc8SMatthew Dillon * Calculate the easy-switch() transactional command. Represents
10418e226bc8SMatthew Dillon * the outer-transaction command for any transaction-create or
10428e226bc8SMatthew Dillon * transaction-delete, and the inner message command for any
10438e226bc8SMatthew Dillon * non-transaction or inside-transaction command. tcmd will be
1044d30cab67SMatthew Dillon * set to 0 if the message state is illegal.
10458e226bc8SMatthew Dillon *
10468e226bc8SMatthew Dillon * The two can be told apart because outer-transaction commands
10478e226bc8SMatthew Dillon * always have a DMSGF_CREATE and/or DMSGF_DELETE flag.
10488e226bc8SMatthew Dillon */
10498e226bc8SMatthew Dillon done:
10508e226bc8SMatthew Dillon if (msg->any.head.cmd & (DMSGF_CREATE | DMSGF_DELETE)) {
10511b8eded1SMatthew Dillon if (state != &iocom->state0) {
10528e226bc8SMatthew Dillon msg->tcmd = (msg->state->icmd & DMSGF_BASECMDMASK) |
10538e226bc8SMatthew Dillon (msg->any.head.cmd & (DMSGF_CREATE |
10548e226bc8SMatthew Dillon DMSGF_DELETE |
10558e226bc8SMatthew Dillon DMSGF_REPLY));
10568e226bc8SMatthew Dillon } else {
10578e226bc8SMatthew Dillon msg->tcmd = 0;
10588e226bc8SMatthew Dillon }
10598e226bc8SMatthew Dillon } else {
10608e226bc8SMatthew Dillon msg->tcmd = msg->any.head.cmd & DMSGF_CMDSWMASK;
10618e226bc8SMatthew Dillon }
10620a9eefcaSMatthew Dillon
10630a9eefcaSMatthew Dillon /*
10640a9eefcaSMatthew Dillon * Adjust the state for DELETE handling now, before making the
10650a9eefcaSMatthew Dillon * callback so we are atomic with other state updates.
10660a9eefcaSMatthew Dillon *
10670a9eefcaSMatthew Dillon * Subq/parent linkages are cleaned up after the callback.
10680a9eefcaSMatthew Dillon * If an error occurred the message is ignored and state is not
10690a9eefcaSMatthew Dillon * updated.
10700a9eefcaSMatthew Dillon */
10710a9eefcaSMatthew Dillon if ((state = msg->state) == NULL || error != 0) {
10725ab1caedSMatthew Dillon kdio_printf(iocom, 1,
10735ab1caedSMatthew Dillon "msgrx: state=%p error %d\n",
10745ab1caedSMatthew Dillon state, error);
10750a9eefcaSMatthew Dillon } else if (msg->any.head.cmd & DMSGF_DELETE) {
10760a9eefcaSMatthew Dillon KKASSERT((state->rxcmd & DMSGF_DELETE) == 0);
10770a9eefcaSMatthew Dillon state->rxcmd |= DMSGF_DELETE;
10780a9eefcaSMatthew Dillon if (state->txcmd & DMSGF_DELETE) {
10790a9eefcaSMatthew Dillon KKASSERT(state->flags & KDMSG_STATE_RBINSERTED);
10800a9eefcaSMatthew Dillon if (state->rxcmd & DMSGF_REPLY) {
10810a9eefcaSMatthew Dillon KKASSERT(msg->any.head.cmd &
10820a9eefcaSMatthew Dillon DMSGF_REPLY);
10830a9eefcaSMatthew Dillon RB_REMOVE(kdmsg_state_tree,
10840a9eefcaSMatthew Dillon &iocom->statewr_tree, state);
10850a9eefcaSMatthew Dillon } else {
10860a9eefcaSMatthew Dillon KKASSERT((msg->any.head.cmd &
10870a9eefcaSMatthew Dillon DMSGF_REPLY) == 0);
10880a9eefcaSMatthew Dillon RB_REMOVE(kdmsg_state_tree,
10890a9eefcaSMatthew Dillon &iocom->staterd_tree, state);
10900a9eefcaSMatthew Dillon }
10910a9eefcaSMatthew Dillon state->flags &= ~KDMSG_STATE_RBINSERTED;
10920a9eefcaSMatthew Dillon kdmsg_state_drop(state); /* state on rbtree */
10930a9eefcaSMatthew Dillon }
10940a9eefcaSMatthew Dillon }
10950a9eefcaSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
10960a9eefcaSMatthew Dillon
10973a5aa68fSMatthew Dillon return (error);
10983a5aa68fSMatthew Dillon }
10993a5aa68fSMatthew Dillon
110003d99ea4SMatthew Dillon /*
110103d99ea4SMatthew Dillon * Called instead of iocom->rcvmsg() if any of the AUTO flags are set.
110203d99ea4SMatthew Dillon * This routine must call iocom->rcvmsg() for anything not automatically
110303d99ea4SMatthew Dillon * handled.
110403d99ea4SMatthew Dillon */
110503d99ea4SMatthew Dillon static int
kdmsg_autorxmsg(kdmsg_msg_t * msg)110603d99ea4SMatthew Dillon kdmsg_autorxmsg(kdmsg_msg_t *msg)
110703d99ea4SMatthew Dillon {
11081b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
11095ab1caedSMatthew Dillon kdmsg_msg_t *rep;
111003d99ea4SMatthew Dillon int error = 0;
11118d6d37b8SMatthew Dillon uint32_t cmd;
111203d99ea4SMatthew Dillon
11138d6d37b8SMatthew Dillon /*
11148e226bc8SMatthew Dillon * Main switch processes transaction create/delete sequences only.
11158e226bc8SMatthew Dillon * Use icmd (DELETEs use DMSG_LNK_ERROR
11168e226bc8SMatthew Dillon *
11178e226bc8SMatthew Dillon * NOTE: If processing in-transaction messages you generally want
11188e226bc8SMatthew Dillon * an inner switch on msg->any.head.cmd.
11198d6d37b8SMatthew Dillon */
11208e226bc8SMatthew Dillon if (msg->state) {
11218e226bc8SMatthew Dillon cmd = (msg->state->icmd & DMSGF_BASECMDMASK) |
11228e226bc8SMatthew Dillon (msg->any.head.cmd & (DMSGF_CREATE |
11238e226bc8SMatthew Dillon DMSGF_DELETE |
11248e226bc8SMatthew Dillon DMSGF_REPLY));
11258e226bc8SMatthew Dillon } else {
11268e226bc8SMatthew Dillon cmd = 0;
11278e226bc8SMatthew Dillon }
11288d6d37b8SMatthew Dillon
11298d6d37b8SMatthew Dillon switch(cmd) {
11305ab1caedSMatthew Dillon case DMSG_LNK_PING:
11315ab1caedSMatthew Dillon /*
11325ab1caedSMatthew Dillon * Received ping, send reply
11335ab1caedSMatthew Dillon */
11345ab1caedSMatthew Dillon rep = kdmsg_msg_alloc(msg->state, DMSG_LNK_PING | DMSGF_REPLY,
11355ab1caedSMatthew Dillon NULL, NULL);
11365ab1caedSMatthew Dillon kdmsg_msg_write(rep);
11375ab1caedSMatthew Dillon break;
11385ab1caedSMatthew Dillon case DMSG_LNK_PING | DMSGF_REPLY:
11395ab1caedSMatthew Dillon /* ignore replies */
11405ab1caedSMatthew Dillon break;
114103d99ea4SMatthew Dillon case DMSG_LNK_CONN | DMSGF_CREATE:
11428d6d37b8SMatthew Dillon case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_DELETE:
114303d99ea4SMatthew Dillon /*
114403d99ea4SMatthew Dillon * Received LNK_CONN transaction. Transmit response and
114503d99ea4SMatthew Dillon * leave transaction open, which allows the other end to
114603d99ea4SMatthew Dillon * start to the SPAN protocol.
11478d6d37b8SMatthew Dillon *
11488d6d37b8SMatthew Dillon * Handle shim after acknowledging the CONN.
114903d99ea4SMatthew Dillon */
11508d6d37b8SMatthew Dillon if ((msg->any.head.cmd & DMSGF_DELETE) == 0) {
115103d99ea4SMatthew Dillon if (iocom->flags & KDMSG_IOCOMF_AUTOCONN) {
115203d99ea4SMatthew Dillon kdmsg_msg_result(msg, 0);
115303d99ea4SMatthew Dillon if (iocom->auto_callback)
115403d99ea4SMatthew Dillon iocom->auto_callback(msg);
115503d99ea4SMatthew Dillon } else {
115603d99ea4SMatthew Dillon error = iocom->rcvmsg(msg);
115703d99ea4SMatthew Dillon }
115803d99ea4SMatthew Dillon break;
11598d6d37b8SMatthew Dillon }
11608d6d37b8SMatthew Dillon /* fall through */
11618d6d37b8SMatthew Dillon case DMSG_LNK_CONN | DMSGF_DELETE:
11628d6d37b8SMatthew Dillon /*
11638d6d37b8SMatthew Dillon * This message is usually simulated after a link is lost
11648d6d37b8SMatthew Dillon * to clean up the transaction.
11658d6d37b8SMatthew Dillon */
11668d6d37b8SMatthew Dillon if (iocom->flags & KDMSG_IOCOMF_AUTOCONN) {
11678d6d37b8SMatthew Dillon if (iocom->auto_callback)
11688d6d37b8SMatthew Dillon iocom->auto_callback(msg);
11698d6d37b8SMatthew Dillon kdmsg_msg_reply(msg, 0);
11708d6d37b8SMatthew Dillon } else {
11718d6d37b8SMatthew Dillon error = iocom->rcvmsg(msg);
11728d6d37b8SMatthew Dillon }
11738d6d37b8SMatthew Dillon break;
117403d99ea4SMatthew Dillon case DMSG_LNK_SPAN | DMSGF_CREATE:
117503d99ea4SMatthew Dillon case DMSG_LNK_SPAN | DMSGF_CREATE | DMSGF_DELETE:
117603d99ea4SMatthew Dillon /*
117703d99ea4SMatthew Dillon * Received LNK_SPAN transaction. We do not have to respond
11781b8eded1SMatthew Dillon * (except on termination), but we must leave the transaction
11791b8eded1SMatthew Dillon * open.
11808d6d37b8SMatthew Dillon *
11818d6d37b8SMatthew Dillon * Handle shim after acknowledging the SPAN.
118203d99ea4SMatthew Dillon */
11838e226bc8SMatthew Dillon if (iocom->flags & KDMSG_IOCOMF_AUTORXSPAN) {
118403d99ea4SMatthew Dillon if ((msg->any.head.cmd & DMSGF_DELETE) == 0) {
118503d99ea4SMatthew Dillon if (iocom->auto_callback)
118603d99ea4SMatthew Dillon iocom->auto_callback(msg);
118703d99ea4SMatthew Dillon break;
118803d99ea4SMatthew Dillon }
118903d99ea4SMatthew Dillon /* fall through */
119003d99ea4SMatthew Dillon } else {
119103d99ea4SMatthew Dillon error = iocom->rcvmsg(msg);
119203d99ea4SMatthew Dillon break;
119303d99ea4SMatthew Dillon }
119403d99ea4SMatthew Dillon /* fall through */
119503d99ea4SMatthew Dillon case DMSG_LNK_SPAN | DMSGF_DELETE:
11968d6d37b8SMatthew Dillon /*
11978d6d37b8SMatthew Dillon * Process shims (auto_callback) before cleaning up the
11988d6d37b8SMatthew Dillon * circuit structure and closing the transactions. Device
11998d6d37b8SMatthew Dillon * driver should ensure that the circuit is not used after
12008d6d37b8SMatthew Dillon * the auto_callback() returns.
12018d6d37b8SMatthew Dillon *
12028d6d37b8SMatthew Dillon * Handle shim before closing the SPAN transaction.
12038d6d37b8SMatthew Dillon */
12048e226bc8SMatthew Dillon if (iocom->flags & KDMSG_IOCOMF_AUTORXSPAN) {
12058d6d37b8SMatthew Dillon if (iocom->auto_callback)
12068d6d37b8SMatthew Dillon iocom->auto_callback(msg);
120703d99ea4SMatthew Dillon kdmsg_msg_reply(msg, 0);
120803d99ea4SMatthew Dillon } else {
120903d99ea4SMatthew Dillon error = iocom->rcvmsg(msg);
121003d99ea4SMatthew Dillon }
121103d99ea4SMatthew Dillon break;
121203d99ea4SMatthew Dillon default:
121303d99ea4SMatthew Dillon /*
121403d99ea4SMatthew Dillon * Anything unhandled goes into rcvmsg.
121503d99ea4SMatthew Dillon *
121603d99ea4SMatthew Dillon * NOTE: Replies to link-level messages initiated by our side
121703d99ea4SMatthew Dillon * are handled by the state callback, they are NOT
121803d99ea4SMatthew Dillon * handled here.
121903d99ea4SMatthew Dillon */
122003d99ea4SMatthew Dillon error = iocom->rcvmsg(msg);
122103d99ea4SMatthew Dillon break;
122203d99ea4SMatthew Dillon }
122303d99ea4SMatthew Dillon return (error);
122403d99ea4SMatthew Dillon }
122503d99ea4SMatthew Dillon
122603d99ea4SMatthew Dillon /*
12278d6d37b8SMatthew Dillon * Post-receive-handling message and state cleanup. This routine is called
12288d6d37b8SMatthew Dillon * after the state function handling/callback to properly dispose of the
12290a9eefcaSMatthew Dillon * message and unlink the state's parent/subq linkage if the state is
12300a9eefcaSMatthew Dillon * completely closed.
12310a9eefcaSMatthew Dillon *
12320a9eefcaSMatthew Dillon * msglk is not held.
12338d6d37b8SMatthew Dillon */
123403d99ea4SMatthew Dillon static
123503d99ea4SMatthew Dillon void
kdmsg_state_cleanuprx(kdmsg_msg_t * msg)12363a5aa68fSMatthew Dillon kdmsg_state_cleanuprx(kdmsg_msg_t *msg)
12373a5aa68fSMatthew Dillon {
12380a9eefcaSMatthew Dillon kdmsg_state_t *state = msg->state;
12390a9eefcaSMatthew Dillon kdmsg_iocom_t *iocom = state->iocom;
12400a9eefcaSMatthew Dillon
12410a9eefcaSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
12420a9eefcaSMatthew Dillon if (state != &iocom->state0) {
12430a9eefcaSMatthew Dillon /*
12440a9eefcaSMatthew Dillon * When terminating a transaction (in either direction), all
12450a9eefcaSMatthew Dillon * sub-states are aborted.
12460a9eefcaSMatthew Dillon */
12470a9eefcaSMatthew Dillon if ((msg->any.head.cmd & DMSGF_DELETE) &&
12480a9eefcaSMatthew Dillon TAILQ_FIRST(&msg->state->subq)) {
12495ab1caedSMatthew Dillon kdio_printf(iocom, 2,
12505ab1caedSMatthew Dillon "simulate failure for substates of "
12515ab1caedSMatthew Dillon "state %p cmd %08x/%08x\n",
12520a9eefcaSMatthew Dillon msg->state,
12530a9eefcaSMatthew Dillon msg->state->rxcmd,
12540a9eefcaSMatthew Dillon msg->state->txcmd);
12550a9eefcaSMatthew Dillon kdmsg_simulate_failure(msg->state,
12560a9eefcaSMatthew Dillon 0, DMSG_ERR_LOSTLINK);
12570a9eefcaSMatthew Dillon }
12580a9eefcaSMatthew Dillon
12590a9eefcaSMatthew Dillon /*
12600a9eefcaSMatthew Dillon * Once the state is fully closed we can (try to) remove it
12610a9eefcaSMatthew Dillon * from the subq topology.
12620a9eefcaSMatthew Dillon */
12630a9eefcaSMatthew Dillon if ((state->flags & KDMSG_STATE_SUBINSERTED) &&
12640a9eefcaSMatthew Dillon (state->rxcmd & DMSGF_DELETE) &&
12650a9eefcaSMatthew Dillon (state->txcmd & DMSGF_DELETE)) {
12660a9eefcaSMatthew Dillon /*
12670a9eefcaSMatthew Dillon * Remove parent linkage if state is completely closed.
12680a9eefcaSMatthew Dillon */
12690a9eefcaSMatthew Dillon kdmsg_subq_delete(state);
12700a9eefcaSMatthew Dillon }
12710a9eefcaSMatthew Dillon }
12720a9eefcaSMatthew Dillon kdmsg_msg_free(msg);
12730a9eefcaSMatthew Dillon
12740a9eefcaSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
12750a9eefcaSMatthew Dillon }
12760a9eefcaSMatthew Dillon
12770a9eefcaSMatthew Dillon /*
12780a9eefcaSMatthew Dillon * Remove state from its parent's subq. This can wind up recursively
12790a9eefcaSMatthew Dillon * dropping the parent upward.
12800a9eefcaSMatthew Dillon *
12810a9eefcaSMatthew Dillon * NOTE: Once we drop the parent, our pstate pointer may become invalid.
12820a9eefcaSMatthew Dillon */
12830a9eefcaSMatthew Dillon static
12840a9eefcaSMatthew Dillon void
kdmsg_subq_delete(kdmsg_state_t * state)12850a9eefcaSMatthew Dillon kdmsg_subq_delete(kdmsg_state_t *state)
12860a9eefcaSMatthew Dillon {
12871b8eded1SMatthew Dillon kdmsg_state_t *pstate;
12883a5aa68fSMatthew Dillon
1289a06d536bSMatthew Dillon if (state->flags & KDMSG_STATE_SUBINSERTED) {
12900a9eefcaSMatthew Dillon pstate = state->parent;
12910a9eefcaSMatthew Dillon KKASSERT(pstate);
12920a9eefcaSMatthew Dillon if (pstate->scan == state)
12930a9eefcaSMatthew Dillon pstate->scan = NULL;
12941b8eded1SMatthew Dillon TAILQ_REMOVE(&pstate->subq, state, entry);
1295a06d536bSMatthew Dillon state->flags &= ~KDMSG_STATE_SUBINSERTED;
12961b8eded1SMatthew Dillon state->parent = NULL;
12970a9eefcaSMatthew Dillon if (TAILQ_EMPTY(&pstate->subq)) {
12980a9eefcaSMatthew Dillon kdmsg_state_drop(pstate);/* pstate->subq */
12990a9eefcaSMatthew Dillon }
13000a9eefcaSMatthew Dillon pstate = NULL; /* safety */
13010a9eefcaSMatthew Dillon kdmsg_state_drop(state); /* pstate->subq */
1302a06d536bSMatthew Dillon } else {
1303a06d536bSMatthew Dillon KKASSERT(state->parent == NULL);
1304a06d536bSMatthew Dillon }
13053a5aa68fSMatthew Dillon }
13063a5aa68fSMatthew Dillon
13073a5aa68fSMatthew Dillon /*
13081f4b0713SMatthew Dillon * Simulate receiving a message which terminates an active transaction
13098d6d37b8SMatthew Dillon * state. Our simulated received message must set DELETE and may also
13108d6d37b8SMatthew Dillon * have to set CREATE. It must also ensure that all fields are set such
13118d6d37b8SMatthew Dillon * that the receive handling code can find the state (kdmsg_state_msgrx())
13128d6d37b8SMatthew Dillon * or an endless loop will ensue.
13138d6d37b8SMatthew Dillon *
13141b8eded1SMatthew Dillon * This is used when the other end of the link is dead so the device driver
13151b8eded1SMatthew Dillon * gets a completed transaction for all pending states.
1316a06d536bSMatthew Dillon *
1317a06d536bSMatthew Dillon * Called with iocom locked.
13188d6d37b8SMatthew Dillon */
13198d6d37b8SMatthew Dillon static
13208d6d37b8SMatthew Dillon void
kdmsg_simulate_failure(kdmsg_state_t * state,int meto,int error)1321a06d536bSMatthew Dillon kdmsg_simulate_failure(kdmsg_state_t *state, int meto, int error)
1322a06d536bSMatthew Dillon {
1323a06d536bSMatthew Dillon kdmsg_state_t *substate;
1324a06d536bSMatthew Dillon
13250a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* aborting */
13260a9eefcaSMatthew Dillon
13270a9eefcaSMatthew Dillon /*
13280a9eefcaSMatthew Dillon * Abort parent state first. Parent will not actually disappear
13290a9eefcaSMatthew Dillon * until children are gone. Device drivers must handle the situation.
13300a9eefcaSMatthew Dillon * The advantage of this is that device drivers can flag the situation
13310a9eefcaSMatthew Dillon * as an interlock against new operations on dying states. And since
13320a9eefcaSMatthew Dillon * device operations are often asynchronous anyway, this sequence of
13330a9eefcaSMatthew Dillon * events works out better.
13340a9eefcaSMatthew Dillon */
1335a06d536bSMatthew Dillon if (meto)
1336a06d536bSMatthew Dillon kdmsg_state_abort(state);
13370a9eefcaSMatthew Dillon
13380a9eefcaSMatthew Dillon /*
13390a9eefcaSMatthew Dillon * Recurse through any children.
13400a9eefcaSMatthew Dillon */
13410a9eefcaSMatthew Dillon again:
13420a9eefcaSMatthew Dillon TAILQ_FOREACH(substate, &state->subq, entry) {
13430a9eefcaSMatthew Dillon if (substate->flags & KDMSG_STATE_ABORTING)
13440a9eefcaSMatthew Dillon continue;
13450a9eefcaSMatthew Dillon state->scan = substate;
13460a9eefcaSMatthew Dillon kdmsg_simulate_failure(substate, 1, error);
13470a9eefcaSMatthew Dillon if (state->scan != substate)
13480a9eefcaSMatthew Dillon goto again;
13490a9eefcaSMatthew Dillon }
1350a06d536bSMatthew Dillon kdmsg_state_drop(state); /* aborting */
1351a06d536bSMatthew Dillon }
1352a06d536bSMatthew Dillon
1353a06d536bSMatthew Dillon static
1354a06d536bSMatthew Dillon void
kdmsg_state_abort(kdmsg_state_t * state)13558d6d37b8SMatthew Dillon kdmsg_state_abort(kdmsg_state_t *state)
13568d6d37b8SMatthew Dillon {
13578d6d37b8SMatthew Dillon kdmsg_msg_t *msg;
13588d6d37b8SMatthew Dillon
13591f4b0713SMatthew Dillon /*
13600a9eefcaSMatthew Dillon * Set ABORTING and DYING, return if already set. If the state was
13610a9eefcaSMatthew Dillon * just allocated we defer the abort operation until the related
13620a9eefcaSMatthew Dillon * message is processed.
13631f4b0713SMatthew Dillon */
1364a06d536bSMatthew Dillon KKASSERT((state->flags & KDMSG_STATE_ABORTING) == 0);
13651f4b0713SMatthew Dillon if (state->flags & KDMSG_STATE_ABORTING)
13661f4b0713SMatthew Dillon return;
13671f4b0713SMatthew Dillon state->flags |= KDMSG_STATE_ABORTING;
13680a9eefcaSMatthew Dillon kdmsg_state_dying(state);
13690a9eefcaSMatthew Dillon if (state->flags & KDMSG_STATE_NEW) {
13705ab1caedSMatthew Dillon kdio_printf(iocom, 5,
13715ab1caedSMatthew Dillon "kdmsg_state_abort(0): state %p rxcmd %08x "
13725ab1caedSMatthew Dillon "txcmd %08x flags %08x - in NEW state\n",
13735ab1caedSMatthew Dillon state, state->rxcmd,
13745ab1caedSMatthew Dillon state->txcmd, state->flags);
13750a9eefcaSMatthew Dillon return;
13760a9eefcaSMatthew Dillon }
13771f4b0713SMatthew Dillon
13781f4b0713SMatthew Dillon /*
13790a9eefcaSMatthew Dillon * NOTE: The DELETE flag might already be set due to an early
13800a9eefcaSMatthew Dillon * termination.
13810a9eefcaSMatthew Dillon *
13821b8eded1SMatthew Dillon * NOTE: Args to kdmsg_msg_alloc() to avoid dynamic state allocation.
13831b8eded1SMatthew Dillon *
13841b8eded1SMatthew Dillon * NOTE: We are simulating a received message using our state
13851b8eded1SMatthew Dillon * (vs a message generated by the other side using its state),
13861b8eded1SMatthew Dillon * so we must invert DMSGF_REVTRANS and DMSGF_REVCIRC.
13871f4b0713SMatthew Dillon */
13885ab1caedSMatthew Dillon kdio_printf(iocom, 5,
13895ab1caedSMatthew Dillon "kdmsg_state_abort(1): state %p rxcmd %08x txcmd %08x\n",
13900a9eefcaSMatthew Dillon state, state->rxcmd, state->txcmd);
1391a06d536bSMatthew Dillon if ((state->rxcmd & DMSGF_DELETE) == 0) {
13921b8eded1SMatthew Dillon msg = kdmsg_msg_alloc(state, DMSG_LNK_ERROR, NULL, NULL);
13938d6d37b8SMatthew Dillon if ((state->rxcmd & DMSGF_CREATE) == 0)
13948d6d37b8SMatthew Dillon msg->any.head.cmd |= DMSGF_CREATE;
1395a06d536bSMatthew Dillon msg->any.head.cmd |= DMSGF_DELETE |
1396a06d536bSMatthew Dillon (state->rxcmd & DMSGF_REPLY);
13971b8eded1SMatthew Dillon msg->any.head.cmd ^= (DMSGF_REVTRANS | DMSGF_REVCIRC);
13988d6d37b8SMatthew Dillon msg->any.head.error = DMSG_ERR_LOSTLINK;
13995ab1caedSMatthew Dillon kdio_printf(iocom, 5,
14005ab1caedSMatthew Dillon "kdmsg_state_abort(a): state %p msgcmd %08x\n",
14010a9eefcaSMatthew Dillon state, msg->any.head.cmd);
14020a9eefcaSMatthew Dillon /* circuit not initialized */
1403a06d536bSMatthew Dillon lockmgr(&state->iocom->msglk, LK_RELEASE);
14048d6d37b8SMatthew Dillon kdmsg_msg_receive_handling(msg);
1405a06d536bSMatthew Dillon lockmgr(&state->iocom->msglk, LK_EXCLUSIVE);
1406a06d536bSMatthew Dillon msg = NULL;
1407a06d536bSMatthew Dillon }
14085ab1caedSMatthew Dillon kdio_printf(iocom, 5,
14095ab1caedSMatthew Dillon "kdmsg_state_abort(2): state %p rxcmd %08x txcmd %08x\n",
14100a9eefcaSMatthew Dillon state, state->rxcmd, state->txcmd);
14110a9eefcaSMatthew Dillon }
1412a06d536bSMatthew Dillon
1413a06d536bSMatthew Dillon /*
14140a9eefcaSMatthew Dillon * Recursively sets KDMSG_STATE_DYING on state and all sub-states, preventing
14150a9eefcaSMatthew Dillon * the transmission of any new messages on these states. This is done
14160a9eefcaSMatthew Dillon * atomically when parent state is terminating, whereas setting ABORTING is
14170a9eefcaSMatthew Dillon * not atomic and can leak races.
1418a06d536bSMatthew Dillon */
14190a9eefcaSMatthew Dillon static
14200a9eefcaSMatthew Dillon void
kdmsg_state_dying(kdmsg_state_t * state)14210a9eefcaSMatthew Dillon kdmsg_state_dying(kdmsg_state_t *state)
14220a9eefcaSMatthew Dillon {
14230a9eefcaSMatthew Dillon kdmsg_state_t *scan;
14240a9eefcaSMatthew Dillon
14250a9eefcaSMatthew Dillon if ((state->flags & KDMSG_STATE_DYING) == 0) {
14260a9eefcaSMatthew Dillon state->flags |= KDMSG_STATE_DYING;
14270a9eefcaSMatthew Dillon TAILQ_FOREACH(scan, &state->subq, entry)
14280a9eefcaSMatthew Dillon kdmsg_state_dying(scan);
1429a06d536bSMatthew Dillon }
14308d6d37b8SMatthew Dillon }
14318d6d37b8SMatthew Dillon
14328d6d37b8SMatthew Dillon /*
14333a5aa68fSMatthew Dillon * Process state tracking for a message prior to transmission.
14343a5aa68fSMatthew Dillon *
14358d6d37b8SMatthew Dillon * Called with msglk held and the msg dequeued. Returns non-zero if
14368d6d37b8SMatthew Dillon * the message is bad and should be deleted by the caller.
14373a5aa68fSMatthew Dillon *
14383a5aa68fSMatthew Dillon * One-off messages are usually with dummy state and msg->state may be NULL
14393a5aa68fSMatthew Dillon * in this situation.
14403a5aa68fSMatthew Dillon *
14413a5aa68fSMatthew Dillon * New transactions (when CREATE is set) will insert the state.
14423a5aa68fSMatthew Dillon *
14433a5aa68fSMatthew Dillon * May request that caller discard the message by setting *discardp to 1.
14443a5aa68fSMatthew Dillon * A NULL state may be returned in this case.
14453a5aa68fSMatthew Dillon */
14468d6d37b8SMatthew Dillon static
14473a5aa68fSMatthew Dillon int
kdmsg_state_msgtx(kdmsg_msg_t * msg)14483a5aa68fSMatthew Dillon kdmsg_state_msgtx(kdmsg_msg_t *msg)
14493a5aa68fSMatthew Dillon {
14501b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
14513a5aa68fSMatthew Dillon kdmsg_state_t *state;
14523a5aa68fSMatthew Dillon int error;
14533a5aa68fSMatthew Dillon
14543a5aa68fSMatthew Dillon /*
14553a5aa68fSMatthew Dillon * Make sure a state structure is ready to go in case we need a new
14563a5aa68fSMatthew Dillon * one. This is the only routine which uses freewr_state so no
14573a5aa68fSMatthew Dillon * races are possible.
14583a5aa68fSMatthew Dillon */
14593a5aa68fSMatthew Dillon if ((state = iocom->freewr_state) == NULL) {
14603a5aa68fSMatthew Dillon state = kmalloc(sizeof(*state), iocom->mmsg, M_WAITOK | M_ZERO);
14613a5aa68fSMatthew Dillon state->flags = KDMSG_STATE_DYNAMIC;
146203d99ea4SMatthew Dillon state->iocom = iocom;
1463a06d536bSMatthew Dillon state->refs = 1;
1464a06d536bSMatthew Dillon TAILQ_INIT(&state->subq);
14653a5aa68fSMatthew Dillon iocom->freewr_state = state;
14663a5aa68fSMatthew Dillon }
14673a5aa68fSMatthew Dillon
14683a5aa68fSMatthew Dillon /*
14693a5aa68fSMatthew Dillon * Lock RB tree. If persistent state is present it will have already
14703a5aa68fSMatthew Dillon * been assigned to msg.
14713a5aa68fSMatthew Dillon */
14723a5aa68fSMatthew Dillon state = msg->state;
14733a5aa68fSMatthew Dillon
14743a5aa68fSMatthew Dillon /*
14753a5aa68fSMatthew Dillon * Short-cut one-off or mid-stream messages (state may be NULL).
14763a5aa68fSMatthew Dillon */
14773a5aa68fSMatthew Dillon if ((msg->any.head.cmd & (DMSGF_CREATE | DMSGF_DELETE |
14783a5aa68fSMatthew Dillon DMSGF_ABORT)) == 0) {
14793a5aa68fSMatthew Dillon return(0);
14803a5aa68fSMatthew Dillon }
14813a5aa68fSMatthew Dillon
14823a5aa68fSMatthew Dillon
14833a5aa68fSMatthew Dillon /*
14843a5aa68fSMatthew Dillon * Switch on CREATE, DELETE, REPLY, and also handle ABORT from
14853a5aa68fSMatthew Dillon * inside the case statements.
14863a5aa68fSMatthew Dillon */
14873a5aa68fSMatthew Dillon switch(msg->any.head.cmd & (DMSGF_CREATE | DMSGF_DELETE |
14883a5aa68fSMatthew Dillon DMSGF_REPLY)) {
14893a5aa68fSMatthew Dillon case DMSGF_CREATE:
14903a5aa68fSMatthew Dillon case DMSGF_CREATE | DMSGF_DELETE:
14913a5aa68fSMatthew Dillon /*
14923a5aa68fSMatthew Dillon * Insert the new persistent message state and mark
14933a5aa68fSMatthew Dillon * half-closed if DELETE is set. Since this is a new
14943a5aa68fSMatthew Dillon * message it isn't possible to transition into the fully
14953a5aa68fSMatthew Dillon * closed state here.
14963a5aa68fSMatthew Dillon *
14973a5aa68fSMatthew Dillon * XXX state must be assigned and inserted by
14983a5aa68fSMatthew Dillon * kdmsg_msg_write(). txcmd is assigned by us
14993a5aa68fSMatthew Dillon * on-transmit.
15003a5aa68fSMatthew Dillon */
15013a5aa68fSMatthew Dillon KKASSERT(state != NULL);
150203d99ea4SMatthew Dillon state->icmd = msg->any.head.cmd & DMSGF_BASECMDMASK;
15033a5aa68fSMatthew Dillon state->txcmd = msg->any.head.cmd & ~DMSGF_DELETE;
15043a5aa68fSMatthew Dillon state->rxcmd = DMSGF_REPLY;
15050a9eefcaSMatthew Dillon state->flags &= ~KDMSG_STATE_NEW;
15063a5aa68fSMatthew Dillon error = 0;
15073a5aa68fSMatthew Dillon break;
15083a5aa68fSMatthew Dillon case DMSGF_DELETE:
15093a5aa68fSMatthew Dillon /*
15103a5aa68fSMatthew Dillon * Sent ABORT+DELETE in case where msgid has already
15113a5aa68fSMatthew Dillon * been fully closed, ignore the message.
15123a5aa68fSMatthew Dillon */
15131b8eded1SMatthew Dillon if (state == &iocom->state0) {
15143a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
15153a5aa68fSMatthew Dillon error = EALREADY;
15163a5aa68fSMatthew Dillon } else {
15175ab1caedSMatthew Dillon kdio_printf(iocom, 1,
15185ab1caedSMatthew Dillon "msgtx: no state match "
15193a5aa68fSMatthew Dillon "for DELETE cmd=%08x msgid=%016jx\n",
15203a5aa68fSMatthew Dillon msg->any.head.cmd,
15213a5aa68fSMatthew Dillon (intmax_t)msg->any.head.msgid);
15223a5aa68fSMatthew Dillon error = EINVAL;
15233a5aa68fSMatthew Dillon }
15243a5aa68fSMatthew Dillon break;
15253a5aa68fSMatthew Dillon }
15263a5aa68fSMatthew Dillon
15273a5aa68fSMatthew Dillon /*
15283a5aa68fSMatthew Dillon * Sent ABORT+DELETE in case where msgid has
15293a5aa68fSMatthew Dillon * already been reused for an unrelated message,
15303a5aa68fSMatthew Dillon * ignore the message.
15313a5aa68fSMatthew Dillon */
15323a5aa68fSMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0) {
15333a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
15343a5aa68fSMatthew Dillon error = EALREADY;
15353a5aa68fSMatthew Dillon } else {
15365ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
15375ab1caedSMatthew Dillon "msgtx: state reused "
15385ab1caedSMatthew Dillon "for DELETE");
15393a5aa68fSMatthew Dillon error = EINVAL;
15403a5aa68fSMatthew Dillon }
15413a5aa68fSMatthew Dillon break;
15423a5aa68fSMatthew Dillon }
15433a5aa68fSMatthew Dillon error = 0;
15443a5aa68fSMatthew Dillon break;
15453a5aa68fSMatthew Dillon default:
15463a5aa68fSMatthew Dillon /*
15473a5aa68fSMatthew Dillon * Check for mid-stream ABORT command sent
15483a5aa68fSMatthew Dillon */
15493a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
15501b8eded1SMatthew Dillon if (state == &state->iocom->state0 ||
15513a5aa68fSMatthew Dillon (state->txcmd & DMSGF_CREATE) == 0) {
15523a5aa68fSMatthew Dillon error = EALREADY;
15533a5aa68fSMatthew Dillon break;
15543a5aa68fSMatthew Dillon }
15553a5aa68fSMatthew Dillon }
15563a5aa68fSMatthew Dillon error = 0;
15573a5aa68fSMatthew Dillon break;
15583a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_CREATE:
15593a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_CREATE | DMSGF_DELETE:
15603a5aa68fSMatthew Dillon /*
15613a5aa68fSMatthew Dillon * When transmitting a reply with CREATE set the original
15623a5aa68fSMatthew Dillon * persistent state message should already exist.
15633a5aa68fSMatthew Dillon */
15641b8eded1SMatthew Dillon if (state == &state->iocom->state0) {
15655ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
15665ab1caedSMatthew Dillon "msgtx: no state match "
15675ab1caedSMatthew Dillon "for REPLY | CREATE");
15683a5aa68fSMatthew Dillon error = EINVAL;
15693a5aa68fSMatthew Dillon break;
15703a5aa68fSMatthew Dillon }
15713a5aa68fSMatthew Dillon state->txcmd = msg->any.head.cmd & ~DMSGF_DELETE;
15723a5aa68fSMatthew Dillon error = 0;
15733a5aa68fSMatthew Dillon break;
15743a5aa68fSMatthew Dillon case DMSGF_REPLY | DMSGF_DELETE:
15753a5aa68fSMatthew Dillon /*
15763a5aa68fSMatthew Dillon * When transmitting a reply with DELETE set the original
15773a5aa68fSMatthew Dillon * persistent state message should already exist.
15783a5aa68fSMatthew Dillon *
15793a5aa68fSMatthew Dillon * This is very similar to the REPLY|CREATE|* case except
15803a5aa68fSMatthew Dillon * txcmd is already stored, so we just add the DELETE flag.
15813a5aa68fSMatthew Dillon *
15823a5aa68fSMatthew Dillon * Sent REPLY+ABORT+DELETE in case where msgid has
15833a5aa68fSMatthew Dillon * already been fully closed, ignore the message.
15843a5aa68fSMatthew Dillon */
15851b8eded1SMatthew Dillon if (state == &state->iocom->state0) {
15863a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
15873a5aa68fSMatthew Dillon error = EALREADY;
15883a5aa68fSMatthew Dillon } else {
15895ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
15905ab1caedSMatthew Dillon "msgtx: no state match "
15915ab1caedSMatthew Dillon "for REPLY | DELETE");
15923a5aa68fSMatthew Dillon error = EINVAL;
15933a5aa68fSMatthew Dillon }
15943a5aa68fSMatthew Dillon break;
15953a5aa68fSMatthew Dillon }
15963a5aa68fSMatthew Dillon
15973a5aa68fSMatthew Dillon /*
15983a5aa68fSMatthew Dillon * Sent REPLY+ABORT+DELETE in case where msgid has already
15993a5aa68fSMatthew Dillon * been reused for an unrelated message, ignore the message.
16003a5aa68fSMatthew Dillon */
16013a5aa68fSMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0) {
16023a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
16033a5aa68fSMatthew Dillon error = EALREADY;
16043a5aa68fSMatthew Dillon } else {
16055ab1caedSMatthew Dillon kdio_printf(iocom, 1, "%s\n",
16065ab1caedSMatthew Dillon "msgtx: state reused "
16075ab1caedSMatthew Dillon "for REPLY | DELETE");
16083a5aa68fSMatthew Dillon error = EINVAL;
16093a5aa68fSMatthew Dillon }
16103a5aa68fSMatthew Dillon break;
16113a5aa68fSMatthew Dillon }
16123a5aa68fSMatthew Dillon error = 0;
16133a5aa68fSMatthew Dillon break;
16143a5aa68fSMatthew Dillon case DMSGF_REPLY:
16153a5aa68fSMatthew Dillon /*
16163a5aa68fSMatthew Dillon * Check for mid-stream ABORT reply sent.
16173a5aa68fSMatthew Dillon *
16183a5aa68fSMatthew Dillon * One-off REPLY messages are allowed for e.g. status updates.
16193a5aa68fSMatthew Dillon */
16203a5aa68fSMatthew Dillon if (msg->any.head.cmd & DMSGF_ABORT) {
16211b8eded1SMatthew Dillon if (state == &state->iocom->state0 ||
16223a5aa68fSMatthew Dillon (state->txcmd & DMSGF_CREATE) == 0) {
16233a5aa68fSMatthew Dillon error = EALREADY;
16243a5aa68fSMatthew Dillon break;
16253a5aa68fSMatthew Dillon }
16263a5aa68fSMatthew Dillon }
16273a5aa68fSMatthew Dillon error = 0;
16283a5aa68fSMatthew Dillon break;
16293a5aa68fSMatthew Dillon }
163045c1a24bSMatthew Dillon
163145c1a24bSMatthew Dillon /*
163245c1a24bSMatthew Dillon * Set interlock (XXX hack) in case the send side blocks and a
163345c1a24bSMatthew Dillon * response is returned before kdmsg_state_cleanuptx() can be
163445c1a24bSMatthew Dillon * run.
163545c1a24bSMatthew Dillon */
163645c1a24bSMatthew Dillon if (state && error == 0)
163745c1a24bSMatthew Dillon state->flags |= KDMSG_STATE_INTERLOCK;
163845c1a24bSMatthew Dillon
16393a5aa68fSMatthew Dillon return (error);
16403a5aa68fSMatthew Dillon }
16413a5aa68fSMatthew Dillon
1642a06d536bSMatthew Dillon /*
1643a06d536bSMatthew Dillon * Called with iocom locked.
1644a06d536bSMatthew Dillon */
16458d6d37b8SMatthew Dillon static
16463a5aa68fSMatthew Dillon void
kdmsg_state_cleanuptx(kdmsg_msg_t * msg)16473a5aa68fSMatthew Dillon kdmsg_state_cleanuptx(kdmsg_msg_t *msg)
16483a5aa68fSMatthew Dillon {
16491b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
16503a5aa68fSMatthew Dillon kdmsg_state_t *state;
16513a5aa68fSMatthew Dillon
16523a5aa68fSMatthew Dillon if ((state = msg->state) == NULL) {
16533a5aa68fSMatthew Dillon kdmsg_msg_free(msg);
165445c1a24bSMatthew Dillon return;
165545c1a24bSMatthew Dillon }
165645c1a24bSMatthew Dillon
165745c1a24bSMatthew Dillon /*
165845c1a24bSMatthew Dillon * Clear interlock (XXX hack) in case the send side blocks and a
165945c1a24bSMatthew Dillon * response is returned in the other thread before
166045c1a24bSMatthew Dillon * kdmsg_state_cleanuptx() can be run. We maintain our hold on
166145c1a24bSMatthew Dillon * iocom->msglk so we can do this before completing our task.
166245c1a24bSMatthew Dillon */
166345c1a24bSMatthew Dillon if (state->flags & KDMSG_STATE_SIGNAL) {
16645ab1caedSMatthew Dillon kdio_printf(iocom, 1, "state %p interlock!\n", state);
166545c1a24bSMatthew Dillon wakeup(state);
166645c1a24bSMatthew Dillon }
166745c1a24bSMatthew Dillon state->flags &= ~(KDMSG_STATE_INTERLOCK | KDMSG_STATE_SIGNAL);
16680a9eefcaSMatthew Dillon kdmsg_state_hold(state);
166945c1a24bSMatthew Dillon
167045c1a24bSMatthew Dillon if (msg->any.head.cmd & DMSGF_DELETE) {
16711f4b0713SMatthew Dillon KKASSERT((state->txcmd & DMSGF_DELETE) == 0);
16723a5aa68fSMatthew Dillon state->txcmd |= DMSGF_DELETE;
16733a5aa68fSMatthew Dillon if (state->rxcmd & DMSGF_DELETE) {
1674a06d536bSMatthew Dillon KKASSERT(state->flags & KDMSG_STATE_RBINSERTED);
16753a5aa68fSMatthew Dillon if (state->txcmd & DMSGF_REPLY) {
16763a5aa68fSMatthew Dillon KKASSERT(msg->any.head.cmd &
16773a5aa68fSMatthew Dillon DMSGF_REPLY);
16783a5aa68fSMatthew Dillon RB_REMOVE(kdmsg_state_tree,
16793a5aa68fSMatthew Dillon &iocom->staterd_tree, state);
16803a5aa68fSMatthew Dillon } else {
16813a5aa68fSMatthew Dillon KKASSERT((msg->any.head.cmd &
16823a5aa68fSMatthew Dillon DMSGF_REPLY) == 0);
16833a5aa68fSMatthew Dillon RB_REMOVE(kdmsg_state_tree,
16843a5aa68fSMatthew Dillon &iocom->statewr_tree, state);
16853a5aa68fSMatthew Dillon }
1686a06d536bSMatthew Dillon state->flags &= ~KDMSG_STATE_RBINSERTED;
16870a9eefcaSMatthew Dillon
16880a9eefcaSMatthew Dillon /*
16890a9eefcaSMatthew Dillon * The subq recursion is used for parent linking and
16900a9eefcaSMatthew Dillon * scanning the topology for aborts, we can only
16910a9eefcaSMatthew Dillon * remove leafs. The circuit is effectively dead now,
16920a9eefcaSMatthew Dillon * but topology won't be torn down until all of its
16930a9eefcaSMatthew Dillon * children have finished/aborted.
16940a9eefcaSMatthew Dillon *
16950a9eefcaSMatthew Dillon * This is particularly important for end-point
16960a9eefcaSMatthew Dillon * devices which might need to access private data
16970a9eefcaSMatthew Dillon * in parent states. Out of order disconnects can
16980a9eefcaSMatthew Dillon * occur if an end-point device is processing a
16990a9eefcaSMatthew Dillon * message transaction asynchronously because abort
17000a9eefcaSMatthew Dillon * requests are basically synchronous and it probably
17010a9eefcaSMatthew Dillon * isn't convenient (or possible) for the end-point
17020a9eefcaSMatthew Dillon * to abort an asynchronous operation.
17030a9eefcaSMatthew Dillon */
17040a9eefcaSMatthew Dillon if (TAILQ_EMPTY(&state->subq))
17050a9eefcaSMatthew Dillon kdmsg_subq_delete(state);
17068d6d37b8SMatthew Dillon kdmsg_msg_free(msg);
1707a06d536bSMatthew Dillon kdmsg_state_drop(state); /* state on rbtree */
1708a06d536bSMatthew Dillon } else {
1709a06d536bSMatthew Dillon kdmsg_msg_free(msg);
1710a06d536bSMatthew Dillon }
1711a06d536bSMatthew Dillon } else {
1712a06d536bSMatthew Dillon kdmsg_msg_free(msg);
1713a06d536bSMatthew Dillon }
17140a9eefcaSMatthew Dillon
17150a9eefcaSMatthew Dillon /*
17160a9eefcaSMatthew Dillon * Deferred abort after transmission.
17170a9eefcaSMatthew Dillon */
17180a9eefcaSMatthew Dillon if ((state->flags & (KDMSG_STATE_ABORTING | KDMSG_STATE_DYING)) &&
17190a9eefcaSMatthew Dillon (state->rxcmd & DMSGF_DELETE) == 0) {
17205ab1caedSMatthew Dillon kdio_printf(iocom, 5,
17215ab1caedSMatthew Dillon "kdmsg_state_cleanuptx: state=%p "
17220a9eefcaSMatthew Dillon "executing deferred abort\n",
17230a9eefcaSMatthew Dillon state);
17240a9eefcaSMatthew Dillon state->flags &= ~KDMSG_STATE_ABORTING;
17250a9eefcaSMatthew Dillon kdmsg_state_abort(state);
17260a9eefcaSMatthew Dillon }
17270a9eefcaSMatthew Dillon kdmsg_state_drop(state);
1728a06d536bSMatthew Dillon }
1729a06d536bSMatthew Dillon
1730a06d536bSMatthew Dillon static
1731a06d536bSMatthew Dillon void
_kdmsg_state_hold(kdmsg_state_t * state KDMSG_DEBUG_ARGS)17320a9eefcaSMatthew Dillon _kdmsg_state_hold(kdmsg_state_t *state KDMSG_DEBUG_ARGS)
1733a06d536bSMatthew Dillon {
1734a06d536bSMatthew Dillon atomic_add_int(&state->refs, 1);
1735a06d536bSMatthew Dillon #if KDMSG_DEBUG
17365ab1caedSMatthew Dillon kd_printf(4, "state %p +%d\t%s:%d\n", state, state->refs, file, line);
1737a06d536bSMatthew Dillon #endif
1738a06d536bSMatthew Dillon }
1739a06d536bSMatthew Dillon
1740a06d536bSMatthew Dillon static
1741a06d536bSMatthew Dillon void
_kdmsg_state_drop(kdmsg_state_t * state KDMSG_DEBUG_ARGS)1742a06d536bSMatthew Dillon _kdmsg_state_drop(kdmsg_state_t *state KDMSG_DEBUG_ARGS)
1743a06d536bSMatthew Dillon {
1744a06d536bSMatthew Dillon KKASSERT(state->refs > 0);
1745a06d536bSMatthew Dillon #if KDMSG_DEBUG
17465ab1caedSMatthew Dillon kd_printf(4, "state %p -%d\t%s:%d\n", state, state->refs, file, line);
1747a06d536bSMatthew Dillon #endif
1748a06d536bSMatthew Dillon if (atomic_fetchadd_int(&state->refs, -1) == 1)
17493a5aa68fSMatthew Dillon kdmsg_state_free(state);
17503a5aa68fSMatthew Dillon }
17513a5aa68fSMatthew Dillon
17528d6d37b8SMatthew Dillon static
17533a5aa68fSMatthew Dillon void
kdmsg_state_free(kdmsg_state_t * state)17543a5aa68fSMatthew Dillon kdmsg_state_free(kdmsg_state_t *state)
17553a5aa68fSMatthew Dillon {
175603d99ea4SMatthew Dillon kdmsg_iocom_t *iocom = state->iocom;
17573a5aa68fSMatthew Dillon
1758a06d536bSMatthew Dillon KKASSERT((state->flags & KDMSG_STATE_RBINSERTED) == 0);
1759a06d536bSMatthew Dillon KKASSERT((state->flags & KDMSG_STATE_SUBINSERTED) == 0);
1760a06d536bSMatthew Dillon KKASSERT(TAILQ_EMPTY(&state->subq));
1761a06d536bSMatthew Dillon
1762a06d536bSMatthew Dillon if (state != &state->iocom->state0)
17633a5aa68fSMatthew Dillon kfree(state, iocom->mmsg);
17648d6d37b8SMatthew Dillon }
17653a5aa68fSMatthew Dillon
17663a5aa68fSMatthew Dillon kdmsg_msg_t *
kdmsg_msg_alloc(kdmsg_state_t * state,uint32_t cmd,int (* func)(kdmsg_state_t *,kdmsg_msg_t *),void * data)17671b8eded1SMatthew Dillon kdmsg_msg_alloc(kdmsg_state_t *state, uint32_t cmd,
17683a5aa68fSMatthew Dillon int (*func)(kdmsg_state_t *, kdmsg_msg_t *), void *data)
17693a5aa68fSMatthew Dillon {
17701b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = state->iocom;
17711b8eded1SMatthew Dillon kdmsg_state_t *pstate;
17723a5aa68fSMatthew Dillon kdmsg_msg_t *msg;
17733a5aa68fSMatthew Dillon size_t hbytes;
17743a5aa68fSMatthew Dillon
177503d99ea4SMatthew Dillon KKASSERT(iocom != NULL);
17763a5aa68fSMatthew Dillon hbytes = (cmd & DMSGF_SIZE) * DMSG_ALIGN;
17773a5aa68fSMatthew Dillon msg = kmalloc(offsetof(struct kdmsg_msg, any) + hbytes,
17783a5aa68fSMatthew Dillon iocom->mmsg, M_WAITOK | M_ZERO);
17793a5aa68fSMatthew Dillon msg->hdr_size = hbytes;
17803a5aa68fSMatthew Dillon
17811b8eded1SMatthew Dillon if ((cmd & (DMSGF_CREATE | DMSGF_REPLY)) == DMSGF_CREATE) {
17823a5aa68fSMatthew Dillon /*
17833a5aa68fSMatthew Dillon * New transaction, requires tracking state and a unique
17843a5aa68fSMatthew Dillon * msgid to be allocated.
17850a9eefcaSMatthew Dillon *
17860a9eefcaSMatthew Dillon * It is possible to race a circuit failure, inherit the
17870a9eefcaSMatthew Dillon * parent's STATE_DYING flag to trigger an abort sequence
17880a9eefcaSMatthew Dillon * in the transmit path. By not inheriting ABORTING the
17890a9eefcaSMatthew Dillon * abort sequence can recurse.
17900a9eefcaSMatthew Dillon *
17910a9eefcaSMatthew Dillon * NOTE: The transactions has not yet been initiated so we
17920a9eefcaSMatthew Dillon * cannot set DMSGF_CREATE/DELETE bits in txcmd or rxcmd.
17930a9eefcaSMatthew Dillon * We have to properly setup DMSGF_REPLY, however.
17943a5aa68fSMatthew Dillon */
17951b8eded1SMatthew Dillon pstate = state;
17963a5aa68fSMatthew Dillon state = kmalloc(sizeof(*state), iocom->mmsg, M_WAITOK | M_ZERO);
17971b8eded1SMatthew Dillon TAILQ_INIT(&state->subq);
17981b8eded1SMatthew Dillon state->iocom = iocom;
17991b8eded1SMatthew Dillon state->parent = pstate;
18000a9eefcaSMatthew Dillon state->flags = KDMSG_STATE_DYNAMIC |
18010a9eefcaSMatthew Dillon KDMSG_STATE_NEW;
18023a5aa68fSMatthew Dillon state->func = func;
18033a5aa68fSMatthew Dillon state->any.any = data;
18043a5aa68fSMatthew Dillon state->msgid = (uint64_t)(uintptr_t)state;
180503d99ea4SMatthew Dillon /*msg->any.head.msgid = state->msgid;XXX*/
18063a5aa68fSMatthew Dillon
18073a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
18083a5aa68fSMatthew Dillon if (RB_INSERT(kdmsg_state_tree, &iocom->statewr_tree, state))
18093a5aa68fSMatthew Dillon panic("duplicate msgid allocated");
18100a9eefcaSMatthew Dillon if (TAILQ_EMPTY(&pstate->subq))
18110a9eefcaSMatthew Dillon kdmsg_state_hold(pstate);/* pstate->subq */
18121b8eded1SMatthew Dillon TAILQ_INSERT_TAIL(&pstate->subq, state, entry);
1813a06d536bSMatthew Dillon state->flags |= KDMSG_STATE_RBINSERTED |
1814a06d536bSMatthew Dillon KDMSG_STATE_SUBINSERTED;
18150a9eefcaSMatthew Dillon state->flags |= pstate->flags & KDMSG_STATE_DYING;
18160a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* pstate->subq */
18170a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* state on rbtree */
18180a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* msg->state */
18193a5aa68fSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
18201b8eded1SMatthew Dillon } else {
18211b8eded1SMatthew Dillon pstate = state->parent;
1822a06d536bSMatthew Dillon KKASSERT(pstate != NULL);
18230a9eefcaSMatthew Dillon kdmsg_state_hold(state); /* msg->state */
18248d6d37b8SMatthew Dillon }
18253a5aa68fSMatthew Dillon
18261b8eded1SMatthew Dillon if (state->flags & KDMSG_STATE_OPPOSITE)
18271b8eded1SMatthew Dillon cmd |= DMSGF_REVTRANS;
18281b8eded1SMatthew Dillon if (pstate->flags & KDMSG_STATE_OPPOSITE)
18291b8eded1SMatthew Dillon cmd |= DMSGF_REVCIRC;
18308d6d37b8SMatthew Dillon
18318d6d37b8SMatthew Dillon msg->any.head.magic = DMSG_HDR_MAGIC;
18328d6d37b8SMatthew Dillon msg->any.head.cmd = cmd;
18331b8eded1SMatthew Dillon msg->any.head.msgid = state->msgid;
18341b8eded1SMatthew Dillon msg->any.head.circuit = pstate->msgid;
18358d6d37b8SMatthew Dillon msg->state = state;
18361b8eded1SMatthew Dillon
18373a5aa68fSMatthew Dillon return (msg);
18383a5aa68fSMatthew Dillon }
18393a5aa68fSMatthew Dillon
18403a5aa68fSMatthew Dillon void
kdmsg_msg_free(kdmsg_msg_t * msg)18413a5aa68fSMatthew Dillon kdmsg_msg_free(kdmsg_msg_t *msg)
18423a5aa68fSMatthew Dillon {
18431b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
1844a06d536bSMatthew Dillon kdmsg_state_t *state;
18453a5aa68fSMatthew Dillon
184603d99ea4SMatthew Dillon if ((msg->flags & KDMSG_FLAG_AUXALLOC) &&
184703d99ea4SMatthew Dillon msg->aux_data && msg->aux_size) {
18483a5aa68fSMatthew Dillon kfree(msg->aux_data, iocom->mmsg);
184993c84330SMatthew Dillon msg->aux_data = NULL;
185003d99ea4SMatthew Dillon msg->flags &= ~KDMSG_FLAG_AUXALLOC;
185103d99ea4SMatthew Dillon }
1852a06d536bSMatthew Dillon if ((state = msg->state) != NULL) {
18538d6d37b8SMatthew Dillon msg->state = NULL;
1854a06d536bSMatthew Dillon kdmsg_state_drop(state); /* msg->state */
1855a06d536bSMatthew Dillon }
18563a5aa68fSMatthew Dillon msg->aux_data = NULL;
18573a5aa68fSMatthew Dillon msg->aux_size = 0;
18583a5aa68fSMatthew Dillon
18591b8eded1SMatthew Dillon kfree(msg, iocom->mmsg);
18608d6d37b8SMatthew Dillon }
18618d6d37b8SMatthew Dillon
18621a2a529dSMatthew Dillon void
kdmsg_detach_aux_data(kdmsg_msg_t * msg,kdmsg_data_t * data)18631a2a529dSMatthew Dillon kdmsg_detach_aux_data(kdmsg_msg_t *msg, kdmsg_data_t *data)
18641a2a529dSMatthew Dillon {
18651a2a529dSMatthew Dillon if (msg->flags & KDMSG_FLAG_AUXALLOC) {
18661a2a529dSMatthew Dillon data->aux_data = msg->aux_data;
18671a2a529dSMatthew Dillon data->aux_size = msg->aux_size;
18681a2a529dSMatthew Dillon data->iocom = msg->state->iocom;
18691a2a529dSMatthew Dillon msg->flags &= ~KDMSG_FLAG_AUXALLOC;
18701a2a529dSMatthew Dillon } else {
18711a2a529dSMatthew Dillon data->aux_data = NULL;
18721a2a529dSMatthew Dillon data->aux_size = 0;
18731a2a529dSMatthew Dillon data->iocom = msg->state->iocom;
18741a2a529dSMatthew Dillon }
18751a2a529dSMatthew Dillon }
18761a2a529dSMatthew Dillon
18771a2a529dSMatthew Dillon void
kdmsg_free_aux_data(kdmsg_data_t * data)18781a2a529dSMatthew Dillon kdmsg_free_aux_data(kdmsg_data_t *data)
18791a2a529dSMatthew Dillon {
188093c84330SMatthew Dillon if (data->aux_data) {
18811a2a529dSMatthew Dillon kfree(data->aux_data, data->iocom->mmsg);
188293c84330SMatthew Dillon data->aux_data = NULL;
188393c84330SMatthew Dillon }
18841a2a529dSMatthew Dillon }
18851a2a529dSMatthew Dillon
18868d6d37b8SMatthew Dillon /*
18873a5aa68fSMatthew Dillon * Indexed messages are stored in a red-black tree indexed by their
18883a5aa68fSMatthew Dillon * msgid. Only persistent messages are indexed.
18893a5aa68fSMatthew Dillon */
18903a5aa68fSMatthew Dillon int
kdmsg_state_cmp(kdmsg_state_t * state1,kdmsg_state_t * state2)18913a5aa68fSMatthew Dillon kdmsg_state_cmp(kdmsg_state_t *state1, kdmsg_state_t *state2)
18923a5aa68fSMatthew Dillon {
189303d99ea4SMatthew Dillon if (state1->iocom < state2->iocom)
18943a5aa68fSMatthew Dillon return(-1);
189503d99ea4SMatthew Dillon if (state1->iocom > state2->iocom)
189603d99ea4SMatthew Dillon return(1);
18973a5aa68fSMatthew Dillon if (state1->msgid < state2->msgid)
18983a5aa68fSMatthew Dillon return(-1);
18993a5aa68fSMatthew Dillon if (state1->msgid > state2->msgid)
19003a5aa68fSMatthew Dillon return(1);
19013a5aa68fSMatthew Dillon return(0);
19023a5aa68fSMatthew Dillon }
19033a5aa68fSMatthew Dillon
19043a5aa68fSMatthew Dillon /*
19053a5aa68fSMatthew Dillon * Write a message. All requisit command flags have been set.
19063a5aa68fSMatthew Dillon *
19073a5aa68fSMatthew Dillon * If msg->state is non-NULL the message is written to the existing
19083a5aa68fSMatthew Dillon * transaction. msgid will be set accordingly.
19093a5aa68fSMatthew Dillon *
19103a5aa68fSMatthew Dillon * If msg->state is NULL and CREATE is set new state is allocated and
19113a5aa68fSMatthew Dillon * (func, data) is installed. A msgid is assigned.
19123a5aa68fSMatthew Dillon *
19133a5aa68fSMatthew Dillon * If msg->state is NULL and CREATE is not set the message is assumed
19143a5aa68fSMatthew Dillon * to be a one-way message. The originator must assign the msgid
19153a5aa68fSMatthew Dillon * (or leave it 0, which is typical.
19163a5aa68fSMatthew Dillon *
19173a5aa68fSMatthew Dillon * This function merely queues the message to the management thread, it
19183a5aa68fSMatthew Dillon * does not write to the message socket/pipe.
19193a5aa68fSMatthew Dillon */
19203a5aa68fSMatthew Dillon void
kdmsg_msg_write(kdmsg_msg_t * msg)19213a5aa68fSMatthew Dillon kdmsg_msg_write(kdmsg_msg_t *msg)
19223a5aa68fSMatthew Dillon {
19231b8eded1SMatthew Dillon kdmsg_iocom_t *iocom = msg->state->iocom;
19243a5aa68fSMatthew Dillon
19250a9eefcaSMatthew Dillon lockmgr(&iocom->msglk, LK_EXCLUSIVE);
19265ab1caedSMatthew Dillon kdmsg_msg_write_locked(iocom, msg);
19275ab1caedSMatthew Dillon lockmgr(&iocom->msglk, LK_RELEASE);
19285ab1caedSMatthew Dillon }
19295ab1caedSMatthew Dillon
19305ab1caedSMatthew Dillon static void
kdmsg_msg_write_locked(kdmsg_iocom_t * iocom,kdmsg_msg_t * msg)19315ab1caedSMatthew Dillon kdmsg_msg_write_locked(kdmsg_iocom_t *iocom, kdmsg_msg_t *msg)
19325ab1caedSMatthew Dillon {
19335ab1caedSMatthew Dillon kdmsg_state_t *state;
19340a9eefcaSMatthew Dillon
19353a5aa68fSMatthew Dillon if (msg->state) {
19363a5aa68fSMatthew Dillon /*
19373a5aa68fSMatthew Dillon * Continuance or termination of existing transaction.
19383a5aa68fSMatthew Dillon * The transaction could have been initiated by either end.
19393a5aa68fSMatthew Dillon *
19403a5aa68fSMatthew Dillon * (Function callback and aux data for the receive side can
19413a5aa68fSMatthew Dillon * be replaced or left alone).
19423a5aa68fSMatthew Dillon */
19433a5aa68fSMatthew Dillon state = msg->state;
19443a5aa68fSMatthew Dillon msg->any.head.msgid = state->msgid;
19453a5aa68fSMatthew Dillon } else {
19463a5aa68fSMatthew Dillon /*
19473a5aa68fSMatthew Dillon * One-off message (always uses msgid 0 to distinguish
19483a5aa68fSMatthew Dillon * between a possibly lost in-transaction message due to
19493a5aa68fSMatthew Dillon * competing aborts and a real one-off message?)
19503a5aa68fSMatthew Dillon */
19518d6d37b8SMatthew Dillon state = NULL;
19523a5aa68fSMatthew Dillon msg->any.head.msgid = 0;
19533a5aa68fSMatthew Dillon }
19543a5aa68fSMatthew Dillon
19558d6d37b8SMatthew Dillon /*
19560a9eefcaSMatthew Dillon * For stateful messages, if the circuit is dead or dying we have
19570a9eefcaSMatthew Dillon * to abort the potentially newly-created state and discard the
19580a9eefcaSMatthew Dillon * message.
1959a06d536bSMatthew Dillon *
1960a06d536bSMatthew Dillon * - We must discard the message because the other end will not
19610a9eefcaSMatthew Dillon * be expecting any more messages over the dead or dying circuit
19620a9eefcaSMatthew Dillon * and might not be able to receive them.
1963a06d536bSMatthew Dillon *
1964a06d536bSMatthew Dillon * - We abort the state by simulating a failure to generate a fake
1965a06d536bSMatthew Dillon * incoming DELETE. This will trigger the state callback and allow
1966a06d536bSMatthew Dillon * the device to clean things up and reply, closing the outgoing
19670a9eefcaSMatthew Dillon * direction and allowing the state to be freed.
1968a06d536bSMatthew Dillon *
19690a9eefcaSMatthew Dillon * This situation occurs quite often, particularly as SPANs stabilize.
1970a06d536bSMatthew Dillon * End-points must do the right thing.
1971a06d536bSMatthew Dillon */
1972a06d536bSMatthew Dillon if (state) {
1973a06d536bSMatthew Dillon KKASSERT((state->txcmd & DMSGF_DELETE) == 0);
19740a9eefcaSMatthew Dillon if (state->flags & KDMSG_STATE_DYING) {
19750a9eefcaSMatthew Dillon #if 0
19760a9eefcaSMatthew Dillon if ((state->flags & KDMSG_STATE_DYING) ||
19770a9eefcaSMatthew Dillon (state->parent->txcmd & DMSGF_DELETE) ||
19780a9eefcaSMatthew Dillon (state->parent->flags & KDMSG_STATE_DYING)) {
19790a9eefcaSMatthew Dillon #endif
19805ab1caedSMatthew Dillon kdio_printf(iocom, 4,
19815ab1caedSMatthew Dillon "kdmsg_msg_write: Write to dying circuit "
19820a9eefcaSMatthew Dillon "state=%p "
1983a06d536bSMatthew Dillon "ptxcmd=%08x prxcmd=%08x flags=%08x\n",
19840a9eefcaSMatthew Dillon state,
1985a06d536bSMatthew Dillon state->parent->rxcmd,
1986a06d536bSMatthew Dillon state->parent->txcmd,
1987a06d536bSMatthew Dillon state->parent->flags);
19880a9eefcaSMatthew Dillon kdmsg_state_hold(state);
1989a06d536bSMatthew Dillon kdmsg_state_msgtx(msg);
1990a06d536bSMatthew Dillon kdmsg_state_cleanuptx(msg);
1991a06d536bSMatthew Dillon kdmsg_state_drop(state);
1992a06d536bSMatthew Dillon return;
1993a06d536bSMatthew Dillon }
1994a06d536bSMatthew Dillon }
1995a06d536bSMatthew Dillon
1996a06d536bSMatthew Dillon /*
19978d6d37b8SMatthew Dillon * Finish up the msg fields. Note that msg->aux_size and the
19988d6d37b8SMatthew Dillon * aux_bytes stored in the message header represent the unaligned
19998d6d37b8SMatthew Dillon * (actual) bytes of data, but the buffer is sized to an aligned
20008d6d37b8SMatthew Dillon * size and the CRC is generated over the aligned length.
20013a5aa68fSMatthew Dillon */
20023a5aa68fSMatthew Dillon msg->any.head.salt = /* (random << 8) | */ (iocom->msg_seq & 255);
20033a5aa68fSMatthew Dillon ++iocom->msg_seq;
20043a5aa68fSMatthew Dillon
20058d6d37b8SMatthew Dillon if (msg->aux_data && msg->aux_size) {
20068d6d37b8SMatthew Dillon uint32_t abytes = DMSG_DOALIGN(msg->aux_size);
20078d6d37b8SMatthew Dillon
20088d6d37b8SMatthew Dillon msg->any.head.aux_bytes = msg->aux_size;
20098d6d37b8SMatthew Dillon msg->any.head.aux_crc = iscsi_crc32(msg->aux_data, abytes);
20108d6d37b8SMatthew Dillon }
20113a5aa68fSMatthew Dillon msg->any.head.hdr_crc = 0;
20123a5aa68fSMatthew Dillon msg->any.head.hdr_crc = iscsi_crc32(msg->any.buf, msg->hdr_size);
20133a5aa68fSMatthew Dillon
201493c84330SMatthew Dillon /*
201593c84330SMatthew Dillon * If termination races new message senders we must drain the
201693c84330SMatthew Dillon * message immediately instead of queue it.
201793c84330SMatthew Dillon */
201893c84330SMatthew Dillon if (iocom->flags & KDMSG_IOCOMF_EXITNOACC)
201993c84330SMatthew Dillon kdmsg_drain_msg(msg);
202093c84330SMatthew Dillon else
20213a5aa68fSMatthew Dillon TAILQ_INSERT_TAIL(&iocom->msgq, msg, qentry);
2022185ace93SMatthew Dillon
2023185ace93SMatthew Dillon if (iocom->msg_ctl & KDMSG_CLUSTERCTL_SLEEPING) {
2024185ace93SMatthew Dillon atomic_clear_int(&iocom->msg_ctl,
2025185ace93SMatthew Dillon KDMSG_CLUSTERCTL_SLEEPING);
2026185ace93SMatthew Dillon wakeup(&iocom->msg_ctl);
2027185ace93SMatthew Dillon }
20283a5aa68fSMatthew Dillon }
20293a5aa68fSMatthew Dillon
20303a5aa68fSMatthew Dillon /*
20313a5aa68fSMatthew Dillon * Reply to a message and terminate our side of the transaction.
20323a5aa68fSMatthew Dillon *
20333a5aa68fSMatthew Dillon * If msg->state is non-NULL we are replying to a one-way message.
20343a5aa68fSMatthew Dillon */
20353a5aa68fSMatthew Dillon void
20363a5aa68fSMatthew Dillon kdmsg_msg_reply(kdmsg_msg_t *msg, uint32_t error)
20373a5aa68fSMatthew Dillon {
20383a5aa68fSMatthew Dillon kdmsg_state_t *state = msg->state;
20393a5aa68fSMatthew Dillon kdmsg_msg_t *nmsg;
20403a5aa68fSMatthew Dillon uint32_t cmd;
20413a5aa68fSMatthew Dillon
20423a5aa68fSMatthew Dillon /*
20433a5aa68fSMatthew Dillon * Reply with a simple error code and terminate the transaction.
20443a5aa68fSMatthew Dillon */
20453a5aa68fSMatthew Dillon cmd = DMSG_LNK_ERROR;
20463a5aa68fSMatthew Dillon
20473a5aa68fSMatthew Dillon /*
20483a5aa68fSMatthew Dillon * Check if our direction has even been initiated yet, set CREATE.
20493a5aa68fSMatthew Dillon *
20503a5aa68fSMatthew Dillon * Check what direction this is (command or reply direction). Note
20513a5aa68fSMatthew Dillon * that txcmd might not have been initiated yet.
20523a5aa68fSMatthew Dillon *
20533a5aa68fSMatthew Dillon * If our direction has already been closed we just return without
20543a5aa68fSMatthew Dillon * doing anything.
20553a5aa68fSMatthew Dillon */
20561b8eded1SMatthew Dillon if (state != &state->iocom->state0) {
20573a5aa68fSMatthew Dillon if (state->txcmd & DMSGF_DELETE)
20583a5aa68fSMatthew Dillon return;
20593a5aa68fSMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0)
20603a5aa68fSMatthew Dillon cmd |= DMSGF_CREATE;
20613a5aa68fSMatthew Dillon if (state->txcmd & DMSGF_REPLY)
20623a5aa68fSMatthew Dillon cmd |= DMSGF_REPLY;
20633a5aa68fSMatthew Dillon cmd |= DMSGF_DELETE;
20643a5aa68fSMatthew Dillon } else {
20653a5aa68fSMatthew Dillon if ((msg->any.head.cmd & DMSGF_REPLY) == 0)
20663a5aa68fSMatthew Dillon cmd |= DMSGF_REPLY;
20673a5aa68fSMatthew Dillon }
20683a5aa68fSMatthew Dillon
20691b8eded1SMatthew Dillon nmsg = kdmsg_msg_alloc(state, cmd, NULL, NULL);
20703a5aa68fSMatthew Dillon nmsg->any.head.error = error;
20713a5aa68fSMatthew Dillon kdmsg_msg_write(nmsg);
20723a5aa68fSMatthew Dillon }
20733a5aa68fSMatthew Dillon
20743a5aa68fSMatthew Dillon /*
20753a5aa68fSMatthew Dillon * Reply to a message and continue our side of the transaction.
20763a5aa68fSMatthew Dillon *
20773a5aa68fSMatthew Dillon * If msg->state is non-NULL we are replying to a one-way message and this
20783a5aa68fSMatthew Dillon * function degenerates into the same as kdmsg_msg_reply().
20793a5aa68fSMatthew Dillon */
20803a5aa68fSMatthew Dillon void
20813a5aa68fSMatthew Dillon kdmsg_msg_result(kdmsg_msg_t *msg, uint32_t error)
20823a5aa68fSMatthew Dillon {
20833a5aa68fSMatthew Dillon kdmsg_state_t *state = msg->state;
20843a5aa68fSMatthew Dillon kdmsg_msg_t *nmsg;
20853a5aa68fSMatthew Dillon uint32_t cmd;
20863a5aa68fSMatthew Dillon
20873a5aa68fSMatthew Dillon /*
20883a5aa68fSMatthew Dillon * Return a simple result code, do NOT terminate the transaction.
20893a5aa68fSMatthew Dillon */
20903a5aa68fSMatthew Dillon cmd = DMSG_LNK_ERROR;
20913a5aa68fSMatthew Dillon
20923a5aa68fSMatthew Dillon /*
20933a5aa68fSMatthew Dillon * Check if our direction has even been initiated yet, set CREATE.
20943a5aa68fSMatthew Dillon *
20953a5aa68fSMatthew Dillon * Check what direction this is (command or reply direction). Note
20963a5aa68fSMatthew Dillon * that txcmd might not have been initiated yet.
20973a5aa68fSMatthew Dillon *
20983a5aa68fSMatthew Dillon * If our direction has already been closed we just return without
20993a5aa68fSMatthew Dillon * doing anything.
21003a5aa68fSMatthew Dillon */
21011b8eded1SMatthew Dillon if (state != &state->iocom->state0) {
21023a5aa68fSMatthew Dillon if (state->txcmd & DMSGF_DELETE)
21033a5aa68fSMatthew Dillon return;
21043a5aa68fSMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0)
21053a5aa68fSMatthew Dillon cmd |= DMSGF_CREATE;
21063a5aa68fSMatthew Dillon if (state->txcmd & DMSGF_REPLY)
21073a5aa68fSMatthew Dillon cmd |= DMSGF_REPLY;
21083a5aa68fSMatthew Dillon /* continuing transaction, do not set MSGF_DELETE */
21093a5aa68fSMatthew Dillon } else {
21103a5aa68fSMatthew Dillon if ((msg->any.head.cmd & DMSGF_REPLY) == 0)
21113a5aa68fSMatthew Dillon cmd |= DMSGF_REPLY;
21123a5aa68fSMatthew Dillon }
21133a5aa68fSMatthew Dillon
21141b8eded1SMatthew Dillon nmsg = kdmsg_msg_alloc(state, cmd, NULL, NULL);
211503d99ea4SMatthew Dillon nmsg->any.head.error = error;
211603d99ea4SMatthew Dillon kdmsg_msg_write(nmsg);
211703d99ea4SMatthew Dillon }
211803d99ea4SMatthew Dillon
211903d99ea4SMatthew Dillon /*
212003d99ea4SMatthew Dillon * Reply to a message and terminate our side of the transaction.
212103d99ea4SMatthew Dillon *
212203d99ea4SMatthew Dillon * If msg->state is non-NULL we are replying to a one-way message.
212303d99ea4SMatthew Dillon */
212403d99ea4SMatthew Dillon void
212503d99ea4SMatthew Dillon kdmsg_state_reply(kdmsg_state_t *state, uint32_t error)
212603d99ea4SMatthew Dillon {
212703d99ea4SMatthew Dillon kdmsg_msg_t *nmsg;
212803d99ea4SMatthew Dillon uint32_t cmd;
212903d99ea4SMatthew Dillon
213003d99ea4SMatthew Dillon /*
213103d99ea4SMatthew Dillon * Reply with a simple error code and terminate the transaction.
213203d99ea4SMatthew Dillon */
213303d99ea4SMatthew Dillon cmd = DMSG_LNK_ERROR;
213403d99ea4SMatthew Dillon
213503d99ea4SMatthew Dillon /*
213603d99ea4SMatthew Dillon * Check if our direction has even been initiated yet, set CREATE.
213703d99ea4SMatthew Dillon *
213803d99ea4SMatthew Dillon * Check what direction this is (command or reply direction). Note
213903d99ea4SMatthew Dillon * that txcmd might not have been initiated yet.
214003d99ea4SMatthew Dillon *
214103d99ea4SMatthew Dillon * If our direction has already been closed we just return without
214203d99ea4SMatthew Dillon * doing anything.
214303d99ea4SMatthew Dillon */
21440a2f67afSMatthew Dillon KKASSERT(state);
214503d99ea4SMatthew Dillon if (state->txcmd & DMSGF_DELETE)
214603d99ea4SMatthew Dillon return;
214703d99ea4SMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0)
214803d99ea4SMatthew Dillon cmd |= DMSGF_CREATE;
214903d99ea4SMatthew Dillon if (state->txcmd & DMSGF_REPLY)
215003d99ea4SMatthew Dillon cmd |= DMSGF_REPLY;
215103d99ea4SMatthew Dillon cmd |= DMSGF_DELETE;
215203d99ea4SMatthew Dillon
21531b8eded1SMatthew Dillon nmsg = kdmsg_msg_alloc(state, cmd, NULL, NULL);
215403d99ea4SMatthew Dillon nmsg->any.head.error = error;
215503d99ea4SMatthew Dillon kdmsg_msg_write(nmsg);
215603d99ea4SMatthew Dillon }
215703d99ea4SMatthew Dillon
215803d99ea4SMatthew Dillon /*
215903d99ea4SMatthew Dillon * Reply to a message and continue our side of the transaction.
216003d99ea4SMatthew Dillon *
216103d99ea4SMatthew Dillon * If msg->state is non-NULL we are replying to a one-way message and this
216203d99ea4SMatthew Dillon * function degenerates into the same as kdmsg_msg_reply().
216303d99ea4SMatthew Dillon */
216403d99ea4SMatthew Dillon void
216503d99ea4SMatthew Dillon kdmsg_state_result(kdmsg_state_t *state, uint32_t error)
216603d99ea4SMatthew Dillon {
216703d99ea4SMatthew Dillon kdmsg_msg_t *nmsg;
216803d99ea4SMatthew Dillon uint32_t cmd;
216903d99ea4SMatthew Dillon
217003d99ea4SMatthew Dillon /*
217103d99ea4SMatthew Dillon * Return a simple result code, do NOT terminate the transaction.
217203d99ea4SMatthew Dillon */
217303d99ea4SMatthew Dillon cmd = DMSG_LNK_ERROR;
217403d99ea4SMatthew Dillon
217503d99ea4SMatthew Dillon /*
217603d99ea4SMatthew Dillon * Check if our direction has even been initiated yet, set CREATE.
217703d99ea4SMatthew Dillon *
217803d99ea4SMatthew Dillon * Check what direction this is (command or reply direction). Note
217903d99ea4SMatthew Dillon * that txcmd might not have been initiated yet.
218003d99ea4SMatthew Dillon *
218103d99ea4SMatthew Dillon * If our direction has already been closed we just return without
218203d99ea4SMatthew Dillon * doing anything.
218303d99ea4SMatthew Dillon */
21840a2f67afSMatthew Dillon KKASSERT(state);
218503d99ea4SMatthew Dillon if (state->txcmd & DMSGF_DELETE)
218603d99ea4SMatthew Dillon return;
218703d99ea4SMatthew Dillon if ((state->txcmd & DMSGF_CREATE) == 0)
218803d99ea4SMatthew Dillon cmd |= DMSGF_CREATE;
218903d99ea4SMatthew Dillon if (state->txcmd & DMSGF_REPLY)
219003d99ea4SMatthew Dillon cmd |= DMSGF_REPLY;
219103d99ea4SMatthew Dillon /* continuing transaction, do not set MSGF_DELETE */
219203d99ea4SMatthew Dillon
21931b8eded1SMatthew Dillon nmsg = kdmsg_msg_alloc(state, cmd, NULL, NULL);
21943a5aa68fSMatthew Dillon nmsg->any.head.error = error;
21953a5aa68fSMatthew Dillon kdmsg_msg_write(nmsg);
21963a5aa68fSMatthew Dillon }
2197