diff --git a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_helpers.c b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_helpers.c
index 706d6aebb..a7d174c69 100644
--- a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_helpers.c
+++ b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_helpers.c
@@ -56,6 +56,28 @@
 #define mdc_chunk_first_dirent(c) \
 	glist_first_entry(&(c)->dirents, mdcache_dir_entry_t, chunk_list)
 
+/**
+ * @brief Drop refs for state chunks
+ *
+ * Drop a ref from each chunk, making sure keep has one
+ *
+ * @param[in] a		First chunk
+ * @param[in] b		Second chunk
+ * @param[in] c		Third chunk
+ * @param[in] keep	Chunk to keep a ref
+ */
+static void drop_state_chunk_refs(struct dir_chunk *a, struct dir_chunk *b,
+				  struct dir_chunk *c, struct dir_chunk *keep)
+{
+	if (keep) {
+		mdcache_lru_ref_chunk(keep);
+	}
+
+	mdcache_lru_unref_chunk(a);
+	mdcache_lru_unref_chunk(b);
+	mdcache_lru_unref_chunk(c);
+}
+
 static inline bool trust_negative_cache(mdcache_entry_t *parent)
 {
 	bool trust = op_ctx_export_has_option(
@@ -217,7 +239,29 @@ static mdcache_entry_t *_mdcache_alloc_handle(
 }
 
 /**
+ * @brief Drop ref on remaining dirents in chunk
  *
+ * @note content_lock of parent dir must be held for WRITE
+ *
+ * @param[in]  dirent	First ref'd dirent
+ */
+static void mdc_unref_chunk_dirents(struct dir_chunk *chunk,
+			    mdcache_dir_entry_t *dirent)
+{
+	for (;
+	     dirent != NULL;
+	     dirent = glist_next_entry(&chunk->dirents,
+				       mdcache_dir_entry_t,
+				       chunk_list,
+				       &dirent->chunk_list)) {
+		if (dirent->entry) {
+			mdcache_put(dirent->entry);
+			dirent->entry = NULL;
+		}
+	}
+}
+
+/**
  * @brief Cleans up an entry so it can be reused
  *
  * @param[in]  entry     The cache entry to clean
@@ -403,17 +447,18 @@ mdc_get_parent_handle(struct mdcache_fsal_export *export,
 
 /* entry's content_lock must not be held, this function will
 get the content_lock in exclusive mode */
-void
+fsal_status_t
 mdc_get_parent(struct mdcache_fsal_export *export, mdcache_entry_t *entry,
 	       struct gsh_buffdesc *parent_out)
 {
 	struct fsal_obj_handle *sub_handle = NULL;
-	fsal_status_t status;
+	fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 };
 
 	PTHREAD_RWLOCK_wrlock(&entry->content_lock);
 
 	if (entry->obj_handle.type != DIRECTORY) {
 		/* Parent pointer only for directories */
+		status.major = ERR_FSAL_INVAL;
 		goto out;
 	}
 
@@ -430,16 +475,26 @@ mdc_get_parent(struct mdcache_fsal_export *export, mdcache_entry_t *entry,
 			    entry->sub_handle, "..", &sub_handle, NULL)
 	       );
 
-	if (FSAL_IS_ERROR(status)) {
-		/* Top of filesystem */
-		goto copy_parent_out;
-	}
-
-	mdcache_free_fh(&entry->fsobj.fsdir.parent);
-	mdc_get_parent_handle(export, entry, sub_handle);
+	if (FSAL_IS_SUCCESS(status)) {
+		/* if we already had a parent handle, then we are
+		 * going to refresh it.
+		 */
+		mdcache_free_fh(&entry->fsobj.fsdir.parent);
+		mdc_get_parent_handle(export, entry, sub_handle);
+	} else if (entry->fsobj.fsdir.parent.len != 0) {
+		/* Lookup of (..) failed, but if we had a cached
+		 * parent handle then we will keep the same and
+		 * not fail this request for getting parent.
+		 */
+		LogDebug(COMPONENT_CACHE_INODE,
+			 "Lookup for (..) failed for entry: %p, but we have a "
+			 "cached parent handle so we will keep it", entry);
+		status.major = ERR_FSAL_NO_ERROR;
+	} else
+		goto out;
 
 copy_parent_out:
-	if (parent_out != NULL  && entry->fsobj.fsdir.parent.len != 0) {
+	if (parent_out != NULL) {
 		/* Copy the parent handle to parent_out */
 		mdcache_copy_fh(parent_out, &entry->fsobj.fsdir.parent);
 	}
@@ -453,6 +508,7 @@ out:
 			    sub_handle->obj_ops->release(sub_handle)
 			   );
 	}
