コード例 #1
0
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;
    }
}
コード例 #2
0
/**
 *
 * 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;
}
コード例 #3
0
ファイル: test_mh_avl.c プロジェクト: dotbugfix/nfs-ganesha
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;
}
コード例 #4
0
ファイル: test_avl.c プロジェクト: JevonQ/nfs-ganesha
/* 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;
}
コード例 #5
0
ファイル: test_mh_avl.c プロジェクト: dotbugfix/nfs-ganesha
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;
}
コード例 #6
0
ファイル: test_avl.c プロジェクト: JevonQ/nfs-ganesha
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;
	}
}
コード例 #7
0
/**
 *
 * 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 */