Merge tag 'gfs2-for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 May 2019 20:16:07 +0000 (13:16 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 May 2019 20:16:07 +0000 (13:16 -0700)
Pull GFS2 updates from Andreas Gruenbacher:
 "We've got the following patches ready for this merge window:

   - "gfs2: Fix loop in gfs2_rbm_find (v2)"

      A rework of a fix we ended up reverting in 5.0 because of an
      iozone performance regression.

   - "gfs2: read journal in large chunks"
     "gfs2: fix race between gfs2_freeze_func and unmount"

      An improved version of a commit we also ended up reverting in 5.0
      because of a regression in xfstest generic/311. It turns out that
      the journal changes were mostly innocent and that unfreeze didn't
      wait for the freeze to complete, which caused the filesystem to be
      unmounted before it was actually idle.

   - "gfs2: Fix occasional glock use-after-free"
     "gfs2: Fix iomap write page reclaim deadlock"
     "gfs2: Fix lru_count going negative"

      Fixes for various problems reported and partially fixed by Citrix
      engineers. Thank you very much.

   - "gfs2: clean_journal improperly set sd_log_flush_head"

      Another fix from Bob.

   - .. and a few other minor cleanups"

* tag 'gfs2-for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: read journal in large chunks
  gfs2: Fix iomap write page reclaim deadlock
  gfs2: fix race between gfs2_freeze_func and unmount
  gfs2: Rename gfs2_trans_{add_unrevoke => remove_revoke}
  gfs2: Rename sd_log_le_{revoke,ordered}
  gfs2: Remove unnecessary extern declarations
  gfs2: Remove misleading comments in gfs2_evict_inode
  gfs2: Replace gl_revokes with a GLF flag
  gfs2: Fix occasional glock use-after-free
  gfs2: clean_journal improperly set sd_log_flush_head
  gfs2: Fix lru_count going negative
  gfs2: Fix loop in gfs2_rbm_find (v2)

1  2 
fs/gfs2/lops.c

diff --cc fs/gfs2/lops.c
index 6f09b5e3dd6e4ec344c07f87297344bad8233c13,ce048a9e058d7cd58784514f22625222c5058a03..33ab662c9aacc5f041e102082f4ad0edc876b5a5
@@@ -376,6 -377,206 +376,205 @@@ void gfs2_log_write_page(struct gfs2_sb
                       gfs2_log_bmap(sdp));
  }
  
 -      int i;
