USB: Fix a memory leak in em28xx_usb_probe()
[cascardo/linux.git] / fs / nfs / write.c
index b084c03..ef97e0c 100644 (file)
@@ -117,19 +117,19 @@ static struct nfs_page *nfs_page_find_request_locked(struct page *page)
        if (PagePrivate(page)) {
                req = (struct nfs_page *)page_private(page);
                if (req != NULL)
-                       atomic_inc(&req->wb_count);
+                       kref_get(&req->wb_kref);
        }
        return req;
 }
 
 static struct nfs_page *nfs_page_find_request(struct page *page)
 {
+       struct inode *inode = page->mapping->host;
        struct nfs_page *req = NULL;
-       spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock;
 
-       spin_lock(req_lock);
+       spin_lock(&inode->i_lock);
        req = nfs_page_find_request_locked(page);
-       spin_unlock(req_lock);
+       spin_unlock(&inode->i_lock);
        return req;
 }
 
@@ -191,8 +191,6 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
        }
        /* Update file length */
        nfs_grow_file(page, offset, count);
-       /* Set the PG_uptodate flag? */
-       nfs_mark_uptodate(page, offset, count);
        nfs_unlock_request(req);
        return 0;
 }
@@ -253,16 +251,16 @@ static void nfs_end_page_writeback(struct page *page)
 static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                                struct page *page)
 {
+       struct inode *inode = page->mapping->host;
+       struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_page *req;
-       struct nfs_inode *nfsi = NFS_I(page->mapping->host);
-       spinlock_t *req_lock = &nfsi->req_lock;
        int ret;
 
-       spin_lock(req_lock);
+       spin_lock(&inode->i_lock);
        for(;;) {
                req = nfs_page_find_request_locked(page);
                if (req == NULL) {
-                       spin_unlock(req_lock);
+                       spin_unlock(&inode->i_lock);
                        return 1;
                }
                if (nfs_lock_request_dontget(req))
@@ -272,30 +270,28 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                 *       succeed provided that someone hasn't already marked the
                 *       request as dirty (in which case we don't care).
                 */
-               spin_unlock(req_lock);
-               /* Prevent deadlock! */
-               nfs_pageio_complete(pgio);
+               spin_unlock(&inode->i_lock);
                ret = nfs_wait_on_request(req);
                nfs_release_request(req);
                if (ret != 0)
                        return ret;
-               spin_lock(req_lock);
+               spin_lock(&inode->i_lock);
        }
        if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
                /* This request is marked for commit */
-               spin_unlock(req_lock);
+               spin_unlock(&inode->i_lock);
                nfs_unlock_request(req);
                nfs_pageio_complete(pgio);
                return 1;
        }
        if (nfs_set_page_writeback(page) != 0) {
-               spin_unlock(req_lock);
+               spin_unlock(&inode->i_lock);
                BUG();
        }
        radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
-                       NFS_PAGE_TAG_WRITEBACK);
+                       NFS_PAGE_TAG_LOCKED);
        ret = test_bit(PG_NEED_FLUSH, &req->wb_flags);
-       spin_unlock(req_lock);
+       spin_unlock(&inode->i_lock);
        nfs_pageio_add_request(pgio, req);
        return ret;
 }
@@ -321,6 +317,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
                pgio = &mypgio;
        }
 
+       nfs_pageio_cond_complete(pgio, page->index);
+
        err = nfs_page_async_flush(pgio, page);
        if (err <= 0)
                goto out;
@@ -329,6 +327,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
        if (!offset)
                goto out;
 
+       nfs_pageio_cond_complete(pgio, page->index);
+
        ctx = nfs_find_open_context(inode, NULL, FMODE_WRITE);
        if (ctx == NULL) {
                err = -EBADF;
@@ -398,7 +398,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
        if (PageDirty(req->wb_page))
                set_bit(PG_NEED_FLUSH, &req->wb_flags);
        nfsi->npages++;
-       atomic_inc(&req->wb_count);
+       kref_get(&req->wb_kref);
        return 0;
 }
 
@@ -407,12 +407,12 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
  */
 static void nfs_inode_remove_request(struct nfs_page *req)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
+       struct inode *inode = req->wb_context->path.dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
 
        BUG_ON (!NFS_WBACK_BUSY(req));
 
-       spin_lock(&nfsi->req_lock);
+       spin_lock(&inode->i_lock);
        set_page_private(req->wb_page, 0);
        ClearPagePrivate(req->wb_page);
        radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
