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