Linux: Use splice to speed up cache storeback
[openafs.git] / src / afs / LINUX / osi_fetchstore.c
1 /*
2  * Copyright (c) 2009 Simon Wilkinson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 /* Linux specific store operations
26  *
27  * The idea of these operations is to reduce the number of copies
28  * that data incurs when passing from the disk cache through to the
29  * RX layer, and vice versa.
30  *
31  * In kernels which support it, we use the splice() operation - in
32  * older kernels, the filesystem's sendpage() operation is used directly.
33  * Either way, this means that we can get direct access to the page contents,
34  * rather than getting a copy.
35  */
36
37 #include <afsconfig.h>
38 #include "afs/param.h"
39
40 #include <linux/fs.h>
41 #if defined(HAVE_SPLICE_DIRECT_TO_ACTOR)
42 # include <linux/splice.h>
43 #else
44 # include <linux/pipe_fs_i.h>
45 #endif
46
47 #include "afs/sysincludes.h"
48 #include "afsincludes.h"
49
50 #if defined(HAVE_SPLICE_DIRECT_TO_ACTOR)
51 static int
52 afs_linux_splice_actor(struct pipe_inode_info *pipe,
53                        struct pipe_buffer *buf,
54                        struct splice_desc *sd)
55 {
56     struct rxfs_storeVariables *svar = sd->u.data;
57     size_t size;
58     int code;
59
60     code = buf->ops->confirm(pipe, buf);
61     if (code)
62         return code;
63
64     size = sd->len;
65
66     /* Eventually, this could be rx_WritePage */
67     code = rx_Write(svar->call, page_address(buf->page), size);
68     if (code != size) {
69         return -33; /* Can't get a proper rx error out from here */
70     }
71
72     return size;
73 }
74
75 static int
76 afs_linux_ds_actor(struct pipe_inode_info *pipe, struct splice_desc *sd)
77 {
78     return __splice_from_pipe(pipe, sd, afs_linux_splice_actor);
79 }
80
81 /* This is a store proc which uses splice to reduce the number
82  * of page copies. */
83 afs_int32
84 afs_linux_storeproc(struct storeOps *ops, void *rock, struct dcache *tdc,
85                     int *shouldwake, afs_size_t *bytesXferred)
86 {
87     struct rxfs_storeVariables *svar = rock;
88     struct file *cacheFp;
89     struct splice_desc sd = {
90         .len    = 0,
91         .total_len = tdc->f.chunkBytes,
92         .pos    = 0,
93         .u.data = rock
94     };
95     int code;
96
97     /* Open the file, splice its contents */
98     AFS_GUNLOCK();
99     cacheFp = afs_linux_raw_open(&tdc->f.inode);
100     code = splice_direct_to_actor(cacheFp, &sd, afs_linux_ds_actor);
101     filp_close(cacheFp, NULL);
102     AFS_GLOCK();
103
104     /* If we're being called from a backing request, then wake up that
105      * request once the file server says its happy. Potentially, we should
106      * do this each time we rx_Write, but that would mean acquiring the
107      * GLOCK in the middle of our actor */
108     if (shouldwake && *shouldwake && ((*ops->status)(rock) == 0)) {
109         *shouldwake = 0;
110         afs_wakeup(svar->vcache);
111     }
112
113     if (code > 0) {
114         *bytesXferred+=code;
115         return 0;
116     }
117
118     return code;
119 }
120
121 # else
122
123 static int
124 afs_linux_read_actor(read_descriptor_t *desc, struct page *page,
125                      unsigned long offset, unsigned long size)
126 {
127     struct rxfs_storeVariables *svar = desc->arg.data;
128     unsigned long count = desc->count;
129     int code;
130
131     if (size > count)
132         size = count;
133
134     /* Eventually, this could be rx_WritePage */
135     code = rx_Write(svar->call, page_address(page) + offset, size);
136
137     if (code != size) {
138         return -33; /* Can't get a proper rx error out from here */
139     }
140
141     desc->count = count - size;
142     desc->written += size;
143
144     return size;
145 }
146
147 afs_int32
148 afs_linux_storeproc(struct storeOps *ops, void *rock, struct dcache *tdc,
149                     int *shouldwake, afs_size_t *bytesXferred)
150 {
151     struct rxfs_storeVariables *svar = rock;
152     struct file *cacheFp;
153     int code;
154     loff_t offset = 0;
155
156     /* Open the file, splice its contents */
157     AFS_GUNLOCK();
158     cacheFp = afs_linux_raw_open(&tdc->f.inode);
159     code = cacheFp->f_op->sendfile(cacheFp, &offset, tdc->f.chunkBytes,
160                                    afs_linux_read_actor, rock);
161     filp_close(cacheFp, NULL);
162     AFS_GLOCK();
163
164     /* If we're being called from a backing request, then wake up that
165      * request once the file server says its happy. Potentially, we should
166      * do this each time we rx_Write, but that would mean acquiring the
167      * GLOCK in the middle of our actor */
168     if (shouldwake && *shouldwake && ((*ops->status)(rock) == 0)) {
169         *shouldwake = 0;
170         afs_wakeup(svar->vcache);
171     }
172
173     if (code > 0) {
174         *bytesXferred+=code;
175         return 0;
176     }
177
178     return code;
179 }
180
181 #endif