@@ -420,11 +420,11 @@ static void nfs_inode_remove_request(struct nfs_page *req)
                __set_page_dirty_nobuffers(req->wb_page);
        nfsi->npages--;
        if (!nfsi->npages) {
-               spin_unlock(&nfsi->req_lock);
+               spin_unlock(&inode->i_lock);
                nfs_end_data_update(inode);
                iput(inode);
        } else
-               spin_unlock(&nfsi->req_lock);
+               spin_unlock(&inode->i_lock);
        nfs_clear_request(req);
        nfs_release_request(req);
 }
@@ -455,14 +455,16 @@ nfs_dirty_request(struct nfs_page *req)
 static void
 nfs_mark_request_commit(struct nfs_page *req)
 {
-       struct inode *inode = req->wb_context->dentry->d_inode;
+       struct inode *inode = req->wb_context->path.dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       spin_lock(&nfsi->req_lock);
-       nfs_list_add_request(req, &nfsi->commit);
+       spin_lock(&inode->i_lock);
        nfsi->ncommit++;
        set_bit(PG_NEED_COMMIT, &(req)->wb_flags);
-       spin_unlock(&nfsi->req_lock);
+       radix_tree_tag_set(&nfsi->nfs_page_tree,
+                       req->wb_index,
+                       NFS_PAGE_TAG_COMMIT);
+       spin_unlock(&inode->i_lock);
        inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
        __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
 }
@@ -524,18 +526,18 @@ static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, u
                idx_end = idx_start + npages - 1;
 
        next = idx_start;
-       while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_WRITEBACK)) {
+       while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_LOCKED)) {
                if (req->wb_index > idx_end)
                        break;
 
                next = req->wb_index + 1;
                BUG_ON(!NFS_WBACK_BUSY(req));
 
-               atomic_inc(&req->wb_count);
-               spin_unlock(&nfsi->req_lock);
+               kref_get(&req->wb_kref);
+               spin_unlock(&inode->i_lock);
                error = nfs_wait_on_request(req);
                nfs_release_request(req);
-               spin_lock(&nfsi->req_lock);
+               spin_lock(&inode->i_lock);
                if (error < 0)
                        return error;
                res++;
@@ -575,10 +577,9 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, u
        int res = 0;
 
        if (nfsi->ncommit != 0) {
-               res = nfs_scan_list(nfsi, &nfsi->commit, dst, idx_start, npages);
+               res = nfs_scan_list(nfsi, dst, idx_start, npages,
+                               NFS_PAGE_TAG_COMMIT);
                nfsi->ncommit -= res;
-               if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
-                       printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
        }
        return res;
 }
@@ -601,7 +602,6 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
 {
        struct address_space *mapping = page->mapping;
        struct inode *inode = mapping->host;
-       struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_page         *req, *new = NULL;
        pgoff_t         rqend, end;
 
@@ -611,13 +611,13 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
                /* Loop over all inode entries and see if we find
                 * A request for the page we wish to update
                 */
-               spin_lock(&nfsi->req_lock);
+               spin_lock(&inode->i_lock);
                req = nfs_page_find_request_locked(page);
                if (req) {
                        if (!nfs_lock_request_dontget(req)) {
                                int error;
 
-                               spin_unlock(&nfsi->req_lock);
+                               spin_unlock(&inode->i_lock);
                                error = nfs_wait_on_request(req);
                                nfs_release_request(req);
                                if (error < 0) {
@@ -627,7 +627,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
                                }
                                continue;
                        }
-                       spin_unlock(&nfsi->req_lock);
+                       spin_unlock(&inode->i_lock);
                        if (new)
                                nfs_release_request(new);
                        break;
@@ -638,14 +638,14 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
                        nfs_lock_request_dontget(new);
                        error = nfs_inode_add_request(inode, new);
                        if (error) {
-                               spin_unlock(&nfsi->req_lock);
+                               spin_unlock(&inode->i_lock);
                                nfs_unlock_request(new);
                                return ERR_PTR(error);
                        }
-                       spin_unlock(&nfsi->req_lock);
+                       spin_unlock(&inode->i_lock);
                        return new;
                }
-               spin_unlock(&nfsi->req_lock);
+               spin_unlock(&inode->i_lock);
 
                new = nfs_create_request(ctx, inode, page, offset, bytes);
                if (IS_ERR(new))
@@ -749,12 +749,17 @@ int nfs_updatepage(struct file *file, struct page *page,
 static void nfs_writepage_release(struct nfs_page *req)
 {
 
-       if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) {
+       if (PageError(req->wb_page)) {
+               nfs_end_page_writeback(req->wb_page);
+               nfs_inode_remove_request(req);
+       } else if (!nfs_reschedule_unstable_write(req)) {
+               /* Set the PG_uptodate flag */
+               nfs_mark_uptodate(req->wb_page, req->wb_pgbase, req->wb_bytes);
                nfs_end_page_writeback(req->wb_page);
                nfs_inode_remove_request(req);
        } else
                nfs_end_page_writeback(req->wb_page);
-       nfs_clear_page_writeback(req);
+       nfs_clear_page_tag_locked(req);
 }
 
 static inline int flush_task_priority(int how)
@@ -784,7 +789,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
         * NB: take care not to mess about with data->commit et al. */
 
        data->req = req;
-       data->inode = inode = req->wb_context->dentry->d_inode;
+       data->inode = inode = req->wb_context->path.dentry->d_inode;
        data->cred = req->wb_context->cred;
 
        data->args.fh     = NFS_FH(inode);
@@ -883,7 +888,7 @@ out_bad:
        }
        nfs_redirty_request(req);
        nfs_end_page_writeback(req->wb_page);
-       nfs_clear_page_writeback(req);
+       nfs_clear_page_tag_locked(req);
        return -ENOMEM;
 }
 