+	return status;
 }
 
 /**
@@ -1179,12 +1235,13 @@ fsal_status_t mdc_lookup(mdcache_entry_t *mdc_parent, const char *name,
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"Lookup parent (..) of %p", mdc_parent);
 
-		mdc_get_parent(export, mdc_parent, &tmpfh);
+		status = mdc_get_parent(export, mdc_parent, &tmpfh);
 
-		status =  mdcache_locate_host(&tmpfh, export, new_entry,
-					      attrs_out);
-
-		mdcache_free_fh(&tmpfh);
+		if (FSAL_IS_SUCCESS(status)) {
+			status =  mdcache_locate_host(&tmpfh, export, new_entry,
+						      attrs_out);
+			mdcache_free_fh(&tmpfh);
+		}
 
 		if (status.major == ERR_FSAL_STALE)
 			status.major = ERR_FSAL_NOENT;
@@ -2043,6 +2100,8 @@ void place_new_dirent(mdcache_entry_t *parent_dir,
 		 */
 		chunk->next_ck = here->ck;
 
+		mdcache_lru_unref_chunk(split);
+
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"Chunk next_ck=%"PRIx64,
 				chunk->next_ck);
@@ -2092,9 +2151,6 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 			 fsal_cookie_t cookie)
 {
 	struct mdcache_populate_cb_state *state = dir_state;
-	struct dir_chunk *chunk = state->cur_chunk;
-	mdcache_entry_t *mdc_parent = container_of(&state->dir->obj_handle,
-						   mdcache_entry_t, obj_handle);
 	struct mdcache_fsal_export *export = mdc_cur_export();
 	mdcache_entry_t *new_entry = NULL;
 	mdcache_dir_entry_t *new_dir_entry = NULL, *allocated_dir_entry = NULL;
@@ -2104,27 +2160,27 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 	enum fsal_dir_result result = DIR_CONTINUE;
 
 #ifdef DEBUG_MDCACHE
-	assert(mdc_parent->content_lock.__data.__writer);
+	assert(state->dir->content_lock.__data.__cur_writer);
 #endif
 
-	if (chunk->num_entries == mdcache_param.dir.avl_chunk) {
+	if (state->cur_chunk->num_entries == mdcache_param.dir.avl_chunk) {
 		/* We are being called readahead. */
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"Readdir readahead first entry in new chunk %s",
 				name);
 
-		state->prev_chunk = chunk;
+		mdcache_lru_unref_chunk(state->prev_chunk);
+		state->prev_chunk = state->cur_chunk;
 		state->prev_chunk->next_ck = cookie;
 
 		/* Chunk is added to the chunks list before being passed in */
 		/* Now start a new chunk, passing this chunk as prev_chunk. */
-		chunk = mdcache_get_chunk(chunk->parent, chunk, 0);
-
-		state->cur_chunk = chunk;
+		state->cur_chunk = mdcache_get_chunk(state->prev_chunk->parent,
+						     state->prev_chunk, 0);
 
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"Chunk %p Prev chunk %p next_ck=%" PRIx64,
-				chunk, state->prev_chunk,
+				state->cur_chunk, state->prev_chunk,
 				state->prev_chunk->next_ck);
 		/* And start accepting entries into the new chunk. */
 	}
