Skip to content

Commit 6dd8093

Browse files
dhowellsbrauner
authored andcommitted
afs: Use netfslib for directories
In the AFS ecosystem, directories are just a special type of file that is downloaded and parsed locally. Download is done by the same mechanism as ordinary files and the data can be cached. There is one important semantic restriction on directories over files: the client must download the entire directory in one go because, for example, the server could fabricate the contents of the blob on the fly with each download and give a different image each time. So that we can cache the directory download, switch AFS directory support over to using the netfslib single-object API, thereby allowing directory content to be stored in the local cache. To make this work, the following changes are made: (1) A directory's contents are now stored in a folio_queue chain attached to the afs_vnode (inode) struct rather than its associated pagecache, though multipage folios are still used to hold the data. The folio queue is discarded when the directory inode is evicted. This also helps with the phasing out of ITER_XARRAY. (2) Various directory operations are made to use and unuse the cache cookie. (3) The content checking, content dumping and content iteration are now performed with a standard iov_iter iterator over the contents of the folio queue. (4) Iteration and modification must be done with the vnode's validate_lock held. In conjunction with (1), this means that the iteration can be done without the need to lock pages or take extra refs on them, unlike when accessing ->i_pages. (5) Convert to using netfs_read_single() to read data. (6) Provide a ->writepages() to call netfs_writeback_single() to save the data to the cache according to the VM's scheduling whilst holding the validate_lock read-locked as (4). (7) Change local directory image editing functions: (a) Provide a function to get a specific block by number from the folio_queue as we can no longer use the i_pages xarray to locate folios by index. This uses a cursor to remember the current position as we need to iterate through the directory contents. The block is kmapped before being returned. (b) Make the function in (a) extend the directory by an extra folio if we run out of space. (c) Raise the check of the block free space counter, for those blocks that have one, higher in the function to eliminate a call to get a block. (d) Remove the page unlocking and putting done during the editing loops. This is no longer necessary as the folio_queue holds the references and the pages are no longer in the pagecache. (e) Mark the inode dirty and pin the cache usage till writeback at the end of a successful edit. (8) Don't set the large_folios flag on the inode as we do the allocation ourselves rather than the VM doing it automatically. (9) Mark the inode as being a single object that isn't uploaded to the server. (10) Enable caching on directories. (11) Only set the upload key for writeback for regular files. Notes: (*) We keep the ->release_folio(), ->invalidate_folio() and ->migrate_folio() ops as we set the mapping pointer on the folio. Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected] cc: Marc Dionne <[email protected]> cc: Jeff Layton <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent b260431 commit 6dd8093

File tree

8 files changed

+517
-470
lines changed

8 files changed

+517
-470
lines changed

fs/afs/dir.c

Lines changed: 385 additions & 362 deletions
Large diffs are not rendered by default.

fs/afs/dir_edit.c

Lines changed: 82 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/namei.h>
1111
#include <linux/pagemap.h>
1212
#include <linux/iversion.h>
13+
#include <linux/folio_queue.h>
1314
#include "internal.h"
1415
#include "xdr_fs.h"
1516

@@ -105,23 +106,57 @@ static void afs_clear_contig_bits(union afs_xdr_dir_block *block,
105106
}
106107

