linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] Improve compression workspaces memory management
@ 2016-05-05 16:36 David Sterba
  2016-05-05 16:36 ` [PATCH 1/4] btrfs: rename and document compression workspace members David Sterba
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: David Sterba @ 2016-05-05 16:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Hi,

the compression workspaces are allocated as needed an this could fail if
there's no free memory. Moreover, as we might be flushing data from the
restricted contexts we should try our best not to fail.

This patchset preallocates one workspace for each compression type at module
load time (and tries to get one if that fails later). If any further request
for new workspace fails, there's still that one to make progress. IOW workspace
allocation will not fail at writeback time.

I have tested this by instrumenting the code to limit the number of workspaces
to one and did some stress tests.

David Sterba (4):
  btrfs: rename and document compression workspace members
  btrfs: preallocate compression workspaces
  btrfs: make find_workspace always succeed
  btrfs: make find_workspace warn if there are no workspaces

 fs/btrfs/compression.c | 85 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 61 insertions(+), 24 deletions(-)

-- 
2.7.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] btrfs: rename and document compression workspace members
  2016-05-05 16:36 [PATCH 0/4] Improve compression workspaces memory management David Sterba
@ 2016-05-05 16:36 ` David Sterba
  2016-05-05 16:36 ` [PATCH 2/4] btrfs: preallocate compression workspaces David Sterba
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2016-05-05 16:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

The names are confusing, pick more fitting names and add comments.

Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/compression.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index ff61a41ac90b..4d5cd9624bb3 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -743,8 +743,11 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
 static struct {
 	struct list_head idle_ws;
 	spinlock_t ws_lock;
-	int num_ws;
-	atomic_t alloc_ws;
+	/* Number of free workspaces */
+	int free_ws;
+	/* Total number of allocated workspaces */
+	atomic_t total_ws;
+	/* Waiters for a free workspace */
 	wait_queue_head_t ws_wait;
 } btrfs_comp_ws[BTRFS_COMPRESS_TYPES];
 
@@ -760,7 +763,7 @@ void __init btrfs_init_compress(void)
 	for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) {
 		INIT_LIST_HEAD(&btrfs_comp_ws[i].idle_ws);
 		spin_lock_init(&btrfs_comp_ws[i].ws_lock);
-		atomic_set(&btrfs_comp_ws[i].alloc_ws, 0);
+		atomic_set(&btrfs_comp_ws[i].total_ws, 0);
 		init_waitqueue_head(&btrfs_comp_ws[i].ws_wait);
 	}
 }
@@ -777,35 +780,35 @@ static struct list_head *find_workspace(int type)
 
 	struct list_head *idle_ws	= &btrfs_comp_ws[idx].idle_ws;
 	spinlock_t *ws_lock		= &btrfs_comp_ws[idx].ws_lock;
-	atomic_t *alloc_ws		= &btrfs_comp_ws[idx].alloc_ws;
+	atomic_t *total_ws		= &btrfs_comp_ws[idx].total_ws;
 	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
-	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
+	int *free_ws			= &btrfs_comp_ws[idx].free_ws;
 again:
 	spin_lock(ws_lock);
 	if (!list_empty(idle_ws)) {
 		workspace = idle_ws->next;
 		list_del(workspace);
-		(*num_ws)--;
+		(*free_ws)--;
 		spin_unlock(ws_lock);
 		return workspace;
 
 	}
-	if (atomic_read(alloc_ws) > cpus) {
+	if (atomic_read(total_ws) > cpus) {
 		DEFINE_WAIT(wait);
 
 		spin_unlock(ws_lock);
 		prepare_to_wait(ws_wait, &wait, TASK_UNINTERRUPTIBLE);
-		if (atomic_read(alloc_ws) > cpus && !*num_ws)
+		if (atomic_read(total_ws) > cpus && !*free_ws)
 			schedule();
 		finish_wait(ws_wait, &wait);
 		goto again;
 	}
-	atomic_inc(alloc_ws);
+	atomic_inc(total_ws);
 	spin_unlock(ws_lock);
 
 	workspace = btrfs_compress_op[idx]->alloc_workspace();
 	if (IS_ERR(workspace)) {
-		atomic_dec(alloc_ws);
+		atomic_dec(total_ws);
 		wake_up(ws_wait);
 	}
 	return workspace;
@@ -820,21 +823,21 @@ static void free_workspace(int type, struct list_head *workspace)
 	int idx = type - 1;
 	struct list_head *idle_ws	= &btrfs_comp_ws[idx].idle_ws;
 	spinlock_t *ws_lock		= &btrfs_comp_ws[idx].ws_lock;
-	atomic_t *alloc_ws		= &btrfs_comp_ws[idx].alloc_ws;
+	atomic_t *total_ws		= &btrfs_comp_ws[idx].total_ws;
 	wait_queue_head_t *ws_wait	= &btrfs_comp_ws[idx].ws_wait;
-	int *num_ws			= &btrfs_comp_ws[idx].num_ws;
+	int *free_ws			= &btrfs_comp_ws[idx].free_ws;
 
 	spin_lock(ws_lock);
-	if (*num_ws < num_online_cpus()) {
+	if (*free_ws < num_online_cpus()) {
 		list_add(workspace, idle_ws);
-		(*num_ws)++;
+		(*free_ws)++;
 		spin_unlock(ws_lock);
 		goto wake;
 	}
 	spin_unlock(ws_lock);
 
 	btrfs_compress_op[idx]->free_workspace(workspace);
-	atomic_dec(alloc_ws);
+	atomic_dec(total_ws);
 wake:
 	/*
 	 * Make sure counter is updated before we wake up waiters.
@@ -857,7 +860,7 @@ static void free_workspaces(void)
 			workspace = btrfs_comp_ws[i].idle_ws.next;
 			list_del(workspace);
 			btrfs_compress_op[i]->free_workspace(workspace);
-			atomic_dec(&btrfs_comp_ws[i].alloc_ws);
+			atomic_dec(&btrfs_comp_ws[i].total_ws);
 		}
 	}
 }
-- 
2.7.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/4] btrfs: preallocate compression workspaces
  2016-05-05 16:36 [PATCH 0/4] Improve compression workspaces memory management David Sterba
  2016-05-05 16:36 ` [PATCH 1/4] btrfs: rename and document compression workspace members David Sterba
