HAMMER 60C/many: Mirroring
[dragonfly.git] / sys / vfs / hammer / hammer_mirror.c
1 /*
2  * Copyright (c) 2008 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  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/vfs/hammer/hammer_mirror.c,v 1.6 2008/07/04 07:25:36 dillon Exp $
35  */
36 /*
37  * HAMMER mirroring ioctls - serialize and deserialize modifications made
38  *                           to a filesystem.
39  */
40
41 #include "hammer.h"
42
43 static int hammer_mirror_check(hammer_cursor_t cursor,
44                                 struct hammer_ioc_mrecord *mrec);
45 static int hammer_mirror_update(hammer_cursor_t cursor,
46                                 struct hammer_ioc_mrecord *mrec);
47 static int hammer_mirror_write(hammer_cursor_t cursor,
48                                 struct hammer_ioc_mrecord *mrec,
49                                 hammer_inode_t ip, char *udata);
50 static int hammer_mirror_localize_data(hammer_data_ondisk_t data,
51                                 hammer_btree_leaf_elm_t leaf);
52
53 /*
54  * All B-Tree records within the specified key range which also conform
55  * to the transaction id range are returned.  Mirroring code keeps track
56  * of the last transaction id fully scanned and can efficiently pick up
57  * where it left off if interrupted.
58  */
59 int
60 hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
61                        struct hammer_ioc_mirror_rw *mirror)
62 {
63         struct hammer_cursor cursor;
64         struct hammer_ioc_mrecord mrec;
65         hammer_btree_leaf_elm_t elm;
66         const int head_size = HAMMER_MREC_HEADSIZE;
67         const int crc_start = HAMMER_MREC_CRCOFF;
68         char *uptr;
69         int error;
70         int data_len;
71         int bytes;
72
73         if ((mirror->key_beg.localization | mirror->key_end.localization) &
74             HAMMER_LOCALIZE_PSEUDOFS_MASK) {
75                 return(EINVAL);
76         }
77         if (hammer_btree_cmp(&mirror->key_beg, &mirror->key_end) > 0)
78                 return(EINVAL);
79
80         mirror->key_cur = mirror->key_beg;
81         mirror->key_cur.localization += ip->obj_localization;
82         bzero(&mrec, sizeof(mrec));
83
84 retry:
85         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
86         if (error) {
87                 hammer_done_cursor(&cursor);
88                 goto failed;
89         }
90         cursor.key_beg = mirror->key_cur;
91         cursor.key_end = mirror->key_end;
92         cursor.key_end.localization += ip->obj_localization;
93
94         cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
95         cursor.flags |= HAMMER_CURSOR_BACKEND;
96
97         /*
98          * This flag filters the search to only return elements whos create
99          * or delete TID is >= mirror_tid.  The B-Tree uses the mirror_tid
100          * field stored with internal and leaf nodes to shortcut the scan.
101          */
102         cursor.flags |= HAMMER_CURSOR_MIRROR_FILTERED;
103         cursor.mirror_tid = mirror->tid_beg;
104
105         error = hammer_btree_first(&cursor);
106         while (error == 0) {
107                 /*
108                  * Leaf node.  Only return elements modified in the range
109                  * requested by userland.
110                  */
111                 KKASSERT(cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF);
112                 elm = &cursor.node->ondisk->elms[cursor.index].leaf;
113
114                 if (elm->base.create_tid < mirror->tid_beg ||
115                     elm->base.create_tid >= mirror->tid_end) {
116                         if (elm->base.delete_tid < mirror->tid_beg ||
117                             elm->base.delete_tid >= mirror->tid_end) {
118                                 goto skip;
119                         }
120                 }
121
122                 mirror->key_cur = elm->base;
123
124                 /*
125                  * Yield to more important tasks
126                  */
127                 if ((error = hammer_signal_check(trans->hmp)) != 0)
128                         break;
129                 if (trans->hmp->sync_lock.wanted) {
130                         tsleep(trans, 0, "hmrslo", hz / 10);
131                 }
132                 if (trans->hmp->locked_dirty_space +
133                     trans->hmp->io_running_space > hammer_limit_dirtybufspace) {
134                         hammer_flusher_async(trans->hmp);
135                         tsleep(trans, 0, "hmrslo", hz / 10);
136                 }
137
138                 /*
139                  * The core code exports the data to userland.
140                  */
141                 data_len = (elm->data_offset) ? elm->data_len : 0;
142                 if (data_len) {
143                         error = hammer_btree_extract(&cursor,
144                                                      HAMMER_CURSOR_GET_DATA);
145                         if (error)
146                                 break;
147                 }
148                 bytes = sizeof(struct hammer_ioc_mrecord) + data_len;
149                 bytes = (bytes + HAMMER_HEAD_ALIGN_MASK) &
150                         ~HAMMER_HEAD_ALIGN_MASK;
151                 if (mirror->count + bytes > mirror->size)
152                         break;
153
154                 /*
155                  * Construct the record for userland and copyout.
156                  *
157                  * The user is asking for a snapshot, if the record was
158                  * deleted beyond the user-requested ending tid, the record
159                  * is not considered deleted from the point of view of
160                  * userland and delete_tid is cleared.
161                  */
162                 mrec.signature = HAMMER_IOC_MIRROR_SIGNATURE;
163                 mrec.type = HAMMER_MREC_TYPE_REC;
164                 mrec.rec_size = bytes;
165                 mrec.leaf = *elm;
166                 if (elm->base.delete_tid >= mirror->tid_end)
167                         mrec.leaf.base.delete_tid = 0;
168                 mrec.rec_crc = crc32(&mrec.rec_size, head_size - crc_start);
169                 uptr = (char *)mirror->ubuf + mirror->count;
170                 error = copyout(&mrec, uptr, head_size);
171                 if (data_len && error == 0) {
172                         error = copyout(cursor.data, uptr + head_size,
173                                         data_len);
174                 }
175                 if (error == 0)
176                         mirror->count += bytes;
177 skip:
178                 if (error == 0) {
179                         cursor.flags |= HAMMER_CURSOR_ATEDISK;
180                         error = hammer_btree_iterate(&cursor);
181                 }
182         }
183         if (error == ENOENT) {
184                 mirror->key_cur = mirror->key_end;
185                 error = 0;
186         }
187         hammer_done_cursor(&cursor);
188         if (error == EDEADLK)
189                 goto retry;
190         if (error == EINTR) {
191                 mirror->head.flags |= HAMMER_IOC_HEAD_INTR;
192                 error = 0;
193         }
194 failed:
195         mirror->key_cur.localization &= HAMMER_LOCALIZE_MASK;
196         return(error);
197 }
198
199 /*
200  * Copy records from userland to the target mirror.  Records which already
201  * exist may only have their delete_tid updated.
202  *
203  * The passed ip is the root ip of the pseudofs
204  */
205 int
206 hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
207                        struct hammer_ioc_mirror_rw *mirror)
208 {
209         struct hammer_cursor cursor;
210         struct hammer_ioc_mrecord mrec;
211         const int head_size = HAMMER_MREC_HEADSIZE;
212         const int crc_start = HAMMER_MREC_CRCOFF;
213         u_int32_t rec_crc;
214         int error;
215         char *uptr;
216
217         if (mirror->size < 0 || mirror->size > 0x70000000)
218                 return(EINVAL);
219
220         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
221 retry:
222         hammer_normalize_cursor(&cursor);
223
224         while (error == 0 && mirror->count + head_size <= mirror->size) {
225                 /*
226                  * Acquire and validate header
227                  */
228                 uptr = (char *)mirror->ubuf + mirror->count;
229                 error = copyin(uptr, &mrec, head_size);
230                 if (error)
231                         break;
232                 rec_crc = crc32(&mrec.rec_size, head_size - crc_start);
233                 if (mrec.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
234                         error = EINVAL;
235                         break;
236                 }
237                 if (mrec.type != HAMMER_MREC_TYPE_REC) {
238                         error = EINVAL;
239                         break;
240                 }
241                 if (rec_crc != mrec.rec_crc) {
242                         error = EINVAL;
243                         break;
244                 }
245                 if (mrec.rec_size < head_size ||
246                     mrec.rec_size > head_size + HAMMER_XBUFSIZE + 16 ||
247                     mirror->count + mrec.rec_size > mirror->size) {
248                         error = EINVAL;
249                         break;
250                 }
251                 if (mrec.leaf.data_len < 0 || 
252                     mrec.leaf.data_len > HAMMER_XBUFSIZE ||
253                     sizeof(struct hammer_ioc_mrecord) + mrec.leaf.data_len > mrec.rec_size) {
254                         error = EINVAL;
255                 }
256
257                 /*
258                  * Re-localize for target.  relocalization of data is handled
259                  * by hammer_mirror_write().
260                  */
261                 mrec.leaf.base.localization &= HAMMER_LOCALIZE_MASK;
262                 mrec.leaf.base.localization += ip->obj_localization;
263
264                 /*
265                  * Locate the record.
266                  *
267                  * If the record exists only the delete_tid may be updated.
268                  *
269                  * If the record does not exist we create it.  For now we
270                  * ignore records with a non-zero delete_tid.  Note that
271                  * mirror operations are effective an as-of operation and
272                  * delete_tid can be 0 for mirroring purposes even if it is
273                  * not actually 0 at the originator.
274                  */
275                 hammer_normalize_cursor(&cursor);
276                 cursor.key_beg = mrec.leaf.base;
277                 cursor.flags |= HAMMER_CURSOR_BACKEND;
278                 cursor.flags &= ~HAMMER_CURSOR_INSERT;
279                 error = hammer_btree_lookup(&cursor);
280
281                 if (error == 0 && hammer_mirror_check(&cursor, &mrec)) {
282                         hammer_sync_lock_sh(trans);
283                         error = hammer_mirror_update(&cursor, &mrec);
284                         hammer_sync_unlock(trans);
285                 } else if (error == ENOENT && mrec.leaf.base.delete_tid == 0) {
286                         hammer_sync_lock_sh(trans);
287                         error = hammer_mirror_write(&cursor, &mrec, ip,
288                                                     uptr + head_size);
289                         hammer_sync_unlock(trans);
290                 }
291
292                 /*
293                  * Setup for loop
294                  */
295                 if (error == EDEADLK) {
296                         hammer_done_cursor(&cursor);
297                         error = hammer_init_cursor(trans, &cursor, NULL, NULL);
298                         goto retry;
299                 }
300                 if (error == 0) {
301                         mirror->count += mrec.rec_size;
302                 }
303         }
304         hammer_done_cursor(&cursor);
305         return(0);
306 }
307
308 /*
309  * Check whether an update is needed in the case where a match already
310  * exists on the target.  The only type of update allowed in this case
311  * is an update of the delete_tid.
312  *
313  * Return non-zero if the update should proceed.
314  */
315 static
316 int
317 hammer_mirror_check(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec)
318 {
319         hammer_btree_leaf_elm_t leaf = cursor->leaf;
320
321         if (leaf->base.delete_tid != mrec->leaf.base.delete_tid) {
322                 if (leaf->base.delete_tid != 0)
323                         return(1);
324         }
325         return(0);
326 }
327
328 /*
329  * Update a record in-place.  Only the delete_tid can change.
330  */
331 static
332 int
333 hammer_mirror_update(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec)
334 {
335         hammer_btree_leaf_elm_t elm;
336
337         elm = cursor->leaf;
338
339         if (mrec->leaf.base.delete_tid == 0) {
340                 kprintf("mirror_write: object %016llx:%016llx deleted on "
341                         "target, not deleted on source\n",
342                         elm->base.obj_id, elm->base.key);
343                 return(0);
344         }
345
346         KKASSERT(elm->base.create_tid < mrec->leaf.base.delete_tid);
347         hammer_modify_node(cursor->trans, cursor->node, elm, sizeof(*elm));
348         elm->base.delete_tid = mrec->leaf.base.delete_tid;
349         elm->delete_ts = mrec->leaf.delete_ts;
350         hammer_modify_node_done(cursor->node);
351         return(0);
352 }
353
354 /*
355  * Write out a new record.
356  *
357  * XXX this is messy.
358  */
359 static
360 int
361 hammer_mirror_write(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec,
362                     hammer_inode_t ip, char *udata)
363 {
364         hammer_buffer_t data_buffer = NULL;
365         hammer_off_t ndata_offset;
366         void *ndata;
367         int error;
368         int doprop;
369         int wanted_skip = 0;
370
371         if (mrec->leaf.data_len && mrec->leaf.data_offset) {
372                 ndata = hammer_alloc_data(cursor->trans, mrec->leaf.data_len,
373                                           mrec->leaf.base.rec_type,
374                                           &ndata_offset, &data_buffer, &error);
375                 if (ndata == NULL)
376                         return(error);
377                 mrec->leaf.data_offset = ndata_offset;
378                 hammer_modify_buffer(cursor->trans, data_buffer, NULL, 0);
379                 error = copyin(udata, ndata, mrec->leaf.data_len);
380                 if (error == 0) {
381                         if (hammer_crc_test_leaf(ndata, &mrec->leaf) == 0) {
382                                 kprintf("data crc mismatch on pipe\n");
383                                 error = EINVAL;
384                         } else {
385                                 error = hammer_mirror_localize_data(
386                                                         ndata, &mrec->leaf);
387                                 if (error)
388                                         wanted_skip = 1;
389                         }
390                 }
391                 hammer_modify_buffer_done(data_buffer);
392         } else {
393                 mrec->leaf.data_offset = 0;
394                 error = 0;
395                 ndata = NULL;
396         }
397         if (error)
398                 goto failed;
399         cursor->flags |= HAMMER_CURSOR_INSERT;
400         error = hammer_btree_lookup(cursor);
401         if (error != ENOENT) {
402                 if (error == 0)
403                         error = EALREADY;
404                 goto failed;
405         }
406         error = 0;
407
408         /*
409          * Physical insertion
410          */
411         error = hammer_btree_insert(cursor, &mrec->leaf, &doprop);
412         if (error == 0 && doprop)
413                 hammer_btree_do_propagation(cursor, ip, &mrec->leaf);
414
415 failed:
416         /*
417          * Cleanup
418          */
419         if (error && mrec->leaf.data_offset) {
420                 hammer_blockmap_free(cursor->trans,
421                                      mrec->leaf.data_offset,
422                                      mrec->leaf.data_len);
423         }
424         if (data_buffer)
425                 hammer_rel_buffer(data_buffer, 0);
426         if (wanted_skip)
427                 error = 0;
428         return(error);
429 }
430
431 /*
432  * Localize the data payload.  Directory entries may need their
433  * localization adjusted.
434  *
435  * Pseudo-fs directory entries must be skipped entirely (EBADF).
436  *
437  * The root inode must be skipped, it will exist on the target with a
438  * different create_tid so updating it would result in a duplicate.  This
439  * also prevents inode updates on the root directory (aka mtime, ctime, etc)
440  * from mirroring, which is ok.
441  *
442  * XXX Root directory inode updates - parent_obj_localization is broken.
443  */
444 static
445 int
446 hammer_mirror_localize_data(hammer_data_ondisk_t data,
447                             hammer_btree_leaf_elm_t leaf)
448 {
449         int modified = 0;
450         int error = 0;
451         u_int32_t localization;
452
453         if (leaf->base.rec_type == HAMMER_RECTYPE_DIRENTRY) {
454                 localization = leaf->base.localization &
455                                HAMMER_LOCALIZE_PSEUDOFS_MASK;
456                 if (data->entry.localization != localization) {
457                         data->entry.localization = localization;
458                         modified = 1;
459                 }
460                 if (data->entry.obj_id == 1)
461                         error = EBADF;
462         }
463         if (leaf->base.obj_id == HAMMER_OBJID_ROOT) {
464                 if (leaf->base.rec_type == HAMMER_RECTYPE_INODE ||
465                     leaf->base.rec_type == HAMMER_RECTYPE_FIX) {
466                         error = EBADF;
467                 }
468         }
469         if (modified)
470                 hammer_crc_set_leaf(data, leaf);
471         return(error);
472 }
473
474 /*
475  * Set mirroring/pseudo-fs information
476  */
477 int
478 hammer_ioc_set_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
479                         struct hammer_ioc_pseudofs_rw *pfs)
480 {
481         hammer_pseudofs_inmem_t pfsm;
482         int error;
483
484         pfsm = ip->pfsm;
485         error = 0;
486
487         if (pfs->pseudoid != ip->obj_localization)
488                 error = EINVAL;
489         if (pfs->bytes != sizeof(pfsm->pfsd))
490                 error = EINVAL;
491         if (pfs->version != HAMMER_IOC_PSEUDOFS_VERSION)
492                 error = EINVAL;
493         if (error == 0 && pfs->ondisk) {
494                 if (ip->obj_id != HAMMER_OBJID_ROOT)
495                         error = EINVAL;
496                 if (error == 0) {
497                         error = copyin(pfs->ondisk, &ip->pfsm->pfsd,
498                                        sizeof(ip->pfsm->pfsd));
499                 }
500                 if (error == 0)
501                         error = hammer_save_pseudofs(trans, ip);
502         }
503         return(error);
504 }
505
506 /*
507  * Get mirroring/pseudo-fs information
508  */
509 int
510 hammer_ioc_get_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
511                         struct hammer_ioc_pseudofs_rw *pfs)
512 {
513         hammer_pseudofs_inmem_t pfsm;
514         int error;
515
516         pfs->pseudoid = ip->obj_localization;
517         pfs->bytes = sizeof(struct hammer_pseudofs_data);
518         pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
519
520         /*
521          * Update pfsm->sync_end_tid if a master
522          */
523         pfsm = ip->pfsm;
524         if (pfsm->pfsd.master_id >= 0)
525                 pfsm->pfsd.sync_end_tid = trans->rootvol->ondisk->vol0_next_tid;
526
527         /*
528          * Return PFS information for root inodes only.
529          */
530         error = 0;
531         if (pfs->ondisk) {
532                 if (ip->obj_id != HAMMER_OBJID_ROOT)
533                         error = EINVAL;
534                 if (error == 0) {
535                         error = copyout(&ip->pfsm->pfsd, pfs->ondisk,
536                                         sizeof(ip->pfsm->pfsd));
537                 }
538         }
539         return(error);
540 }
541