107108
/*
108-
* Get a new directory folio.
109+
* Get a specific block, extending the directory storage to cover it as needed.
109110
*/
110-
static struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index)
111+
static union afs_xdr_dir_block *afs_dir_get_block(struct afs_dir_iter *iter, size_t block)
111112
{
112-
struct address_space *mapping = vnode->netfs.inode.i_mapping;
113+
struct folio_queue *fq;
114+
struct afs_vnode *dvnode = iter->dvnode;
113115
struct folio *folio;
116+
size_t blpos = block * AFS_DIR_BLOCK_SIZE;
117+
size_t blend = (block + 1) * AFS_DIR_BLOCK_SIZE, fpos = iter->fpos;
118+
int ret;
119+
120+
if (dvnode->directory_size < blend) {
121+
size_t cur_size = dvnode->directory_size;
122+
123+
ret = netfs_alloc_folioq_buffer(
124+
NULL, &dvnode->directory, &cur_size, blend,
125+
mapping_gfp_mask(dvnode->netfs.inode.i_mapping));
126+
dvnode->directory_size = cur_size;
127+
if (ret < 0)
128+
goto fail;
129+
}
114130

115-
folio = __filemap_get_folio(mapping, index,
116-
FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
117-
mapping->gfp_mask);
118-
if (IS_ERR(folio)) {
119-
afs_invalidate_dir(vnode, afs_dir_invalid_edit_get_block);
120-
return NULL;
131+
fq = iter->fq;
132+
if (!fq)
133+
fq = dvnode->directory;
134+
135+
/* Search the folio queue for the folio containing the block... */
136+
for (; fq; fq = fq->next) {
137+
for (int s = iter->fq_slot; s < folioq_count(fq); s++) {
138+
size_t fsize = folioq_folio_size(fq, s);
139+
140+
if (blend <= fpos + fsize) {
141+
/* ... and then return the mapped block. */
142+
folio = folioq_folio(fq, s);
143+
if (WARN_ON_ONCE(folio_pos(folio) != fpos))
144+
goto fail;
145+
iter->fq = fq;
146+
iter->fq_slot = s;
147+
iter->fpos = fpos;
148+
return kmap_local_folio(folio, blpos - fpos);
149+
}
150+
fpos += fsize;
151+
}
152+
iter->fq_slot = 0;
121153
}
122-
if (!folio_test_private(folio))
123-
folio_attach_private(folio, (void *)1);
124-
return folio;
154+
155+
fail:
156+
iter->fq = NULL;
157+
iter->fq_slot = 0;
158+
afs_invalidate_dir(dvnode, afs_dir_invalid_edit_get_block);
159+
return NULL;
125160
}
126161

127162
/*
@@ -209,9 +244,8 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
209244
{
210245
union afs_xdr_dir_block *meta, *block;
211246
union afs_xdr_dirent *de;
212-
struct folio *folio0, *folio;
247+
struct afs_dir_iter iter = { .dvnode = vnode };
213248
unsigned int need_slots, nr_blocks, b;
214-
pgoff_t index;
215249
loff_t i_size;
216250
int slot;
217251

@@ -224,16 +258,13 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
224258
return;
225259
}
226260

227-
folio0 = afs_dir_get_folio(vnode, 0);
228-
if (!folio0) {
229-
_leave(" [fgp]");
261+
meta = afs_dir_get_block(&iter, 0);
262+
if (!meta)
230263
return;
231-
}
232264

233265
/* Work out how many slots we're going to need. */
234266
need_slots = afs_dir_calc_slots(name->len);
235267

236-
meta = kmap_local_folio(folio0, 0);
237268
if (i_size == 0)
238269
goto new_directory;
239270
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
@@ -245,18 +276,17 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
245276
/* If the directory extended into a new folio, then we need to
246277
* tack a new folio on the end.
247278
*/
248-
index = b / AFS_DIR_BLOCKS_PER_PAGE;
249279
if (nr_blocks >= AFS_DIR_MAX_BLOCKS)
250280
goto error_too_many_blocks;
251-
if (index >= folio_nr_pages(folio0)) {
252-
folio = afs_dir_get_folio(vnode, index);
253-
if (!folio)
254-
goto error;
255-
} else {
256-
folio = folio0;
257-
}
258281

259-
block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio));
282+
/* Lower dir blocks have a counter in the header we can check. */
283+
if (b < AFS_DIR_BLOCKS_WITH_CTR &&
284+
meta->meta.alloc_ctrs[b] < need_slots)
285+
continue;
286+
287+
block = afs_dir_get_block(&iter, b);
288+
if (!block)
289+
goto error;
260290

261291
/* Abandon the edit if we got a callback break. */
262292
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
@@ -275,24 +305,16 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
275305
afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE);
276306
}
277307