@@ -2141,7 +2197,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 		*state->status = status;
 		LogInfoAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 			   "mdcache_new_entry failed on %s in dir %p with %s",
-			   name, mdc_parent, fsal_err_txt(status));
+			   name, state->dir, fsal_err_txt(status));
 		return DIR_TERMINATE;
 	}
 
@@ -2152,10 +2208,10 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 			"Add mdcache entry %p for %s for FSAL %s",
 			new_entry, name, new_entry->sub_handle->fsal->name);
 
-	/* in cache avl, we always insert on mdc_parent */
+	/* in cache avl, we always insert on state->dir */
 	new_dir_entry = gsh_calloc(1, sizeof(mdcache_dir_entry_t) + namesize);
 	new_dir_entry->flags = DIR_ENTRY_FLAG_NONE;
-	new_dir_entry->chunk = chunk;
+	new_dir_entry->chunk = state->cur_chunk;
 	new_dir_entry->ck = cookie;
 	allocated_dir_entry = new_dir_entry;
 
@@ -2172,7 +2228,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 	mdcache_key_dup(&new_dir_entry->ckey, &new_entry->fh_hk.key);
 
 	/* add to avl */
-	code = mdcache_avl_insert(mdc_parent, &new_dir_entry);
+	code = mdcache_avl_insert(state->dir, &new_dir_entry);
 
 	if (code < 0) {
 		/* We can get here with the following possibilities:
@@ -2209,7 +2265,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 	}
 
 	/* Note that if this dirent was already in the lookup by name AVL
-	 * tree (mdc_parent->fsobj.fsdir.avl.t), then mdcache_avl_qp_insert
+	 * tree (state->dir->fsobj.fsdir.avl.t), then mdcache_avl_qp_insert
 	 * freed the dirent we allocated above, and returned the one that was
 	 * in tree. It will have set chunk, ck, and nk.
 	 *
@@ -2221,7 +2277,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 				"Swapped %s using %p instead of %p, new_dir_entry->chunk=%p chunk=%p",
 				new_dir_entry->name, new_dir_entry,
 				allocated_dir_entry, new_dir_entry->chunk,
-				chunk);
+				state->cur_chunk);
 	}
 
 	assert(new_dir_entry->chunk);
@@ -2240,7 +2296,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 
 		node = avltree_inline_insert(
 					&new_dir_entry->node_sorted,
-					&mdc_parent->fsobj.fsdir.avl.sorted,
+					&state->dir->fsobj.fsdir.avl.sorted,
 					avl_dirent_sorted_cmpf);
 
 		if (node != NULL) {
@@ -2278,11 +2334,12 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 		 * old dirent that was not part of a chunk, but it is NOT the
 		 * same dirent that was already part of some other chunk.
 		 */
-		glist_add_tail(&chunk->dirents, &new_dir_entry->chunk_list);
-		chunk->num_entries++;
+		glist_add_tail(&state->cur_chunk->dirents,
+			       &new_dir_entry->chunk_list);
+		state->cur_chunk->num_entries++;
 	}
 
