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