hammer2 - wire up msg transaction state machine
[dragonfly.git] / sys / vfs / hammer2 / hammer2_msg.c
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  */
104 int
105 hammer2_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
283 void
284 hammer2_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  */
329 int
330 hammer2_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
513 void
514 hammer2_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
546 void
547 hammer2_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
559 void
560 hammer2_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  */
574 int
575 hammer2_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  */
600 int
601 hammer2_msg_execute(hammer2_pfsmount_t *pmp __unused, hammer2_msg_t *msg __unused)
602 {
603         return(0);
604 }