HAMMER 4/many - more core infrastructure
[dragonfly.git] / sys / vfs / hammer / hammer_object.c
1 /*
2  * Copyright (c) 2007 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_object.c,v 1.3 2007/11/20 07:16:28 dillon Exp $
35  */
36
37 #include "hammer.h"
38
39 static int hammer_mem_add(hammer_transaction_t trans,
40                              hammer_record_t record);
41 static int hammer_mem_search(hammer_cursor_t cursor, hammer_inode_t ip);
42
43 /*
44  * Red-black tree support.
45  */
46 static int
47 hammer_rec_rb_compare(hammer_record_t rec1, hammer_record_t rec2)
48 {
49         if (rec1->rec.base.base.rec_type < rec2->rec.base.base.rec_type)
50                 return(-1);
51         if (rec1->rec.base.base.rec_type > rec2->rec.base.base.rec_type)
52                 return(1);
53
54         if (rec1->rec.base.base.key < rec2->rec.base.base.key)
55                 return(-1);
56         if (rec1->rec.base.base.key > rec2->rec.base.base.key)
57                 return(1);
58
59         if (rec1->rec.base.base.create_tid < rec2->rec.base.base.create_tid)
60                 return(-1);
61         if (rec1->rec.base.base.create_tid > rec2->rec.base.base.create_tid)
62                 return(1);
63         return(0);
64 }
65
66 static int
67 hammer_rec_compare(hammer_base_elm_t info, hammer_record_t rec)
68 {
69         /*
70          * A key1->rec_type of 0 matches any record type.
71          */
72         if (info->rec_type) {
73                 if (info->rec_type < rec->rec.base.base.rec_type)
74                         return(-3);
75                 if (info->rec_type > rec->rec.base.base.rec_type)
76                         return(3);
77         }
78
79         /*
80          * There is no special case for key.  0 means 0.
81          */
82         if (info->key < rec->rec.base.base.key)
83                 return(-2);
84         if (info->key > rec->rec.base.base.key)
85                 return(2);
86
87         /*
88          * This test has a number of special cases.  create_tid in key1 is
89          * the as-of transction id, and delete_tid in key1 is NOT USED.
90          *
91          * A key1->create_tid of 0 matches any record regardles of when
92          * it was created or destroyed.  0xFFFFFFFFFFFFFFFFULL should be
93          * used to search for the most current state of the object.
94          *
95          * key2->create_tid is a HAMMER record and will never be
96          * 0.   key2->delete_tid is the deletion transaction id or 0 if
97          * the record has not yet been deleted.
98          */
99         if (info->create_tid) {
100                 if (info->create_tid < rec->rec.base.base.create_tid)
101                         return(-1);
102                 if (rec->rec.base.base.delete_tid &&
103                     info->create_tid >= rec->rec.base.base.delete_tid) {
104                         return(1);
105                 }
106         }
107         return(0);
108 }
109
110 RB_GENERATE(hammer_rec_rb_tree, hammer_record, rb_node, hammer_rec_rb_compare);
111 RB_GENERATE_XLOOKUP(hammer_rec_rb_tree, INFO, hammer_record, rb_node,
112                     hammer_rec_compare, hammer_base_elm_t);
113
114 /*
115  * Allocate a record for the caller to finish filling in
116  */
117 hammer_record_t
118 hammer_alloc_mem_record(struct hammer_transaction *trans, hammer_inode_t ip)
119 {
120         hammer_record_t record;
121
122         record = kmalloc(sizeof(*record), M_HAMMER, M_WAITOK|M_ZERO);
123         record->ip = ip;
124         return (record);
125 }
126
127 /*
128  * Release a memory record.  If the record is marked for defered deletion,
129  * destroy the record when the last reference goes away.
130  */
131 void
132 hammer_rel_mem_record(struct hammer_record **recordp)
133 {
134         hammer_record_t rec;
135
136         if ((rec = *recordp) != NULL) {
137                 if (hammer_islastref(&rec->lock)) {
138                         hammer_unref(&rec->lock);
139                         if (rec->flags & HAMMER_RECF_DELETED)
140                                 hammer_free_mem_record(rec);
141                 } else {
142                         hammer_unref(&rec->lock);
143                 }
144                 *recordp = NULL;
145         }
146 }
147
148 /*
149  * Free a record.  Clean the structure up even though we are throwing it
150  * away as a sanity check.  The actual free operation is delayed while
151  * the record is referenced.
152  */
153 void
154 hammer_free_mem_record(hammer_record_t record)
155 {
156         if (record->lock.refs) {
157                 record->flags |= HAMMER_RECF_DELETED;
158                 return;
159         }
160
161         if (record->flags & HAMMER_RECF_ONRBTREE) {
162                 RB_REMOVE(hammer_rec_rb_tree, &record->ip->rec_tree, record);
163                 record->flags &= ~HAMMER_RECF_ONRBTREE;
164         }
165         if (record->flags & HAMMER_RECF_ALLOCDATA) {
166                 kfree(record->data, M_HAMMER);
167                 record->flags &= ~HAMMER_RECF_ALLOCDATA;
168         }
169         record->data = NULL;
170         kfree(record, M_HAMMER);
171 }
172
173 /*
174  * Lookup an in-memory record given the key specified in the cursor.  Works
175  * just like hammer_btree_lookup() but operates on an inode's in-memory
176  * record list.
177  */
178 static
179 int
180 hammer_mem_search(hammer_cursor_t cursor, hammer_inode_t ip)
181 {
182         int error;
183
184         if (cursor->iprec)
185                 hammer_rel_mem_record(&cursor->iprec);
186         cursor->ip = ip;
187         cursor->iprec = hammer_rec_rb_tree_RB_LOOKUP_INFO(
188                                 &ip->rec_tree, &cursor->key_beg);
189         if (cursor->iprec == NULL) {
190                 error = ENOENT;
191         } else {
192                 hammer_ref(&cursor->iprec->lock);
193                 error = 0;
194         }
195         return(error);
196 }
197
198 /************************************************************************
199  *                   HAMMER IN-MEMORY RECORD FUNCTIONS                  *
200  ************************************************************************
201  *
202  * These functions manipulate in-memory records.  Such records typically
203  * exist prior to being committed to disk or indexed via the on-disk B-Tree.
204  */
205
206 /*
207  * Add a directory entry (dip,ncp) which references inode (ip).
208  *
209  * Note that the low 32 bits of the namekey are set temporarily to create
210  * a unique in-memory record, and may be modified a second time when the
211  * record is synchronized to disk.  In particular, the low 32 bits cannot be
212  * all 0's when synching to disk, which is not handled here.
213  */
214 int
215 hammer_ip_add_directory(struct hammer_transaction *trans,
216                      struct hammer_inode *dip, struct namecache *ncp,
217                      struct hammer_inode *ip)
218 {
219         hammer_record_t record;
220         int error;
221         int bytes;
222
223         record = hammer_alloc_mem_record(trans, dip);
224
225         bytes = ncp->nc_nlen + 1;
226
227         record->rec.entry.base.base.obj_id = dip->obj_id;
228         record->rec.entry.base.base.key = hammer_directory_namekey(ncp->nc_name, bytes - 1);
229         record->rec.entry.base.base.key += trans->hmp->namekey_iterator++;
230         record->rec.entry.base.base.create_tid = trans->tid;
231         record->rec.entry.base.base.rec_type = HAMMER_RECTYPE_DIRENTRY;
232         record->rec.entry.base.base.obj_type = ip->ino_rec.base.base.obj_type;
233         record->rec.entry.base.base.obj_id = ip->obj_id;
234         if (bytes <= sizeof(record->rec.entry.den_name)) {
235                 record->data = (void *)record->rec.entry.den_name;
236         } else {
237                 record->data = kmalloc(bytes, M_HAMMER, M_WAITOK);
238                 record->flags |= HAMMER_RECF_ALLOCDATA;
239                 bcopy(ncp->nc_name, record->data, bytes);
240         }
241         record->data_len = bytes;
242         ++ip->ino_rec.ino_nlinks;
243         hammer_modify_inode(trans, ip, HAMMER_INODE_RDIRTY);
244         error = hammer_mem_add(trans, record);
245         return(error);
246 }
247
248 /*
249  * Delete the directory entry and update the inode link count.  The
250  * cursor must be seeked to the directory entry record being deleted.
251  *
252  * NOTE: HAMMER_CURSOR_DELETE may not have been set.  XXX remove flag.
253  */
254 int
255 hammer_ip_del_directory(struct hammer_transaction *trans,
256                      hammer_cursor_t cursor, struct hammer_inode *dip,
257                      struct hammer_inode *ip)
258 {
259         int error;
260
261         if (cursor->record == &cursor->iprec->rec) {
262                 /*
263                  * The directory entry was in-memory, just scrap the
264                  * record.
265                  */
266                 hammer_free_mem_record(cursor->iprec);
267                 error = 0;
268         } else {
269                 /*
270                  * The directory entry was on-disk, mark the record and
271                  * B-Tree entry as deleted.  The B-Tree entry does not
272                  * have to be reindexed because a 'current' delete transid
273                  * will wind up in the same position as the live record.
274                  */
275                 KKASSERT(ip->flags & HAMMER_INODE_ONDISK);
276                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
277                 if (error == 0) {
278                         cursor->node->ondisk->elms[cursor->index].base.delete_tid = trans->tid;
279                         cursor->record->base.base.delete_tid = trans->tid;
280                         hammer_modify_node(cursor->node);
281                         hammer_modify_buffer(cursor->record_buffer);
282
283                 }
284         }
285
286         /*
287          * One less link
288          */
289         if (error == 0) {
290                 --ip->ino_rec.ino_nlinks;
291                 hammer_modify_inode(trans, ip, HAMMER_INODE_RDIRTY);
292         }
293         return(error);
294 }
295
296 /*
297  * Add a data record to the filesystem.
298  *
299  * This is called via the strategy code, typically when the kernel wants to
300  * flush a buffer cache buffer, so this operation goes directly to the disk.
301  */
302 int
303 hammer_ip_add_data(hammer_transaction_t trans, hammer_inode_t ip,
304                        int64_t offset, void *data, int bytes)
305 {
306         panic("hammer_ip_add_data");
307 }
308
309 /*
310  * Add the record to the inode's rec_tree.  The low 32 bits of a directory
311  * entry's key is used to deal with hash collisions in the upper 32 bits.
312  * A unique 64 bit key is generated in-memory and may be regenerated a
313  * second time when the directory record is flushed to the on-disk B-Tree.
314  */
315 static
316 int
317 hammer_mem_add(struct hammer_transaction *trans, hammer_record_t record)
318 {
319         while (RB_INSERT(hammer_rec_rb_tree, &record->ip->rec_tree, record)) {
320                 if (record->rec.base.base.rec_type != HAMMER_RECTYPE_DIRENTRY){
321                         hammer_free_mem_record(record);
322                         return (EEXIST);
323                 }
324                 if (++trans->hmp->namekey_iterator == 0)
325                         ++trans->hmp->namekey_iterator;
326                 record->rec.base.base.key &= ~(0xFFFFFFFFLL);
327                 record->rec.base.base.key |= trans->hmp->namekey_iterator;
328         }
329         record->flags |= HAMMER_RECF_ONRBTREE;
330         return(0);
331 }
332
333 /************************************************************************
334  *                   HAMMER INODE MERGED-RECORD FUNCTIONS               *
335  ************************************************************************
336  *
337  * These functions augment the B-Tree scanning functions in hammer_btree.c
338  * by merging in-memory records with on-disk records.
339  */
340
341 /*
342  * Locate a particular record either in-memory or on-disk.
343  *
344  * NOTE: This is basically a standalone routine, hammer_ip_next() may
345  * NOT be called to iterate results.
346  */
347 int
348 hammer_ip_lookup(hammer_cursor_t cursor, struct hammer_inode *ip)
349 {
350         int error;
351
352         /*
353          * If the element is in-memory return it without searching the
354          * on-disk B-Tree
355          */
356         error = hammer_mem_search(cursor, ip);
357         if (error == 0) {
358                 cursor->record = &cursor->iprec->rec;
359                 return(error);
360         }
361         if (error != ENOENT)
362                 return(error);
363
364         /*
365          * If the inode has on-disk components search the on-disk B-Tree.
366          */
367         if ((ip->flags & HAMMER_INODE_ONDISK) == 0)
368                 return(error);
369         error = hammer_btree_lookup(cursor);
370         if (error == 0)
371                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
372         return(error);
373 }
374
375 /*
376  * Locate the first record within the cursor's key_beg/key_end range,
377  * restricted to a particular inode.  0 is returned on success, ENOENT
378  * if no records matched the requested range, or some other error.
379  *
380  * When 0 is returned hammer_ip_next() may be used to iterate additional
381  * records within the requested range.
382  */
383 int
384 hammer_ip_first(hammer_cursor_t cursor, struct hammer_inode *ip)
385 {
386         int error;
387
388         /*
389          * Clean up fields and setup for merged scan
390          */
391         cursor->flags &= ~(HAMMER_CURSOR_ATEDISK | HAMMER_CURSOR_ATEMEM);
392         cursor->flags |= HAMMER_CURSOR_DISKEOF | HAMMER_CURSOR_MEMEOF;
393         if (cursor->iprec)
394                 hammer_rel_mem_record(&cursor->iprec);
395
396         /*
397          * Search the on-disk B-Tree
398          */
399         if (ip->flags & HAMMER_INODE_ONDISK) {
400                 error = hammer_btree_lookup(cursor);
401                 if (error && error != ENOENT)
402                         return(error);
403                 if (error == 0)
404                         cursor->flags &= ~HAMMER_CURSOR_DISKEOF;
405         }
406
407         /*
408          * Search the in-memory record list (Red-Black tree)
409          */
410         error = hammer_mem_search(cursor, ip);
411         if (error && error != ENOENT)
412                 return(error);
413         if (error == 0)
414                 cursor->flags &= ~HAMMER_CURSOR_MEMEOF;
415
416         /*
417          * This will return the first matching record.
418          */
419         return(hammer_ip_next(cursor));
420 }
421
422 /*
423  * Retrieve the next record in a merged iteration within the bounds of the
424  * cursor.  This call may be made multiple times after the cursor has been
425  * initially searched with hammer_ip_first().
426  *
427  * 0 is returned on success, ENOENT if no further records match the
428  * requested range, or some other error code is returned.
429  */
430 int
431 hammer_ip_next(hammer_cursor_t cursor)
432 {
433         hammer_btree_elm_t elm;
434         hammer_record_t rec;
435         int error;
436         int r;
437
438         /*
439          * Load the current on-disk and in-memory record.  If we ate any
440          * records we have to get the next one. 
441          *
442          * Get the next on-disk record
443          */
444         if (cursor->flags & HAMMER_CURSOR_ATEDISK) {
445                 if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
446                         error = hammer_btree_iterate(cursor);
447                         if (error == 0)
448                                 cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
449                         else
450                                 cursor->flags |= HAMMER_CURSOR_DISKEOF;
451                 }
452         }
453
454         /*
455          * Get the next in-memory record.  Records marked for defered
456          * deletion must be skipped.
457          */
458         if (cursor->flags & HAMMER_CURSOR_ATEMEM) {
459                 if ((cursor->flags & HAMMER_CURSOR_MEMEOF) == 0) {
460                         rec = cursor->iprec;
461                         do {
462                                 rec = hammer_rec_rb_tree_RB_NEXT(rec);
463                         } while(rec && (rec->flags & HAMMER_RECF_DELETED));
464                         if (rec) {
465                                 cursor->flags &= ~HAMMER_CURSOR_ATEMEM;
466                                 hammer_ref(&rec->lock);
467                         } else {
468                                 cursor->flags |= HAMMER_CURSOR_MEMEOF;
469                         }
470                         hammer_rel_mem_record(&cursor->iprec);
471                         cursor->iprec = rec;
472                 }
473         }
474
475         /*
476          * Extract either the disk or memory record depending on their
477          * relative position.
478          */
479         error = 0;
480         switch(cursor->flags & (HAMMER_CURSOR_DISKEOF | HAMMER_CURSOR_MEMEOF)) {
481         case 0:
482                 /*
483                  * Both entries valid
484                  */
485                 elm = &cursor->node->ondisk->elms[cursor->index];
486                 r = hammer_btree_cmp(&elm->base,
487                                      &cursor->iprec->rec.base.base);
488                 if (r < 0) {
489                         error = hammer_btree_extract(cursor,
490                                                      HAMMER_CURSOR_GET_RECORD);
491                         cursor->flags |= HAMMER_CURSOR_ATEDISK;
492                         break;
493                 }
494                 /* fall through to the memory entry */
495         case HAMMER_CURSOR_DISKEOF:
496                 /*
497                  * Only the memory entry is valid
498                  */
499                 cursor->record = &cursor->iprec->rec;
500                 cursor->flags |= HAMMER_CURSOR_ATEMEM;
501                 break;
502         case HAMMER_CURSOR_MEMEOF:
503                 /*
504                  * Only the disk entry is valid
505                  */
506                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_RECORD);
507                 cursor->flags |= HAMMER_CURSOR_ATEDISK;
508                 break;
509         default:
510                 /*
511                  * Neither entry is valid
512                  *
513                  * XXX error not set properly
514                  */
515                 cursor->record = NULL;
516                 error = ENOENT;
517                 break;
518         }
519         return(error);
520 }
521
522 /*
523  * Resolve the cursor->data pointer for the current cursor position in
524  * a merged iteration.
525  */
526 int
527 hammer_ip_resolve_data(hammer_cursor_t cursor)
528 {
529         int error;
530
531         if (cursor->iprec && cursor->record == &cursor->iprec->rec) {
532                 cursor->data = cursor->iprec->data;
533                 error = 0;
534         } else {
535                 error = hammer_btree_extract(cursor, HAMMER_CURSOR_GET_DATA);
536         }
537         return(error);
538 }
539
540 /*
541  * Delete all records within the specified range for inode ip.
542  *
543  * NOTE: An unaligned range will cause new records to be added to cover
544  * the edge cases.
545  *
546  * NOTE: ran_end is inclusive (e.g. 0,1023 instead of 0,1024).
547  */
548 int
549 hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
550                        int64_t ran_beg, int64_t ran_end)
551 {
552         struct hammer_cursor cursor;
553         hammer_record_ondisk_t rec;
554         hammer_base_elm_t base;
555         int error;
556         int64_t off;
557
558         hammer_init_cursor_ip(&cursor, ip);
559
560         cursor.key_beg.obj_id = ip->obj_id;
561         cursor.key_beg.create_tid = ip->obj_asof;
562         cursor.key_beg.delete_tid = 0;
563         cursor.key_beg.obj_type = 0;
564         cursor.key_beg.key = ran_beg;
565         cursor.key_end = cursor.key_beg;
566         if (ip->ino_rec.base.base.obj_type == HAMMER_OBJTYPE_DBFILE) {
567                 cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
568                 cursor.key_end.rec_type = HAMMER_RECTYPE_DB;
569                 cursor.key_end.key = ran_end;
570         } else {
571                 cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
572                 cursor.key_end.rec_type = HAMMER_RECTYPE_DATA;
573                 if (ran_end + MAXPHYS < ran_end)
574                         cursor.key_end.key = 0x7FFFFFFFFFFFFFFFLL;
575                 else
576                         cursor.key_end.key = ran_end + MAXPHYS;
577         }
578
579         error = hammer_ip_first(&cursor, ip);
580
581         /*
582          * Iterate through matching records and mark them as deleted.
583          */
584         while (error == 0) {
585                 rec = cursor.record;
586                 base = &rec->base.base;
587
588                 KKASSERT(base->delete_tid == 0);
589
590                 /*
591                  * There may be overlap cases for regular file data.  Also
592                  * remember the key for a regular file record is the offset
593                  * of the last byte of the record (base + len - 1), NOT the
594                  * base offset.
595                  */
596                 if (base->rec_type == HAMMER_RECTYPE_DATA) {
597                         off = base->key - rec->base.data_len + 1;
598                         /*
599                          * Check the left edge case
600                          */
601                         if (off < ran_beg) {
602                                 panic("hammer left edge case\n");
603                         }
604
605                         /*
606                          * Check the right edge case.  Note that the
607                          * record can be completely out of bounds, which
608                          * terminates the search.
609                          *
610                          * base->key is (base_offset + bytes - 1), ran_end
611                          * works the same way.
612                          */
613                         if (base->key > ran_end) {
614                                 if (base->key - rec->base.data_len + 1 > ran_end) {
615                                         kprintf("right edge OOB\n");
616                                         break;
617                                 }
618                                 panic("hammer right edge case\n");
619                         }
620                 }
621
622                 /*
623                  * Mark the record and B-Tree entry as deleted
624                  */
625                 if (cursor.record == &cursor.iprec->rec) {
626                         hammer_free_mem_record(cursor.iprec);
627
628                 } else {
629                         cursor.node->ondisk->elms[cursor.index].base.delete_tid = trans->tid;
630                         cursor.record->base.base.delete_tid = trans->tid;
631                         hammer_modify_node(cursor.node);
632                         hammer_modify_buffer(cursor.record_buffer);
633                 }
634                 error = hammer_ip_next(&cursor);
635         }
636         hammer_done_cursor(&cursor);
637         if (error == ENOENT)
638                 error = 0;
639         return(error);
640 }
641