-	if (new_dir_entry->chunk != chunk) {
+	if (new_dir_entry->chunk != state->cur_chunk) {
 		/* We have the situation where we have collided with a
 		 * previously used chunk (and thus we have a partial chunk).
 		 * Since dirent is pointing to the existing dirent and the one
@@ -2298,25 +2355,25 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 				"Collision old chunk %p next_ck=%"PRIx64
 				" new chunk %p next_ck=%"PRIx64,
 				new_dir_entry->chunk,
-				new_dir_entry->chunk->next_ck, chunk,
-				chunk->next_ck);
-		if (chunk->num_entries == 0) {
+				new_dir_entry->chunk->next_ck, state->cur_chunk,
+				state->cur_chunk->next_ck);
+		if (state->cur_chunk->num_entries == 0) {
 
 			LogFullDebugAlt(COMPONENT_NFS_READDIR,
 				    COMPONENT_CACHE_INODE,
-				    "Nuking empty Chunk %p", chunk);
+				    "Nuking empty Chunk %p", state->cur_chunk);
 			/* We read-ahead into an existing chunk, and this chunk
 			 * is empty.  Just ditch it now, to avoid any issue. */
-			mdcache_lru_unref_chunk(chunk);
-			if (state->first_chunk == chunk) {
+			mdcache_lru_unref_chunk(state->cur_chunk);
+			if (state->first_chunk == state->cur_chunk) {
 				/* Drop the first_chunk ref */
 				mdcache_lru_unref_chunk(state->first_chunk);
 				state->first_chunk = new_dir_entry->chunk;
 				/* And take the first_chunk ref */
 				mdcache_lru_ref_chunk(state->first_chunk);
 			}
-			chunk = new_dir_entry->chunk;
-			state->cur_chunk = chunk;
+			state->cur_chunk = new_dir_entry->chunk;
+			mdcache_lru_ref_chunk(state->cur_chunk);
 			if (new_dir_entry->entry) {
 				/* This was ref'd already; drop extra ref */
 				mdcache_put(new_dir_entry->entry);
@@ -2328,10 +2385,12 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 		} else {
 			LogFullDebugAlt(COMPONENT_NFS_READDIR,
 					COMPONENT_CACHE_INODE,
-					"keeping non-empty Chunk %p", chunk);
-			chunk->next_ck = new_dir_entry->ck;
+					"keeping non-empty Chunk %p",
+					state->cur_chunk);
+			state->cur_chunk->next_ck = new_dir_entry->ck;
 		}
-	} else if (chunk->num_entries == mdcache_param.dir.avl_chunk) {
+	} else if (state->cur_chunk->num_entries ==
+		   mdcache_param.dir.avl_chunk) {
 		/* Chunk is full. Since dirent is pointing to the existing
 		 * dirent and the one we allocated above has been freed we don't
 		 * need to do any cleanup.
@@ -2347,7 +2406,7 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 	if (new_entry->obj_handle.type == DIRECTORY) {
 		/* Insert Parent's key */
 		PTHREAD_RWLOCK_wrlock(&new_entry->content_lock);
-		mdc_dir_add_parent(new_entry, mdc_parent);
+		mdc_dir_add_parent(new_entry, state->dir);
 		PTHREAD_RWLOCK_unlock(&new_entry->content_lock);
 	}
 
@@ -2356,10 +2415,16 @@ mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle,
 			new_entry,
 			atomic_fetch_int32_t(&new_entry->lru.refcnt));
 
-	/* Note that this entry is ref'd, so that mdcache_readdir_chunked can
-	 * un-ref it.  Pass this ref off to the dir_entry for this purpose. */
-	assert(!new_dir_entry->entry);
-	new_dir_entry->entry = new_entry;
+	if (new_dir_entry != allocated_dir_entry && new_dir_entry->entry) {
+		/* This was swapped and already has a refcounted entry. Drop our
+		 * ref. */
+		mdcache_put(new_entry);
+	} else {
+		/* This is a new dirent, or doesn't have an entry. Pass our ref
+		 * on entry off to mdcache_readdir_chunked */
+		assert(!new_dir_entry->entry);
+		new_dir_entry->entry = new_entry;
+	}
 
 	return result;
 }