278-
/* Only lower dir blocks have a counter in the header. */
279-
if (b >= AFS_DIR_BLOCKS_WITH_CTR ||
280-
meta->meta.alloc_ctrs[b] >= need_slots) {
281-
/* We need to try and find one or more consecutive
282-
* slots to hold the entry.
283-
*/
284-
slot = afs_find_contig_bits(block, need_slots);
285-
if (slot >= 0) {
286-
_debug("slot %u", slot);
287-
goto found_space;
288-
}
308+
/* We need to try and find one or more consecutive slots to
309+
* hold the entry.
310+
*/
311+
slot = afs_find_contig_bits(block, need_slots);
312+
if (slot >= 0) {
313+
_debug("slot %u", slot);
314+
goto found_space;
289315
}
290316

291317
kunmap_local(block);
292-
if (folio != folio0) {
293-
folio_unlock(folio);
294-
folio_put(folio);
295-
}
296318
}
297319

298320
/* There are no spare slots of sufficient size, yet the operation
@@ -307,8 +329,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
307329
i_size = AFS_DIR_BLOCK_SIZE;
308330
afs_set_i_size(vnode, i_size);
309331
slot = AFS_DIR_RESV_BLOCKS0;
310-
folio = folio0;
311-
block = kmap_local_folio(folio, 0);
332+
block = afs_dir_get_block(&iter, 0);
312333
nr_blocks = 1;
313334
b = 0;
314335

@@ -328,10 +349,6 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
328349
/* Adjust the bitmap. */
329350
afs_set_contig_bits(block, slot, need_slots);
330351
kunmap_local(block);
331-
if (folio != folio0) {
332-
folio_unlock(folio);
333-
folio_put(folio);
334-
}
335352

336353
/* Adjust the allocation counter. */
337354
if (b < AFS_DIR_BLOCKS_WITH_CTR)
@@ -341,20 +358,16 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
341358
afs_stat_v(vnode, n_dir_cr);
342359
_debug("Insert %s in %u[%u]", name->name, b, slot);
343360

361+
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
362+
344363
out_unmap:
345364
kunmap_local(meta);
346-
folio_unlock(folio0);
347-
folio_put(folio0);
348365
_leave("");
349366
return;
350367

351368
already_invalidated:
352369
trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name);
353370
kunmap_local(block);
354-
if (folio != folio0) {
355-
folio_unlock(folio);
356-
folio_put(folio);
357-
}
358371
goto out_unmap;
359372

360373
error_too_many_blocks:
@@ -376,9 +389,8 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
376389
{
377390
union afs_xdr_dir_block *meta, *block;
378391
union afs_xdr_dirent *de;
379-
struct folio *folio0, *folio;
392+
struct afs_dir_iter iter = { .dvnode = vnode };
380393
unsigned int need_slots, nr_blocks, b;
381-
pgoff_t index;
382394
loff_t i_size;
383395
int slot;
384396

@@ -393,31 +405,20 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
393405
}
394406
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
395407

396-
folio0 = afs_dir_get_folio(vnode, 0);
397-
if (!folio0) {
398-
_leave(" [fgp]");
408+
meta = afs_dir_get_block(&iter, 0);
409+
if (!meta)
399410
return;
400-
}
401411

402412
/* Work out how many slots we're going to discard. */
403413
need_slots = afs_dir_calc_slots(name->len);
404414

405-
meta = kmap_local_folio(folio0, 0);
406-
407415
/* Find a block that has sufficient slots available. Each folio
408416
* contains two or more directory blocks.
409417
*/
410418
for (b = 0; b < nr_blocks; b++) {
411-
index = b / AFS_DIR_BLOCKS_PER_PAGE;
412-
if (index >= folio_nr_pages(folio0)) {
413-
folio = afs_dir_get_folio(vnode, index);
414-
if (!folio)
415-
goto error;
416-
} else {
417-
folio = folio0;
418-
}
419-
420-
block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio));
419+
block = afs_dir_get_block(&iter, b);
420+
if (!block)
421+
goto error;
421422

422423
/* Abandon the edit if we got a callback break. */
423424
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
@@ -431,10 +432,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
431432
}
432433

