diff options
author | Jeff Layton <jlayton@redhat.com> | 2012-03-23 14:40:56 -0400 |
---|---|---|
committer | Jeff Layton <jlayton@redhat.com> | 2012-03-23 14:40:56 -0400 |
commit | da82f7e755d2808ba726c9b23267d5bb23980e94 (patch) | |
tree | 16fe36b382416b4bc01edd886f3f1670c3aac4db /fs | |
parent | 597b027f694481ffeebcffe634c24b807198d46c (diff) |
cifs: convert cifs_iovec_write to use async writes
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 223 |
3 files changed, 143 insertions, 86 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 57ce6f834220..96192c1e380a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -483,6 +483,8 @@ int cifs_async_readv(struct cifs_readdata *rdata); | |||
483 | /* asynchronous write support */ | 483 | /* asynchronous write support */ |
484 | struct cifs_writedata { | 484 | struct cifs_writedata { |
485 | struct kref refcount; | 485 | struct kref refcount; |
486 | struct list_head list; | ||
487 | struct completion done; | ||
486 | enum writeback_sync_modes sync_mode; | 488 | enum writeback_sync_modes sync_mode; |
487 | struct work_struct work; | 489 | struct work_struct work; |
488 | struct cifsFileInfo *cfile; | 490 | struct cifsFileInfo *cfile; |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b63bf5f0698a..8fecc99be344 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -2081,8 +2081,10 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) | |||
2081 | wdata = kzalloc(sizeof(*wdata) + | 2081 | wdata = kzalloc(sizeof(*wdata) + |
2082 | sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); | 2082 | sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); |
2083 | if (wdata != NULL) { | 2083 | if (wdata != NULL) { |
2084 | INIT_WORK(&wdata->work, complete); | ||
2085 | kref_init(&wdata->refcount); | 2084 | kref_init(&wdata->refcount); |
2085 | INIT_LIST_HEAD(&wdata->list); | ||
2086 | init_completion(&wdata->done); | ||
2087 | INIT_WORK(&wdata->work, complete); | ||
2086 | } | 2088 | } |
2087 | return wdata; | 2089 | return wdata; |
2088 | } | 2090 | } |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6883b08f848c..daaaca82eeb2 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -2106,24 +2106,79 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) | |||
2106 | return num_pages; | 2106 | return num_pages; |
2107 | } | 2107 | } |
2108 | 2108 | ||
2109 | static void | ||
2110 | cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata) | ||
2111 | { | ||
2112 | int i; | ||
2113 | size_t bytes = wdata->bytes; | ||
2114 | |||
2115 | /* marshal up the pages into iov array */ | ||
2116 | for (i = 0; i < wdata->nr_pages; i++) { | ||
2117 | iov[i + 1].iov_len = min(bytes, PAGE_SIZE); | ||
2118 | iov[i + 1].iov_base = kmap(wdata->pages[i]); | ||
2119 | bytes -= iov[i + 1].iov_len; | ||
2120 | } | ||
2121 | } | ||
2122 | |||
2123 | static void | ||
2124 | cifs_uncached_writev_complete(struct work_struct *work) | ||
2125 | { | ||
2126 | int i; | ||
2127 | struct cifs_writedata *wdata = container_of(work, | ||
2128 | struct cifs_writedata, work); | ||
2129 | struct inode *inode = wdata->cfile->dentry->d_inode; | ||
2130 | struct cifsInodeInfo *cifsi = CIFS_I(inode); | ||
2131 | |||
2132 | spin_lock(&inode->i_lock); | ||
2133 | cifs_update_eof(cifsi, wdata->offset, wdata->bytes); | ||
2134 | if (cifsi->server_eof > inode->i_size) | ||
2135 | i_size_write(inode, cifsi->server_eof); | ||
2136 | spin_unlock(&inode->i_lock); | ||
2137 | |||
2138 | complete(&wdata->done); | ||
2139 | |||
2140 | if (wdata->result != -EAGAIN) { | ||
2141 | for (i = 0; i < wdata->nr_pages; i++) | ||
2142 | put_page(wdata->pages[i]); | ||
2143 | } | ||
2144 | |||
2145 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
2146 | } | ||
2147 | |||
2148 | /* attempt to send write to server, retry on any -EAGAIN errors */ | ||
2149 | static int | ||
2150 | cifs_uncached_retry_writev(struct cifs_writedata *wdata) | ||
2151 | { | ||
2152 | int rc; | ||
2153 | |||
2154 | do { | ||
2155 | if (wdata->cfile->invalidHandle) { | ||
2156 | rc = cifs_reopen_file(wdata->cfile, false); | ||
2157 | if (rc != 0) | ||
2158 | continue; | ||
2159 | } | ||
2160 | rc = cifs_async_writev(wdata); | ||
2161 | } while (rc == -EAGAIN); | ||
2162 | |||
2163 | return rc; | ||
2164 | } | ||
2165 | |||
2109 | static ssize_t | 2166 | static ssize_t |
2110 | cifs_iovec_write(struct file *file, const struct iovec *iov, | 2167 | cifs_iovec_write(struct file *file, const struct iovec *iov, |
2111 | unsigned long nr_segs, loff_t *poffset) | 2168 | unsigned long nr_segs, loff_t *poffset) |
2112 | { | 2169 | { |
2113 | unsigned int written; | 2170 | unsigned long nr_pages, i; |
2114 | unsigned long num_pages, npages, i; | ||
2115 | size_t copied, len, cur_len; | 2171 | size_t copied, len, cur_len; |
2116 | ssize_t total_written = 0; | 2172 | ssize_t total_written = 0; |
2117 | struct kvec *to_send; | 2173 | loff_t offset = *poffset; |
2118 | struct page **pages; | ||
2119 | struct iov_iter it; | 2174 | struct iov_iter it; |
2120 | struct inode *inode; | ||
2121 | struct cifsFileInfo *open_file; | 2175 | struct cifsFileInfo *open_file; |
2122 | struct cifs_tcon *pTcon; | 2176 | struct cifs_tcon *tcon; |
2123 | struct cifs_sb_info *cifs_sb; | 2177 | struct cifs_sb_info *cifs_sb; |
2124 | struct cifs_io_parms io_parms; | 2178 | struct cifs_writedata *wdata, *tmp; |
2125 | int xid, rc; | 2179 | struct list_head wdata_list; |
2126 | __u32 pid; | 2180 | int rc; |
2181 | pid_t pid; | ||
2127 | 2182 | ||
2128 | len = iov_length(iov, nr_segs); | 2183 | len = iov_length(iov, nr_segs); |
2129 | if (!len) | 2184 | if (!len) |
@@ -2133,105 +2188,103 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, | |||
2133 | if (rc) | 2188 | if (rc) |
2134 | return rc; | 2189 | return rc; |
2135 | 2190 | ||
2191 | INIT_LIST_HEAD(&wdata_list); | ||
2136 | cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); | 2192 | cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); |
2137 | num_pages = get_numpages(cifs_sb->wsize, len, &cur_len); | ||
2138 | |||
2139 | pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL); | ||
2140 | if (!pages) | ||
2141 | return -ENOMEM; | ||
2142 | |||
2143 | to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL); | ||
2144 | if (!to_send) { | ||
2145 | kfree(pages); | ||
2146 | return -ENOMEM; | ||
2147 | } | ||
2148 | |||
2149 | rc = cifs_write_allocate_pages(pages, num_pages); | ||
2150 | if (rc) { | ||
2151 | kfree(pages); | ||
2152 | kfree(to_send); | ||
2153 | return rc; | ||
2154 | } | ||
2155 | |||
2156 | xid = GetXid(); | ||
2157 | open_file = file->private_data; | 2193 | open_file = file->private_data; |
2194 | tcon = tlink_tcon(open_file->tlink); | ||
2158 | 2195 | ||
2159 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) | 2196 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) |
2160 | pid = open_file->pid; | 2197 | pid = open_file->pid; |
2161 | else | 2198 | else |
2162 | pid = current->tgid; | 2199 | pid = current->tgid; |
2163 | 2200 | ||
2164 | pTcon = tlink_tcon(open_file->tlink); | ||
2165 | inode = file->f_path.dentry->d_inode; | ||
2166 | |||
2167 | iov_iter_init(&it, iov, nr_segs, len, 0); | 2201 | iov_iter_init(&it, iov, nr_segs, len, 0); |
2168 | npages = num_pages; | ||
2169 | |||
2170 | do { | 2202 | do { |
2171 | size_t save_len = cur_len; | 2203 | size_t save_len; |
2172 | for (i = 0; i < npages; i++) { | 2204 | |
2173 | copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE); | 2205 | nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); |
2174 | copied = iov_iter_copy_from_user(pages[i], &it, 0, | 2206 | wdata = cifs_writedata_alloc(nr_pages, |
2175 | copied); | 2207 | cifs_uncached_writev_complete); |
2208 | if (!wdata) { | ||
2209 | rc = -ENOMEM; | ||
2210 | break; | ||
2211 | } | ||
2212 | |||
2213 | rc = cifs_write_allocate_pages(wdata->pages, nr_pages); | ||
2214 | if (rc) { | ||
2215 | kfree(wdata); | ||
2216 | break; | ||
2217 | } | ||
2218 | |||
2219 | save_len = cur_len; | ||
2220 | for (i = 0; i < nr_pages; i++) { | ||
2221 | copied = min_t(const size_t, cur_len, PAGE_SIZE); | ||
2222 | copied = iov_iter_copy_from_user(wdata->pages[i], &it, | ||
2223 | 0, copied); | ||
2176 | cur_len -= copied; | 2224 | cur_len -= copied; |
2177 | iov_iter_advance(&it, copied); | 2225 | iov_iter_advance(&it, copied); |
2178 | to_send[i+1].iov_base = kmap(pages[i]); | ||
2179 | to_send[i+1].iov_len = copied; | ||
2180 | } | 2226 | } |
2181 | |||
2182 | cur_len = save_len - cur_len; | 2227 | cur_len = save_len - cur_len; |
2183 | 2228 | ||
2184 | do { | 2229 | wdata->sync_mode = WB_SYNC_ALL; |
2185 | if (open_file->invalidHandle) { | 2230 | wdata->nr_pages = nr_pages; |
2186 | rc = cifs_reopen_file(open_file, false); | 2231 | wdata->offset = (__u64)offset; |
2187 | if (rc != 0) | 2232 | wdata->cfile = cifsFileInfo_get(open_file); |
2188 | break; | 2233 | wdata->pid = pid; |
2189 | } | 2234 | wdata->bytes = cur_len; |
2190 | io_parms.netfid = open_file->netfid; | 2235 | wdata->marshal_iov = cifs_uncached_marshal_iov; |
2191 | io_parms.pid = pid; | 2236 | rc = cifs_uncached_retry_writev(wdata); |
2192 | io_parms.tcon = pTcon; | 2237 | if (rc) { |
2193 | io_parms.offset = *poffset; | 2238 | kref_put(&wdata->refcount, cifs_writedata_release); |
2194 | io_parms.length = cur_len; | ||
2195 | rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send, | ||
2196 | npages, 0); | ||
2197 | } while (rc == -EAGAIN); | ||
2198 | |||
2199 | for (i = 0; i < npages; i++) | ||
2200 | kunmap(pages[i]); | ||
2201 | |||
2202 | if (written) { | ||
2203 | len -= written; | ||
2204 | total_written += written; | ||
2205 | spin_lock(&inode->i_lock); | ||
2206 | cifs_update_eof(CIFS_I(inode), *poffset, written); | ||
2207 | spin_unlock(&inode->i_lock); | ||
2208 | *poffset += written; | ||
2209 | } else if (rc < 0) { | ||
2210 | if (!total_written) | ||
2211 | total_written = rc; | ||
2212 | break; | 2239 | break; |
2213 | } | 2240 | } |
2214 | 2241 | ||
2215 | /* get length and number of kvecs of the next write */ | 2242 | list_add_tail(&wdata->list, &wdata_list); |
2216 | npages = get_numpages(cifs_sb->wsize, len, &cur_len); | 2243 | offset += cur_len; |
2244 | len -= cur_len; | ||
2217 | } while (len > 0); | 2245 | } while (len > 0); |
2218 | 2246 | ||
2219 | if (total_written > 0) { | 2247 | /* |
2220 | spin_lock(&inode->i_lock); | 2248 | * If at least one write was successfully sent, then discard any rc |
2221 | if (*poffset > inode->i_size) | 2249 | * value from the later writes. If the other write succeeds, then |
2222 | i_size_write(inode, *poffset); | 2250 | * we'll end up returning whatever was written. If it fails, then |
2223 | spin_unlock(&inode->i_lock); | 2251 | * we'll get a new rc value from that. |
2252 | */ | ||
2253 | if (!list_empty(&wdata_list)) | ||
2254 | rc = 0; | ||
2255 | |||
2256 | /* | ||
2257 | * Wait for and collect replies for any successful sends in order of | ||
2258 | * increasing offset. Once an error is hit or we get a fatal signal | ||
2259 | * while waiting, then return without waiting for any more replies. | ||
2260 | */ | ||
2261 | restart_loop: | ||
2262 | list_for_each_entry_safe(wdata, tmp, &wdata_list, list) { | ||
2263 | if (!rc) { | ||
2264 | /* FIXME: freezable too? */ | ||
2265 | rc = wait_for_completion_killable(&wdata->done); | ||
2266 | if (rc) | ||
2267 | rc = -EINTR; | ||
2268 | else if (wdata->result) | ||
2269 | rc = wdata->result; | ||
2270 | else | ||
2271 | total_written += wdata->bytes; | ||
2272 | |||
2273 | /* resend call if it's a retryable error */ | ||
2274 | if (rc == -EAGAIN) { | ||
2275 | rc = cifs_uncached_retry_writev(wdata); | ||
2276 | goto restart_loop; | ||
2277 | } | ||
2278 | } | ||
2279 | list_del_init(&wdata->list); | ||
2280 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
2224 | } | 2281 | } |
2225 | 2282 | ||
2226 | cifs_stats_bytes_written(pTcon, total_written); | 2283 | if (total_written > 0) |
2227 | mark_inode_dirty_sync(inode); | 2284 | *poffset += total_written; |
2228 | 2285 | ||
2229 | for (i = 0; i < num_pages; i++) | 2286 | cifs_stats_bytes_written(tcon, total_written); |
2230 | put_page(pages[i]); | 2287 | return total_written ? total_written : (ssize_t)rc; |
2231 | kfree(to_send); | ||
2232 | kfree(pages); | ||
2233 | FreeXid(xid); | ||
2234 | return total_written; | ||
2235 | } | 2288 | } |
2236 | 2289 | ||
2237 | ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, | 2290 | ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, |