diff options
-rw-r--r-- | fs/fuse/file.c | 100 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 1 |
2 files changed, 91 insertions, 10 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index bf765cf7b112..f8ff019cc6ec 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -1414,7 +1414,9 @@ static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) | |||
1414 | 1414 | ||
1415 | for (i = 0; i < req->num_pages; i++) | 1415 | for (i = 0; i < req->num_pages; i++) |
1416 | __free_page(req->pages[i]); | 1416 | __free_page(req->pages[i]); |
1417 | fuse_file_put(req->ff, false); | 1417 | |
1418 | if (req->ff) | ||
1419 | fuse_file_put(req->ff, false); | ||
1418 | } | 1420 | } |
1419 | 1421 | ||
1420 | static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) | 1422 | static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) |
@@ -1496,6 +1498,14 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) | |||
1496 | 1498 | ||
1497 | mapping_set_error(inode->i_mapping, req->out.h.error); | 1499 | mapping_set_error(inode->i_mapping, req->out.h.error); |
1498 | spin_lock(&fc->lock); | 1500 | spin_lock(&fc->lock); |
1501 | while (req->misc.write.next) { | ||
1502 | struct fuse_req *next = req->misc.write.next; | ||
1503 | req->misc.write.next = next->misc.write.next; | ||
1504 | next->misc.write.next = NULL; | ||
1505 | list_add(&next->writepages_entry, &fi->writepages); | ||
1506 | list_add_tail(&next->list, &fi->queued_writes); | ||
1507 | fuse_flush_writepages(inode); | ||
1508 | } | ||
1499 | fi->writectr--; | 1509 | fi->writectr--; |
1500 | fuse_writepage_finish(fc, req); | 1510 | fuse_writepage_finish(fc, req); |
1501 | spin_unlock(&fc->lock); | 1511 | spin_unlock(&fc->lock); |
@@ -1548,6 +1558,7 @@ static int fuse_writepage_locked(struct page *page) | |||
1548 | 1558 | ||
1549 | copy_highpage(tmp_page, page); | 1559 | copy_highpage(tmp_page, page); |
1550 | req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; | 1560 | req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; |
1561 | req->misc.write.next = NULL; | ||
1551 | req->in.argpages = 1; | 1562 | req->in.argpages = 1; |
1552 | req->num_pages = 1; | 1563 | req->num_pages = 1; |
1553 | req->pages[0] = tmp_page; | 1564 | req->pages[0] = tmp_page; |
@@ -1612,6 +1623,62 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) | |||
1612 | end_page_writeback(data->orig_pages[i]); | 1623 | end_page_writeback(data->orig_pages[i]); |
1613 | } | 1624 | } |
1614 | 1625 | ||
1626 | static bool fuse_writepage_in_flight(struct fuse_req *new_req, | ||
1627 | struct page *page) | ||
1628 | { | ||
1629 | struct fuse_conn *fc = get_fuse_conn(new_req->inode); | ||
1630 | struct fuse_inode *fi = get_fuse_inode(new_req->inode); | ||
1631 | struct fuse_req *tmp; | ||
1632 | struct fuse_req *old_req; | ||
1633 | bool found = false; | ||
1634 | pgoff_t curr_index; | ||
1635 | |||
1636 | BUG_ON(new_req->num_pages != 0); | ||
1637 | |||
1638 | spin_lock(&fc->lock); | ||
1639 | list_del(&new_req->writepages_entry); | ||
1640 | new_req->num_pages = 1; | ||
1641 | list_for_each_entry(old_req, &fi->writepages, writepages_entry) { | ||
1642 | BUG_ON(old_req->inode != new_req->inode); | ||
1643 | curr_index = old_req->misc.write.in.offset >> PAGE_CACHE_SHIFT; | ||
1644 | if (curr_index <= page->index && | ||
1645 | page->index < curr_index + old_req->num_pages) { | ||
1646 | found = true; | ||
1647 | break; | ||
1648 | } | ||
1649 | } | ||
1650 | if (!found) | ||
1651 | goto out_unlock; | ||
1652 | |||
1653 | for (tmp = old_req; tmp != NULL; tmp = tmp->misc.write.next) { | ||
1654 | BUG_ON(tmp->inode != new_req->inode); | ||
1655 | curr_index = tmp->misc.write.in.offset >> PAGE_CACHE_SHIFT; | ||
1656 | if (tmp->num_pages == 1 && | ||
1657 | curr_index == page->index) { | ||
1658 | old_req = tmp; | ||
1659 | } | ||
1660 | } | ||
1661 | |||
1662 | if (old_req->num_pages == 1 && (old_req->state == FUSE_REQ_INIT || | ||
1663 | old_req->state == FUSE_REQ_PENDING)) { | ||
1664 | copy_highpage(old_req->pages[0], page); | ||
1665 | spin_unlock(&fc->lock); | ||
1666 | |||
1667 | dec_bdi_stat(page->mapping->backing_dev_info, BDI_WRITEBACK); | ||
1668 | dec_zone_page_state(page, NR_WRITEBACK_TEMP); | ||
1669 | fuse_writepage_free(fc, new_req); | ||
1670 | fuse_request_free(new_req); | ||
1671 | goto out; | ||
1672 | } else { | ||
1673 | new_req->misc.write.next = old_req->misc.write.next; | ||
1674 | old_req->misc.write.next = new_req; | ||
1675 | } | ||
1676 | out_unlock: | ||
1677 | spin_unlock(&fc->lock); | ||
1678 | out: | ||
1679 | return found; | ||
1680 | } | ||
1681 | |||
1615 | static int fuse_writepages_fill(struct page *page, | 1682 | static int fuse_writepages_fill(struct page *page, |
1616 | struct writeback_control *wbc, void *_data) | 1683 | struct writeback_control *wbc, void *_data) |
1617 | { | 1684 | { |
@@ -1620,6 +1687,7 @@ static int fuse_writepages_fill(struct page *page, | |||
1620 | struct inode *inode = data->inode; | 1687 | struct inode *inode = data->inode; |
1621 | struct fuse_conn *fc = get_fuse_conn(inode); | 1688 | struct fuse_conn *fc = get_fuse_conn(inode); |
1622 | struct page *tmp_page; | 1689 | struct page *tmp_page; |
1690 | bool is_writeback; | ||
1623 | int err; | 1691 | int err; |
1624 | 1692 | ||
1625 | if (!data->ff) { | 1693 | if (!data->ff) { |
@@ -1629,15 +1697,20 @@ static int fuse_writepages_fill(struct page *page, | |||
1629 | goto out_unlock; | 1697 | goto out_unlock; |
1630 | } | 1698 | } |
1631 | 1699 | ||
1632 | if (req) { | 1700 | /* |
1633 | BUG_ON(!req->num_pages); | 1701 | * Being under writeback is unlikely but possible. For example direct |
1634 | if (req->num_pages == FUSE_MAX_PAGES_PER_REQ || | 1702 | * read to an mmaped fuse file will set the page dirty twice; once when |
1635 | (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_write || | 1703 | * the pages are faulted with get_user_pages(), and then after the read |
1636 | data->orig_pages[req->num_pages - 1]->index + 1 != page->index) { | 1704 | * completed. |
1705 | */ | ||
1706 | is_writeback = fuse_page_is_writeback(inode, page->index); | ||
1637 | 1707 | ||
1638 | fuse_writepages_send(data); | 1708 | if (req && req->num_pages && |
1639 | data->req = NULL; | 1709 | (is_writeback || req->num_pages == FUSE_MAX_PAGES_PER_REQ || |
1640 | } | 1710 | (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_write || |
1711 | data->orig_pages[req->num_pages - 1]->index + 1 != page->index)) { | ||
1712 | fuse_writepages_send(data); | ||
1713 | data->req = NULL; | ||
1641 | } | 1714 | } |
1642 | err = -ENOMEM; | 1715 | err = -ENOMEM; |
1643 | tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); | 1716 | tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); |
@@ -1669,6 +1742,7 @@ static int fuse_writepages_fill(struct page *page, | |||
1669 | 1742 | ||
1670 | fuse_write_fill(req, data->ff, page_offset(page), 0); | 1743 | fuse_write_fill(req, data->ff, page_offset(page), 0); |
1671 | req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; | 1744 | req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; |
1745 | req->misc.write.next = NULL; | ||
1672 | req->in.argpages = 1; | 1746 | req->in.argpages = 1; |
1673 | req->background = 1; | 1747 | req->background = 1; |
1674 | req->num_pages = 0; | 1748 | req->num_pages = 0; |
@@ -1690,6 +1764,13 @@ static int fuse_writepages_fill(struct page *page, | |||
1690 | 1764 | ||
1691 | inc_bdi_stat(page->mapping->backing_dev_info, BDI_WRITEBACK); | 1765 | inc_bdi_stat(page->mapping->backing_dev_info, BDI_WRITEBACK); |
1692 | inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); | 1766 | inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); |
1767 | |||
1768 | err = 0; | ||
1769 | if (is_writeback && fuse_writepage_in_flight(req, page)) { | ||
1770 | end_page_writeback(page); | ||
1771 | data->req = NULL; | ||
1772 | goto out_unlock; | ||
1773 | } | ||
1693 | data->orig_pages[req->num_pages] = page; | 1774 | data->orig_pages[req->num_pages] = page; |
1694 | 1775 | ||
1695 | /* | 1776 | /* |
@@ -1700,7 +1781,6 @@ static int fuse_writepages_fill(struct page *page, | |||
1700 | req->num_pages++; | 1781 | req->num_pages++; |
1701 | spin_unlock(&fc->lock); | 1782 | spin_unlock(&fc->lock); |
1702 | 1783 | ||
1703 | err = 0; | ||
1704 | out_unlock: | 1784 | out_unlock: |
1705 | unlock_page(page); | 1785 | unlock_page(page); |
1706 | 1786 | ||
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5b9e6f3b6aef..643274852c8b 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -321,6 +321,7 @@ struct fuse_req { | |||
321 | struct { | 321 | struct { |
322 | struct fuse_write_in in; | 322 | struct fuse_write_in in; |
323 | struct fuse_write_out out; | 323 | struct fuse_write_out out; |
324 | struct fuse_req *next; | ||
324 | } write; | 325 | } write; |
325 | struct fuse_notify_retrieve_in retrieve_in; | 326 | struct fuse_notify_retrieve_in retrieve_in; |
326 | struct fuse_lk_in lk_in; | 327 | struct fuse_lk_in lk_in; |