@@ -2429,10 +2494,12 @@ static struct dir_chunk *mdcache_skip_chunks(mdcache_entry_t *directory,
  * @brief Read the next chunk of a directory
  *
  * If called for an FSAL that only supports whence as the dirent name to
- * continue from, and prev_chunk is NULL, we must scan the directory from the
- * beginning. If prev_chunk is not NULL, we can scan the directory starting with
- * the last dirent name in prev_chunk, but we must still scan the directory
- * until we find whence.
+ * continue from, and @a prev_chunk is NULL, we must scan the directory from the
+ * beginning. If @a prev_chunk is not NULL, we can scan the directory starting
+ * with the last dirent name in @a prev_chunk, but we must still scan the
+ * directory until we find whence.
+ *
+ * @a prev_chunk must have a ref taken on it, and this ref will be released
  *
  * @note this returns a ref on the chunk containing @a dirent
  *
@@ -2454,30 +2521,30 @@ fsal_status_t mdcache_populate_dir_chunk(mdcache_entry_t *directory,
 	fsal_status_t status = {0, 0};
 	fsal_status_t readdir_status = {0, 0};
 	struct mdcache_populate_cb_state state;
-	struct dir_chunk *chunk;
 	attrmask_t attrmask;
 	fsal_cookie_t *whence_ptr = &whence;
 
-	chunk = mdcache_get_chunk(directory, prev_chunk, whence);
-
 	attrmask = op_ctx->fsal_export->exp_ops.fs_supported_attrs(
 					op_ctx->fsal_export) | ATTR_RDATTR_ERR;
 
-	/* Take a ref on the first chunk */
-	mdcache_lru_ref_chunk(chunk);
-
 	state.export = mdc_cur_export();
 	state.dir = directory;
 	state.status = &status;
 	state.cb = NULL;  /* We don't use the call back during chunking. */
-	state.first_chunk = state.cur_chunk = chunk;
-	state.prev_chunk = prev_chunk;
 	state.cookie = whence;
 	state.dirent = dirent;
 	state.whence_is_name = op_ctx->fsal_export->exp_ops.fs_supports(
 				op_ctx->fsal_export, fso_whence_is_name);
-	state.whence_search = state.whence_is_name && whence != 0 &&
-							prev_chunk == NULL;
+	state.whence_search = state.whence_is_name && whence != 0;
+
+	/* Set up chunks */
+	state.first_chunk = mdcache_get_chunk(directory, prev_chunk, whence);
+
+	state.cur_chunk = state.first_chunk;
+	mdcache_lru_ref_chunk(state.cur_chunk);
+
+	/* prev_chunk was passed in with a ref on it */
+	state.prev_chunk = prev_chunk;
 
 	if (state.whence_is_name) {
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
@@ -2496,12 +2563,12 @@ again:
 	 * prev_chunk has been updated to point to the last cached chunk.
 	 */
 	if (state.whence_is_name) {
-		if (prev_chunk != NULL) {
+		if (state.prev_chunk != NULL) {
 			/* Start from end of prev_chunk */
 			/* If end of directory, mark last dirent as eod. */
 			mdcache_dir_entry_t *last;
 
-			last = glist_last_entry(&prev_chunk->dirents,
+			last = glist_last_entry(&state.prev_chunk->dirents,
 						mdcache_dir_entry_t,
 						chunk_list);
 			whence_ptr = (fsal_cookie_t *)last->name;
@@ -2555,7 +2622,8 @@ again:
 			    "FSAL readdir status=%s",
 			    fsal_err_txt(readdir_status));
 		*dirent = NULL;
-		mdcache_lru_unref_chunk(chunk);
+		drop_state_chunk_refs(state.first_chunk, state.cur_chunk,
+				      state.prev_chunk, NULL);
 		return readdir_status;
 	}
 
@@ -2564,32 +2632,33 @@ again:
 			    "status=%s",
 			    fsal_err_txt(status));
 		*dirent = NULL;
-		mdcache_lru_unref_chunk(chunk);
+		drop_state_chunk_refs(state.first_chunk, state.cur_chunk,
+				      state.prev_chunk, NULL);
 		return status;
 	}
 
-	/* Recover the most recent chunk from cur_chunk, if we had readahead.
-	 * it might have changed.
-	 */
-	chunk = state.cur_chunk;
-
-	if (chunk->num_entries == 0) {
-		/* Chunk is empty - should only happen for an empty directory
-		 * but could happen if the FSAL failed to indicate end of
-		 * directory. This COULD happen on a readahead chunk, but it
+	if (state.cur_chunk->num_entries == 0) {
+		/* cur_chunk is empty - should only happen for an empty
+		 * directory but could happen if the FSAL failed to indicate end
+		 * of directory. This COULD happen on a readahead chunk, but it
 		 * would be unusual.
 		 */
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"Empty chunk");
 
-		mdcache_lru_unref_chunk(chunk);
+		/* Put our ref */
+		mdcache_lru_unref_chunk(state.cur_chunk);
+		/* Put sentinal ref */
+		mdcache_lru_unref_chunk(state.cur_chunk);
 
-		if (chunk == state.first_chunk) {
+		if (state.cur_chunk == state.first_chunk) {
 			/* We really got nothing on this readdir, so don't
 			 * return a dirent.
 			 */
 			*dirent = NULL;
-			mdcache_lru_unref_chunk(chunk);
+			mdcache_lru_unref_chunk(state.first_chunk);
+			/* cur_chunk was freed */
+			mdcache_lru_unref_chunk(state.prev_chunk);
 			LogDebugAlt(COMPONENT_NFS_READDIR,
 				    COMPONENT_CACHE_INODE,
 				    "status=%s",
@@ -2598,15 +2667,18 @@ again:
 		}
 
 		/* If the empty chunk wasn't first, then prev_chunk is valid */
-		chunk = state.prev_chunk;
+		state.cur_chunk = state.prev_chunk;
+		if (state.cur_chunk) {
+			mdcache_lru_ref_chunk(state.cur_chunk);
+		}
 	}
 
 	if (*eod_met) {
 		/* If end of directory, mark last dirent as eod. */
 		mdcache_dir_entry_t *last;
 
-		last = glist_last_entry(&chunk->dirents, mdcache_dir_entry_t,
-					chunk_list);
+		last = glist_last_entry(&state.cur_chunk->dirents,
+					mdcache_dir_entry_t, chunk_list);
 		last->eod = true;
 	}
 
@@ -2618,6 +2690,10 @@ again:
 	if (state.whence_search && *dirent == NULL) {
 		if (*eod_met) {
 			/* Did not find cookie. */
+			drop_state_chunk_refs(state.first_chunk,
+					      state.cur_chunk,
+					      state.prev_chunk,
+					      NULL);
 			status = fsalstat(ERR_FSAL_BADCOOKIE, 0);
 			LogDebugAlt(COMPONENT_NFS_READDIR,
 				    COMPONENT_CACHE_INODE,
@@ -2644,34 +2720,44 @@ again:
 				"Rescan dir to find cookie needs to continue search for %"
 				PRIx64, state.cookie);
 
-		if (chunk->next_ck != 0) {
+		if (state.cur_chunk->next_ck != 0) {
+			fsal_cookie_t next_ck = state.cur_chunk->next_ck;
+
+			mdcache_lru_unref_chunk(state.cur_chunk);
 			/* In the collision case, chunk->next_ck was set,
 			 * so now start skipping.
 			 */
 			LogFullDebugAlt(COMPONENT_NFS_READDIR,
 					COMPONENT_CACHE_INODE,
 					"Search skipping from cookie %"PRIx64,
-					chunk->next_ck);
-			chunk = mdcache_skip_chunks(directory, chunk->next_ck);
+					state.cur_chunk->next_ck);
+			state.cur_chunk = mdcache_skip_chunks(directory,
+							      next_ck);
+			mdcache_lru_ref_chunk(state.cur_chunk);
 		}
 
+		/* drop refs on state, except cur_chunk which we're saving */
+		drop_state_chunk_refs(state.first_chunk, NULL,
+				      state.prev_chunk, NULL);
+
 		/* We need to start a new FSAL readdir call, but we don't just
 		 * want to call mdcache_populate_dir_chunk raw, so set up a few
 		 * things and jump to again...
 		 */
+
 		/* The chunk we just dealt with is now prev_chunk. */
-		prev_chunk = chunk;
+		state.prev_chunk = state.cur_chunk;
 
 		LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 				"About to allocate a new chunk to continue search, prev chunk = %p",
-				prev_chunk);
+				state.prev_chunk);
 
 		/* And we need to allocate a fresh chunk. */
-		chunk = mdcache_get_chunk(directory, chunk, 0);
+		state.first_chunk = mdcache_get_chunk(directory,
+						      state.prev_chunk, 0);
+		state.cur_chunk = state.first_chunk;
+		mdcache_lru_ref_chunk(state.cur_chunk);
 
-		/* And switch over to new chunk. */
-		state.cur_chunk = chunk;
-		state.prev_chunk = prev_chunk;
 
 		/* And go start a new FSAL readdir call.  */
 		goto again;
@@ -2686,6 +2772,14 @@ again:
 					    chunk_list);
 	}
 
+	/* At this point, we have ref's on first_chunk, cur_chunk, and
+	 * prev_chunk.  Drop the refs and make sure the chunk containing dirent
+	 * is ref'd */
+	drop_state_chunk_refs(state.first_chunk, state.cur_chunk,
+			      state.prev_chunk, (*dirent)->chunk);
+
+	/* At this point, only dirent->chunk should be ref'd */
+
 	LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 		    "status=%s",
 		    fsal_err_txt(status));
@@ -2970,12 +3064,6 @@ again:
 	/* Bump the chunk in the LRU */
 	lru_bump_chunk(chunk);
 
-	/* We can drop the ref now, we've bumped.  This cannot be the last ref
-	 * drop.  To get here, we had at least 2 refs, and we also hold the
-	 * content_lock for at least read.  This means noone holds it for write,
-	 * and all final ref drops are done with it held for write. */
-	mdcache_lru_unref_chunk(chunk);
-
 	LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
 			"About to read directory=%p cookie=%" PRIx64,
 			directory, next_ck);
