diff options
Diffstat (limited to 'fs/nfs/direct.c')
| -rw-r--r-- | fs/nfs/direct.c | 118 |
1 files changed, 101 insertions, 17 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 10bf07280f4a..7077521acdf4 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
| @@ -66,6 +66,10 @@ static struct kmem_cache *nfs_direct_cachep; | |||
| 66 | /* | 66 | /* |
| 67 | * This represents a set of asynchronous requests that we're waiting on | 67 | * This represents a set of asynchronous requests that we're waiting on |
| 68 | */ | 68 | */ |
| 69 | struct nfs_direct_mirror { | ||
| 70 | ssize_t count; | ||
| 71 | }; | ||
| 72 | |||
| 69 | struct nfs_direct_req { | 73 | struct nfs_direct_req { |
| 70 | struct kref kref; /* release manager */ | 74 | struct kref kref; /* release manager */ |
| 71 | 75 | ||
| @@ -78,8 +82,13 @@ struct nfs_direct_req { | |||
| 78 | /* completion state */ | 82 | /* completion state */ |
| 79 | atomic_t io_count; /* i/os we're waiting for */ | 83 | atomic_t io_count; /* i/os we're waiting for */ |
| 80 | spinlock_t lock; /* protect completion state */ | 84 | spinlock_t lock; /* protect completion state */ |
| 85 | |||
| 86 | struct nfs_direct_mirror mirrors[NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX]; | ||
| 87 | int mirror_count; | ||
| 88 | |||
| 81 | ssize_t count, /* bytes actually processed */ | 89 | ssize_t count, /* bytes actually processed */ |
| 82 | bytes_left, /* bytes left to be sent */ | 90 | bytes_left, /* bytes left to be sent */ |
| 91 | io_start, /* start of IO */ | ||
| 83 | error; /* any reported error */ | 92 | error; /* any reported error */ |
| 84 | struct completion completion; /* wait for i/o completion */ | 93 | struct completion completion; /* wait for i/o completion */ |
| 85 | 94 | ||
| @@ -108,26 +117,56 @@ static inline int put_dreq(struct nfs_direct_req *dreq) | |||
| 108 | return atomic_dec_and_test(&dreq->io_count); | 117 | return atomic_dec_and_test(&dreq->io_count); |
| 109 | } | 118 | } |
| 110 | 119 | ||
| 120 | void nfs_direct_set_resched_writes(struct nfs_direct_req *dreq) | ||
| 121 | { | ||
| 122 | dreq->flags = NFS_ODIRECT_RESCHED_WRITES; | ||
| 123 | } | ||
| 124 | EXPORT_SYMBOL_GPL(nfs_direct_set_resched_writes); | ||
| 125 | |||
| 126 | static void | ||
| 127 | nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr) | ||
| 128 | { | ||
| 129 | int i; | ||
| 130 | ssize_t count; | ||
| 131 | |||
| 132 | WARN_ON_ONCE(hdr->pgio_mirror_idx >= dreq->mirror_count); | ||
| 133 | |||
| 134 | count = dreq->mirrors[hdr->pgio_mirror_idx].count; | ||
| 135 | if (count + dreq->io_start < hdr->io_start + hdr->good_bytes) { | ||
| 136 | count = hdr->io_start + hdr->good_bytes - dreq->io_start; | ||
| 137 | dreq->mirrors[hdr->pgio_mirror_idx].count = count; | ||
| 138 | } | ||
| 139 | |||
| 140 | /* update the dreq->count by finding the minimum agreed count from all | ||
| 141 | * mirrors */ | ||
| 142 | count = dreq->mirrors[0].count; | ||
| 143 | |||
| 144 | for (i = 1; i < dreq->mirror_count; i++) | ||
| 145 | count = min(count, dreq->mirrors[i].count); | ||
| 146 | |||
| 147 | dreq->count = count; | ||
| 148 | } | ||
| 149 | |||
| 111 | /* | 150 | /* |
| 112 | * nfs_direct_select_verf - select the right verifier | 151 | * nfs_direct_select_verf - select the right verifier |
| 113 | * @dreq - direct request possibly spanning multiple servers | 152 | * @dreq - direct request possibly spanning multiple servers |
| 114 | * @ds_clp - nfs_client of data server or NULL if MDS / non-pnfs | 153 | * @ds_clp - nfs_client of data server or NULL if MDS / non-pnfs |
| 115 | * @ds_idx - index of data server in data server list, only valid if ds_clp set | 154 | * @commit_idx - commit bucket index for the DS |
| 116 | * | 155 | * |
| 117 | * returns the correct verifier to use given the role of the server | 156 | * returns the correct verifier to use given the role of the server |
| 118 | */ | 157 | */ |
| 119 | static struct nfs_writeverf * | 158 | static struct nfs_writeverf * |
| 120 | nfs_direct_select_verf(struct nfs_direct_req *dreq, | 159 | nfs_direct_select_verf(struct nfs_direct_req *dreq, |
| 121 | struct nfs_client *ds_clp, | 160 | struct nfs_client *ds_clp, |
| 122 | int ds_idx) | 161 | int commit_idx) |
| 123 | { | 162 | { |
| 124 | struct nfs_writeverf *verfp = &dreq->verf; | 163 | struct nfs_writeverf *verfp = &dreq->verf; |
| 125 | 164 | ||
| 126 | #ifdef CONFIG_NFS_V4_1 | 165 | #ifdef CONFIG_NFS_V4_1 |
| 127 | if (ds_clp) { | 166 | if (ds_clp) { |
| 128 | /* pNFS is in use, use the DS verf */ | 167 | /* pNFS is in use, use the DS verf */ |
| 129 | if (ds_idx >= 0 && ds_idx < dreq->ds_cinfo.nbuckets) | 168 | if (commit_idx >= 0 && commit_idx < dreq->ds_cinfo.nbuckets) |
| 130 | verfp = &dreq->ds_cinfo.buckets[ds_idx].direct_verf; | 169 | verfp = &dreq->ds_cinfo.buckets[commit_idx].direct_verf; |
| 131 | else | 170 | else |
| 132 | WARN_ON_ONCE(1); | 171 | WARN_ON_ONCE(1); |
| 133 | } | 172 | } |
| @@ -148,8 +187,7 @@ static void nfs_direct_set_hdr_verf(struct nfs_direct_req *dreq, | |||
| 148 | { | 187 | { |
| 149 | struct nfs_writeverf *verfp; | 188 | struct nfs_writeverf *verfp; |
| 150 | 189 | ||
| 151 | verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, | 190 | verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, hdr->ds_commit_idx); |
| 152 | hdr->ds_idx); | ||
| 153 | WARN_ON_ONCE(verfp->committed >= 0); | 191 | WARN_ON_ONCE(verfp->committed >= 0); |
| 154 | memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf)); | 192 | memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf)); |
| 155 | WARN_ON_ONCE(verfp->committed < 0); | 193 | WARN_ON_ONCE(verfp->committed < 0); |
| @@ -169,8 +207,7 @@ static int nfs_direct_set_or_cmp_hdr_verf(struct nfs_direct_req *dreq, | |||
| 169 | { | 207 | { |
| 170 | struct nfs_writeverf *verfp; | 208 | struct nfs_writeverf *verfp; |
| 171 | 209 | ||
| 172 | verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, | 210 | verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, hdr->ds_commit_idx); |
| 173 | hdr->ds_idx); | ||
| 174 | if (verfp->committed < 0) { | 211 | if (verfp->committed < 0) { |
| 175 | nfs_direct_set_hdr_verf(dreq, hdr); | 212 | nfs_direct_set_hdr_verf(dreq, hdr); |
| 176 | return 0; | 213 | return 0; |
| @@ -193,7 +230,11 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq, | |||
| 193 | 230 | ||
| 194 | verfp = nfs_direct_select_verf(dreq, data->ds_clp, | 231 | verfp = nfs_direct_select_verf(dreq, data->ds_clp, |
| 195 | data->ds_commit_index); | 232 | data->ds_commit_index); |
| 196 | WARN_ON_ONCE(verfp->committed < 0); | 233 | |
| 234 | /* verifier not set so always fail */ | ||
| 235 | if (verfp->committed < 0) | ||
| 236 | return 1; | ||
| 237 | |||
| 197 | return memcmp(verfp, &data->verf, sizeof(struct nfs_writeverf)); | 238 | return memcmp(verfp, &data->verf, sizeof(struct nfs_writeverf)); |
| 198 | } | 239 | } |
| 199 | 240 | ||
| @@ -212,6 +253,12 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq, | |||
| 212 | */ | 253 | */ |
| 213 | ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos) | 254 | ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos) |
| 214 | { | 255 | { |
| 256 | struct inode *inode = iocb->ki_filp->f_mapping->host; | ||
| 257 | |||
| 258 | /* we only support swap file calling nfs_direct_IO */ | ||
| 259 | if (!IS_SWAPFILE(inode)) | ||
| 260 | return 0; | ||
| 261 | |||
| 215 | #ifndef CONFIG_NFS_SWAP | 262 | #ifndef CONFIG_NFS_SWAP |
| 216 | dprintk("NFS: nfs_direct_IO (%pD) off/no(%Ld/%lu) EINVAL\n", | 263 | dprintk("NFS: nfs_direct_IO (%pD) off/no(%Ld/%lu) EINVAL\n", |
| 217 | iocb->ki_filp, (long long) pos, iter->nr_segs); | 264 | iocb->ki_filp, (long long) pos, iter->nr_segs); |
| @@ -243,6 +290,18 @@ void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo, | |||
| 243 | cinfo->completion_ops = &nfs_direct_commit_completion_ops; | 290 | cinfo->completion_ops = &nfs_direct_commit_completion_ops; |
| 244 | } | 291 | } |
| 245 | 292 | ||
| 293 | static inline void nfs_direct_setup_mirroring(struct nfs_direct_req *dreq, | ||
| 294 | struct nfs_pageio_descriptor *pgio, | ||
| 295 | struct nfs_page *req) | ||
| 296 | { | ||
| 297 | int mirror_count = 1; | ||
| 298 | |||
| 299 | if (pgio->pg_ops->pg_get_mirror_count) | ||
| 300 | mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req); | ||
| 301 | |||
| 302 | dreq->mirror_count = mirror_count; | ||
| 303 | } | ||
| 304 | |||
| 246 | static inline struct nfs_direct_req *nfs_direct_req_alloc(void) | 305 | static inline struct nfs_direct_req *nfs_direct_req_alloc(void) |
| 247 | { | 306 | { |
| 248 | struct nfs_direct_req *dreq; | 307 | struct nfs_direct_req *dreq; |
| @@ -257,6 +316,7 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) | |||
| 257 | INIT_LIST_HEAD(&dreq->mds_cinfo.list); | 316 | INIT_LIST_HEAD(&dreq->mds_cinfo.list); |
| 258 | dreq->verf.committed = NFS_INVALID_STABLE_HOW; /* not set yet */ | 317 | dreq->verf.committed = NFS_INVALID_STABLE_HOW; /* not set yet */ |
| 259 | INIT_WORK(&dreq->work, nfs_direct_write_schedule_work); | 318 | INIT_WORK(&dreq->work, nfs_direct_write_schedule_work); |
| 319 | dreq->mirror_count = 1; | ||
| 260 | spin_lock_init(&dreq->lock); | 320 | spin_lock_init(&dreq->lock); |
| 261 | 321 | ||
| 262 | return dreq; | 322 | return dreq; |
| @@ -363,7 +423,8 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) | |||
| 363 | if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && (hdr->good_bytes == 0)) | 423 | if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && (hdr->good_bytes == 0)) |
| 364 | dreq->error = hdr->error; | 424 | dreq->error = hdr->error; |
| 365 | else | 425 | else |
| 366 | dreq->count += hdr->good_bytes; | 426 | nfs_direct_good_bytes(dreq, hdr); |
| 427 | |||
| 367 | spin_unlock(&dreq->lock); | 428 | spin_unlock(&dreq->lock); |
| 368 | 429 | ||
| 369 | while (!list_empty(&hdr->pages)) { | 430 | while (!list_empty(&hdr->pages)) { |
| @@ -541,6 +602,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, | |||
| 541 | 602 | ||
| 542 | dreq->inode = inode; | 603 | dreq->inode = inode; |
| 543 | dreq->bytes_left = count; | 604 | dreq->bytes_left = count; |
| 605 | dreq->io_start = pos; | ||
| 544 | dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); | 606 | dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); |
| 545 | l_ctx = nfs_get_lock_context(dreq->ctx); | 607 | l_ctx = nfs_get_lock_context(dreq->ctx); |
| 546 | if (IS_ERR(l_ctx)) { | 608 | if (IS_ERR(l_ctx)) { |
| @@ -573,6 +635,20 @@ out: | |||
| 573 | return result; | 635 | return result; |
| 574 | } | 636 | } |
| 575 | 637 | ||
| 638 | static void | ||
| 639 | nfs_direct_write_scan_commit_list(struct inode *inode, | ||
| 640 | struct list_head *list, | ||
| 641 | struct nfs_commit_info *cinfo) | ||
| 642 | { | ||
| 643 | spin_lock(cinfo->lock); | ||
| 644 | #ifdef CONFIG_NFS_V4_1 | ||
| 645 | if (cinfo->ds != NULL && cinfo->ds->nwritten != 0) | ||
| 646 | NFS_SERVER(inode)->pnfs_curr_ld->recover_commit_reqs(list, cinfo); | ||
| 647 | #endif | ||
| 648 | nfs_scan_commit_list(&cinfo->mds->list, list, cinfo, 0); | ||
| 649 | spin_unlock(cinfo->lock); | ||
| 650 | } | ||
| 651 | |||
| 576 | static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) | 652 | static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) |
| 577 | { | 653 | { |
| 578 | struct nfs_pageio_descriptor desc; | 654 | struct nfs_pageio_descriptor desc; |
| @@ -580,20 +656,23 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) | |||
| 580 | LIST_HEAD(reqs); | 656 | LIST_HEAD(reqs); |
| 581 | struct nfs_commit_info cinfo; | 657 | struct nfs_commit_info cinfo; |
| 582 | LIST_HEAD(failed); | 658 | LIST_HEAD(failed); |
| 659 | int i; | ||
| 583 | 660 | ||
| 584 | nfs_init_cinfo_from_dreq(&cinfo, dreq); | 661 | nfs_init_cinfo_from_dreq(&cinfo, dreq); |
| 585 | pnfs_recover_commit_reqs(dreq->inode, &reqs, &cinfo); | 662 | nfs_direct_write_scan_commit_list(dreq->inode, &reqs, &cinfo); |
| 586 | spin_lock(cinfo.lock); | ||
| 587 | nfs_scan_commit_list(&cinfo.mds->list, &reqs, &cinfo, 0); | ||
| 588 | spin_unlock(cinfo.lock); | ||
| 589 | 663 | ||
| 590 | dreq->count = 0; | 664 | dreq->count = 0; |
| 665 | for (i = 0; i < dreq->mirror_count; i++) | ||
| 666 | dreq->mirrors[i].count = 0; | ||
| 591 | get_dreq(dreq); | 667 | get_dreq(dreq); |
| 592 | 668 | ||
| 593 | nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE, false, | 669 | nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE, false, |
| 594 | &nfs_direct_write_completion_ops); | 670 | &nfs_direct_write_completion_ops); |
| 595 | desc.pg_dreq = dreq; | 671 | desc.pg_dreq = dreq; |
| 596 | 672 | ||
| 673 | req = nfs_list_entry(reqs.next); | ||
| 674 | nfs_direct_setup_mirroring(dreq, &desc, req); | ||
| 675 | |||
| 597 | list_for_each_entry_safe(req, tmp, &reqs, wb_list) { | 676 | list_for_each_entry_safe(req, tmp, &reqs, wb_list) { |
| 598 | if (!nfs_pageio_add_request(&desc, req)) { | 677 | if (!nfs_pageio_add_request(&desc, req)) { |
| 599 | nfs_list_remove_request(req); | 678 | nfs_list_remove_request(req); |
| @@ -640,7 +719,7 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data) | |||
| 640 | nfs_list_remove_request(req); | 719 | nfs_list_remove_request(req); |
| 641 | if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) { | 720 | if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) { |
| 642 | /* Note the rewrite will go through mds */ | 721 | /* Note the rewrite will go through mds */ |
| 643 | nfs_mark_request_commit(req, NULL, &cinfo); | 722 | nfs_mark_request_commit(req, NULL, &cinfo, 0); |
| 644 | } else | 723 | } else |
| 645 | nfs_release_request(req); | 724 | nfs_release_request(req); |
| 646 | nfs_unlock_and_release_request(req); | 725 | nfs_unlock_and_release_request(req); |
| @@ -715,7 +794,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) | |||
| 715 | dreq->error = hdr->error; | 794 | dreq->error = hdr->error; |
| 716 | } | 795 | } |
| 717 | if (dreq->error == 0) { | 796 | if (dreq->error == 0) { |
| 718 | dreq->count += hdr->good_bytes; | 797 | nfs_direct_good_bytes(dreq, hdr); |
| 719 | if (nfs_write_need_commit(hdr)) { | 798 | if (nfs_write_need_commit(hdr)) { |
| 720 | if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) | 799 | if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) |
| 721 | request_commit = true; | 800 | request_commit = true; |
| @@ -739,7 +818,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) | |||
| 739 | nfs_list_remove_request(req); | 818 | nfs_list_remove_request(req); |
| 740 | if (request_commit) { | 819 | if (request_commit) { |
| 741 | kref_get(&req->wb_kref); | 820 | kref_get(&req->wb_kref); |
| 742 | nfs_mark_request_commit(req, hdr->lseg, &cinfo); | 821 | nfs_mark_request_commit(req, hdr->lseg, &cinfo, |
| 822 | hdr->ds_commit_idx); | ||
| 743 | } | 823 | } |
| 744 | nfs_unlock_and_release_request(req); | 824 | nfs_unlock_and_release_request(req); |
| 745 | } | 825 | } |
| @@ -820,6 +900,9 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, | |||
| 820 | result = PTR_ERR(req); | 900 | result = PTR_ERR(req); |
| 821 | break; | 901 | break; |
| 822 | } | 902 | } |
| 903 | |||
| 904 | nfs_direct_setup_mirroring(dreq, &desc, req); | ||
| 905 | |||
| 823 | nfs_lock_request(req); | 906 | nfs_lock_request(req); |
| 824 | req->wb_index = pos >> PAGE_SHIFT; | 907 | req->wb_index = pos >> PAGE_SHIFT; |
| 825 | req->wb_offset = pos & ~PAGE_MASK; | 908 | req->wb_offset = pos & ~PAGE_MASK; |
| @@ -928,6 +1011,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, | |||
| 928 | 1011 | ||
| 929 | dreq->inode = inode; | 1012 | dreq->inode = inode; |
| 930 | dreq->bytes_left = count; | 1013 | dreq->bytes_left = count; |
| 1014 | dreq->io_start = pos; | ||
| 931 | dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); | 1015 | dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); |
| 932 | l_ctx = nfs_get_lock_context(dreq->ctx); | 1016 | l_ctx = nfs_get_lock_context(dreq->ctx); |
| 933 | if (IS_ERR(l_ctx)) { | 1017 | if (IS_ERR(l_ctx)) { |
