Skip to content

Commit e1b1240

Browse files
committed
netfs: Add write_begin helper
Add a helper to do the pre-reading work for the netfs write_begin address space op. Changes v6: - Fixed a missing rreq put in netfs_write_begin()[3]. - Use DEFINE_READAHEAD()[4]. v5: - Made the wait for PG_fscache in netfs_write_begin() killable[2]. v4: - Added flag to netfs_subreq_terminated() to indicate that the caller may have been running async and stuff that might sleep needs punting to a workqueue (can't use in_softirq()[1]). Signed-off-by: David Howells <[email protected]> Reviewed-and-tested-by: Jeff Layton <[email protected]> Tested-by: Dave Wysochanski <[email protected]> Tested-By: Marc Dionne <[email protected]> cc: Matthew Wilcox <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] Link: https://lore.kernel.org/r/[email protected]/ [1] Link: https://lore.kernel.org/r/[email protected]/ [2] Link: https://lore.kernel.org/r/161781042127.463527.9154479794406046987.stgit@warthog.procyon.org.uk/ [3] Link: https://lore.kernel.org/r/[email protected]/ [4] Link: https://lore.kernel.org/r/160588543960.3465195.2792938973035886168.stgit@warthog.procyon.org.uk/ # rfc Link: https://lore.kernel.org/r/161118140165.1232039.16418853874312234477.stgit@warthog.procyon.org.uk/ # rfc Link: https://lore.kernel.org/r/161161035539.2537118.15674887534950908530.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/161340398368.1303470.11242918276563276090.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/161539541541.286939.1889738674057013729.stgit@warthog.procyon.org.uk/ # v4 Link: https://lore.kernel.org/r/161653798616.2770958.17213315845968485563.stgit@warthog.procyon.org.uk/ # v5 Link: https://lore.kernel.org/r/161789080530.6155.1011847312392330491.stgit@warthog.procyon.org.uk/ # v6
1 parent 289af54 commit e1b1240

File tree

5 files changed

+185
-4
lines changed

5 files changed

+185
-4
lines changed

fs/netfs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ extern atomic_t netfs_n_rh_read_failed;
3434
extern atomic_t netfs_n_rh_zero;
3535
extern atomic_t netfs_n_rh_short_read;
3636
extern atomic_t netfs_n_rh_write;
37+
extern atomic_t netfs_n_rh_write_begin;
3738
extern atomic_t netfs_n_rh_write_done;
3839
extern atomic_t netfs_n_rh_write_failed;
40+
extern atomic_t netfs_n_rh_write_zskip;
3941

4042

4143
static inline void netfs_stat(atomic_t *stat)

fs/netfs/read_helper.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,3 +772,167 @@ int netfs_readpage(struct file *file,
772772
return ret;
773773
}
774774
EXPORT_SYMBOL(netfs_readpage);
775+
776+
static void netfs_clear_thp(struct page *page)
777+
{
778+
unsigned int i;
779+
780+
for (i = 0; i < thp_nr_pages(page); i++)
781+
clear_highpage(page + i);
782+
}
783+
784+
/**
785+
* netfs_write_begin - Helper to prepare for writing
786+
* @file: The file to read from
787+
* @mapping: The mapping to read from
788+
* @pos: File position at which the write will begin
789+
* @len: The length of the write in this page
790+
* @flags: AOP_* flags
791+
* @_page: Where to put the resultant page
792+
* @_fsdata: Place for the netfs to store a cookie
793+
* @ops: The network filesystem's operations for the helper to use
794+
* @netfs_priv: Private netfs data to be retained in the request
795+
*
796+
* Pre-read data for a write-begin request by drawing data from the cache if
797+
* possible, or the netfs if not. Space beyond the EOF is zero-filled.
798+
* Multiple I/O requests from different sources will get munged together. If
799+
* necessary, the readahead window can be expanded in either direction to a
800+
* more convenient alighment for RPC efficiency or to make storage in the cache
801+
* feasible.
802+
*
803+
* The calling netfs must provide a table of operations, only one of which,
804+
* issue_op, is mandatory.
805+
*
806+
* The check_write_begin() operation can be provided to check for and flush
807+
* conflicting writes once the page is grabbed and locked. It is passed a
808+
* pointer to the fsdata cookie that gets returned to the VM to be passed to
809+
* write_end. It is permitted to sleep. It should return 0 if the request
810+
* should go ahead; unlock the page and return -EAGAIN to cause the page to be
811+
* regot; or return an error.
812+
*
813+
* This is usable whether or not caching is enabled.
814+
*/
815+
int netfs_write_begin(struct file *file, struct address_space *mapping,
816+
loff_t pos, unsigned int len, unsigned int flags,
817+
struct page **_page, void **_fsdata,
818+
const struct netfs_read_request_ops *ops,
819+
void *netfs_priv)
820+
{
821+
struct netfs_read_request *rreq;
822+
struct page *page, *xpage;
823+
struct inode *inode = file_inode(file);
824+
unsigned int debug_index = 0;
825+
pgoff_t index = pos >> PAGE_SHIFT;
826+
int pos_in_page = pos & ~PAGE_MASK;
827+
loff_t size;
828+
int ret;
829+
830+
DEFINE_READAHEAD(ractl, file, NULL, mapping, index);
831+
832+
retry:
833+
page = grab_cache_page_write_begin(mapping, index, 0);
834+
if (!page)
835+
return -ENOMEM;
836+
837+
if (ops->check_write_begin) {
838+
/* Allow the netfs (eg. ceph) to flush conflicts. */
839+
ret = ops->check_write_begin(file, pos, len, page, _fsdata);
840+
if (ret < 0) {
841+
if (ret == -EAGAIN)
842+
goto retry;
843+
goto error;
844+
}
845+
}
846+
847+
if (PageUptodate(page))
848+
goto have_page;
849+
850+
/* If the page is beyond the EOF, we want to clear it - unless it's
851+
* within the cache granule containing the EOF, in which case we need
852+
* to preload the granule.
853+
*/
854+
size = i_size_read(inode);
855+
if (!ops->is_cache_enabled(inode) &&
856+
((pos_in_page == 0 && len == thp_size(page)) ||
857+
(pos >= size) ||
858+
(pos_in_page == 0 && (pos + len) >= size))) {
859+
netfs_clear_thp(page);
860+
SetPageUptodate(page);
861+
netfs_stat(&netfs_n_rh_write_zskip);
862+
goto have_page_no_wait;
863+
}
864+
865+
ret = -ENOMEM;
866+
rreq = netfs_alloc_read_request(ops, netfs_priv, file);
867+
if (!rreq)
868+
goto error;
869+
rreq->mapping = page->mapping;
870+
rreq->start = page->index * PAGE_SIZE;
871+
rreq->len = thp_size(page);
872+
rreq->no_unlock_page = page->index;
873+
__set_bit(NETFS_RREQ_NO_UNLOCK_PAGE, &rreq->flags);
874+
netfs_priv = NULL;
875+
876+
netfs_stat(&netfs_n_rh_write_begin);
877+
trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
878+
879+
/* Expand the request to meet caching requirements and download
880+
* preferences.
881+
*/
882+
ractl._nr_pages = thp_nr_pages(page);
883+
netfs_rreq_expand(rreq, &ractl);
884+
netfs_get_read_request(rreq);
885+
886+
/* We hold the page locks, so we can drop the references */
887+
while ((xpage = readahead_page(&ractl)))
888+
if (xpage != page)
889+
put_page(xpage);
890+
891+
atomic_set(&rreq->nr_rd_ops, 1);
892+
do {
893+
if (!netfs_rreq_submit_slice(rreq, &debug_index))
894+
break;
895+
896+
} while (rreq->submitted < rreq->len);
897+
898+
/* Keep nr_rd_ops incremented so that the ref always belongs to us, and
899+
* the service code isn't punted off to a random thread pool to
900+
* process.
901+
*/
902+
for (;;) {
903+
wait_var_event(&rreq->nr_rd_ops, atomic_read(&rreq->nr_rd_ops) == 1);
904+
netfs_rreq_assess(rreq, false);
905+
if (!test_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags))
906+
break;
907+
cond_resched();
908+
}
909+
910+
ret = rreq->error;
911+
if (ret == 0 && rreq->submitted < rreq->len)
912+
ret = -EIO;
913+
netfs_put_read_request(rreq, false);
914+
if (ret < 0)
915+
goto error;
916+
917+
have_page:
918+
ret = wait_on_page_fscache_killable(page);
919+
if (ret < 0)
920+
goto error;
921+
have_page_no_wait:
922+
if (netfs_priv)
923+
ops->cleanup(netfs_priv, mapping);
924+
*_page = page;
925+
_leave(" = 0");
926+
return 0;
927+
928+
error_put:
929+
netfs_put_read_request(rreq, false);
930+
error:
931+
unlock_page(page);
932+
put_page(page);
933+
if (netfs_priv)
934+
ops->cleanup(netfs_priv, mapping);
935+
_leave(" = %d", ret);
936+
return ret;
937+
}
938+
EXPORT_SYMBOL(netfs_write_begin);