@@ -3042,6 +3130,7 @@ again:
 				 * reloading the chunk.
 				 */
 				first_pass = true;
+				mdcache_lru_unref_chunk(chunk);
 				chunk = NULL;
 
 				/* Now we need to look for this dirent again.
@@ -3069,6 +3158,7 @@ again:
 				/* In order to get here, we passed the has_write
 				 * check above, and took the write lock. */
 				mdcache_lru_unref_chunk(chunk);
+				mdcache_lru_unref_chunk(chunk);
 				chunk = NULL;
 				goto again;
 			}
@@ -3090,6 +3180,7 @@ again:
 				if (status.major == ERR_FSAL_STALE)
 					mdcache_kill_entry(directory);
 
+				mdcache_lru_unref_chunk(chunk);
 				return status;
 			}
 
@@ -3100,6 +3191,7 @@ again:
 				 * don't match.  If the new dirent couldn't be
 				 * placed, we need to restart readdir.
 				 */
+				mdcache_lru_unref_chunk(chunk);
 				goto restart;
 			}
 		}
@@ -3158,6 +3250,7 @@ again:
 					fsal_err_txt(status));
 
 			mdcache_put(entry);
+			mdcache_lru_unref_chunk(chunk);
 			return status;
 		}
 
@@ -3199,11 +3292,20 @@ again:
 							 MDCACHE_DIR_POPULATED);
 			}
 
