static inline void displace_large_file(reiserfs_blocknr_hint_t *hint) { if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super)) hint->search_start = hint->beg + keyed_hash((char *)(&INODE_PKEY(hint->inode)->k_dir_id), 4) % (hint->end - hint->beg); else hint->search_start = hint->beg + keyed_hash((char *)(&INODE_PKEY(hint->inode)->k_objectid), 4) % (hint->end - hint->beg); }
static inline void displace_new_packing_locality (reiserfs_blocknr_hint_t *hint) { struct reiserfs_key * key = &hint->key; hint->th->displace_new_blocks = 0; hint->search_start = hint->beg + keyed_hash((char*)(&key->k_objectid),4) % (hint->end - hint->beg); }
// if root directory is empty - we set default - Yura's - hash and // warn about it // FIXME: we look for only one name in a directory. If tea and yura // bith have the same value - we ask user to send report to the // mailing list __u32 find_hash_out (struct super_block * s) { int retval; struct inode * inode; struct cpu_key key; INITIALIZE_PATH (path); struct reiserfs_dir_entry de; __u32 hash = DEFAULT_HASH; inode = s->s_root->d_inode; do { // Some serious "goto"-hater was there ;) u32 teahash, r5hash, yurahash; make_cpu_key (&key, inode, ~0, TYPE_DIRENTRY, 3); retval = search_by_entry_key (s, &key, &path, &de); if (retval == IO_ERROR) { pathrelse (&path); return UNSET_HASH ; } if (retval == NAME_NOT_FOUND) de.de_entry_num --; set_de_name_and_namelen (&de); if (deh_offset( &(de.de_deh[de.de_entry_num]) ) == DOT_DOT_OFFSET) { /* allow override in this case */ if (reiserfs_rupasov_hash(s)) { hash = YURA_HASH ; } reiserfs_warning("reiserfs: FS seems to be empty, autodetect " "is using the default hash\n"); break; } r5hash=GET_HASH_VALUE (r5_hash (de.de_name, de.de_namelen)); teahash=GET_HASH_VALUE (keyed_hash (de.de_name, de.de_namelen)); yurahash=GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen)); if ( ( (teahash == r5hash) && (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash) ) || ( (teahash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) || ( (r5hash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) ) { reiserfs_warning("reiserfs: Unable to automatically detect hash" "function for device %s\n" "please mount with -o hash={tea,rupasov,r5}\n", kdevname (s->s_dev)); hash = UNSET_HASH; break; } if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == yurahash) hash = YURA_HASH; else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == teahash) hash = TEA_HASH; else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == r5hash) hash = R5_HASH; else { reiserfs_warning("reiserfs: Unrecognised hash function for " "device %s\n", kdevname (s->s_dev)); hash = UNSET_HASH; } } while (0); pathrelse (&path); return hash; }
static inline void hundredth_slices (reiserfs_blocknr_hint_t * hint) { struct reiserfs_key * key = &hint->key; b_blocknr_t slice_start; slice_start = (keyed_hash((char*)(&key->k_dir_id),4) % 100) * (hint->end / 100); if ( slice_start > hint->search_start || slice_start + (hint->end / 100) <= hint->search_start) { hint->search_start = slice_start; } }
static inline void hash_formatted_node(reiserfs_blocknr_hint_t *hint) { char * hash_in; if (!hint->inode) hash_in = (char*)&hint->key.k_dir_id; else if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super)) hash_in = (char *)(&INODE_PKEY(hint->inode)->k_dir_id); else hash_in = (char *)(&INODE_PKEY(hint->inode)->k_objectid); hint->search_start = hint->beg + keyed_hash(hash_in, 4) % (hint->end - hint->beg); }
/* Keyed 32-bit hash function using TEA in a Davis-Meyer function */ static unsigned long get_third_component (const char * name, int len) { unsigned long res; if (!len || (len == 1 && name[0] == '.')) return DOT_OFFSET; if (len == 2 && name[0] == '.' && name[1] == '.') return DOT_DOT_OFFSET; res = keyed_hash (name, len); res = GET_HASH_VALUE(res); if (res == 0) res = 128; return res + MAX_GEN_NUMBER; }
// if root directory is empty - we set default - Yura's - hash and // warn about it // FIXME: we look for only one name in a directory. If tea and yura // bith have the same value - we ask user to send report to the // mailing list __u32 find_hash_out (struct super_block * s) { int retval; struct inode * inode; struct cpu_key key; INITIALIZE_PATH (path); struct reiserfs_dir_entry de; __u32 hash = DEFAULT_HASH; inode = s->s_root->d_inode; while (1) { make_cpu_key (&key, inode, ~0, TYPE_DIRENTRY, 3); retval = search_by_entry_key (s, &key, &path, &de); if (retval == IO_ERROR) { pathrelse (&path); return UNSET_HASH ; } if (retval == NAME_NOT_FOUND) de.de_entry_num --; set_de_name_and_namelen (&de); if (deh_offset( &(de.de_deh[de.de_entry_num]) ) == DOT_DOT_OFFSET) { /* allow override in this case */ if (reiserfs_rupasov_hash(s)) { hash = YURA_HASH ; } reiserfs_warning("reiserfs: FS seems to be empty, autodetect " "is using the default hash\n"); break; } if (GET_HASH_VALUE(yura_hash (de.de_name, de.de_namelen)) == GET_HASH_VALUE(keyed_hash (de.de_name, de.de_namelen))) { reiserfs_warning ("reiserfs: Could not detect hash function " "please mount with -o hash={tea,rupasov,r5}\n") ; hash = UNSET_HASH ; break; } if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen))) hash = YURA_HASH; else hash = TEA_HASH; break; } pathrelse (&path); return hash; }
static inline int old_hashed_relocation (reiserfs_blocknr_hint_t * hint) { b_blocknr_t border; u32 hash_in; if (hint->formatted_node || hint->inode == NULL) { return 0; } hash_in = le32_to_cpu((INODE_PKEY(hint->inode))->k_dir_id); border = hint->beg + (u32) keyed_hash(((char *) (&hash_in)), 4) % (hint->end - hint->beg - 1); if (border > hint->search_start) hint->search_start = border; return 1; }
static void inline new_hashed_relocation (reiserfs_blocknr_hint_t * hint) { char * hash_in; if (hint->formatted_node) { hash_in = (char*)&hint->key.k_dir_id; } else { if (!hint->inode) { //hint->search_start = hint->beg; hash_in = (char*)&hint->key.k_dir_id; } else if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super)) hash_in = (char *)(&INODE_PKEY(hint->inode)->k_dir_id); else hash_in = (char *)(&INODE_PKEY(hint->inode)->k_objectid); } hint->search_start = hint->beg + keyed_hash(hash_in, 4) % (hint->end - hint->beg); }
static int bmap_hash_id(struct super_block *s, u32 id) { char * hash_in = NULL; unsigned long hash; unsigned bm; if (id <= 2) { bm = 1; } else { hash_in = (char *)(&id); hash = keyed_hash(hash_in, 4); bm = hash % SB_BMAP_NR(s); if (!bm) bm = 1; } /* this can only be true when SB_BMAP_NR = 1 */ if (bm >= SB_BMAP_NR(s)) bm = 0; return bm; }
/* * If root directory is empty - we set default - Yura's - hash and warn * about it. * FIXME: we look for only one name in a directory. If tea and yura both * have the same value - we ask user to send report to the mailing list */ uint32_t find_hash_out(struct reiserfs_mount *rmp) { int retval; struct cpu_key key; INITIALIZE_PATH(path); struct reiserfs_node *ip; struct reiserfs_sb_info *sbi; struct reiserfs_dir_entry de; uint32_t hash = DEFAULT_HASH; get_root_node(rmp, &ip); if (!ip) return (UNSET_HASH); sbi = rmp->rm_reiserfs; do { uint32_t teahash, r5hash, yurahash; reiserfs_log(LOG_DEBUG, "make_cpu_key\n"); make_cpu_key(&key, ip, ~0, TYPE_DIRENTRY, 3); reiserfs_log(LOG_DEBUG, "search_by_entry_key for " "key(objectid=%d,dirid=%d)\n", key.on_disk_key.k_objectid, key.on_disk_key.k_dir_id); retval = search_by_entry_key(sbi, &key, &path, &de); if (retval == IO_ERROR) { pathrelse(&path); return (UNSET_HASH); } if (retval == NAME_NOT_FOUND) de.de_entry_num--; reiserfs_log(LOG_DEBUG, "name found\n"); set_de_name_and_namelen(&de); if (deh_offset(&(de.de_deh[de.de_entry_num])) == DOT_DOT_OFFSET) { /* Allow override in this case */ if (reiserfs_rupasov_hash(sbi)) { hash = YURA_HASH; } reiserfs_log(LOG_DEBUG, "FS seems to be empty, autodetect " "is using the default hash"); break; } r5hash = GET_HASH_VALUE(r5_hash(de.de_name, de.de_namelen)); teahash = GET_HASH_VALUE(keyed_hash(de.de_name, de.de_namelen)); yurahash = GET_HASH_VALUE(yura_hash(de.de_name, de.de_namelen)); if (((teahash == r5hash) && (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash)) || ((teahash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))))) || ((r5hash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))))) { reiserfs_log(LOG_ERR, "unable to automatically detect hash " "function. Please mount with -o " "hash={tea,rupasov,r5}"); hash = UNSET_HASH; break; } if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == yurahash) { reiserfs_log(LOG_DEBUG, "detected YURA hash\n"); hash = YURA_HASH; } else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == teahash) { reiserfs_log(LOG_DEBUG, "detected TEA hash\n"); hash = TEA_HASH; } else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash) { reiserfs_log(LOG_DEBUG, "detected R5 hash\n"); hash = R5_HASH; } else { reiserfs_log(LOG_WARNING, "unrecognised hash function"); hash = UNSET_HASH; } } while (0); pathrelse(&path); return (hash); }
id = sa->id_i; } else { nonce = sa->n_i; sk = sa->sk_p_r; id = sa->id_r; } IF_TRACE({ TRACE((PLOGLOC, "SK\n")); plogdump(PLOG_DEBUG, PLOGLOC, 0, sk->v, sk->l); TRACE((PLOGLOC, "ID\n")); plogdump(PLOG_DEBUG, PLOGLOC, 0, id->v, id->l); }); /* prf(SK, ID) */ prf_output = keyed_hash(prf, sk, id); if (!prf_output) goto end; IF_TRACE({ TRACE((PLOGLOC, "prf(SK, ID)\n")); plogdump(PLOG_DEBUG, PLOGLOC, 0, prf_output->v, prf_output->l); }); /* octets = message | N | prf(SK, ID) */ octets = rc_vmalloc(message->l + nonce->l + prf_output->l); if (!octets) goto end; p = (uint8_t *)octets->v; VCONCAT(octets, p, message);
/* ** We pre-allocate 8 blocks. Pre-allocation is used for files > 16 KB only. ** This lowers fragmentation on large files by grabbing a contiguous set of ** blocks at once. It also limits the number of times the bitmap block is ** logged by making X number of allocation changes in a single transaction. ** ** We are using a border to divide the disk into two parts. The first part ** is used for tree blocks, which have a very high turnover rate (they ** are constantly allocated then freed) ** ** The second part of the disk is for the unformatted nodes of larger files. ** Putting them away from the tree blocks lowers fragmentation, and makes ** it easier to group files together. There are a number of different ** allocation schemes being tried right now, each is documented below. ** ** A great deal of the allocator's speed comes because reiserfs_get_block ** sends us the block number of the last unformatted node in the file. Once ** a given block is allocated past the border, we don't collide with the ** blocks near the search_start again. ** */ int reiserfs_new_unf_blocknrs2 (struct reiserfs_transaction_handle *th, struct inode * p_s_inode, unsigned long * free_blocknrs, unsigned long search_start) { int ret=0, blks_gotten=0; unsigned long border = 0; unsigned long bstart = 0; unsigned long hash_in, hash_out; unsigned long saved_search_start=search_start; int allocated[PREALLOCATION_SIZE]; int blks; if (!reiserfs_no_border(th->t_super)) { /* we default to having the border at the 10% mark of the disk. This ** is an arbitrary decision and it needs tuning. It also needs a limit ** to prevent it from taking too much space on huge drives. */ bstart = (SB_BLOCK_COUNT(th->t_super) / 10); } if (!reiserfs_no_unhashed_relocation(th->t_super)) { /* this is a very simple first attempt at preventing too much grouping ** around the border value. Since k_dir_id is never larger than the ** highest allocated oid, it is far from perfect, and files will tend ** to be grouped towards the start of the border */ border = le32_to_cpu(INODE_PKEY(p_s_inode)->k_dir_id) % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ; } else { /* why would we want to delcare a local variable to this if statement ** name border????? -chris ** unsigned long border = 0; */ if (!reiserfs_hashed_relocation(th->t_super)) { hash_in = le32_to_cpu((INODE_PKEY(p_s_inode))->k_dir_id); /* I wonder if the CPU cost of the hash will obscure the layout effect? Of course, whether that effect is good or bad we don't know.... :-) */ hash_out = keyed_hash(((char *) (&hash_in)), 4); border = hash_out % (SB_BLOCK_COUNT(th->t_super) - bstart - 1) ; } } border += bstart ; allocated[0] = 0 ; /* important. Allows a check later on to see if at * least one block was allocated. This prevents false * no disk space returns */ if ( (p_s_inode->i_size < 4 * 4096) || !(S_ISREG(p_s_inode->i_mode)) ) { if ( search_start < border || ( /* allow us to test whether it is a good idea to prevent files from getting too far away from their packing locality by some unexpected means. This might be poor code for directories whose files total larger than 1/10th of the disk, and it might be good code for suffering from old insertions when the disk was almost full. */ /* changed from !reiserfs_test3(th->t_super), which doesn't ** seem like a good idea. Think about adding blocks to ** a large file. If you've allocated 10% of the disk ** in contiguous blocks, you start over at the border value ** for every new allocation. This throws away all the ** information sent in about the last block that was allocated ** in the file. Not a good general case at all. ** -chris */ reiserfs_test4(th->t_super) && (search_start > border + (SB_BLOCK_COUNT(th->t_super) / 10)) ) ) search_start=border; ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 1/*amount_needed*/, 0/*use reserved blocks for root */, 1/*for_formatted*/, 0/*for prealloc */) ; return ret; } /* take a block off the prealloc list and return it -Hans */ if (p_s_inode->u.reiserfs_i.i_prealloc_count > 0) { p_s_inode->u.reiserfs_i.i_prealloc_count--; *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block++; /* if no more preallocated blocks, remove inode from list */ if (! p_s_inode->u.reiserfs_i.i_prealloc_count) { list_del(&p_s_inode->u.reiserfs_i.i_prealloc_list); } return ret; } /* else get a new preallocation for the file */ reiserfs_discard_prealloc (th, p_s_inode); /* this uses the last preallocated block as the search_start. discard ** prealloc does not zero out this number. */ if (search_start <= p_s_inode->u.reiserfs_i.i_prealloc_block) { search_start = p_s_inode->u.reiserfs_i.i_prealloc_block; } /* doing the compare again forces search_start to be >= the border, ** even if the file already had prealloction done. This seems extra, ** and should probably be removed */ if ( search_start < border ) search_start=border; /* If the disk free space is already below 10% we should ** start looking for the free blocks from the beginning ** of the partition, before the border line. */ if ( SB_FREE_BLOCKS(th->t_super) <= (SB_BLOCK_COUNT(th->t_super) / 10) ) { search_start=saved_search_start; } *free_blocknrs = 0; blks = PREALLOCATION_SIZE-1; for (blks_gotten=0; blks_gotten<PREALLOCATION_SIZE; blks_gotten++) { ret = do_reiserfs_new_blocknrs(th, free_blocknrs, search_start, 1/*amount_needed*/, 0/*for root reserved*/, 1/*for_formatted*/, (blks_gotten > 0)/*must_be_contiguous*/) ; /* if we didn't find a block this time, adjust blks to reflect ** the actual number of blocks allocated */ if (ret != CARRY_ON) { blks = blks_gotten > 0 ? (blks_gotten - 1) : 0 ; break ; } allocated[blks_gotten]= *free_blocknrs; #ifdef CONFIG_REISERFS_CHECK if ( (blks_gotten>0) && (allocated[blks_gotten] - allocated[blks_gotten-1]) != 1 ) { /* this should be caught by new_blocknrs now, checking code */ reiserfs_warning("yura-1, reiserfs_new_unf_blocknrs2: pre-allocated not contiguous set of blocks!\n") ; reiserfs_free_block(th, allocated[blks_gotten]); blks = blks_gotten-1; break; } #endif if (blks_gotten==0) { p_s_inode->u.reiserfs_i.i_prealloc_block = *free_blocknrs; } search_start = *free_blocknrs; *free_blocknrs = 0; } p_s_inode->u.reiserfs_i.i_prealloc_count = blks; *free_blocknrs = p_s_inode->u.reiserfs_i.i_prealloc_block; p_s_inode->u.reiserfs_i.i_prealloc_block++; /* if inode has preallocated blocks, link him to list */ if (p_s_inode->u.reiserfs_i.i_prealloc_count) { list_add(&p_s_inode->u.reiserfs_i.i_prealloc_list, &SB_JOURNAL(th->t_super)->j_prealloc_list); } /* we did actually manage to get 1 block */ if (ret != CARRY_ON && allocated[0] > 0) { return CARRY_ON ; } /* NO_MORE_UNUSED_CONTIGUOUS_BLOCKS should only mean something to ** the preallocation code. The rest of the filesystem asks for a block ** and should either get it, or know the disk is full. The code ** above should never allow ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCK, ** as it doesn't send for_prealloc = 1 to do_reiserfs_new_blocknrs ** unless it has already successfully allocated at least one block. ** Just in case, we translate into a return value the rest of the ** filesystem can understand. ** ** It is an error to change this without making the ** rest of the filesystem understand NO_MORE_UNUSED_CONTIGUOUS_BLOCKS ** If you consider it a bug to return NO_DISK_SPACE here, fix the rest ** of the fs first. */ if (ret == NO_MORE_UNUSED_CONTIGUOUS_BLOCKS) { #ifdef CONFIG_REISERFS_CHECK reiserfs_warning("reiser-2015: this shouldn't happen, may cause false out of disk space error"); #endif return NO_DISK_SPACE; } return ret; }