fs/netfs/stats.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,24 @@ atomic_t netfs_n_rh_read_failed;
2424
atomic_t netfs_n_rh_zero;
2525
atomic_t netfs_n_rh_short_read;
2626
atomic_t netfs_n_rh_write;
27+
atomic_t netfs_n_rh_write_begin;
2728
atomic_t netfs_n_rh_write_done;
2829
atomic_t netfs_n_rh_write_failed;
30+
atomic_t netfs_n_rh_write_zskip;
2931

3032
void netfs_stats_show(struct seq_file *m)
3133
{
32-
seq_printf(m, "RdHelp : RA=%u RP=%u rr=%u sr=%u\n",
34+
seq_printf(m, "RdHelp : RA=%u RP=%u WB=%u WBZ=%u rr=%u sr=%u\n",
3335
atomic_read(&netfs_n_rh_readahead),
3436
atomic_read(&netfs_n_rh_readpage),
37+
atomic_read(&netfs_n_rh_write_begin),
38+
atomic_read(&netfs_n_rh_write_zskip),
3539
atomic_read(&netfs_n_rh_rreq),
3640
atomic_read(&netfs_n_rh_sreq));
37-
seq_printf(m, "RdHelp : ZR=%u sh=%u\n",
41+
seq_printf(m, "RdHelp : ZR=%u sh=%u sk=%u\n",
3842
atomic_read(&netfs_n_rh_zero),
39-
atomic_read(&netfs_n_rh_short_read));
43+
atomic_read(&netfs_n_rh_short_read),
44+
atomic_read(&netfs_n_rh_write_zskip));
4045
seq_printf(m, "RdHelp : DL=%u ds=%u df=%u di=%u\n",
4146
atomic_read(&netfs_n_rh_download),
4247
atomic_read(&netfs_n_rh_download_done),

include/linux/netfs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,14 @@ struct netfs_read_request {
147147
* Operations the network filesystem can/must provide to the helpers.
148148
*/
149149
struct netfs_read_request_ops {
150+
bool (*is_cache_enabled)(struct inode *inode);
150151
void (*init_rreq)(struct netfs_read_request *rreq, struct file *file);
151152
void (*expand_readahead)(struct netfs_read_request *rreq);
152153
bool (*clamp_length)(struct netfs_read_subrequest *subreq);
153154
void (*issue_op)(struct netfs_read_subrequest *subreq);
154155
bool (*is_still_valid)(struct netfs_read_request *rreq);
156+
int (*check_write_begin)(struct file *file, loff_t pos, unsigned len,
157+
struct page *page, void **_fsdata);
155158
void (*done)(struct netfs_read_request *rreq);
156159
void (*cleanup)(struct address_space *mapping, void *netfs_priv);
157160
};
@@ -164,6 +167,11 @@ extern int netfs_readpage(struct file *,
164167
struct page *,
165168
const struct netfs_read_request_ops *,
166169
void *);
170+
extern int netfs_write_begin(struct file *, struct address_space *,
171+
loff_t, unsigned int, unsigned int, struct page **,
172+
void **,
173+
const struct netfs_read_request_ops *,
174+
void *);
167175

168176
extern void netfs_subreq_terminated(struct netfs_read_subrequest *, ssize_t, bool);
169177
extern void netfs_stats_show(struct seq_file *);

include/trace/events/netfs.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum netfs_read_trace {
2222
netfs_read_trace_expanded,
2323
netfs_read_trace_readahead,
2424
netfs_read_trace_readpage,
25+
netfs_read_trace_write_begin,
2526
};
2627

2728
enum netfs_rreq_trace {
@@ -50,7 +51,8 @@ enum netfs_sreq_trace {
5051
#define netfs_read_traces \
5152
EM(netfs_read_trace_expanded, "EXPANDED ") \
5253
EM(netfs_read_trace_readahead, "READAHEAD") \
53-
E_(netfs_read_trace_readpage, "READPAGE ")
54+
EM(netfs_read_trace_readpage, "READPAGE ") \
55+
E_(netfs_read_trace_write_begin, "WRITEBEGN")
5456

5557
#define netfs_rreq_traces \
5658
EM(netfs_rreq_trace_assess, "ASSESS") \

0 commit comments

Comments
 (0)