diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h index 415a4b5..48dd4d0 100644 --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -118,6 +118,7 @@ typedef struct hammer_transaction *hammer_transaction_t; #define HAMMER_TRANSF_NEWINODE 0x0001 #define HAMMER_TRANSF_DIDIO 0x0002 +#define HAMMER_TRANSF_CRCDOM 0x0004 /* EDOM on CRC error, less critical */ /* * HAMMER locks @@ -587,6 +588,9 @@ struct hammer_node { #define HAMMER_NODE_CRCGOOD 0x0004 #define HAMMER_NODE_NEEDSCRC 0x0008 #define HAMMER_NODE_NEEDSMIRROR 0x0010 +#define HAMMER_NODE_CRCBAD 0x0020 + +#define HAMMER_NODE_CRCANY (HAMMER_NODE_CRCGOOD | HAMMER_NODE_CRCBAD) typedef struct hammer_node *hammer_node_t; @@ -1028,7 +1032,7 @@ int hammer_vfs_export(struct mount *mp, int op, hammer_node_t hammer_get_node(hammer_transaction_t trans, hammer_off_t node_offset, int isnew, int *errorp); void hammer_ref_node(hammer_node_t node); -hammer_node_t hammer_ref_node_safe(struct hammer_mount *hmp, +hammer_node_t hammer_ref_node_safe(hammer_transaction_t trans, hammer_node_cache_t cache, int *errorp); void hammer_rel_node(hammer_node_t node); void hammer_delete_node(hammer_transaction_t trans, @@ -1245,12 +1249,14 @@ hammer_lock_ex(struct hammer_lock *lock) static __inline void hammer_modify_node_noundo(hammer_transaction_t trans, hammer_node_t node) { + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, NULL, 0); } static __inline void hammer_modify_node_all(hammer_transaction_t trans, struct hammer_node *node) { + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, node->ondisk, sizeof(*node->ondisk)); } @@ -1264,6 +1270,7 @@ hammer_modify_node(hammer_transaction_t trans, hammer_node_t node, KKASSERT((char *)base >= (char *)node->ondisk && (char *)base + len <= (char *)node->ondisk + sizeof(*node->ondisk)); + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, base, len); crcptr = &node->ondisk->crc; hammer_modify_buffer(trans, node->buffer, crcptr, sizeof(hammer_crc_t)); diff --git a/sys/vfs/hammer/hammer_btree.c b/sys/vfs/hammer/hammer_btree.c index e51f7f7..56a4b34 100644 --- a/sys/vfs/hammer/hammer_btree.c +++ b/sys/vfs/hammer/hammer_btree.c @@ -710,7 +710,12 @@ hammer_btree_extract(hammer_cursor_t cursor, int flags) if (hammer_crc_test_leaf(cursor->data, &elm->leaf) == 0) { kprintf("CRC DATA @ %016llx/%d FAILED\n", elm->leaf.data_offset, elm->leaf.data_len); - Debugger("CRC FAILED: DATA"); + if (hammer_debug_debug & 0x0001) + Debugger("CRC FAILED: DATA"); + if (cursor->trans->flags & HAMMER_TRANSF_CRCDOM) + error = EDOM; /* less critical (mirroring) */ + else + error = EIO; /* critical */ } return(error); } diff --git a/sys/vfs/hammer/hammer_cursor.c b/sys/vfs/hammer/hammer_cursor.c index 7b340bf..679eceb 100644 --- a/sys/vfs/hammer/hammer_cursor.c +++ b/sys/vfs/hammer/hammer_cursor.c @@ -73,7 +73,7 @@ hammer_init_cursor(hammer_transaction_t trans, hammer_cursor_t cursor, * Step 1 - acquire a locked node from the cache if possible */ if (cache && cache->node) { - node = hammer_ref_node_safe(trans->hmp, cache, &error); + node = hammer_ref_node_safe(trans, cache, &error); if (error == 0) { hammer_lock_sh(&node->lock); if (node->flags & HAMMER_NODE_DELETED) { diff --git a/sys/vfs/hammer/hammer_inode.c b/sys/vfs/hammer/hammer_inode.c index 73c75de..abe6dc1 100644 --- a/sys/vfs/hammer/hammer_inode.c +++ b/sys/vfs/hammer/hammer_inode.c @@ -2508,7 +2508,7 @@ hammer_sync_inode(hammer_transaction_t trans, hammer_inode_t ip) * out from under us. */ if (error == 0) { - tmp_node = hammer_ref_node_safe(ip->hmp, &ip->cache[0], &error); + tmp_node = hammer_ref_node_safe(trans, &ip->cache[0], &error); if (tmp_node) { hammer_cursor_downgrade(&cursor); hammer_lock_sh(&tmp_node->lock); diff --git a/sys/vfs/hammer/hammer_ioctl.h b/sys/vfs/hammer/hammer_ioctl.h index 73ab091..137d422 100644 --- a/sys/vfs/hammer/hammer_ioctl.h +++ b/sys/vfs/hammer/hammer_ioctl.h @@ -324,6 +324,13 @@ union hammer_ioc_mrecord_any { typedef union hammer_ioc_mrecord_any *hammer_ioc_mrecord_any_t; +/* + * MREC types. Flags are in the upper 16 bits but some are also included + * in the type mask to force them into any switch() on the type. + * + * NOTE: Any record whos data is CRC-errored will have HAMMER_MRECF_CRC set, + * and the bit is also part of the type mask. + */ #define HAMMER_MREC_TYPE_RESERVED 0 #define HAMMER_MREC_TYPE_REC 1 /* record w/ data */ #define HAMMER_MREC_TYPE_PFSD 2 /* (userland only) */ @@ -334,6 +341,14 @@ typedef union hammer_ioc_mrecord_any *hammer_ioc_mrecord_any_t; #define HAMMER_MREC_TYPE_TERM 7 /* (userland only) */ #define HAMMER_MREC_TYPE_IDLE 8 /* (userland only) */ +#define HAMMER_MREC_TYPE_REC_BADCRC (HAMMER_MREC_TYPE_REC | \ + HAMMER_MRECF_CRC_ERROR) + +#define HAMMER_MRECF_TYPE_MASK 0x800000FF +#define HAMMER_MRECF_CRC_ERROR 0x80000000 +#define HAMMER_MRECF_DATA_CRC_BAD 0x40000000 +#define HAMMER_MRECF_RECD_CRC_BAD 0x20000000 + #define HAMMER_MREC_CRCOFF (offsetof(struct hammer_ioc_mrecord_head, rec_size)) #define HAMMER_MREC_HEADSIZE sizeof(struct hammer_ioc_mrecord_head) diff --git a/sys/vfs/hammer/hammer_mirror.c b/sys/vfs/hammer/hammer_mirror.c index 6eaf126..4d054c1 100644 --- a/sys/vfs/hammer/hammer_mirror.c +++ b/sys/vfs/hammer/hammer_mirror.c @@ -89,6 +89,7 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip, int data_len; int bytes; int eatdisk; + int mrec_flags; u_int32_t localization; u_int32_t rec_crc; @@ -107,6 +108,12 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip, bzero(&mrec, sizeof(mrec)); bzero(&cmirror, sizeof(cmirror)); + /* + * Make CRC errors non-fatal (at least on data), causing an EDOM + * error instead of EIO. + */ + trans->flags |= HAMMER_TRANSF_CRCDOM; + retry: error = hammer_init_cursor(trans, &cursor, NULL, NULL); if (error) { @@ -224,13 +231,21 @@ retry: /* * The core code exports the data to userland. + * + * CRC errors on data are reported but passed through, + * but the data must be washed by the user program. */ + mrec_flags = 0; data_len = (elm->data_offset) ? elm->data_len : 0; if (data_len) { error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_DATA); - if (error) - break; + if (error) { + if (error != EDOM) + break; + mrec_flags |= HAMMER_MRECF_CRC_ERROR | + HAMMER_MRECF_DATA_CRC_BAD; + } } bytes = sizeof(mrec.rec) + data_len; @@ -246,9 +261,10 @@ retry: * userland and delete_tid is cleared. */ mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE; - mrec.head.type = HAMMER_MREC_TYPE_REC; + mrec.head.type = HAMMER_MREC_TYPE_REC | mrec_flags; mrec.head.rec_size = bytes; mrec.rec.leaf = *elm; + if (elm->base.delete_tid > mirror->tid_end) mrec.rec.leaf.base.delete_tid = 0; rec_crc = crc32(&mrec.head.rec_size, diff --git a/sys/vfs/hammer/hammer_object.c b/sys/vfs/hammer/hammer_object.c index aca7a29..ee30f33 100644 --- a/sys/vfs/hammer/hammer_object.c +++ b/sys/vfs/hammer/hammer_object.c @@ -703,7 +703,8 @@ failed: * cursor must be seeked to the directory entry record being deleted. * * The related inode should be share-locked by the caller. The caller is - * on the frontend. + * on the frontend. It could also be NULL indicating that the directory + * entry being removed has no related inode. * * This function can return EDEADLK requiring the caller to terminate * the cursor, any locks, wait on the returned record, and retry. @@ -749,9 +750,18 @@ hammer_ip_del_directory(struct hammer_transaction *trans, record->type = HAMMER_MEM_RECORD_DEL; record->leaf.base = cursor->leaf->base; + /* + * ip may be NULL, indicating the deletion of a directory + * entry which has no related inode. + */ record->target_ip = ip; - record->flush_state = HAMMER_FST_SETUP; - TAILQ_INSERT_TAIL(&ip->target_list, record, target_entry); + if (ip) { + record->flush_state = HAMMER_FST_SETUP; + TAILQ_INSERT_TAIL(&ip->target_list, record, + target_entry); + } else { + record->flush_state = HAMMER_FST_IDLE; + } /* * The inode now has a dependancy and must be taken out of @@ -762,7 +772,7 @@ hammer_ip_del_directory(struct hammer_transaction *trans, * reflush when the dependancies are disposed of if someone * is waiting on the inode. */ - if (ip->flush_state == HAMMER_FST_IDLE) { + if (ip && ip->flush_state == HAMMER_FST_IDLE) { hammer_ref(&ip->lock); ip->flush_state = HAMMER_FST_SETUP; if (ip->flags & HAMMER_INODE_FLUSHW) @@ -786,15 +796,18 @@ hammer_ip_del_directory(struct hammer_transaction *trans, * on-media until we unmount. */ if (error == 0) { - --ip->ino_data.nlinks; + if (ip) + --ip->ino_data.nlinks; /* do before we might block */ dip->ino_data.mtime = trans->time; hammer_modify_inode(dip, HAMMER_INODE_MTIME); - hammer_modify_inode(ip, HAMMER_INODE_DDIRTY); - if (ip->ino_data.nlinks == 0 && - (ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))) { - hammer_done_cursor(cursor); - hammer_inode_unloadable_check(ip, 1); - hammer_flush_inode(ip, 0); + if (ip) { + hammer_modify_inode(ip, HAMMER_INODE_DDIRTY); + if (ip->ino_data.nlinks == 0 && + (ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))) { + hammer_done_cursor(cursor); + hammer_inode_unloadable_check(ip, 1); + hammer_flush_inode(ip, 0); + } } } diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c index 214e1ff..cc1bf08 100644 --- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -48,7 +48,8 @@ static void hammer_free_volume(hammer_volume_t volume); static int hammer_load_volume(hammer_volume_t volume); static int hammer_load_buffer(hammer_buffer_t buffer, int isnew); -static int hammer_load_node(hammer_node_t node, int isnew); +static int hammer_load_node(hammer_transaction_t trans, + hammer_node_t node, int isnew); static int hammer_vol_rb_compare(hammer_volume_t vol1, hammer_volume_t vol2) @@ -1109,7 +1110,7 @@ again: if (node->ondisk) { *errorp = 0; } else { - *errorp = hammer_load_node(node, isnew); + *errorp = hammer_load_node(trans, node, isnew); trans->flags |= HAMMER_TRANSF_DIDIO; } if (*errorp) { @@ -1133,7 +1134,7 @@ hammer_ref_node(hammer_node_t node) * Load a node's on-disk data reference. */ static int -hammer_load_node(hammer_node_t node, int isnew) +hammer_load_node(hammer_transaction_t trans, hammer_node_t node, int isnew) { hammer_buffer_t buffer; hammer_off_t buf_offset; @@ -1175,13 +1176,28 @@ hammer_load_node(hammer_node_t node, int isnew) goto failed; node->ondisk = (void *)((char *)buffer->ondisk + (node->node_offset & HAMMER_BUFMASK)); + + /* + * Check CRC. NOTE: Neither flag is set and the CRC is not + * generated on new B-Tree nodes. + */ if (isnew == 0 && - (node->flags & HAMMER_NODE_CRCGOOD) == 0) { - if (hammer_crc_test_btree(node->ondisk) == 0) - Debugger("CRC FAILED: B-TREE NODE"); - node->flags |= HAMMER_NODE_CRCGOOD; + (node->flags & HAMMER_NODE_CRCANY) == 0) { + if (hammer_crc_test_btree(node->ondisk) == 0) { + if (hammer_debug_debug & 0x0002) + Debugger("CRC FAILED: B-TREE NODE"); + node->flags |= HAMMER_NODE_CRCBAD; + } else { + node->flags |= HAMMER_NODE_CRCGOOD; + } } } + if (node->flags & HAMMER_NODE_CRCBAD) { + if (trans->flags & HAMMER_TRANSF_CRCDOM) + error = EDOM; + else + error = EIO; + } failed: --node->loading; hammer_unlock(&node->lock); @@ -1192,7 +1208,7 @@ failed: * Safely reference a node, interlock against flushes via the IO subsystem. */ hammer_node_t -hammer_ref_node_safe(struct hammer_mount *hmp, hammer_node_cache_t cache, +hammer_ref_node_safe(hammer_transaction_t trans, hammer_node_cache_t cache, int *errorp) { hammer_node_t node; @@ -1200,10 +1216,18 @@ hammer_ref_node_safe(struct hammer_mount *hmp, hammer_node_cache_t cache, node = cache->node; if (node != NULL) { hammer_ref(&node->lock); - if (node->ondisk) - *errorp = 0; - else - *errorp = hammer_load_node(node, 0); + if (node->ondisk) { + if (node->flags & HAMMER_NODE_CRCBAD) { + if (trans->flags & HAMMER_TRANSF_CRCDOM) + *errorp = EDOM; + else + *errorp = EIO; + } else { + *errorp = 0; + } + } else { + *errorp = hammer_load_node(trans, node, 0); + } if (*errorp) { hammer_rel_node(node); node = NULL; diff --git a/sys/vfs/hammer/hammer_vnops.c b/sys/vfs/hammer/hammer_vnops.c index c2abe5f..e39a69c 100644 --- a/sys/vfs/hammer/hammer_vnops.c +++ b/sys/vfs/hammer/hammer_vnops.c @@ -1298,6 +1298,10 @@ hammer_vop_readdir(struct vop_readdir_args *ap) /* * Handle artificial entries + * + * It should be noted that the minimum value for a directory + * hash key on-media is 0x0000000100000000, so we can use anything + * less then that to represent our 'special' key space. */ error = 0; if (saveoff == 0) { @@ -2811,15 +2815,17 @@ retry: 0, &error); hammer_lock_sh(&cursor.ip->lock); if (error == ENOENT) { - kprintf("obj_id %016llx\n", cursor.data->entry.obj_id); - Debugger("ENOENT unlinking object that should exist"); + kprintf("HAMMER: Warning: Removing directory entry" + " whos inode does not exist: %016llx\n", + (long long)cursor.data->entry.obj_id); + error = 0; } /* * If isdir >= 0 we validate that the entry is or is not a * directory. If isdir < 0 we don't care. */ - if (error == 0 && isdir >= 0) { + if (error == 0 && isdir >= 0 && ip) { if (isdir && ip->ino_data.obj_type != HAMMER_OBJTYPE_DIRECTORY) { error = ENOTDIR; @@ -2841,8 +2847,8 @@ retry: * If any changes whatsoever have been made to the cursor * set EDEADLK and retry. */ - if (error == 0 && ip->ino_data.obj_type == - HAMMER_OBJTYPE_DIRECTORY) { + if (error == 0 && ip && ip->ino_data.obj_type == + HAMMER_OBJTYPE_DIRECTORY) { hammer_unlock_cursor(&cursor); error = hammer_ip_check_directory_empty(trans, ip); hammer_lock_cursor(&cursor); @@ -2870,7 +2876,7 @@ retry: cache_setunresolved(nch); cache_setvp(nch, NULL); /* XXX locking */ - if (ip->vp) { + if (ip && ip->vp) { hammer_knote(ip->vp, NOTE_DELETE); cache_inval_vp(ip->vp, CINV_DESTROY); } diff --git a/sys/vm/vm_contig.c b/sys/vm/vm_contig.c index f45ad0f..4de57af 100644 --- a/sys/vm/vm_contig.c +++ b/sys/vm/vm_contig.c @@ -474,7 +474,10 @@ contigmalloc_map( index = vm_contig_pg_alloc(size, low, high, alignment, boundary, flags); if (index < 0) { - kprintf("contigmalloc_map: failed in index < 0 case!\n"); + kprintf("contigmalloc_map: failed size %lu low=%llx " + "high=%llx align=%lu boundary=%lu flags=%08x\n", + size, (long long)low, (long long)high, + alignment, boundary, flags); return NULL; }