@@ -926,7 +931,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i
                nfs_list_remove_request(req);
                nfs_redirty_request(req);
                nfs_end_page_writeback(req->wb_page);
-               nfs_clear_page_writeback(req);
+               nfs_clear_page_tag_locked(req);
        }
        return -ENOMEM;
 }
@@ -952,8 +957,8 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
        struct page             *page = req->wb_page;
 
        dprintk("NFS: write (%s/%Ld %d@%Ld)",
-               req->wb_context->dentry->d_inode->i_sb->s_id,
-               (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+               req->wb_context->path.dentry->d_inode->i_sb->s_id,
+               (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
                req->wb_bytes,
                (long long)req_offset(req));
 
@@ -968,9 +973,9 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
        }
 
        if (nfs_write_need_commit(data)) {
-               spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock;
+               struct inode *inode = page->mapping->host;
 
-               spin_lock(req_lock);
+               spin_lock(&inode->i_lock);
                if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) {
                        /* Do nothing we need to resend the writes */
                } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) {
@@ -981,7 +986,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
                        clear_bit(PG_NEED_COMMIT, &req->wb_flags);
                        dprintk(" server reboot detected\n");
                }
-               spin_unlock(req_lock);
+               spin_unlock(&inode->i_lock);
        } else
                dprintk(" OK\n");
 
@@ -1018,8 +1023,8 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
                page = req->wb_page;
 
                dprintk("NFS: write (%s/%Ld %d@%Ld)",
-                       req->wb_context->dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+                       req->wb_context->path.dentry->d_inode->i_sb->s_id,
+                       (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
                        req->wb_bytes,
                        (long long)req_offset(req));
 
@@ -1037,12 +1042,14 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
                        dprintk(" marked for commit\n");
                        goto next;
                }
+               /* Set the PG_uptodate flag? */
+               nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
                dprintk(" OK\n");
 remove_request:
                nfs_end_page_writeback(page);
                nfs_inode_remove_request(req);
        next:
-               nfs_clear_page_writeback(req);
+               nfs_clear_page_tag_locked(req);
        }
 }
 
@@ -1155,7 +1162,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
 
        list_splice_init(head, &data->pages);
        first = nfs_list_entry(data->pages.next);
-       inode = first->wb_context->dentry->d_inode;
+       inode = first->wb_context->path.dentry->d_inode;
 
        data->inode       = inode;
        data->cred        = first->wb_context->cred;
@@ -1205,7 +1212,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
                nfs_list_remove_request(req);
                nfs_mark_request_commit(req);
                dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
-               nfs_clear_page_writeback(req);
+               nfs_clear_page_tag_locked(req);
        }
        return -ENOMEM;
 }
