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