static void debug_print_dirents(cache_entry_t *dir_pentry) { struct avltree_node *dirent_node; cache_inode_dir_entry_t *dirent; int size, ix; size = avltree_size(&dir_pentry->object.dir.cookies); LogCrit(COMPONENT_CACHE_INODE, "cookie avl size: %d", size); dirent_node = avltree_first(&dir_pentry->object.dir.cookies); ix = 0; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_c); LogCrit(COMPONENT_CACHE_INODE, "cookie: ix %d %p (%s, %"PRIu64")", ix, dirent, dirent->name.name, dirent->cookie); dirent_node = avltree_next(dirent_node); ++ix; } size = avltree_size(&dir_pentry->object.dir.dentries); LogCrit(COMPONENT_CACHE_INODE, "name avl size: %d", size); dirent_node = avltree_first(&dir_pentry->object.dir.dentries); ix = 0; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); LogCrit(COMPONENT_CACHE_INODE, "name: ix %d %p (%s, %"PRIu64")", ix, dirent, dirent->name.name, dirent->cookie); dirent_node = avltree_next(dirent_node); ++ix; } }
/** * * revalidate_cookie_cache: sync cookie avl offsets with the dentry name avl. * * If no cookies are cached, add those of any dirents in the name avl. The * entry is assumed to be write locked. * * @param pentry [IN] entry for the parent directory to be read. * * @return void * */ static void revalidate_cookie_cache(cache_entry_t *dir_pentry, cache_inode_client_t *pclient) { struct avltree_node *dirent_node; cache_inode_dir_entry_t *dirent; int size_n, size_c, ix; /* we'll try to add entries to any directory whose cookie * avl cache is currently empty (mutating dirent operations * clear it) */ #if 0 if (avltree_size(&dir_pentry->object.dir.cookies) > 0) return; #else size_c = avltree_size(&dir_pentry->object.dir.cookies); size_n = avltree_size(&dir_pentry->object.dir.dentries); if (size_c == size_n) return; /* the following is safe to call arbitrarily many times, with * CACHE_INODE_AVL_COOKIES -only-. */ cache_inode_release_dirents(dir_pentry, pclient, CACHE_INODE_AVL_COOKIES); #endif dirent_node = avltree_first(&dir_pentry->object.dir.dentries); dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); ix = 3; /* first non-reserved cookie value */ dirent_node = &dirent->node_n; while (dirent_node) { dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); dirent->cookie = ix; /* XXX we could optimize this somewhat (saving an internal * lookup in avltree_insert), by adding an avl append * operation */ (void) avltree_insert(&dirent->node_c, &dir_pentry->object.dir.cookies); dirent_node = avltree_next(dirent_node); ++ix; } return; }
static avl_unit_val_t *qp_avl_lookup_s(struct avltree *t, avl_unit_val_t *v, int maxj) { struct avltree_node *node; avl_unit_val_t *v2; uint32_t j, j2; uint32_t hk[4]; assert(avltree_size(t) < UINT64_MAX); MurmurHash3_x64_128(v->name, strlen(v->name), 67, hk); memcpy(&v->hk.k, hk, 8); for (j = 0; j < maxj; j++) { v->hk.k = (v->hk.k + (j * 2)); node = avltree_inline_lookup(&v->node_hk, t); if (node) { /* it's almost but not entirely certain that node is * related to v. in the general case, j is also not * constrained to be v->hk.p */ v2 = avltree_container_of(node, avl_unit_val_t, node_hk); if (!strcmp(v->name, v2->name)) return v2; } } /* warn crit */ return NULL; }
/* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite1(void) { if (avltree_size(&avl_tree_1) > 0) avl_unit_clear_tree(&avl_tree_1); avltree_destroy(&avl_tree_1); return 0; }
static int qp_avl_insert(struct avltree *t, avl_unit_val_t *v) { /* * Insert with quadatic, linear probing. A unique k is assured for * any k whenever size(t) < max(uint64_t). * * First try quadratic probing, with coeff. 2 (since m = 2^n.) * A unique k is not assured, since the codomain is not prime. * If this fails, fall back to linear probing from hk.k+1. * * On return, the stored key is in v->hk.k, the iteration * count in v->hk.p. **/ struct avltree_node *tmpnode; uint32_t j, j2; uint32_t hk[4]; assert(avltree_size(t) < UINT64_MAX); MurmurHash3_x64_128(v->name, strlen(v->name), 67, hk); memcpy(&v->hk.k, hk, 8); for (j = 0; j < UINT64_MAX; j++) { v->hk.k = (v->hk.k + (j * 2)); tmpnode = avltree_insert(&v->node_hk, t); if (!tmpnode) { /* success, note iterations and return */ v->hk.p = j; return 0; } } /* warn debug */ memcpy(&v->hk.k, hk, 8); for (j2 = 1 /* tried j=0 */; j2 < UINT64_MAX; j2++) { v->hk.k = v->hk.k + j2; tmpnode = avltree_insert(&v->node_hk, t); if (!tmpnode) { /* success, note iterations and return */ v->hk.p = j + j2; return 0; } j2++; } /* warn crit */ return -1; }
void avl_unit_clear_tree(struct avltree *t) { avl_unit_val_t *v; struct avltree_node *node, *next_node; if (avltree_size(t) < 1) return; node = avltree_first(t); while (node) { next_node = avltree_next(node); v = avltree_container_of(node, avl_unit_val_t, node_k); avltree_remove(&v->node_k, &avl_tree_1); avl_unit_free_val(v); node = next_node; } }
/** * * cache_inode_readdir: Reads a directory. * * Looks up for a name in a directory indicated by a cached entry. The * directory should have been cached before. * * NEW: pending new (C-language) callback based dirent unpacking into caller * structures, we eliminate copies by returning dir entries by pointer. To * permit this, we introduce lock donation. If new int pointer argument * unlock is 1 on return, the calling thread holds pentry read-locked and * must release this lock after dirent processing. * * This is the only function in the cache_inode_readdir.c file that manages MT * safety on a directory cache entry. * * @param pentry [IN] entry for the parent directory to be read. * @param cookie [IN] cookie for the readdir operation (basically the offset). * @param nbwanted [IN] Maximum number of directory entries wanted. * @param peod_met [OUT] A flag to know if end of directory was met during this call. * @param dirent_array [OUT] the resulting array of found directory entries. * @param ht [IN] hash table used for the cache, unused in this call. * @param unlock [OUT] the caller shall release read-lock on pentry when done * @param pclient [INOUT] ressource allocated by the client for the nfs management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_BAD_TYPE if entry is not related to a directory\n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_readdir(cache_entry_t * dir_pentry, cache_inode_policy_t policy, uint64_t cookie, unsigned int nbwanted, unsigned int *pnbfound, uint64_t *pend_cookie, cache_inode_endofdir_t *peod_met, cache_inode_dir_entry_t **dirent_array, hash_table_t *ht, int *unlock, cache_inode_client_t *pclient, fsal_op_context_t *pcontext, cache_inode_status_t *pstatus) { cache_inode_dir_entry_t dirent_key[1], *dirent; struct avltree_node *dirent_node; fsal_accessflags_t access_mask = 0; uint64_t inoff = cookie; int i = 0; /* Guide to parameters: * the first cookie is parameter 'cookie' * number of entries queried is set by parameter 'nbwanted' * number of found entries before eod is return is '*pnbfound' * '*peod_met' is set if end of directory is encountered */ /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; dirent = NULL; /* Set initial value of unlock */ *unlock = FALSE; /* end cookie initial value is the begin cookie */ LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: setting pend_cookie to cookie=%" PRIu64, cookie); *pend_cookie = cookie; /* stats */ pclient->stat.nb_call_total++; (pclient->stat.func_stats.nb_call[CACHE_INODE_READDIR])++; LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: parameters are cookie=%"PRIu64 "nbwanted=%u", cookie, nbwanted); /* Sanity check */ if(nbwanted == 0) { /* Asking for nothing is not a crime !!!!! * build a 'dummy' return in this case */ *pstatus = CACHE_INODE_SUCCESS; *pnbfound = 0; *peod_met = TO_BE_CONTINUED; /* stats */ (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; return *pstatus; } /* Force dir content invalidation if policy enforced no name cache */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) return cache_inode_readdir_nonamecache( dir_pentry, policy, cookie, nbwanted, pnbfound, pend_cookie, peod_met, dirent_array, ht, unlock, pclient, pcontext, pstatus ) ; P_w(&dir_pentry->lock); /* Renew the entry (to avoid having it being garbagged */ if(cache_inode_renew_entry(dir_pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++; V_w(&dir_pentry->lock); return *pstatus; } /* readdir can be done only with a directory */ if(dir_pentry->internal_md.type != DIRECTORY) { V_w(&dir_pentry->lock); *pstatus = CACHE_INODE_BAD_TYPE; /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; return *pstatus; } /* Check is user (as specified by the credentials) is authorized to read * the directory or not */ access_mask = FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR); if(cache_inode_access_no_mutex(dir_pentry, access_mask, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { V_w(&dir_pentry->lock); (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; return *pstatus; } /* Is the directory fully cached (this is done if a readdir call is done on the directory) */ if(dir_pentry->object.dir.has_been_readdir != CACHE_INODE_YES) { /* populate the cache */ if(cache_inode_readdir_populate(dir_pentry, policy, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; V_w(&dir_pentry->lock); return *pstatus; } } /* deal with dentry cache invalidates */ revalidate_cookie_cache(dir_pentry, pclient); /* Downgrade Writer lock to a reader one. */ rw_lock_downgrade(&dir_pentry->lock); /* deal with initial cookie value: * 1. cookie is invalid (-should- be checked by caller) * 2. cookie is 0 (first cookie) -- ok * 3. cookie is > than highest dirent position (error) * 4. cookie <= highest dirent position but > highest cached cookie * (currently equivalent to #2, because we pre-populate the cookie avl) * 5. cookie is in cached range -- ok */ if (cookie > 0) { if (cookie < 3) { *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } if ((inoff-3) > avltree_size(&dir_pentry->object.dir.dentries)) { LogCrit(COMPONENT_NFS_V4, "Bad initial cookie %"PRIu64, inoff); *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } /* we assert this can now succeed */ dirent_key->cookie = inoff; dirent_node = avltree_lookup(&dirent_key->node_c, &dir_pentry->object.dir.cookies); if (! dirent_node) { LogCrit(COMPONENT_NFS_READDIR, "%s: seek to cookie=%"PRIu64" fail", __func__, inoff); *pstatus = CACHE_INODE_NOT_FOUND; V_r(&dir_pentry->lock); return *pstatus; } /* switch avls */ dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_c); dirent_node = &dirent->node_n; /* client wants the cookie -after- the last we sent, and * the Linux 3.0 and 3.1.0-rc7 clients misbehave if we * resend the last one */ dirent_node = avltree_next(dirent_node); } else { /* initial readdir */ dirent_node = avltree_first(&dir_pentry->object.dir.dentries); } LogFullDebug(COMPONENT_NFS_READDIR, "About to readdir in cache_inode_readdir: pentry=%p " "cookie=%"PRIu64, dir_pentry, cookie); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *pnbfound = 0; *peod_met = TO_BE_CONTINUED; for(i = 0; i < nbwanted; ++i) { if (!dirent_node) break; dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); dirent_array[i] = dirent; (*pnbfound)++; dirent_node = avltree_next(dirent_node); } if (*pnbfound > 0) { if (!dirent) { LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: " "UNEXPECTED CASE: dirent is NULL whereas nbfound>0"); *pstatus = CACHE_INODE_INCONSISTENT_ENTRY; return CACHE_INODE_INCONSISTENT_ENTRY; } *pend_cookie = dirent->cookie; } if (! dirent_node) *peod_met = END_OF_DIR; *pstatus = cache_inode_valid(dir_pentry, CACHE_INODE_OP_GET, pclient); /* stats */ if(*pstatus != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; V_r(&dir_pentry->lock); } else { (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; *unlock = TRUE; } return *pstatus; } /* cache_inode_readdir */