Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfab36e8 authored by Anton Altaparmakov's avatar Anton Altaparmakov Committed by Linus Torvalds
Browse files

NTFS: Fix a mount time deadlock.



Big thanks go to Mathias Kolehmainen for reporting the bug, providing
debug output and testing the patches I sent him to get it working.

The fix was to stop calling ntfs_attr_set() at mount time as that causes
balance_dirty_pages_ratelimited() to be called which on systems with
little memory actually tries to go and balance the dirty pages which tries
to take the s_umount semaphore but because we are still in fill_super()
across which the VFS holds s_umount for writing this results in a
deadlock.

We now do the dirty work by hand by submitting individual buffers.  This
has the annoying "feature" that mounting can take a few seconds if the
journal is large as we have clear it all.  One day someone should improve
on this by deferring the journal clearing to a helper kernel thread so it
can be done in the background but I don't have time for this at the moment
and the current solution works fine so I am leaving it like this for now.

Signed-off-by: default avatarAnton Altaparmakov <aia21@cantab.net>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f26e51f6
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -407,7 +407,7 @@ raiddev /dev/md0
	device		/dev/hda5
	device		/dev/hda5
	raid-disk	0
	raid-disk	0
	device		/dev/hdb1
	device		/dev/hdb1
	raid-disl	1
	raid-disk	1


For linear raid, just change the raid-level above to "raid-level linear", for
For linear raid, just change the raid-level above to "raid-level linear", for
mirrors, change it to "raid-level 1", and for stripe sets with parity, change
mirrors, change it to "raid-level 1", and for stripe sets with parity, change
@@ -457,6 +457,8 @@ ChangeLog


Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.


2.1.29:
	- Fix a deadlock when mounting read-write.
2.1.28:
2.1.28:
	- Fix a deadlock.
	- Fix a deadlock.
2.1.27:
2.1.27:
+12 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,18 @@ ToDo/Notes:
	  happen is unclear however so it is worth waiting until someone hits
	  happen is unclear however so it is worth waiting until someone hits
	  the problem.
	  the problem.


2.1.29 - Fix a deadlock at mount time.

	- During mount the VFS holds s_umount lock on the superblock.  So when
	  we try to empty the journal $LogFile contents by calling
	  ntfs_attr_set() when the machine does not have much memory and the
	  journal is large ntfs_attr_set() results in the VM trying to balance
	  dirty pages which in turn tries to that the s_umount lock and thus we
	  get a deadlock.  The solution is to not use ntfs_attr_set() and
	  instead do the zeroing by hand at the block level rather than page
	  cache level.
	- Fix sparse warnings.

2.1.28 - Fix a deadlock.
2.1.28 - Fix a deadlock.


	- Fix deadlock in fs/ntfs/inode.c::ntfs_put_inode().  Thanks to Sergey
	- Fix deadlock in fs/ntfs/inode.c::ntfs_put_inode().  Thanks to Sergey
+1 −1
Original line number Original line Diff line number Diff line
@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
	     index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
	     index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
	     unistr.o upcase.o
	     unistr.o upcase.o


EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.28\"
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.29\"


ifeq ($(CONFIG_NTFS_DEBUG),y)
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
EXTRA_CFLAGS += -DDEBUG
+11 −11
Original line number Original line Diff line number Diff line
@@ -2,7 +2,7 @@
 * aops.c - NTFS kernel address space operations and page cache handling.
 * aops.c - NTFS kernel address space operations and page cache handling.
 *	    Part of the Linux-NTFS project.
 *	    Part of the Linux-NTFS project.
 *
 *
 * Copyright (c) 2001-2006 Anton Altaparmakov
 * Copyright (c) 2001-2007 Anton Altaparmakov
 * Copyright (c) 2002 Richard Russon
 * Copyright (c) 2002 Richard Russon
 *
 *
 * This program/include file is free software; you can redistribute it and/or
 * This program/include file is free software; you can redistribute it and/or
@@ -396,7 +396,7 @@ static int ntfs_readpage(struct file *file, struct page *page)
	loff_t i_size;
	loff_t i_size;
	struct inode *vi;
	struct inode *vi;
	ntfs_inode *ni, *base_ni;
	ntfs_inode *ni, *base_ni;
	u8 *kaddr;
	u8 *addr;
	ntfs_attr_search_ctx *ctx;
	ntfs_attr_search_ctx *ctx;
	MFT_RECORD *mrec;
	MFT_RECORD *mrec;
	unsigned long flags;
	unsigned long flags;
