xref: /dragonfly/sys/vfs/hammer2/hammer2_iocom.c (revision dead10d5)
1 /*
2  * Copyright (c) 2011-2015 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 tag off the LNK_CONN to trigger our LNK_VOLCONF messages which
193  * advertises all available hammer2 super-root volumes.
194  *
195  * We collect span state
196  */
197 static void hammer2_update_spans(hammer2_dev_t *hmp, kdmsg_state_t *state);
198 
199 static void
200 hammer2_autodmsg(kdmsg_msg_t *msg)
201 {
202 	hammer2_dev_t *hmp = msg->state->iocom->handle;
203 	int copyid;
204 
205 	switch(msg->tcmd) {
206 	case DMSG_LNK_CONN | DMSGF_CREATE:
207 	case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_DELETE:
208 	case DMSG_LNK_CONN | DMSGF_DELETE:
209 		/*
210 		 * NOTE: kern_dmsg will automatically issue a result,
211 		 *       leaving the transaction open, for CREATEs,
212 		 *	 and will automatically issue a terminating reply
213 		 *	 for DELETEs.
214 		 */
215 		break;
216 	case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_REPLY:
217 	case DMSG_LNK_CONN | DMSGF_CREATE | DMSGF_DELETE | DMSGF_REPLY:
218 		/*
219 		 * Do a volume configuration dump when we receive a reply
220 		 * to our auto-CONN (typically leaving the transaction open).
221 		 */
222 		if (msg->any.head.cmd & DMSGF_CREATE) {
223 			kprintf("HAMMER2: VOLDATA DUMP\n");
224 
225 			/*
226 			 * Dump the configuration stored in the volume header.
227 			 * This will typically be import/export access rights,
228 			 * master encryption keys (encrypted), etc.
229 			 */
230 			hammer2_voldata_lock(hmp);
231 			copyid = 0;
232 			while (copyid < HAMMER2_COPYID_COUNT) {
233 				if (hmp->voldata.copyinfo[copyid].copyid)
234 					hammer2_volconf_update(hmp, copyid);
235 				++copyid;
236 			}
237 			hammer2_voldata_unlock(hmp);
238 
239 			kprintf("HAMMER2: INITIATE SPANs\n");
240 			hammer2_update_spans(hmp, msg->state);
241 		}
242 		if ((msg->any.head.cmd & DMSGF_DELETE) &&
243 		    msg->state && (msg->state->txcmd & DMSGF_DELETE) == 0) {
244 			kprintf("HAMMER2: CONN WAS TERMINATED\n");
245 		}
246 		break;
247 	case DMSG_LNK_SPAN | DMSGF_CREATE:
248 		/*
249 		 * Monitor SPANs and issue a result, leaving the SPAN open
250 		 * if it is something we can use now or in the future.
251 		 */
252 		if (msg->any.lnk_span.peer_type != DMSG_PEER_HAMMER2) {
253 			kdmsg_msg_reply(msg, 0);
254 			break;
255 		}
256 		if (msg->any.lnk_span.proto_version != DMSG_SPAN_PROTO_1) {
257 			kdmsg_msg_reply(msg, 0);
258 			break;
259 		}
260 		DMSG_TERMINATE_STRING(msg->any.lnk_span.peer_label);
261 		if (hammer2_debug & 0x0100) {
262 			kprintf("H2 +RXSPAN cmd=%08x (%-20s) cl=",
263 				msg->any.head.cmd,
264 				msg->any.lnk_span.peer_label);
265 			printf_uuid(&msg->any.lnk_span.peer_id);
266 			kprintf(" fs=");
267 			printf_uuid(&msg->any.lnk_span.pfs_id);
268 			kprintf(" type=%d\n", msg->any.lnk_span.pfs_type);
269 		}
270 		kdmsg_msg_result(msg, 0);
271 		break;
272 	case DMSG_LNK_SPAN | DMSGF_DELETE:
273 		/*
274 		 * NOTE: kern_dmsg will automatically reply to DELETEs.
275 		 */
276 		if (hammer2_debug & 0x0100)
277 			kprintf("H2 -RXSPAN\n");
278 		break;
279 	default:
280 		break;
281 	}
282 }
283 
284 /*
285  * Update LNK_SPAN state
286  */
287 static void
288 hammer2_update_spans(hammer2_dev_t *hmp, kdmsg_state_t *state)
289 {
290 	const hammer2_inode_data_t *ripdata;
291 	hammer2_chain_t *parent;
292 	hammer2_chain_t *chain;
293 	hammer2_pfs_t *spmp;
294 	hammer2_key_t key_next;
295 	kdmsg_msg_t *rmsg;
296 	size_t name_len;
297 	int error;
298 
299 	/*
300 	 * Lookup mount point under the media-localized super-root.
301 	 *
302 	 * cluster->pmp will incorrectly point to spmp and must be fixed
303 	 * up later on.
304 	 */
305 	spmp = hmp->spmp;
306 	hammer2_inode_lock(spmp->iroot, 0);
307 	error = 0;
308 
309 	parent = hammer2_inode_chain(spmp->iroot, 0, HAMMER2_RESOLVE_ALWAYS);
310 	chain = NULL;
311 	if (parent == NULL)
312 		goto done;
313 	chain = hammer2_chain_lookup(&parent, &key_next,
314 				     HAMMER2_KEY_MIN, HAMMER2_KEY_MAX,
315 				     &error, 0);
316 	while (chain) {
317 		if (chain->bref.type != HAMMER2_BREF_TYPE_INODE)
318 			continue;
319 		ripdata = &chain->data->ipdata;
320 #if 0
321 		kprintf("UPDATE SPANS: %s\n", ripdata->filename);
322 #endif
323 
324 		rmsg = kdmsg_msg_alloc(&hmp->iocom.state0,
325 				       DMSG_LNK_SPAN | DMSGF_CREATE,
326 				       hammer2_lnk_span_reply, NULL);
327 		rmsg->any.lnk_span.peer_id = ripdata->meta.pfs_clid;
328 		rmsg->any.lnk_span.pfs_id = ripdata->meta.pfs_fsid;
329 		rmsg->any.lnk_span.pfs_type = ripdata->meta.pfs_type;
330 		rmsg->any.lnk_span.peer_type = DMSG_PEER_HAMMER2;
331 		rmsg->any.lnk_span.proto_version = DMSG_SPAN_PROTO_1;
332 		name_len = ripdata->meta.name_len;
333 		if (name_len >= sizeof(rmsg->any.lnk_span.peer_label))
334 			name_len = sizeof(rmsg->any.lnk_span.peer_label) - 1;
335 		bcopy(ripdata->filename,
336 		      rmsg->any.lnk_span.peer_label,
337 		      name_len);
338 
339 		kdmsg_msg_write(rmsg);
340 
341 		chain = hammer2_chain_next(&parent, chain, &key_next,
342 					       key_next, HAMMER2_KEY_MAX,
343 					       &error, 0);
344 	}
345 	hammer2_inode_unlock(spmp->iroot);
346 	/* XXX do something with error */
347 done:
348 	if (chain) {
349 		hammer2_chain_unlock(chain);
350 		hammer2_chain_drop(chain);
351 	}
352 	if (parent) {
353 		hammer2_chain_unlock(parent);
354 		hammer2_chain_drop(parent);
355 	}
356 }
357 
358 static
359 int
360 hammer2_lnk_span_reply(kdmsg_state_t *state, kdmsg_msg_t *msg)
361 {
362 	if ((state->txcmd & DMSGF_DELETE) == 0 &&
363 	    (msg->any.head.cmd & DMSGF_DELETE)) {
364 		kdmsg_msg_reply(msg, 0);
365 	}
366 	return 0;
367 }
368 
369 /*
370  * Volume configuration updates are passed onto the userland service
371  * daemon via the open LNK_CONN transaction.
372  */
373 void
374 hammer2_volconf_update(hammer2_dev_t *hmp, int index)
375 {
376 	kdmsg_msg_t *msg;
377 
378 	/* XXX interlock against connection state termination */
379 	kprintf("volconf update %p\n", hmp->iocom.conn_state);
380 	if (hmp->iocom.conn_state) {
381 		kprintf("TRANSMIT VOLCONF VIA OPEN CONN TRANSACTION\n");
382 		msg = kdmsg_msg_alloc(hmp->iocom.conn_state,
383 				      DMSG_LNK_HAMMER2_VOLCONF,
384 				      NULL, NULL);
385 		H2_LNK_VOLCONF(msg)->copy = hmp->voldata.copyinfo[index];
386 		H2_LNK_VOLCONF(msg)->mediaid = hmp->voldata.fsid;
387 		H2_LNK_VOLCONF(msg)->index = index;
388 		kdmsg_msg_write(msg);
389 	}
390 }
391