+			if (has_write) {
+				/* We need to drop the ref on the rest of the
+				 * entries in this chunk, so that they don't
+				 * hang around until the directory is
+				 * invalidated. */
+				mdc_unref_chunk_dirents(chunk, dirent);
+			}
+
 			LogDebugAlt(COMPONENT_NFS_READDIR,
 				    COMPONENT_CACHE_INODE,
 				    "readdir completed, eod = %s",
 				    *eod_met ? "true" : "false");
 
+			mdcache_lru_unref_chunk(chunk);
 			PTHREAD_RWLOCK_unlock(&directory->content_lock);
 
 			return status;
@@ -3255,6 +3357,10 @@ again:
 	 *       lock just in case the next chunk actually
 	 *       happens to be populated.
 	 */
+
+	/* Note: We're passing our ref on chunk into
+	 * mdcache_populate_dir_chunk(), so don't drop it here.
+	 */
 	first_pass = false;
 	goto again;
 }
diff --git a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_int.h b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_int.h
index 6b4cc1eff..9a71349a1 100644
--- a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_int.h
+++ b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_int.h
@@ -504,7 +504,7 @@ fsal_status_t mdcache_readdir_chunked(mdcache_entry_t *directory,
 				      attrmask_t attrmask,
 				      bool *eod_met);
 
