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 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 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 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 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 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 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 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 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