@@ -1232,8 +1239,8 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
                dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
 
                dprintk("NFS: commit (%s/%Ld %d@%Ld)",
-                       req->wb_context->dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
+                       req->wb_context->path.dentry->d_inode->i_sb->s_id,
+                       (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
                        req->wb_bytes,
                        (long long)req_offset(req));
                if (task->tk_status < 0) {
@@ -1247,6 +1254,9 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
                 * returned by the server against all stored verfs. */
                if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
                        /* We have a match */
+                       /* Set the PG_uptodate flag */
+                       nfs_mark_uptodate(req->wb_page, req->wb_pgbase,
+                                       req->wb_bytes);
                        nfs_inode_remove_request(req);
                        dprintk(" OK\n");
                        goto next;
@@ -1255,7 +1265,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
                dprintk(" mismatch\n");
                nfs_redirty_request(req);
        next:
-               nfs_clear_page_writeback(req);
+               nfs_clear_page_tag_locked(req);
        }
 }
 
@@ -1266,13 +1276,12 @@ static const struct rpc_call_ops nfs_commit_ops = {
 
 int nfs_commit_inode(struct inode *inode, int how)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
        LIST_HEAD(head);
        int res;
 
-       spin_lock(&nfsi->req_lock);
+       spin_lock(&inode->i_lock);
        res = nfs_scan_commit(inode, &head, 0, 0);
-       spin_unlock(&nfsi->req_lock);
+       spin_unlock(&inode->i_lock);
        if (res) {
                int error = nfs_commit_list(inode, &head, how);
                if (error < 0)
@@ -1290,7 +1299,6 @@ static inline int nfs_commit_list(struct inode *inode, struct list_head *head, i
 long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
 {
        struct inode *inode = mapping->host;
-       struct nfs_inode *nfsi = NFS_I(inode);
        pgoff_t idx_start, idx_end;
        unsigned int npages = 0;
        LIST_HEAD(head);
@@ -1312,7 +1320,7 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
                }
        }
        how &= ~FLUSH_NOCOMMIT;
-       spin_lock(&nfsi->req_lock);
+       spin_lock(&inode->i_lock);
        do {
                ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
                if (ret != 0)
@@ -1323,18 +1331,19 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
                if (pages == 0)
                        break;
                if (how & FLUSH_INVALIDATE) {
-                       spin_unlock(&nfsi->req_lock);
+                       spin_unlock(&inode->i_lock);
                        nfs_cancel_commit_list(&head);
                        ret = pages;
-                       spin_lock(&nfsi->req_lock);
+                       spin_lock(&inode->i_lock);
                        continue;
                }
                pages += nfs_scan_commit(inode, &head, 0, 0);
-               spin_unlock(&nfsi->req_lock);
+               spin_unlock(&inode->i_lock);
                ret = nfs_commit_list(inode, &head, how);
-               spin_lock(&nfsi->req_lock);
+               spin_lock(&inode->i_lock);
+
        } while (ret >= 0);
-       spin_unlock(&nfsi->req_lock);
+       spin_unlock(&inode->i_lock);
        return ret;
 }
 
@@ -1428,7 +1437,6 @@ int nfs_set_page_dirty(struct page *page)
 {
        struct address_space *mapping = page->mapping;
        struct inode *inode;
-       spinlock_t *req_lock;
        struct nfs_page *req;
        int ret;
 
@@ -1437,18 +1445,17 @@ int nfs_set_page_dirty(struct page *page)
        inode = mapping->host;
        if (!inode)
                goto out_raced;
-       req_lock = &NFS_I(inode)->req_lock;
-       spin_lock(req_lock);
+       spin_lock(&inode->i_lock);
        req = nfs_page_find_request_locked(page);
        if (req != NULL) {
                /* Mark any existing write requests for flushing */
                ret = !test_and_set_bit(PG_NEED_FLUSH, &req->wb_flags);
-               spin_unlock(req_lock);
+               spin_unlock(&inode->i_lock);
                nfs_release_request(req);
                return ret;
        }
        ret = __set_page_dirty_nobuffers(page);
-       spin_unlock(req_lock);
+       spin_unlock(&inode->i_lock);
        return ret;
 out_raced:
        return !TestSetPageDirty(page);
@@ -1460,7 +1467,7 @@ int __init nfs_init_writepagecache(void)
        nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
                                             sizeof(struct nfs_write_data),
                                             0, SLAB_HWCACHE_ALIGN,
-                                            NULL, NULL);
+                                            NULL);
        if (nfs_wdata_cachep == NULL)
                return -ENOMEM;