diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-07-07 09:28:51 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-07-07 09:28:51 -0400 |
commit | c55a01d360afafcd52bc405c044a6ebf5de436d5 (patch) | |
tree | f3aee71085c2c831563e06f0c78dfc21b931e342 /fs/fuse | |
parent | 233a01fa9c4c7c41238537e8db8434667ff28a2f (diff) |
fuse: avoid scheduling while atomic
As reported by Richard Sharpe, an attempt to use fuse_notify_inval_entry()
triggers complains about scheduling while atomic:
BUG: scheduling while atomic: fuse.hf/13976/0x10000001
This happens because fuse_notify_inval_entry() attempts to allocate memory
with GFP_KERNEL, holding "struct fuse_copy_state" mapped by kmap_atomic().
Introduced by commit 58bda1da4b3c "fuse/dev: use atomic maps"
Fix by moving the map/unmap to just cover the actual memcpy operation.
Original patch from Maxim Patlasov <mpatlasov@parallels.com>
Reported-by: Richard Sharpe <realrichardsharpe@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Cc: <stable@vger.kernel.org> # v3.15+
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dev.c | 51 |
1 files changed, 23 insertions, 28 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index aac71ce373e4..75fa055012b2 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -643,9 +643,8 @@ struct fuse_copy_state { | |||
643 | unsigned long seglen; | 643 | unsigned long seglen; |
644 | unsigned long addr; | 644 | unsigned long addr; |
645 | struct page *pg; | 645 | struct page *pg; |
646 | void *mapaddr; | ||
647 | void *buf; | ||
648 | unsigned len; | 646 | unsigned len; |
647 | unsigned offset; | ||
649 | unsigned move_pages:1; | 648 | unsigned move_pages:1; |
650 | }; | 649 | }; |
651 | 650 | ||
@@ -666,23 +665,17 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) | |||
666 | if (cs->currbuf) { | 665 | if (cs->currbuf) { |
667 | struct pipe_buffer *buf = cs->currbuf; | 666 | struct pipe_buffer *buf = cs->currbuf; |
668 | 667 | ||
669 | if (!cs->write) { | 668 | if (cs->write) |
670 | kunmap_atomic(cs->mapaddr); | ||
671 | } else { | ||
672 | kunmap_atomic(cs->mapaddr); | ||
673 | buf->len = PAGE_SIZE - cs->len; | 669 | buf->len = PAGE_SIZE - cs->len; |
674 | } | ||
675 | cs->currbuf = NULL; | 670 | cs->currbuf = NULL; |
676 | cs->mapaddr = NULL; | 671 | } else if (cs->pg) { |
677 | } else if (cs->mapaddr) { | ||
678 | kunmap_atomic(cs->mapaddr); | ||
679 | if (cs->write) { | 672 | if (cs->write) { |
680 | flush_dcache_page(cs->pg); | 673 | flush_dcache_page(cs->pg); |
681 | set_page_dirty_lock(cs->pg); | 674 | set_page_dirty_lock(cs->pg); |
682 | } | 675 | } |
683 | put_page(cs->pg); | 676 | put_page(cs->pg); |
684 | cs->mapaddr = NULL; | ||
685 | } | 677 | } |
678 | cs->pg = NULL; | ||
686 | } | 679 | } |
687 | 680 | ||
688 | /* | 681 | /* |
@@ -691,7 +684,7 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) | |||
691 | */ | 684 | */ |
692 | static int fuse_copy_fill(struct fuse_copy_state *cs) | 685 | static int fuse_copy_fill(struct fuse_copy_state *cs) |
693 | { | 686 | { |
694 | unsigned long offset; | 687 | struct page *page; |
695 | int err; | 688 | int err; |
696 | 689 | ||
697 | unlock_request(cs->fc, cs->req); | 690 | unlock_request(cs->fc, cs->req); |
@@ -706,14 +699,12 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
706 | 699 | ||
707 | BUG_ON(!cs->nr_segs); | 700 | BUG_ON(!cs->nr_segs); |
708 | cs->currbuf = buf; | 701 | cs->currbuf = buf; |
709 | cs->mapaddr = kmap_atomic(buf->page); | 702 | cs->pg = buf->page; |
703 | cs->offset = buf->offset; | ||
710 | cs->len = buf->len; | 704 | cs->len = buf->len; |
711 | cs->buf = cs->mapaddr + buf->offset; | ||
712 | cs->pipebufs++; | 705 | cs->pipebufs++; |
713 | cs->nr_segs--; | 706 | cs->nr_segs--; |
714 | } else { | 707 | } else { |
715 | struct page *page; | ||
716 | |||
717 | if (cs->nr_segs == cs->pipe->buffers) | 708 | if (cs->nr_segs == cs->pipe->buffers) |
718 | return -EIO; | 709 | return -EIO; |
719 | 710 | ||
@@ -726,8 +717,8 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
726 | buf->len = 0; | 717 | buf->len = 0; |
727 | 718 | ||
728 | cs->currbuf = buf; | 719 | cs->currbuf = buf; |
729 | cs->mapaddr = kmap_atomic(page); | 720 | cs->pg = page; |
730 | cs->buf = cs->mapaddr; | 721 | cs->offset = 0; |
731 | cs->len = PAGE_SIZE; | 722 | cs->len = PAGE_SIZE; |
732 | cs->pipebufs++; | 723 | cs->pipebufs++; |
733 | cs->nr_segs++; | 724 | cs->nr_segs++; |
@@ -740,14 +731,13 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
740 | cs->iov++; | 731 | cs->iov++; |
741 | cs->nr_segs--; | 732 | cs->nr_segs--; |
742 | } | 733 | } |
743 | err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); | 734 | err = get_user_pages_fast(cs->addr, 1, cs->write, &page); |
744 | if (err < 0) | 735 | if (err < 0) |
745 | return err; | 736 | return err; |
746 | BUG_ON(err != 1); | 737 | BUG_ON(err != 1); |
747 | offset = cs->addr % PAGE_SIZE; | 738 | cs->pg = page; |
748 | cs->mapaddr = kmap_atomic(cs->pg); | 739 | cs->offset = cs->addr % PAGE_SIZE; |
749 | cs->buf = cs->mapaddr + offset; | 740 | cs->len = min(PAGE_SIZE - cs->offset, cs->seglen); |
750 | cs->len = min(PAGE_SIZE - offset, cs->seglen); | ||
751 | cs->seglen -= cs->len; | 741 | cs->seglen -= cs->len; |
752 | cs->addr += cs->len; | 742 | cs->addr += cs->len; |
753 | } | 743 | } |
@@ -760,15 +750,20 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) | |||
760 | { | 750 | { |
761 | unsigned ncpy = min(*size, cs->len); | 751 | unsigned ncpy = min(*size, cs->len); |
762 | if (val) { | 752 | if (val) { |
753 | void *pgaddr = kmap_atomic(cs->pg); | ||
754 | void *buf = pgaddr + cs->offset; | ||
755 | |||
763 | if (cs->write) | 756 | if (cs->write) |
764 | memcpy(cs->buf, *val, ncpy); | 757 | memcpy(buf, *val, ncpy); |
765 | else | 758 | else |
766 | memcpy(*val, cs->buf, ncpy); | 759 | memcpy(*val, buf, ncpy); |
760 | |||
761 | kunmap_atomic(pgaddr); | ||
767 | *val += ncpy; | 762 | *val += ncpy; |
768 | } | 763 | } |
769 | *size -= ncpy; | 764 | *size -= ncpy; |
770 | cs->len -= ncpy; | 765 | cs->len -= ncpy; |
771 | cs->buf += ncpy; | 766 | cs->offset += ncpy; |
772 | return ncpy; | 767 | return ncpy; |
773 | } | 768 | } |
774 | 769 | ||
@@ -874,8 +869,8 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) | |||
874 | out_fallback_unlock: | 869 | out_fallback_unlock: |
875 | unlock_page(newpage); | 870 | unlock_page(newpage); |
876 | out_fallback: | 871 | out_fallback: |
877 | cs->mapaddr = kmap_atomic(buf->page); | 872 | cs->pg = buf->page; |
878 | cs->buf = cs->mapaddr + buf->offset; | 873 | cs->offset = buf->offset; |
879 | 874 | ||
880 | err = lock_request(cs->fc, cs->req); | 875 | err = lock_request(cs->fc, cs->req); |
881 | if (err) | 876 | if (err) |