-void mdc_get_parent(struct mdcache_fsal_export *exp,
+fsal_status_t mdc_get_parent(struct mdcache_fsal_export *exp,
 		    mdcache_entry_t *entry,
 		    struct gsh_buffdesc *parent_out);
 
diff --git a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.c b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.c
index af73fcc24..738b4b693 100644
--- a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.c
+++ b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.c
@@ -869,7 +869,7 @@ lru_reap_chunk_impl(enum lru_q_id qid, mdcache_entry_t *parent)
  * @param[in] prev_chunk If non-NULL, the previous chunk in this directory
  * @param[in] whence	 If @a prev_chunk is NULL, the starting whence of chunk
  *
- * @return reused or allocated chunk
+ * @return reused or allocated chunk with a ref taken for the caller
  */
 struct dir_chunk *mdcache_get_chunk(mdcache_entry_t *parent,
 				    struct dir_chunk *prev_chunk,
@@ -920,7 +920,7 @@ struct dir_chunk *mdcache_get_chunk(mdcache_entry_t *parent,
 		chunk->reload_ck = whence;
 	}
 
-	chunk->chunk_lru.refcnt = 1;
+	chunk->chunk_lru.refcnt = 2;
 	chunk->chunk_lru.cf = 0;
 	chunk->chunk_lru.lane = lru_lane_of(chunk);
 
@@ -2033,7 +2033,7 @@ static void lru_clean_chunk(struct dir_chunk *chunk)
 	mdcache_clean_dirent_chunk(chunk);
 }
 
-void mdcache_lru_ref_chunk(struct dir_chunk *chunk)
+void _mdcache_lru_ref_chunk(struct dir_chunk *chunk, const char *func, int line)
 {
 	atomic_inc_int32_t(&chunk->chunk_lru.refcnt);
 }
@@ -2043,12 +2043,17 @@ void mdcache_lru_ref_chunk(struct dir_chunk *chunk)
  * Should be called with content_lock held in write mode.
  * @param [in] chunk	The chunk to unref
  */
-void mdcache_lru_unref_chunk(struct dir_chunk *chunk)
+void _mdcache_lru_unref_chunk(struct dir_chunk *chunk, const char *func,
+			      int line)
 {
 	int refcnt;
-	uint32_t lane = chunk->chunk_lru.lane;
+	uint32_t lane;
 	struct lru_q_lane *qlane;
 
+	if (!chunk)
+		return;
+
+	lane = chunk->chunk_lru.lane;
 	qlane = &CHUNK_LRU[lane];
 	QLOCK(qlane);
 
diff --git a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.h b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.h
index 31356febe..4933d46ce 100644
--- a/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.h
+++ b/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.h
@@ -196,8 +196,14 @@ static inline void mdcache_put(mdcache_entry_t *entry)
 	mdcache_lru_unref(entry);
 }
 
-void mdcache_lru_ref_chunk(struct dir_chunk *chunk);
-void mdcache_lru_unref_chunk(struct dir_chunk *chunk);
+#define mdcache_lru_ref_chunk(chunk) \
+	_mdcache_lru_ref_chunk(chunk, __func__, __LINE__)
+void _mdcache_lru_ref_chunk(struct dir_chunk *chunk, const char *func,
+			    int line);
+#define mdcache_lru_unref_chunk(chunk) \
+	_mdcache_lru_unref_chunk(chunk, __func__, __LINE__)
+void _mdcache_lru_unref_chunk(struct dir_chunk *chunk, const char *func,
+			      int line);
 struct dir_chunk *mdcache_get_chunk(mdcache_entry_t *parent,
 				    struct dir_chunk *prev_chunk,
 				    fsal_cookie_t whence);