@@ -491,15 +491,15 @@ static int ntfs_readpage(struct file *file, struct page *page)
		/* Race with shrinking truncate. */
		/* Race with shrinking truncate. */
		attr_len = i_size;
		attr_len = i_size;
	}
	}
	kaddr = kmap_atomic(page, KM_USER0);
	addr = kmap_atomic(page, KM_USER0);
	/* Copy the data to the page. */
	/* Copy the data to the page. */
	memcpy(kaddr, (u8*)ctx->attr +
	memcpy(addr, (u8*)ctx->attr +
			le16_to_cpu(ctx->attr->data.resident.value_offset),
			le16_to_cpu(ctx->attr->data.resident.value_offset),
			attr_len);
			attr_len);
	/* Zero the remainder of the page. */
	/* Zero the remainder of the page. */
	memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
	memset(addr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
	flush_dcache_page(page);
	flush_dcache_page(page);
	kunmap_atomic(kaddr, KM_USER0);
	kunmap_atomic(addr, KM_USER0);
put_unm_err_out:
put_unm_err_out:
	ntfs_attr_put_search_ctx(ctx);
	ntfs_attr_put_search_ctx(ctx);
unm_err_out:
unm_err_out:
@@ -1344,7 +1344,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
	loff_t i_size;
	loff_t i_size;
	struct inode *vi = page->mapping->host;
	struct inode *vi = page->mapping->host;
	ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
	ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
	char *kaddr;
	char *addr;
	ntfs_attr_search_ctx *ctx = NULL;
	ntfs_attr_search_ctx *ctx = NULL;
	MFT_RECORD *m = NULL;
	MFT_RECORD *m = NULL;
	u32 attr_len;
	u32 attr_len;
@@ -1484,14 +1484,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
		/* Shrinking cannot fail. */
		/* Shrinking cannot fail. */
		BUG_ON(err);
		BUG_ON(err);
	}
	}
	kaddr = kmap_atomic(page, KM_USER0);
	addr = kmap_atomic(page, KM_USER0);
	/* Copy the data from the page to the mft record. */
	/* Copy the data from the page to the mft record. */
	memcpy((u8*)ctx->attr +
	memcpy((u8*)ctx->attr +
			le16_to_cpu(ctx->attr->data.resident.value_offset),
			le16_to_cpu(ctx->attr->data.resident.value_offset),
			kaddr, attr_len);
			addr, attr_len);
	/* Zero out of bounds area in the page cache page. */
	/* Zero out of bounds area in the page cache page. */
	memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
	memset(addr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
	kunmap_atomic(kaddr, KM_USER0);
	kunmap_atomic(addr, KM_USER0);
	flush_dcache_page(page);
	flush_dcache_page(page);
	flush_dcache_mft_record_page(ctx->ntfs_ino);
	flush_dcache_mft_record_page(ctx->ntfs_ino);
	/* We are done with the page. */
	/* We are done with the page. */
+6 −2
Original line number Original line Diff line number Diff line
/**
/**
 * attrib.c - NTFS attribute operations.  Part of the Linux-NTFS project.
 * attrib.c - NTFS attribute operations.  Part of the Linux-NTFS project.
 *
 *
 * Copyright (c) 2001-2006 Anton Altaparmakov
 * Copyright (c) 2001-2007 Anton Altaparmakov
 * Copyright (c) 2002 Richard Russon
 * Copyright (c) 2002 Richard Russon
 *
 *
 * This program/include file is free software; you can redistribute it and/or
 * This program/include file is free software; you can redistribute it and/or
@@ -2500,7 +2500,7 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
	struct page *page;
	struct page *page;
	u8 *kaddr;
	u8 *kaddr;
	pgoff_t idx, end;
	pgoff_t idx, end;
	unsigned int start_ofs, end_ofs, size;
	unsigned start_ofs, end_ofs, size;


	ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.",
	ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.",
			(long long)ofs, (long long)cnt, val);
			(long long)ofs, (long long)cnt, val);
@@ -2548,6 +2548,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
		kunmap_atomic(kaddr, KM_USER0);
		kunmap_atomic(kaddr, KM_USER0);
		set_page_dirty(page);
		set_page_dirty(page);
		page_cache_release(page);
		page_cache_release(page);
		balance_dirty_pages_ratelimited(mapping);
		cond_resched();
		if (idx == end)
		if (idx == end)
			goto done;
			goto done;
		idx++;
		idx++;
@@ -2604,6 +2606,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
		kunmap_atomic(kaddr, KM_USER0);
		kunmap_atomic(kaddr, KM_USER0);
		set_page_dirty(page);
		set_page_dirty(page);
		page_cache_release(page);
		page_cache_release(page);
		balance_dirty_pages_ratelimited(mapping);
		cond_resched();
	}
	}
done:
done:
	ntfs_debug("Done.");
	ntfs_debug("Done.");
Loading