@ 2016-05-05 16:36 ` David Sterba
  2016-05-05 16:36 ` [PATCH 3/4] btrfs: make find_workspace always succeed David Sterba
  2016-05-05 16:36 ` [PATCH 4/4] btrfs: make find_workspace warn if there are no workspaces David Sterba
  3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2016-05-05 16:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Preallocate one workspace for each compression type so we can guarantee
forward progress in the worst case. A failure cannot be a hard error as
we might not use compression at all on the filesystem. If we can't
allocate the workspaces later when need them, it might actually
deadlock, but in such situation the system has effectively not enough
memory to operate properly.

Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/compression.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 4d5cd9624bb3..38c058bcf359 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -761,10 +761,26 @@ void __init btrfs_init_compress(void)
 	int i;
 
 	for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) {
+		struct list_head *workspace;
+
 		INIT_LIST_HEAD(&btrfs_comp_ws[i].idle_ws);
 		spin_lock_init(&btrfs_comp_ws[i].ws_lock);
 		atomic_set(&btrfs_comp_ws[i].total_ws, 0);
 		init_waitqueue_head(&btrfs_comp_ws[i].ws_wait);
+
+		/*
+		 * Preallocate one workspace for each compression type so
+		 * we can guarantee forward progress in the worst case
+		 */
+		workspace = btrfs_compress_op[i]->alloc_workspace();
+		if (IS_ERR(workspace)) {
+			printk(KERN_WARNING
+	"BTRFS: cannot preallocate compression workspace, will try later");
+		} else {
+			atomic_set(&btrfs_comp_ws[i].total_ws, 1);
+			btrfs_comp_ws[i].free_ws = 1;
+			list_add(workspace, &btrfs_comp_ws[i].idle_ws);
+		}
 	}
 }
 
-- 
2.7.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/4] btrfs: make find_workspace always succeed
  2016-05-05 16:36 [PATCH 0/4] Improve compression workspaces memory management David Sterba
  2016-05-05 16:36 ` [PATCH 1/4] btrfs: rename and document compression workspace members David Sterba
  2016-05-05 16:36 ` [PATCH 2/4] btrfs: preallocate compression workspaces David Sterba
@ 2016-05-05 16:36 ` David Sterba
  2016-05-05 16:36 ` [PATCH 4/4] btrfs: make find_workspace warn if there are no workspaces David Sterba
  3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2016-05-05 16:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

With just one preallocated workspace we can guarantee forward progress
even if there's no memory available for new workspaces. The cost is more
waiting but we also get rid of several error paths.

On average, there will be several idle workspaces, so the waiting
penalty won't be so bad.

In the worst case, all cpus will compete for one workspace until there's
some memory. Attempts to allocate a new one are done each time the
waiters are woken up.

Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/compression.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 38c058bcf359..c70625560265 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -785,8 +785,10 @@ void __init btrfs_init_compress(void)
 }
 
 /*
- * this finds an available workspace or allocates a new one
- * ERR_PTR is returned if things go bad.
+ * This finds an available workspace or allocates a new one.
+ * If it's not possible to allocate a new one, waits until there's one.
+ * Preallocation makes a forward progress guarantees and we do not return
+ * errors.
  */
 static struct list_head *find_workspace(int type)
 {
@@ -826,6 +828,14 @@ static struct list_head *find_workspace(int type)
 	if (IS_ERR(workspace)) {
 		atomic_dec(total_ws);
 		wake_up(ws_wait);
+
+		/*
+		 * Do not return the error but go back to waiting. There's a
+		 * workspace preallocated for each type and the compression
+		 * time is bounded so we get to a workspace eventually. This
+		 * makes our caller's life easier.
+		 */
+		goto again;
 	}
 	return workspace;
 }
@@ -913,8 +923,6 @@ int btrfs_compress_pages(int type, struct address_space *mapping,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
-		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping,
 						      start, len, pages,
@@ -949,8 +957,6 @@ static int btrfs_decompress_biovec(int type, struct page **pages_in,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
-		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in,
 							 disk_start,
@@ -971,8 +977,6 @@ int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
 	int ret;
 
 	workspace = find_workspace(type);
-	if (IS_ERR(workspace))
-		return PTR_ERR(workspace);
 
 	ret = btrfs_compress_op[type-1]->decompress(workspace, data_in,
 						  dest_page, start_byte,
-- 
2.7.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/4] btrfs: make find_workspace warn if there are no workspaces
  2016-05-05 16:36 [PATCH 0/4] Improve compression workspaces memory management David Sterba
                   ` (2 preceding siblings ...)
  2016-05-05 16:36 ` [PATCH 3/4] btrfs: make find_workspace always succeed David Sterba
@ 2016-05-05 16:36 ` David Sterba
  3 siblings, 0 replies; 5+ messages in thread
