hammer2 - wire up msg transaction state machine
[dragonfly.git] / sys / vfs / hammer2 / hammer2_msg.c
CommitLineData
26bf1a36
MD
1/*-
2 * Copyright (c) 2012 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "hammer2.h"
36
37/*
38 * Process state tracking for a message after reception, prior to
39 * execution.
40 *
41 * Called with msglk held and the msg dequeued.
42 *
43 * All messages are called with dummy state and return actual state.
44 * (One-off messages often just return the same dummy state).
45 *
46 * May request that caller discard the message by setting *discardp to 1.
47 * The returned state is not used in this case and is allowed to be NULL.
48 *
49 * --
50 *
51 * These routines handle persistent and command/reply message state via the
52 * CREATE and DELETE flags. The first message in a command or reply sequence
53 * sets CREATE, the last message in a command or reply sequence sets DELETE.
54 *
55 * There can be any number of intermediate messages belonging to the same
56 * sequence sent inbetween the CREATE message and the DELETE message,
57 * which set neither flag. This represents a streaming command or reply.
58 *
59 * Any command message received with CREATE set expects a reply sequence to
60 * be returned. Reply sequences work the same as command sequences except the
61 * REPLY bit is also sent. Both the command side and reply side can
62 * degenerate into a single message with both CREATE and DELETE set. Note
63 * that one side can be streaming and the other side not, or neither, or both.
64 *
65 * The msgid is unique for the initiator. That is, two sides sending a new
66 * message can use the same msgid without colliding.
67 *
68 * --
69 *
70 * ABORT sequences work by setting the ABORT flag along with normal message
71 * state. However, ABORTs can also be sent on half-closed messages, that is
72 * even if the command or reply side has already sent a DELETE, as long as
73 * the message has not been fully closed it can still send an ABORT+DELETE
74 * to terminate the half-closed message state.
75 *
76 * Since ABORT+DELETEs can race we silently discard ABORT's for message
77 * state which has already been fully closed. REPLY+ABORT+DELETEs can
78 * also race, and in this situation the other side might have already
79 * initiated a new unrelated command with the same message id. Since
80 * the abort has not set the CREATE flag the situation can be detected
81 * and the message will also be discarded.
82 *
83 * Non-blocking requests can be initiated with ABORT+CREATE[+DELETE].
84 * The ABORT request is essentially integrated into the command instead
85 * of being sent later on. In this situation the command implementation
86 * detects that CREATE and ABORT are both set (vs ABORT alone) and can
87 * special-case non-blocking operation for the command.
88 *
89 * NOTE! Messages with ABORT set without CREATE or DELETE are considered
90 * to be mid-stream aborts for command/reply sequences. ABORTs on
91 * one-way messages are not supported.
92 *
93 * NOTE! If a command sequence does not support aborts the ABORT flag is
94 * simply ignored.
95 *
96 * --
97 *
98 * One-off messages (no reply expected) are sent with neither CREATE or DELETE
99 * set. One-off messages cannot be aborted and typically aren't processed
100 * by these routines. The REPLY bit can be used to distinguish whether a
101 * one-off message is a command or reply. For example, one-off replies
102 * will typically just contain status updates.
103 */
104int
105hammer2_state_msgrx(hammer2_pfsmount_t *pmp, hammer2_msg_t *msg)
106{
107 hammer2_state_t *state;
108 int error;
109
110 /*
111 * Make sure a state structure is ready to go in case we need a new
112 * one. This is the only routine which uses freerd_state so no
113 * races are possible.
114 */
115 if ((state = pmp->freerd_state) == NULL) {
116 state = kmalloc(sizeof(*state), pmp->mmsg, M_WAITOK | M_ZERO);
117 state->pmp = pmp;
118 state->flags = HAMMER2_STATE_DYNAMIC;
119 pmp->freerd_state = state;
120 }
121
122 /*
123 * Lock RB tree and locate existing persistent state, if any.
124 *
125 * If received msg is a command state is on staterd_tree.
126 * If received msg is a reply state is on statewr_tree.
127 */
128 lockmgr(&pmp->msglk, LK_EXCLUSIVE);
129
130 state->msgid = msg->any.head.msgid;
131 if (msg->any.head.cmd & HAMMER2_MSGF_REPLY)
132 state = RB_FIND(hammer2_state_tree, &pmp->statewr_tree, state);
133 else
134 state = RB_FIND(hammer2_state_tree, &pmp->staterd_tree, state);
135 msg->state = state;
136
137 /*
138 * Short-cut one-off or mid-stream messages (state may be NULL).
139 */
140 if ((msg->any.head.cmd & (HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE |
141 HAMMER2_MSGF_ABORT)) == 0) {
142 lockmgr(&pmp->msglk, LK_RELEASE);
143 return(0);
144 }
145
146 /*
147 * Switch on CREATE, DELETE, REPLY, and also handle ABORT from
148 * inside the case statements.
149 */
150 switch(msg->any.head.cmd & (HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE |
151 HAMMER2_MSGF_REPLY)) {
152 case HAMMER2_MSGF_CREATE:
153 case HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE:
154 /*
155 * New persistant command received.
156 */
157 if (state) {
158 kprintf("hammer2_state_msgrx: duplicate transaction\n");
159 error = EINVAL;
160 break;
161 }
162 state = pmp->freerd_state;
163 pmp->freerd_state = NULL;
164 msg->state = state;
165 state->msg = msg;
166 state->rxcmd = msg->any.head.cmd & ~HAMMER2_MSGF_DELETE;
167 RB_INSERT(hammer2_state_tree, &pmp->staterd_tree, state);
168 state->flags |= HAMMER2_STATE_INSERTED;
169 error = 0;
170 break;
171 case HAMMER2_MSGF_DELETE:
172 /*
173 * Persistent state is expected but might not exist if an
174 * ABORT+DELETE races the close.
175 */
176 if (state == NULL) {
177 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
178 error = EALREADY;
179 } else {
180 kprintf("hammer2_state_msgrx: no state "
181 "for DELETE\n");
182 error = EINVAL;
183 }
184 break;
185 }
186
187 /*
188 * Handle another ABORT+DELETE case if the msgid has already
189 * been reused.
190 */
191 if ((state->rxcmd & HAMMER2_MSGF_CREATE) == 0) {
192 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
193 error = EALREADY;
194 } else {
195 kprintf("hammer2_state_msgrx: state reused "
196 "for DELETE\n");
197 error = EINVAL;
198 }
199 break;
200 }
201 error = 0;
202 break;
203 default:
204 /*
205 * Check for mid-stream ABORT command received, otherwise
206 * allow.
207 */
208 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
209 if (state == NULL ||
210 (state->rxcmd & HAMMER2_MSGF_CREATE) == 0) {
211 error = EALREADY;
212 break;
213 }
214 }
215 error = 0;
216 break;
217 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_CREATE:
218 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE:
219 /*
220 * When receiving a reply with CREATE set the original
221 * persistent state message should already exist.
222 */
223 if (state == NULL) {
224 kprintf("hammer2_state_msgrx: no state match for "
225 "REPLY\n");
226 error = EINVAL;
227 break;
228 }
229 state->rxcmd = msg->any.head.cmd & ~HAMMER2_MSGF_DELETE;
230 error = 0;
231 break;
232 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_DELETE:
233 /*
234 * Received REPLY+ABORT+DELETE in case where msgid has
235 * already been fully closed, ignore the message.
236 */
237 if (state == NULL) {
238 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
239 error = EALREADY;
240 } else {
241 kprintf("hammer2_state_msgrx: no state match "
242 "for REPLY|DELETE\n");
243 error = EINVAL;
244 }
245 break;
246 }
247
248 /*
249 * Received REPLY+ABORT+DELETE in case where msgid has
250 * already been reused for an unrelated message,
251 * ignore the message.
252 */
253 if ((state->rxcmd & HAMMER2_MSGF_CREATE) == 0) {
254 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
255 error = EALREADY;
256 } else {
257 kprintf("hammer2_state_msgrx: state reused "
258 "for REPLY|DELETE\n");
259 error = EINVAL;
260 }
261 break;
262 }
263 error = 0;
264 break;
265 case HAMMER2_MSGF_REPLY:
266 /*
267 * Check for mid-stream ABORT reply received to sent command.
268 */
269 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
270 if (state == NULL ||
271 (state->rxcmd & HAMMER2_MSGF_CREATE) == 0) {
272 error = EALREADY;
273 break;
274 }
275 }
276 error = 0;
277 break;
278 }
279 lockmgr(&pmp->msglk, LK_RELEASE);
280 return (error);
281}
282
283void
284hammer2_state_cleanuprx(hammer2_pfsmount_t *pmp, hammer2_msg_t *msg)
285{
286 hammer2_state_t *state;
287
288 if ((state = msg->state) == NULL) {
289 hammer2_msg_free(pmp, msg);
290 } else if (msg->any.head.cmd & HAMMER2_MSGF_DELETE) {
291 lockmgr(&pmp->msglk, LK_EXCLUSIVE);
292 state->rxcmd |= HAMMER2_MSGF_DELETE;
293 if (state->txcmd & HAMMER2_MSGF_DELETE) {
294 if (state->msg == msg)
295 state->msg = NULL;
296 KKASSERT(state->flags & HAMMER2_STATE_INSERTED);
297 if (msg->any.head.cmd & HAMMER2_MSGF_REPLY) {
298 RB_REMOVE(hammer2_state_tree,
299 &pmp->statewr_tree, state);
300 } else {
301 RB_REMOVE(hammer2_state_tree,
302 &pmp->staterd_tree, state);
303 }
304 state->flags &= ~HAMMER2_STATE_INSERTED;
305 lockmgr(&pmp->msglk, LK_RELEASE);
306 hammer2_state_free(state);
307 } else {
308 lockmgr(&pmp->msglk, LK_RELEASE);
309 }
310 hammer2_msg_free(pmp, msg);
311 } else if (state->msg != msg) {
312 hammer2_msg_free(pmp, msg);
313 }
314}
315
316/*
317 * Process state tracking for a message prior to transmission.
318 *
319 * Called with msglk held and the msg dequeued.
320 *
321 * One-off messages are usually with dummy state and msg->state may be NULL
322 * in this situation.
323 *
324 * New transactions (when CREATE is set) will insert the state.
325 *
326 * May request that caller discard the message by setting *discardp to 1.
327 * A NULL state may be returned in this case.
328 */
329int
330hammer2_state_msgtx(hammer2_pfsmount_t *pmp, hammer2_msg_t *msg)
331{
332 hammer2_state_t *state;
333 int error;
334
335 /*
336 * Make sure a state structure is ready to go in case we need a new
337 * one. This is the only routine which uses freewr_state so no
338 * races are possible.
339 */
340 if ((state = pmp->freewr_state) == NULL) {
341 state = kmalloc(sizeof(*state), pmp->mmsg, M_WAITOK | M_ZERO);
342 state->pmp = pmp;
343 state->flags = HAMMER2_STATE_DYNAMIC;
344 pmp->freewr_state = state;
345 }
346
347 /*
348 * Lock RB tree. If persistent state is present it will have already
349 * been assigned to msg.
350 */
351 lockmgr(&pmp->msglk, LK_EXCLUSIVE);
352 state = msg->state;
353
354 /*
355 * Short-cut one-off or mid-stream messages (state may be NULL).
356 */
357 if ((msg->any.head.cmd & (HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE |
358 HAMMER2_MSGF_ABORT)) == 0) {
359 lockmgr(&pmp->msglk, LK_RELEASE);
360 return(0);
361 }
362
363
364 /*
365 * Switch on CREATE, DELETE, REPLY, and also handle ABORT from
366 * inside the case statements.
367 */
368 switch(msg->any.head.cmd & (HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE |
369 HAMMER2_MSGF_REPLY)) {
370 case HAMMER2_MSGF_CREATE:
371 case HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE:
372 /*
373 * Insert the new persistent message state and mark
374 * half-closed if DELETE is set. Since this is a new
375 * message it isn't possible to transition into the fully
376 * closed state here.
377 */
378 if (state == NULL) {
379 state = pmp->freerd_state;
380 pmp->freerd_state = NULL;
381 msg->state = state;
382 state->msg = msg;
383 }
384 KKASSERT((state->flags & HAMMER2_STATE_INSERTED) == 0);
385 if (RB_INSERT(hammer2_state_tree, &pmp->staterd_tree, state)) {
386 kprintf("hammer2_state_msgtx: duplicate transaction\n");
387 error = EINVAL;
388 break;
389 }
390 state->flags |= HAMMER2_STATE_INSERTED;
391 state->txcmd = msg->any.head.cmd & ~HAMMER2_MSGF_DELETE;
392 error = 0;
393 break;
394 case HAMMER2_MSGF_DELETE:
395 /*
396 * Sent ABORT+DELETE in case where msgid has already
397 * been fully closed, ignore the message.
398 */
399 if (state == NULL) {
400 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
401 error = EALREADY;
402 } else {
403 kprintf("hammer2_state_msgtx: no state match "
404 "for DELETE\n");
405 error = EINVAL;
406 }
407 break;
408 }
409
410 /*
411 * Sent ABORT+DELETE in case where msgid has
412 * already been reused for an unrelated message,
413 * ignore the message.
414 */
415 if ((state->txcmd & HAMMER2_MSGF_CREATE) == 0) {
416 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
417 error = EALREADY;
418 } else {
419 kprintf("hammer2_state_msgtx: state reused "
420 "for DELETE\n");
421 error = EINVAL;
422 }
423 break;
424 }
425 error = 0;
426 break;
427 default:
428 /*
429 * Check for mid-stream ABORT command sent
430 */
431 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
432 if (state == NULL ||
433 (state->txcmd & HAMMER2_MSGF_CREATE) == 0) {
434 error = EALREADY;
435 break;
436 }
437 }
438 error = 0;
439 break;
440 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_CREATE:
441 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_CREATE | HAMMER2_MSGF_DELETE:
442 /*
443 * When transmitting a reply with CREATE set the original
444 * persistent state message should already exist.
445 */
446 if (state == NULL) {
447 kprintf("hammer2_state_msgtx: no state match "
448 "for REPLY | CREATE\n");
449 error = EINVAL;
450 break;
451 }
452 state->txcmd = msg->any.head.cmd & ~HAMMER2_MSGF_DELETE;
453 error = 0;
454 break;
455 case HAMMER2_MSGF_REPLY | HAMMER2_MSGF_DELETE:
456 /*
457 * When transmitting a reply with DELETE set the original
458 * persistent state message should already exist.
459 *
460 * This is very similar to the REPLY|CREATE|* case except
461 * txcmd is already stored, so we just add the DELETE flag.
462 *
463 * Sent REPLY+ABORT+DELETE in case where msgid has
464 * already been fully closed, ignore the message.
465 */
466 if (state == NULL) {
467 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
468 error = EALREADY;
469 } else {
470 kprintf("hammer2_state_msgtx: no state match "
471 "for REPLY | DELETE\n");
472 error = EINVAL;
473 }
474 break;
475 }
476
477 /*
478 * Sent REPLY+ABORT+DELETE in case where msgid has already
479 * been reused for an unrelated message, ignore the message.
480 */
481 if ((state->txcmd & HAMMER2_MSGF_CREATE) == 0) {
482 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
483 error = EALREADY;
484 } else {
485 kprintf("hammer2_state_msgtx: state reused "
486 "for REPLY | DELETE\n");
487 error = EINVAL;
488 }
489 break;
490 }
491 error = 0;
492 break;
493 case HAMMER2_MSGF_REPLY:
494 /*
495 * Check for mid-stream ABORT reply sent.
496 *
497 * One-off REPLY messages are allowed for e.g. status updates.
498 */
499 if (msg->any.head.cmd & HAMMER2_MSGF_ABORT) {
500 if (state == NULL ||
501 (state->txcmd & HAMMER2_MSGF_CREATE) == 0) {
502 error = EALREADY;
503 break;
504 }
505 }
506 error = 0;
507 break;
508 }
509 lockmgr(&pmp->msglk, LK_RELEASE);
510 return (error);
511}
512
513void
514hammer2_state_cleanuptx(hammer2_pfsmount_t *pmp, hammer2_msg_t *msg)
515{
516 hammer2_state_t *state;
517
518 if ((state = msg->state) == NULL) {
519 hammer2_msg_free(pmp, msg);
520 } else if (msg->any.head.cmd & HAMMER2_MSGF_DELETE) {
521 lockmgr(&pmp->msglk, LK_EXCLUSIVE);
522 state->txcmd |= HAMMER2_MSGF_DELETE;
523 if (state->rxcmd & HAMMER2_MSGF_DELETE) {
524 if (state->msg == msg)
525 state->msg = NULL;
526 KKASSERT(state->flags & HAMMER2_STATE_INSERTED);
527 if (msg->any.head.cmd & HAMMER2_MSGF_REPLY) {
528 RB_REMOVE(hammer2_state_tree,
529 &pmp->staterd_tree, state);
530 } else {
531 RB_REMOVE(hammer2_state_tree,
532 &pmp->statewr_tree, state);
533 }
534 state->flags &= ~HAMMER2_STATE_INSERTED;
535 lockmgr(&pmp->msglk, LK_RELEASE);
536 hammer2_state_free(state);
537 } else {
538 lockmgr(&pmp->msglk, LK_RELEASE);
539 }
540 hammer2_msg_free(pmp, msg);
541 } else if (state->msg != msg) {
542 hammer2_msg_free(pmp, msg);
543 }
544}
545
546void
547hammer2_state_free(hammer2_state_t *state)
548{
549 hammer2_pfsmount_t *pmp = state->pmp;
550 hammer2_msg_t *msg;
551
552 msg = state->msg;
553 state->msg = NULL;
554 kfree(state, pmp->mmsg);
555 if (msg)
556 hammer2_msg_free(pmp, msg);
557}
558
559void
560hammer2_msg_free(hammer2_pfsmount_t *pmp, hammer2_msg_t *msg)
561{
562 if (msg->aux_data && msg->aux_size) {
563 kfree(msg->aux_data, pmp->mmsg);
564 msg->aux_data = NULL;
565 msg->aux_size = 0;
566 }
567 kfree(msg, pmp->mmsg);
568}
569
570/*
571 * Indexed messages are stored in a red-black tree indexed by their
572 * msgid. Only persistent messages are indexed.
573 */
574int
575hammer2_state_cmp(hammer2_state_t *state1, hammer2_state_t *state2)
576{
577 if (state1->msgid < state2->msgid)
578 return(-1);
579 if (state1->msgid > state2->msgid)
580 return(1);
581 return(0);
582}
583
584/*
585 * Execute a received message.
586 *
587 * If msg is part of a transaction msg->state will be non-NULL. In this
588 * situation state->msg saves the original command and can be replaced
589 * by ongoing activity if desired. Any msg which does not match state->msg
590 * will be destroyed after we return.
591 *
592 * Single-element transactions set both CREATE and DELETE.
593 *
594 * Non-blocking transactions also set ABORT with CREATE.
595 *
596 * Specific transactional ABORTs can also be received and must be handled,
597 * and can be received even when the other end has closed (after DELETE
598 * received) if we have not yet closed our end of the transaction.
599 */
600int
601hammer2_msg_execute(hammer2_pfsmount_t *pmp __unused, hammer2_msg_t *msg __unused)
602{
603 return(0);
604}