+ /**
+  * gfs2_end_log_read - end I/O callback for reads from the log
+  * @bio: The bio
+  *
+  * Simply unlock the pages in the bio. The main thread will wait on them and
+  * process them in order as necessary.
+  */
+ static void gfs2_end_log_read(struct bio *bio)
+ {
+       struct page *page;
+       struct bio_vec *bvec;
 -      bio_for_each_segment_all(bvec, bio, i, iter_all) {
+       struct bvec_iter_all iter_all;
++      bio_for_each_segment_all(bvec, bio, iter_all) {
+               page = bvec->bv_page;
+               if (bio->bi_status) {
+                       int err = blk_status_to_errno(bio->bi_status);
+                       SetPageError(page);
+                       mapping_set_error(page->mapping, err);
+               }
+               unlock_page(page);
+       }
+       bio_put(bio);
+ }
+ /**
+  * gfs2_jhead_pg_srch - Look for the journal head in a given page.
+  * @jd: The journal descriptor
+  * @page: The page to look in
+  *
+  * Returns: 1 if found, 0 otherwise.
+  */
+ static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd,
+                             struct gfs2_log_header_host *head,
+                             struct page *page)
+ {
+       struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+       struct gfs2_log_header_host uninitialized_var(lh);
+       void *kaddr = kmap_atomic(page);
+       unsigned int offset;
+       bool ret = false;
+       for (offset = 0; offset < PAGE_SIZE; offset += sdp->sd_sb.sb_bsize) {
+               if (!__get_log_header(sdp, kaddr + offset, 0, &lh)) {
+                       if (lh.lh_sequence > head->lh_sequence)
+                               *head = lh;
+                       else {
+                               ret = true;
+                               break;
+                       }
+               }
+       }
+       kunmap_atomic(kaddr);
+       return ret;
+ }
+ /**
+  * gfs2_jhead_process_page - Search/cleanup a page
+  * @jd: The journal descriptor
+  * @index: Index of the page to look into
+  * @done: If set, perform only cleanup, else search and set if found.
+  *
+  * Find the page with 'index' in the journal's mapping. Search the page for
+  * the journal head if requested (cleanup == false). Release refs on the
+  * page so the page cache can reclaim it (put_page() twice). We grabbed a
+  * reference on this page two times, first when we did a find_or_create_page()
+  * to obtain the page to add it to the bio and second when we do a
+  * find_get_page() here to get the page to wait on while I/O on it is being
+  * completed.
+  * This function is also used to free up a page we might've grabbed but not
+  * used. Maybe we added it to a bio, but not submitted it for I/O. Or we
+  * submitted the I/O, but we already found the jhead so we only need to drop
+  * our references to the page.
+  */
+ static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index,
+                                   struct gfs2_log_header_host *head,
+                                   bool *done)
+ {
+       struct page *page;
+       page = find_get_page(jd->jd_inode->i_mapping, index);
+       wait_on_page_locked(page);
+       if (PageError(page))
+               *done = true;
+       if (!*done)
+               *done = gfs2_jhead_pg_srch(jd, head, page);
+       put_page(page); /* Once for find_get_page */
+       put_page(page); /* Once more for find_or_create_page */
+ }
+ /**
+  * gfs2_find_jhead - find the head of a log
+  * @jd: The journal descriptor
+  * @head: The log descriptor for the head of the log is returned here
+  *
+  * Do a search of a journal by reading it in large chunks using bios and find
+  * the valid log entry with the highest sequence number.  (i.e. the log head)
+  *
+  * Returns: 0 on success, errno otherwise
+  */
+ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head,
+                   bool keep_cache)
+ {
+       struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
+       struct address_space *mapping = jd->jd_inode->i_mapping;
+       unsigned int block = 0, blocks_submitted = 0, blocks_read = 0;
+       unsigned int bsize = sdp->sd_sb.sb_bsize;
+       unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
+       unsigned int shift = PAGE_SHIFT - bsize_shift;
+       unsigned int readhead_blocks = BIO_MAX_PAGES << shift;
+       struct gfs2_journal_extent *je;
+       int sz, ret = 0;
+       struct bio *bio = NULL;
+       struct page *page = NULL;
+       bool done = false;
+       errseq_t since;
+       memset(head, 0, sizeof(*head));
+       if (list_empty(&jd->extent_list))
+               gfs2_map_journal_extents(sdp, jd);
+       since = filemap_sample_wb_err(mapping);
+       list_for_each_entry(je, &jd->extent_list, list) {
+               for (; block < je->lblock + je->blocks; block++) {
+                       u64 dblock;
+                       if (!page) {
+                               page = find_or_create_page(mapping,
+                                               block >> shift, GFP_NOFS);
+                               if (!page) {
+                                       ret = -ENOMEM;
+                                       done = true;
+                                       goto out;
+                               }
+                       }
+                       if (bio) {
+                               unsigned int off;
+                               off = (block << bsize_shift) & ~PAGE_MASK;
+                               sz = bio_add_page(bio, page, bsize, off);
+                               if (sz == bsize) { /* block added */
+                                       if (off + bsize == PAGE_SIZE) {
+                                               page = NULL;
+                                               goto page_added;
+                                       }
+                                       continue;
+                               }
+                               blocks_submitted = block + 1;
+                               submit_bio(bio);
+                               bio = NULL;
+                       }
+                       dblock = je->dblock + (block - je->lblock);
+                       bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read);
+                       bio->bi_opf = REQ_OP_READ;
+                       sz = bio_add_page(bio, page, bsize, 0);
+                       gfs2_assert_warn(sdp, sz == bsize);
+                       if (bsize == PAGE_SIZE)
+                               page = NULL;
+ page_added:
+                       if (blocks_submitted < blocks_read + readhead_blocks) {
+                               /* Keep at least one bio in flight */
+                               continue;
+                       }
+                       gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+                       blocks_read += PAGE_SIZE >> bsize_shift;
+                       if (done)
+                               goto out;  /* found */
+               }
+       }
+ out:
+       if (bio)
+               submit_bio(bio);
+       while (blocks_read < block) {
+               gfs2_jhead_process_page(jd, blocks_read >> shift, head, &done);
+               blocks_read += PAGE_SIZE >> bsize_shift;
+       }
+       if (!ret)
+               ret = filemap_check_wb_err(mapping, since);
+       if (!keep_cache)
+               truncate_inode_pages(mapping, 0);
+       return ret;
+ }
  static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
                                      u32 ld_length, u32 ld_data1)
  {