From: David Sterba @ 2016-05-05 16:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Be verbose if there are no workspaces at all, ie. the module init time
preallocation failed.

Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/compression.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index c70625560265..658c39b70fba 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -834,7 +834,21 @@ static struct list_head *find_workspace(int type)
 		 * workspace preallocated for each type and the compression
 		 * time is bounded so we get to a workspace eventually. This
 		 * makes our caller's life easier.
+		 *
+		 * To prevent silent and low-probability deadlocks (when the
+		 * initial preallocation fails), check if there are any
+		 * workspaces at all.
 		 */
+		if (atomic_read(total_ws) == 0) {
+			static DEFINE_RATELIMIT_STATE(_rs,
+					/* once per minute */ 60 * HZ,
+					/* no burst */ 1);
+
+			if (__ratelimit(&_rs)) {
+				printk(KERN_WARNING
+			    "no compression workspaces, low memory, retrying");
+			}
+		}
 		goto again;
 	}
 	return workspace;
-- 
2.7.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-05-05 16:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-05-05 16:36 [PATCH 0/4] Improve compression workspaces memory management David Sterba
2016-05-05 16:36 ` [PATCH 1/4] btrfs: rename and document compression workspace members David Sterba
2016-05-05 16:36 ` [PATCH 2/4] btrfs: preallocate compression workspaces David Sterba
2016-05-05 16:36 ` [PATCH 3/4] btrfs: make find_workspace always succeed David Sterba
2016-05-05 16:36 ` [PATCH 4/4] btrfs: make find_workspace warn if there are no workspaces David Sterba

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).