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