Skip to content

Commit a5b5bee

Browse files
dhowellsbrauner
authored andcommitted
afs: Use the contained hashtable to search a directory
Each directory image contains a hashtable with 128 buckets to speed up searching. Currently, kafs does not use this, but rather iterates over all the occupied slots in the image as it can share this with readdir. Switch kafs to use the hashtable for lookups to reduce the latency. Care must be taken that the hash chains are acyclic. Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected] cc: Marc Dionne <[email protected]> cc: [email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 836bb70 commit a5b5bee

File tree

5 files changed

+350
-73
lines changed

5 files changed

+350
-73
lines changed

fs/afs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ kafs-y := \
1111
cmservice.o \
1212
dir.o \
1313
dir_edit.o \
14+
dir_search.o \
1415
dir_silly.o \
1516
dynroot.o \
1617
file.o \

fs/afs/dir.c

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ struct afs_lookup_one_cookie {
8888
struct afs_lookup_cookie {
8989
struct dir_context ctx;
9090
struct qstr name;
91-
bool found;
92-
bool one_only;
9391
unsigned short nr_fids;
9492
struct afs_fid fids[50];
9593
};
@@ -309,7 +307,7 @@ ssize_t afs_read_single(struct afs_vnode *dvnode, struct file *file)
309307
* Read the directory into a folio_queue buffer in one go, scrubbing the
310308
* previous contents. We return -ESTALE if the caller needs to call us again.
311309
*/
312-
static ssize_t afs_read_dir(struct afs_vnode *dvnode, struct file *file)
310+
ssize_t afs_read_dir(struct afs_vnode *dvnode, struct file *file)
313311
__acquires(&dvnode->validate_lock)
314312
{
315313
ssize_t ret;
@@ -649,19 +647,10 @@ static bool afs_lookup_filldir(struct dir_context *ctx, const char *name,
649647
BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
650648
BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
651649

652-
if (cookie->found) {
653-
if (cookie->nr_fids < 50) {
654-
cookie->fids[cookie->nr_fids].vnode = ino;
655-
cookie->fids[cookie->nr_fids].unique = dtype;
656-
cookie->nr_fids++;
657-
}
658-
} else if (cookie->name.len == nlen &&
659-
memcmp(cookie->name.name, name, nlen) == 0) {
660-
cookie->fids[1].vnode = ino;
661-
cookie->fids[1].unique = dtype;
662-
cookie->found = 1;
663-
if (cookie->one_only)
664-
return false;
650+
if (cookie->nr_fids < 50) {
651+
cookie->fids[cookie->nr_fids].vnode = ino;
652+
cookie->fids[cookie->nr_fids].unique = dtype;
653+
cookie->nr_fids++;
665654
}
666655

667656
return cookie->nr_fids < 50;
@@ -789,6 +778,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)
789778
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
790779
struct inode *inode = NULL, *ti;
791780
afs_dataversion_t data_version = READ_ONCE(dvnode->status.data_version);
781+
bool supports_ibulk;
792782
long ret;
793783
int i;
794784

@@ -805,19 +795,19 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)
805795
cookie->nr_fids = 2; /* slot 1 is saved for the fid we actually want
806796
* and slot 0 for the directory */
807797

808-
if (!afs_server_supports_ibulk(dvnode))
809-
cookie->one_only = true;
810-
811-
/* search the directory */
812-
ret = afs_dir_iterate(dir, &cookie->ctx, NULL, &data_version);
798+
/* Search the directory for the named entry using the hash table... */
799+
ret = afs_dir_search(dvnode, &dentry->d_name, &cookie->fids[1], &data_version);
813800
if (ret < 0)
814801
goto out;
815802

816-
dentry->d_fsdata = (void *)(unsigned long)data_version;
803+
supports_ibulk = afs_server_supports_ibulk(dvnode);
804+
if (supports_ibulk) {
805+
/* ...then scan linearly from that point for entries to lookup-ahead. */
806+
cookie->ctx.pos = (ret + 1) * AFS_DIR_DIRENT_SIZE;
807+
afs_dir_iterate(dir, &cookie->ctx, NULL, &data_version);
808+
}
817809

818-
ret = -ENOENT;
819-
if (!cookie->found)
820-
goto out;
810+
dentry->d_fsdata = (void *)(unsigned long)data_version;
821811

822812
/* Check to see if we already have an inode for the primary fid. */
823813
inode = ilookup5(dir->i_sb, cookie->fids[1].vnode,
@@ -876,7 +866,7 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry)
876866
* the whole operation.
877867
*/
878868
afs_op_set_error(op, -ENOTSUPP);
879-
if (!cookie->one_only) {
869+
if (supports_ibulk) {
880870
op->ops = &afs_inline_bulk_status_operation;
881871
afs_begin_vnode_operation(op);
882872
afs_wait_for_operation(op);

fs/afs/dir_edit.c

Lines changed: 88 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
245245
union afs_xdr_dir_block *meta, *block;
246246
union afs_xdr_dirent *de;
247247
struct afs_dir_iter iter = { .dvnode = vnode };
248-
unsigned int need_slots, nr_blocks, b;
248+
unsigned int nr_blocks, b, entry;
249249
loff_t i_size;
250250
int slot;
251251

@@ -263,7 +263,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
263263
return;
264264

265265
/* Work out how many slots we're going to need. */
266-
need_slots = afs_dir_calc_slots(name->len);
266+
iter.nr_slots = afs_dir_calc_slots(name->len);
267267

268268
if (i_size == 0)
269269
goto new_directory;
@@ -281,7 +281,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
281281

282282
/* Lower dir blocks have a counter in the header we can check. */
283283
if (b < AFS_DIR_BLOCKS_WITH_CTR &&
284-
meta->meta.alloc_ctrs[b] < need_slots)
284+
meta->meta.alloc_ctrs[b] < iter.nr_slots)
285285
continue;
286286

287287
block = afs_dir_get_block(&iter, b);
@@ -308,7 +308,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
308308
/* We need to try and find one or more consecutive slots to
309309
* hold the entry.
310310
*/
311-
slot = afs_find_contig_bits(block, need_slots);
311+
slot = afs_find_contig_bits(block, iter.nr_slots);
312312
if (slot >= 0) {
313313
_debug("slot %u", slot);
314314
goto found_space;
@@ -347,12 +347,18 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
347347
de->u.name[name->len] = 0;
348348

349349
/* Adjust the bitmap. */
350-
afs_set_contig_bits(block, slot, need_slots);
351-
kunmap_local(block);
350+
afs_set_contig_bits(block, slot, iter.nr_slots);
352351

353352
/* Adjust the allocation counter. */
354353
if (b < AFS_DIR_BLOCKS_WITH_CTR)
355-
meta->meta.alloc_ctrs[b] -= need_slots;
354+
meta->meta.alloc_ctrs[b] -= iter.nr_slots;
355+
356+
/* Adjust the hash chain. */
357+
entry = b * AFS_DIR_SLOTS_PER_BLOCK + slot;
358+
iter.bucket = afs_dir_hash_name(name);
359+
de->u.hash_next = meta->meta.hashtable[iter.bucket];
360+
meta->meta.hashtable[iter.bucket] = htons(entry);
361+
kunmap_local(block);
356362

357363
inode_inc_iversion_raw(&vnode->netfs.inode);
358364
afs_stat_v(vnode, n_dir_cr);
@@ -387,12 +393,14 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
387393
void afs_edit_dir_remove(struct afs_vnode *vnode,
388394
struct qstr *name, enum afs_edit_dir_reason why)
389395
{
390-
union afs_xdr_dir_block *meta, *block;
391-
union afs_xdr_dirent *de;
396+
union afs_xdr_dir_block *meta, *block, *pblock;
397+
union afs_xdr_dirent *de, *pde;
392398
struct afs_dir_iter iter = { .dvnode = vnode };
393-
unsigned int need_slots, nr_blocks, b;
399+
struct afs_fid fid;
400+
unsigned int b, slot, entry;
394401
loff_t i_size;
395-
int slot;
402+
__be16 next;
403+
int found;
396404

397405
_enter(",,{%d,%s},", name->len, name->name);
398406

@@ -403,59 +411,90 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
403411
afs_invalidate_dir(vnode, afs_dir_invalid_edit_rem_bad_size);
404412
return;
405413
}
406-
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
407414

408-
meta = afs_dir_get_block(&iter, 0);
409-
if (!meta)
415+
if (!afs_dir_init_iter(&iter, name))
410416
return;
411417

412-
/* Work out how many slots we're going to discard. */
413-
need_slots = afs_dir_calc_slots(name->len);
414-
415-
/* Find a block that has sufficient slots available. Each folio
416-
* contains two or more directory blocks.
417-
*/
418-
for (b = 0; b < nr_blocks; b++) {
419-
block = afs_dir_get_block(&iter, b);
420-
if (!block)
421-
goto error;
422-
423-
/* Abandon the edit if we got a callback break. */
424-
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
425-
goto already_invalidated;
426-
427-
if (b > AFS_DIR_BLOCKS_WITH_CTR ||
428-
meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) {
429-
slot = afs_dir_scan_block(block, name, b);
430-
if (slot >= 0)
431-
goto found_dirent;
432-
}
418+
meta = afs_dir_find_block(&iter, 0);
419+
if (!meta)
420+
return;
433421

434-
kunmap_local(block);
422+
/* Find the entry in the blob. */
423+
found = afs_dir_search_bucket(&iter, name, &fid);
424+
if (found < 0) {
425+
/* Didn't find the dirent to clobber. Re-download. */
426+
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
427+
0, 0, 0, 0, name->name);
428+
afs_invalidate_dir(vnode, afs_dir_invalid_edit_rem_wrong_name);
429+
goto out_unmap;
435430
}
436431

437-
/* Didn't find the dirent to clobber. Download the directory again. */
438-
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
439-
0, 0, 0, 0, name->name);
440-
afs_invalidate_dir(vnode, afs_dir_invalid_edit_rem_wrong_name);
441-
goto out_unmap;
432+
entry = found;
433+
b = entry / AFS_DIR_SLOTS_PER_BLOCK;
434+
slot = entry % AFS_DIR_SLOTS_PER_BLOCK;
442435

443-
found_dirent:
436+
block = afs_dir_find_block(&iter, b);
437+
if (!block)
438+
goto error;
439+
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
440+
goto already_invalidated;
441+
442+
/* Check and clear the entry. */
444443
de = &block->dirents[slot];
444+
if (de->u.valid != 1)
445+
goto error_unmap;
445446

446447
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
447448
ntohl(de->u.vnode), ntohl(de->u.unique),
448449
name->name);
449450

450-
memset(de, 0, sizeof(*de) * need_slots);
451-
452451
/* Adjust the bitmap. */
453-
afs_clear_contig_bits(block, slot, need_slots);
454-
kunmap_local(block);
452+
afs_clear_contig_bits(block, slot, iter.nr_slots);
455453

456454
/* Adjust the allocation counter. */
457455
if (b < AFS_DIR_BLOCKS_WITH_CTR)
458-
meta->meta.alloc_ctrs[b] += need_slots;
456+
meta->meta.alloc_ctrs[b] += iter.nr_slots;
457+
458+
/* Clear the constituent entries. */
459+
next = de->u.hash_next;
460+
memset(de, 0, sizeof(*de) * iter.nr_slots);
461+
kunmap_local(block);
462+
463+
/* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head
464+
* index is previous; otherwise it's slot number of the previous entry.
465+
*/
466+
if (!iter.prev_entry) {
467+
__be16 prev_next = meta->meta.hashtable[iter.bucket];
468+
469+
if (unlikely(prev_next != htons(entry))) {
470+
pr_warn("%llx:%llx:%x: not head of chain b=%x p=%x,%x e=%x %*s",
471+
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
472+
iter.bucket, iter.prev_entry, prev_next, entry,
473+
name->len, name->name);
474+
goto error;
475+
}
476+
meta->meta.hashtable[iter.bucket] = next;
477+
} else {
478+
unsigned int pb = iter.prev_entry / AFS_DIR_SLOTS_PER_BLOCK;
479+
unsigned int ps = iter.prev_entry % AFS_DIR_SLOTS_PER_BLOCK;
480+
__be16 prev_next;
481+
482+
pblock = afs_dir_find_block(&iter, pb);
483+
if (!pblock)
484+
goto error;
485+
pde = &pblock->dirents[ps];
486+
prev_next = pde->u.hash_next;
487+
if (prev_next != htons(entry)) {
488+
kunmap_local(pblock);
489+
pr_warn("%llx:%llx:%x: not prev in chain b=%x p=%x,%x e=%x %*s",
490+
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
491+
iter.bucket, iter.prev_entry, prev_next, entry,
492+
name->len, name->name);
493+
goto error;
494+
}
495+
pde->u.hash_next = next;
496+
kunmap_local(pblock);
497+
}
459498

460499
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
461500

@@ -474,6 +513,8 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
474513
0, 0, 0, 0, name->name);
475514
goto out_unmap;
476515

516+
error_unmap:
517+
kunmap_local(block);
477518
error:
478519
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
479520
0, 0, 0, 0, name->name);

0 commit comments

Comments
 (0)