diff -X /home/cel/src/linux/dont-diff -Naurp 15-async-read-hookup/fs/nfs/write.c 16-async-write-intro/fs/nfs/write.c --- 15-async-read-hookup/fs/nfs/write.c 2004-02-02 13:16:02.702257000 -0500 +++ 16-async-write-intro/fs/nfs/write.c 2004-02-02 13:27:31.453258000 -0500 @@ -791,6 +791,77 @@ nfs_execute_write(struct nfs_write_data } /* + * Compute how many wsize write operations it will take to + * get our write request onto the server. + */ +static inline unsigned int +nfs_write_count(struct nfs_page *req, unsigned wsize) +{ + return (req->wb_bytes + wsize - 1) / wsize; +} + +/* + * Generate multiple small requests to write out a single + * contiguous dirty area on one page. + */ +static int +nfs_flush_multi(struct list_head *head, struct inode *inode, int how) +{ + struct nfs_page *req = nfs_list_entry(head->next); + struct page *page = req->wb_page; + struct nfs_write_data *data; + unsigned int requests, offset, wsize = NFS_SERVER(inode)->wsize; + int remaining = req->wb_bytes; + LIST_HEAD(list); + + nfs_list_remove_request(req); + + requests = nfs_write_count(req, wsize); + while (requests--) { + data = nfs_writedata_alloc(); + if (!data) + goto out_bad; + list_add(&data->pages, &list); + } + + atomic_inc(&req->wb_complete); + SetPageWriteback(req->wb_page); + + offset = 0; + while (!list_empty(&list)) { + data = list_entry(list.next, struct nfs_write_data, pages); + list_del_init(&data->pages); + + data->req = req; + data->pagevec[0] = page; + atomic_inc(&req->wb_complete); + + nfs_write_rpcsetup(req, data, (remaining < wsize ? + remaining : wsize), offset, how); + + nfs_execute_write(data); + + offset += wsize; + remaining -= wsize; + } + + if (atomic_dec_and_test(&req->wb_complete)) + nfs_writepage_release(req); + + return 0; + +out_bad: + while (!list_empty(&list)) { + data = list_entry(list.next, struct nfs_write_data, pages); + list_del_init(&data->pages); + nfs_writedata_free(data); + } + nfs_mark_request_dirty(req); + nfs_unlock_request(req); + return -ENOMEM; +} + +/* * Create an RPC task for the given write request and kick it. * The page must have been locked by the caller. * @@ -864,6 +935,100 @@ nfs_flush_list(struct list_head *head, i return error; } +/* + * Handle a write reply that flushed part of a page. + */ +static void +nfs_writeback_done_partial(struct nfs_write_data *data, int status) +{ + struct nfs_page *req = data->req; + struct page *page = req->wb_page; + + dprintk("NFS: write (%s/%Ld %d@%Ld)", + req->wb_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_inode), + req->wb_bytes, + (long long)req_offset(req)); + + if (status < 0) { + ClearPageUptodate(page); + SetPageError(page); + if (req->wb_file) + req->wb_file->f_error = status; + dprintk(", error = %d\n", status); + } else { +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) + if (data->verf.committed < NFS_FILE_SYNC) { + if (!NFS_NEED_COMMIT(req)) { + nfs_defer_commit(req); + memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); + dprintk(" defer commit\n"); + } else if (memcmp(&req->wb_verf, &data->verf, sizeof(req->wb_verf))) { + nfs_defer_reschedule(req); + dprintk(" server reboot detected\n"); + } + } else +#endif + dprintk(" OK\n"); + } + + if (atomic_dec_and_test(&req->wb_complete)) + nfs_writepage_release(req); +} + +/* + * Handle a write reply that flushes a whole page. + * + * FIXME: There is an inherent race with invalidate_inode_pages and + * writebacks since the page->count is kept > 1 for as long + * as the page has a write request pending. + */ +static void +nfs_writeback_done_full(struct nfs_write_data *data, int status) +{ + struct nfs_page *req; + struct page *page; + + /* Update attributes as result of writeback. */ + while (!list_empty(&data->pages)) { + req = nfs_list_entry(data->pages.next); + nfs_list_remove_request(req); + page = req->wb_page; + + dprintk("NFS: write (%s/%Ld %d@%Ld)", + req->wb_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_inode), + req->wb_bytes, + (long long)req_offset(req)); + + if (status < 0) { + ClearPageUptodate(page); + SetPageError(page); + if (req->wb_file) + req->wb_file->f_error = status; + end_page_writeback(page); + nfs_inode_remove_request(req); + dprintk(", error = %d\n", status); + goto next; + } + end_page_writeback(page); + +#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) + if (data->args.stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { + nfs_inode_remove_request(req); + dprintk(" OK\n"); + goto next; + } + memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); + nfs_mark_request_commit(req); + dprintk(" marked for commit\n"); +#else + nfs_inode_remove_request(req); +#endif + next: + nfs_unlock_request(req); + } +} /* * This function is called when the WRITE call is complete. @@ -874,8 +1039,6 @@ nfs_writeback_done(struct rpc_task *task struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; - struct nfs_page *req; - struct page *page; dprintk("NFS: %4d nfs_writeback_done (status %d)\n", task->tk_pid, task->tk_status); @@ -914,50 +1077,7 @@ nfs_writeback_done(struct rpc_task *task } #endif - /* - * Update attributes as result of writeback. - * FIXME: There is an inherent race with invalidate_inode_pages and - * writebacks since the page->count is kept > 1 for as long - * as the page has a write request pending. - */ - while (!list_empty(&data->pages)) { - req = nfs_list_entry(data->pages.next); - nfs_list_remove_request(req); - page = req->wb_page; - - dprintk("NFS: write (%s/%Ld %d@%Ld)", - req->wb_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_inode), - req->wb_bytes, - (long long)req_offset(req)); - - if (task->tk_status < 0) { - ClearPageUptodate(page); - SetPageError(page); - if (req->wb_file) - req->wb_file->f_error = task->tk_status; - end_page_writeback(page); - nfs_inode_remove_request(req); - dprintk(", error = %d\n", task->tk_status); - goto next; - } - end_page_writeback(page); - -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) - if (argp->stable != NFS_UNSTABLE || data->verf.committed == NFS_FILE_SYNC) { - nfs_inode_remove_request(req); - dprintk(" OK\n"); - goto next; - } - memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); - nfs_mark_request_commit(req); - dprintk(" marked for commit\n"); -#else - nfs_inode_remove_request(req); -#endif - next: - nfs_unlock_request(req); - } + nfs_writeback_done_full(data, task->tk_status); }