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