433434
kunmap_local(block);
434-
if (folio != folio0) {
435-
folio_unlock(folio);
436-
folio_put(folio);
437-
}
438435
}
439436

440437
/* Didn't find the dirent to clobber. Download the directory again. */
@@ -455,34 +452,26 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
455452
/* Adjust the bitmap. */
456453
afs_clear_contig_bits(block, slot, need_slots);
457454
kunmap_local(block);
458-
if (folio != folio0) {
459-
folio_unlock(folio);
460-
folio_put(folio);
461-
}
462455

463456
/* Adjust the allocation counter. */
464457
if (b < AFS_DIR_BLOCKS_WITH_CTR)
465458
meta->meta.alloc_ctrs[b] += need_slots;
466459

460+
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
461+
467462
inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version);
468463
afs_stat_v(vnode, n_dir_rm);
469464
_debug("Remove %s from %u[%u]", name->name, b, slot);
470465

471466
out_unmap:
472467
kunmap_local(meta);
473-
folio_unlock(folio0);
474-
folio_put(folio0);
475468
_leave("");
476469
return;
477470

478471
already_invalidated:
472+
kunmap_local(block);
479473
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
480474
0, 0, 0, 0, name->name);
481-
kunmap_local(block);
482-
if (folio != folio0) {
483-
folio_unlock(folio);
484-
folio_put(folio);
485-
}
486475
goto out_unmap;
487476

488477
error:
@@ -500,9 +489,8 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
500489
{
501490
union afs_xdr_dir_block *block;
502491
union afs_xdr_dirent *de;
503-
struct folio *folio;
492+
struct afs_dir_iter iter = { .dvnode = vnode };
504493
unsigned int nr_blocks, b;
505-
pgoff_t index;
506494
loff_t i_size;
507495
int slot;
508496

@@ -513,19 +501,17 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
513501
afs_invalidate_dir(vnode, afs_dir_invalid_edit_upd_bad_size);
514502
return;
515503
}
504+
516505
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
517506

518507
/* Find a block that has sufficient slots available. Each folio
519508
* contains two or more directory blocks.
520509
*/
521510
for (b = 0; b < nr_blocks; b++) {
522-
index = b / AFS_DIR_BLOCKS_PER_PAGE;
523-
folio = afs_dir_get_folio(vnode, index);
524-
if (!folio)
511+
block = afs_dir_get_block(&iter, b);
512+
if (!block)
525513
goto error;
526514

527-
block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio));
528-
529515
/* Abandon the edit if we got a callback break. */
530516
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
531517
goto already_invalidated;
@@ -535,8 +521,6 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
535521
goto found_dirent;
536522

537523
kunmap_local(block);
538-
folio_unlock(folio);
539-
folio_put(folio);
540524
}
541525

542526
/* Didn't find the dirent to clobber. Download the directory again. */
@@ -554,8 +538,7 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
554538
ntohl(de->u.vnode), ntohl(de->u.unique), "..");
555539

556540
kunmap_local(block);
557-
folio_unlock(folio);
558-
folio_put(folio);
541+
netfs_single_mark_inode_dirty(&vnode->netfs.inode);
559542
inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version);
560543

561544
out:
@@ -564,8 +547,6 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
564547

565548
already_invalidated:
566549
kunmap_local(block);
567-
folio_unlock(folio);
568-
folio_put(folio);
569550
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_inval,
570551
0, 0, 0, 0, "..");
571552
goto out;

fs/afs/file.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,14 @@ static int afs_init_request(struct netfs_io_request *rreq, struct file *file)
389389
rreq->netfs_priv = key;
390390
}
391391
break;
392+
case NETFS_WRITEBACK:
393+
case NETFS_WRITETHROUGH:
394+
case NETFS_UNBUFFERED_WRITE:
395+
case NETFS_DIO_WRITE:
396+
if (S_ISREG(rreq->inode->i_mode))
397+
rreq->io_streams[0].avail = true;
398+
break;
399+
case NETFS_WRITEBACK_SINGLE:
392400
default:
393401
break;
394402
}

0 commit comments

Comments
 (0)