xref: /dragonfly/sys/vfs/hammer2/hammer2_iocom.c (revision c12cfc4ad07eb9a5d7d3b0c346f718520ed82b99)
1 /*
2  * Copyright (c) 2011-2018 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  * by Daniel Flores (GSOC 2013 - mentored by Matthew Dillon, compression)
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/mount.h>
39 #include <sys/uuid.h>
40 #include <sys/socket.h>
41 #include <sys/proc.h>
42 #include <sys/file.h>
43 
44 #include "hammer2.h"
45 
46 static int hammer2_rcvdmsg(kdmsg_msg_t *msg);
47 static void hammer2_autodmsg(kdmsg_msg_t *msg);
48 static int hammer2_lnk_span_reply(kdmsg_state_t *state, kdmsg_msg_t *msg);
49 
50 void
hammer2_iocom_init(hammer2_dev_t * hmp)51 hammer2_iocom_init(hammer2_dev_t *hmp)
52 {
53           /*
54            * Automatic LNK_CONN
55            * Automatic LNK_SPAN handling
56            * No automatic LNK_SPAN generation (we generate multiple spans
57            *                                           ourselves).
58            */
59           kdmsg_iocom_init(&hmp->iocom, hmp,
60                                KDMSG_IOCOMF_AUTOCONN |
61                                KDMSG_IOCOMF_AUTORXSPAN,
62                                hmp->mmsg, hammer2_rcvdmsg);
63 }
64 
65 void
hammer2_iocom_uninit(hammer2_dev_t * hmp)66 hammer2_iocom_uninit(hammer2_dev_t *hmp)
67 {
68           /* XXX chain depend deadlck? */
69           if (hmp->iocom.mmsg)
70                     kdmsg_iocom_uninit(&hmp->iocom);
71 }
72 
73 /*
74  * Reconnect using the passed file pointer.  The caller must ref the
75  * fp for us.
76  */
77 void
hammer2_cluster_reconnect(hammer2_dev_t * hmp,struct file * fp)78 hammer2_cluster_reconnect(hammer2_dev_t *hmp, struct file *fp)
79 {
80           /*
81            * Closes old comm descriptor, kills threads, cleans up
82            * states, then installs the new descriptor and creates
83            * new threads.
84            */
85           kdmsg_iocom_reconnect(&hmp->iocom, fp, "hammer2");
86 
87           /*
88            * Setup LNK_CONN fields for autoinitiated state machine.  LNK_CONN
89            * does not have to be unique.  peer_id can be used to filter incoming
90            * LNK_SPANs automatically if desired (though we still need to check).
91            * peer_label typically identifies who we are and is not a filter.
92            *
93            * Since we will be initiating multiple LNK_SPANs we cannot use
94            * AUTOTXSPAN, but we do use AUTORXSPAN so kdmsg tracks received
95            * LNK_SPANs, and we simply monitor those messages.
96            */
97           bzero(&hmp->iocom.auto_lnk_conn.peer_id,
98                 sizeof(hmp->iocom.auto_lnk_conn.peer_id));
99           /* hmp->iocom.auto_lnk_conn.peer_id = hmp->voldata.fsid; */
100           hmp->iocom.auto_lnk_conn.proto_version = DMSG_SPAN_PROTO_1;
101 #if 0
102           hmp->iocom.auto_lnk_conn.peer_type = hmp->voldata.peer_type;
103 #endif
104           hmp->iocom.auto_lnk_conn.peer_type = DMSG_PEER_HAMMER2;
105 
106           /*
107            * We just want to receive LNK_SPANs related to HAMMER2 matching
108            * peer_id.
109            */
110           hmp->iocom.auto_lnk_conn.peer_mask = 1LLU << DMSG_PEER_HAMMER2;
111 
112 #if 0
113           switch (ipdata->meta.pfs_type) {
114           case DMSG_PFSTYPE_CLIENT:
115                     hmp->iocom.auto_lnk_conn.peer_mask &=
116                                         ~(1LLU << DMSG_PFSTYPE_CLIENT);
117                     break;
118           default:
119                     break;
120           }
121 #endif
122 
123           bzero(&hmp->iocom.auto_lnk_conn.peer_label,
124                 sizeof(hmp->iocom.auto_lnk_conn.peer_label));
125           ksnprintf(hmp->iocom.auto_lnk_conn.peer_label,
126                       sizeof(hmp->iocom.auto_lnk_conn.peer_label),
127                       "%s/%s",
128                       hostname, "hammer2-mount");
129           kdmsg_iocom_autoinitiate(&hmp->iocom, hammer2_autodmsg);
130 }
131 
132 static int
hammer2_rcvdmsg(kdmsg_msg_t * msg)133 hammer2_rcvdmsg(kdmsg_msg_t *msg)
134 {
135           kprintf("RCVMSG %08x\n", msg->tcmd);
136 
137           switch(msg->tcmd) {
138           case DMSG_DBG_SHELL:
139                     /*
140                      * (non-transaction)
141                      * Execute shell command (not supported atm)
142                      */
143                     kdmsg_msg_result(msg, DMSG_ERR_NOSUPP);
144                     break;
145           case DMSG_DBG_SHELL | DMSGF_REPLY:
146                     /*
147                      * (non-transaction)
148                      */
149                     if (msg->aux_data) {
150                               msg->aux_data[msg->aux_size - 1] = 0;
151                               kprintf("HAMMER2 DBG: %s\n", msg->aux_data);
152                     }
153                     break;
154           default:
155                     /*
156                      * Unsupported message received.  We only need to
157                      * reply if it's a transaction in order to close our end.
158                      * Ignore any one-way messages or any further messages
159                      * associated with the transaction.
160                      *
161                      * NOTE: This case also includes DMSG_LNK_ERROR messages
162                      *         which might be one-way, replying to those would
163                      *         cause an infinite ping-pong.
164                      */
165                     if (msg->any.head.cmd & DMSGF_CREATE)
166                               kdmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
167                     break;
168           }
169           return(0);
170 }
171 
172 /*
173  * This function is called after KDMSG has automatically handled processing
174  * of a LNK layer message (typically CONN or SPAN).
175  *
176  * We trampoline off LNK_CONN (link level connection advertising the presence
177  * of H2 partitions) to generate LNK_HAMMER2_VOLCONF's from the vol->copyinfo[]
178  * array in the volume header, and to generate LNK_SPANs advertising all PFSs
179  * available from that volume.  Currently the VOLCONFs go through the motions
180  * but are otherwise ignored.
181  *
182  * We collect LNK_SPAN state for hammer2 peers and turn those into 'remote'
183  * PFSs which look similar to local (direct storage) PFSs.  These PFSs are
184  * merged with local PFSs and may be mounted locally or merged with other
185  * elements that might or might not include local cluster components (local
186  * PFSs).  If you wish to ensure that a mandatory PFS rendezvous is present
187  * even without network connectivity you would normally just create a dummy
188  * local PFS configured as PFSTYPE_CACHE (that can be small and ram-backed).
189  *
190  * Remote PFSs are object-based and operate using an (inode,key) DMSG API
191  * instead of hammer2_chain's + direct I/O.  A high level XOP API is employed
192  * to retain integrity across operations such as rename()s.
193  */
194 static void hammer2_update_spans(hammer2_dev_t *hmp, kdmsg_state_t *state);
195 
196 static void
hammer2_autodmsg(kdmsg_msg_t * msg)197 hammer2_autodmsg(kdmsg_msg_t *msg)
198 {
199           hammer2_dev_t *hmp = msg->state->iocom->handle;
200           int copyid;
201 
202           switch(msg->tcmd) {
203           case DMSG_LNK_CONN | DMSGF_CREATE:
204           case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_DELETE:
205           case DMSG_LNK_CONN | DMSGF_DELETE:
206                     /*
207                      * NOTE: kern_dmsg will automatically issue a result,
208                      *       leaving the transaction open, for CREATEs,
209                      *         and will automatically issue a terminating reply
210                      *         for DELETEs.
211                      */
212                     break;
213           case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_REPLY:
214           case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_DELETE | DMSGF_REPLY:
215                     /*
216                      * Do a volume configuration dump when we receive a reply
217                      * to our auto-CONN (typically leaving the transaction open).
218                      */
219                     if (msg->any.head.cmd & DMSGF_CREATE) {
220                               kprintf("HAMMER2: VOLDATA DUMP\n");
221 
222                               /*
223                                * Dump the configuration stored in the volume header.
224                                * This will typically be import/export access rights,
225                                * master encryption keys (encrypted), etc.
226                                */
227                               hammer2_voldata_lock(hmp);
228                               copyid = 0;
229                               while (copyid < HAMMER2_COPYID_COUNT) {
230                                         if (hmp->voldata.copyinfo[copyid].copyid)
231                                                   hammer2_volconf_update(hmp, copyid);
232                                         ++copyid;
233                               }
234                               hammer2_voldata_unlock(hmp);
235 
236                               kprintf("HAMMER2: INITIATE SPANs\n");
237                               hammer2_update_spans(hmp, msg->state);
238                     }
239                     if ((msg->any.head.cmd & DMSGF_DELETE) &&
240                         msg->state && (msg->state->txcmd & DMSGF_DELETE) == 0) {
241                               kprintf("HAMMER2: CONN WAS TERMINATED\n");
242                     }
243                     break;
244           case DMSG_LNK_SPAN | DMSGF_CREATE:
245                     /*
246                      * Monitor SPANs and issue a result, leaving the SPAN open
247                      * if it is something we can use now or in the future.
248                      */
249                     if (msg->any.lnk_span.peer_type != DMSG_PEER_HAMMER2) {
250                               kdmsg_msg_reply(msg, 0);
251                               break;
252                     }
253                     if (msg->any.lnk_span.proto_version != DMSG_SPAN_PROTO_1) {
254                               kdmsg_msg_reply(msg, 0);
255                               break;
256                     }
257                     DMSG_TERMINATE_STRING(msg->any.lnk_span.peer_label);
258                     if (hammer2_debug & 0x0100) {
259                               kprintf("H2 +RXSPAN cmd=%08x (%-20s) cl=",
260                                         msg->any.head.cmd,
261                                         msg->any.lnk_span.peer_label);
262                               printf_uuid(&msg->any.lnk_span.peer_id);
263                               kprintf(" fs=");
264                               printf_uuid(&msg->any.lnk_span.pfs_id);
265                               kprintf(" type=%d\n", msg->any.lnk_span.pfs_type);
266                     }
267                     kdmsg_msg_result(msg, 0);
268                     break;
269           case DMSG_LNK_SPAN | DMSGF_DELETE:
270                     /*
271                      * NOTE: kern_dmsg will automatically reply to DELETEs.
272                      */
273                     if (hammer2_debug & 0x0100)
274                               kprintf("H2 -RXSPAN\n");
275                     break;
276           default:
277                     break;
278           }
279 }
280 
281 /*
282  * Update LNK_SPAN state
283  */
284 static void
hammer2_update_spans(hammer2_dev_t * hmp,kdmsg_state_t * state)285 hammer2_update_spans(hammer2_dev_t *hmp, kdmsg_state_t *state)
286 {
287           const hammer2_inode_data_t *ripdata;
288           hammer2_chain_t *parent;
289           hammer2_chain_t *chain;
290           hammer2_pfs_t *spmp;
291           hammer2_key_t key_next;
292           kdmsg_msg_t *rmsg;
293           size_t name_len;
294           int error;
295 
296           /*
297            * Lookup mount point under the media-localized super-root.
298            *
299            * cluster->pmp will incorrectly point to spmp and must be fixed
300            * up later on.
301            */
302           spmp = hmp->spmp;
303           hammer2_inode_lock(spmp->iroot, 0);
304           error = 0;
305 
306           parent = hammer2_inode_chain(spmp->iroot, 0, HAMMER2_RESOLVE_ALWAYS);
307           chain = NULL;
308           if (parent == NULL)
309                     goto done;
310           chain = hammer2_chain_lookup(&parent, &key_next,
311                                              HAMMER2_KEY_MIN, HAMMER2_KEY_MAX,
312                                              &error, 0);
313           while (chain) {
314                     if (chain->bref.type != HAMMER2_BREF_TYPE_INODE)
315                               continue;
316                     ripdata = &chain->data->ipdata;
317 #if 0
318                     kprintf("UPDATE SPANS: %s\n", ripdata->filename);
319 #endif
320 
321                     rmsg = kdmsg_msg_alloc(&hmp->iocom.state0,
322                                                DMSG_LNK_SPAN | DMSGF_CREATE,
323                                                hammer2_lnk_span_reply, NULL);
324                     rmsg->any.lnk_span.peer_id = ripdata->meta.pfs_clid;
325                     rmsg->any.lnk_span.pfs_id = ripdata->meta.pfs_fsid;
326                     rmsg->any.lnk_span.pfs_type = ripdata->meta.pfs_type;
327                     rmsg->any.lnk_span.peer_type = DMSG_PEER_HAMMER2;
328                     rmsg->any.lnk_span.proto_version = DMSG_SPAN_PROTO_1;
329                     name_len = ripdata->meta.name_len;
330                     if (name_len >= sizeof(rmsg->any.lnk_span.peer_label))
331                               name_len = sizeof(rmsg->any.lnk_span.peer_label) - 1;
332                     bcopy(ripdata->filename,
333                           rmsg->any.lnk_span.peer_label,
334                           name_len);
335 
336                     kdmsg_msg_write(rmsg);
337 
338                     chain = hammer2_chain_next(&parent, chain, &key_next,
339                                                          key_next, HAMMER2_KEY_MAX,
340                                                          &error, 0);
341           }
342           hammer2_inode_unlock(spmp->iroot);
343           /* XXX do something with error */
344 done:
345           if (chain) {
346                     hammer2_chain_unlock(chain);
347                     hammer2_chain_drop(chain);
348           }
349           if (parent) {
350                     hammer2_chain_unlock(parent);
351                     hammer2_chain_drop(parent);
352           }
353 }
354 
355 static
356 int
hammer2_lnk_span_reply(kdmsg_state_t * state,kdmsg_msg_t * msg)357 hammer2_lnk_span_reply(kdmsg_state_t *state, kdmsg_msg_t *msg)
358 {
359           if ((state->txcmd & DMSGF_DELETE) == 0 &&
360               (msg->any.head.cmd & DMSGF_DELETE)) {
361                     kdmsg_msg_reply(msg, 0);
362           }
363           return 0;
364 }
365 
366 /*
367  * Volume configuration updates are passed onto the userland service
368  * daemon via the open LNK_CONN transaction.
369  */
370 void
hammer2_volconf_update(hammer2_dev_t * hmp,int index)371 hammer2_volconf_update(hammer2_dev_t *hmp, int index)
372 {
373           kdmsg_msg_t *msg;
374 
375           /* XXX interlock against connection state termination */
376           kprintf("volconf update %p\n", hmp->iocom.conn_state);
377           if (hmp->iocom.conn_state) {
378                     kprintf("TRANSMIT VOLCONF VIA OPEN CONN TRANSACTION\n");
379                     msg = kdmsg_msg_alloc(hmp->iocom.conn_state,
380                                               DMSG_LNK_HAMMER2_VOLCONF,
381                                               NULL, NULL);
382                     H2_LNK_VOLCONF(msg)->copy = hmp->voldata.copyinfo[index];
383                     H2_LNK_VOLCONF(msg)->mediaid = hmp->voldata.fsid;
384                     H2_LNK_VOLCONF(msg)->index = index;
385                     kdmsg_msg_write(msg);
386           }
387 }
388