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

Commit 474a503d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable:
  Btrfs: fix file clone ioctl for bookend extents
  Btrfs: fix uninit compiler warning in cow_file_range_nocow
  Btrfs: constify dentry_operations
  Btrfs: optimize back reference update during btrfs_drop_snapshot
  Btrfs: remove negative dentry when deleting subvolumne
  Btrfs: optimize fsync for the single writer case
  Btrfs: async delalloc flushing under space pressure
  Btrfs: release delalloc reservations on extent item insertion
  Btrfs: delay clearing EXTENT_DELALLOC for compressed extents
  Btrfs: cleanup extent_clear_unlock_delalloc flags
  Btrfs: fix possible softlockup in the allocator
  Btrfs: fix deadlock on async thread startup
parents d43c36dc ac6889cb
Loading
Loading
Loading
Loading
+73 −8
Original line number Diff line number Diff line
@@ -63,6 +63,51 @@ struct btrfs_worker_thread {
	int idle;
};

/*
 * btrfs_start_workers uses kthread_run, which can block waiting for memory
 * for a very long time.  It will actually throttle on page writeback,
 * and so it may not make progress until after our btrfs worker threads
 * process all of the pending work structs in their queue
 *
 * This means we can't use btrfs_start_workers from inside a btrfs worker
 * thread that is used as part of cleaning dirty memory, which pretty much
 * involves all of the worker threads.
 *
 * Instead we have a helper queue who never has more than one thread
 * where we scheduler thread start operations.  This worker_start struct
 * is used to contain the work and hold a pointer to the queue that needs
 * another worker.
 */
struct worker_start {
	struct btrfs_work work;
	struct btrfs_workers *queue;
};

static void start_new_worker_func(struct btrfs_work *work)
{
	struct worker_start *start;
	start = container_of(work, struct worker_start, work);
	btrfs_start_workers(start->queue, 1);
	kfree(start);
}

static int start_new_worker(struct btrfs_workers *queue)
{
	struct worker_start *start;
	int ret;

	start = kzalloc(sizeof(*start), GFP_NOFS);
	if (!start)
		return -ENOMEM;

	start->work.func = start_new_worker_func;
	start->queue = queue;
	ret = btrfs_queue_worker(queue->atomic_worker_start, &start->work);
	if (ret)
		kfree(start);
	return ret;
}

/*
 * helper function to move a thread onto the idle list after it
 * has finished some requests.
@@ -118,11 +163,13 @@ static void check_pending_worker_creates(struct btrfs_worker_thread *worker)
		goto out;

	workers->atomic_start_pending = 0;
	if (workers->num_workers >= workers->max_workers)
	if (workers->num_workers + workers->num_workers_starting >=
	    workers->max_workers)
		goto out;

	workers->num_workers_starting += 1;
	spin_unlock_irqrestore(&workers->lock, flags);
	btrfs_start_workers(workers, 1);
	start_new_worker(workers);
	return;

out:
@@ -390,9 +437,11 @@ int btrfs_stop_workers(struct btrfs_workers *workers)
/*
 * simple init on struct btrfs_workers
 */
void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max)
void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
			struct btrfs_workers *async_helper)
{
	workers->num_workers = 0;
	workers->num_workers_starting = 0;
	INIT_LIST_HEAD(&workers->worker_list);
	INIT_LIST_HEAD(&workers->idle_list);
	INIT_LIST_HEAD(&workers->order_list);
@@ -404,14 +453,15 @@ void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max)
	workers->name = name;
	workers->ordered = 0;
	workers->atomic_start_pending = 0;
	workers->atomic_worker_start = 0;
	workers->atomic_worker_start = async_helper;
}

/*
 * starts new worker threads.  This does not enforce the max worker
 * count in case you need to temporarily go past it.
 */
int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
static int __btrfs_start_workers(struct btrfs_workers *workers,
				 int num_workers)
{
	struct btrfs_worker_thread *worker;
	int ret = 0;
@@ -444,6 +494,8 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
		list_add_tail(&worker->worker_list, &workers->idle_list);
		worker->idle = 1;
		workers->num_workers++;
		workers->num_workers_starting--;
		WARN_ON(workers->num_workers_starting < 0);
		spin_unlock_irq(&workers->lock);
	}
	return 0;
@@ -452,6 +504,14 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
	return ret;
}

int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
{
	spin_lock_irq(&workers->lock);
	workers->num_workers_starting += num_workers;
	spin_unlock_irq(&workers->lock);
	return __btrfs_start_workers(workers, num_workers);
}

/*
 * run through the list and find a worker thread that doesn't have a lot
 * to do right now.  This can return null if we aren't yet at the thread
@@ -461,7 +521,10 @@ static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers)
{
	struct btrfs_worker_thread *worker;
	struct list_head *next;
	int enforce_min = workers->num_workers < workers->max_workers;
	int enforce_min;

	enforce_min = (workers->num_workers + workers->num_workers_starting) <
		workers->max_workers;

	/*
	 * if we find an idle thread, don't move it to the end of the
@@ -509,15 +572,17 @@ static struct btrfs_worker_thread *find_worker(struct btrfs_workers *workers)
	worker = next_worker(workers);

	if (!worker) {
		if (workers->num_workers >= workers->max_workers) {
		if (workers->num_workers + workers->num_workers_starting >=
		    workers->max_workers) {
			goto fallback;
		} else if (workers->atomic_worker_start) {
			workers->atomic_start_pending = 1;
			goto fallback;
		} else {
			workers->num_workers_starting++;
			spin_unlock_irqrestore(&workers->lock, flags);
			/* we're below the limit, start another worker */
			btrfs_start_workers(workers, 1);
			__btrfs_start_workers(workers, 1);
			goto again;
		}
	}
+7 −3
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ struct btrfs_workers {
	/* current number of running workers */
	int num_workers;

	int num_workers_starting;

	/* max number of workers allowed.  changed by btrfs_start_workers */
	int max_workers;

@@ -78,9 +80,10 @@ struct btrfs_workers {

	/*
	 * are we allowed to sleep while starting workers or are we required
	 * to start them at a later time?
	 * to start them at a later time?  If we can't sleep, this indicates
	 * which queue we need to use to schedule thread creation.
	 */
	int atomic_worker_start;
	struct btrfs_workers *atomic_worker_start;

	/* list with all the work threads.  The workers on the idle thread
	 * may be actively servicing jobs, but they haven't yet hit the
@@ -109,7 +112,8 @@ struct btrfs_workers {
int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work);
int btrfs_start_workers(struct btrfs_workers *workers, int num_workers);
int btrfs_stop_workers(struct btrfs_workers *workers);
void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max);
void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max,
			struct btrfs_workers *async_starter);
int btrfs_requeue_work(struct btrfs_work *work);
void btrfs_set_work_high_prio(struct btrfs_work *work);
#endif
+7 −5
Original line number Diff line number Diff line
@@ -128,12 +128,14 @@ struct btrfs_inode {
	u64 last_unlink_trans;

	/*
	 * These two counters are for delalloc metadata reservations.  We keep
	 * track of how many extents we've accounted for vs how many extents we
	 * have.
	 * Counters to keep track of the number of extent item's we may use due
	 * to delalloc and such.  outstanding_extents is the number of extent
	 * items we think we'll end up using, and reserved_extents is the number
	 * of extent items we've reserved metadata for.
	 */
	int delalloc_reserved_extents;
	int delalloc_extents;
	spinlock_t accounting_lock;
	int reserved_extents;
	int outstanding_extents;

	/*
	 * ordered_data_close is set by truncate when a file that used
+11 −4
Original line number Diff line number Diff line
@@ -691,14 +691,17 @@ struct btrfs_space_info {

	struct list_head list;

	/* for controlling how we free up space for allocations */
	wait_queue_head_t allocate_wait;
	wait_queue_head_t flush_wait;
	int allocating_chunk;
	int flushing;

	/* for block groups in our same type */
	struct list_head block_groups;
	spinlock_t lock;
	struct rw_semaphore groups_sem;
	atomic_t caching_threads;

	int allocating_chunk;
	wait_queue_head_t wait;
};

/*
@@ -907,6 +910,7 @@ struct btrfs_fs_info {
	 * A third pool does submit_bio to avoid deadlocking with the other
	 * two
	 */
	struct btrfs_workers generic_worker;
	struct btrfs_workers workers;
	struct btrfs_workers delalloc_workers;
	struct btrfs_workers endio_workers;
@@ -914,6 +918,7 @@ struct btrfs_fs_info {
	struct btrfs_workers endio_meta_write_workers;
	struct btrfs_workers endio_write_workers;
	struct btrfs_workers submit_workers;
	struct btrfs_workers enospc_workers;
	/*
	 * fixup workers take dirty pages that didn't properly go through
	 * the cow mechanism and make them safe to write.  It happens
@@ -1005,6 +1010,8 @@ struct btrfs_root {
	atomic_t log_commit[2];
	unsigned long log_transid;
	unsigned long log_batch;
	pid_t log_start_pid;
	bool log_multiple_pids;

	u64 objectid;
	u64 last_trans;
@@ -2323,7 +2330,7 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);
void btrfs_orphan_cleanup(struct btrfs_root *root);
int btrfs_cont_expand(struct inode *inode, loff_t size);
int btrfs_invalidate_inodes(struct btrfs_root *root);
extern struct dentry_operations btrfs_dentry_operations;
extern const struct dentry_operations btrfs_dentry_operations;

/* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+29 −19
Original line number Diff line number Diff line
@@ -1746,21 +1746,25 @@ struct btrfs_root *open_ctree(struct super_block *sb,
		err = -EINVAL;
		goto fail_iput;
	}
printk("thread pool is %d\n", fs_info->thread_pool_size);
	/*
	 * we need to start all the end_io workers up front because the
	 * queue work function gets called at interrupt time, and so it
	 * cannot dynamically grow.
	 */

	btrfs_init_workers(&fs_info->generic_worker,
			   "genwork", 1, NULL);

	btrfs_init_workers(&fs_info->workers, "worker",
			   fs_info->thread_pool_size);
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);

	btrfs_init_workers(&fs_info->delalloc_workers, "delalloc",
			   fs_info->thread_pool_size);
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);

	btrfs_init_workers(&fs_info->submit_workers, "submit",
			   min_t(u64, fs_devices->num_devices,
			   fs_info->thread_pool_size));
			   fs_info->thread_pool_size),
			   &fs_info->generic_worker);
	btrfs_init_workers(&fs_info->enospc_workers, "enospc",
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);

	/* a higher idle thresh on the submit workers makes it much more
	 * likely that bios will be send down in a sane order to the
@@ -1774,15 +1778,20 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
	fs_info->delalloc_workers.idle_thresh = 2;
	fs_info->delalloc_workers.ordered = 1;

	btrfs_init_workers(&fs_info->fixup_workers, "fixup", 1);
	btrfs_init_workers(&fs_info->fixup_workers, "fixup", 1,
			   &fs_info->generic_worker);
	btrfs_init_workers(&fs_info->endio_workers, "endio",
			   fs_info->thread_pool_size);
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);
	btrfs_init_workers(&fs_info->endio_meta_workers, "endio-meta",
			   fs_info->thread_pool_size);
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);
	btrfs_init_workers(&fs_info->endio_meta_write_workers,
			   "endio-meta-write", fs_info->thread_pool_size);
			   "endio-meta-write", fs_info->thread_pool_size,
			   &fs_info->generic_worker);
	btrfs_init_workers(&fs_info->endio_write_workers, "endio-write",
			   fs_info->thread_pool_size);
			   fs_info->thread_pool_size,
			   &fs_info->generic_worker);

	/*
	 * endios are largely parallel and should have a very
@@ -1794,12 +1803,8 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
	fs_info->endio_write_workers.idle_thresh = 2;
	fs_info->endio_meta_write_workers.idle_thresh = 2;

	fs_info->endio_workers.atomic_worker_start = 1;
	fs_info->endio_meta_workers.atomic_worker_start = 1;
	fs_info->endio_write_workers.atomic_worker_start = 1;
	fs_info->endio_meta_write_workers.atomic_worker_start = 1;

	btrfs_start_workers(&fs_info->workers, 1);
	btrfs_start_workers(&fs_info->generic_worker, 1);
	btrfs_start_workers(&fs_info->submit_workers, 1);
	btrfs_start_workers(&fs_info->delalloc_workers, 1);
	btrfs_start_workers(&fs_info->fixup_workers, 1);
@@ -1807,6 +1812,7 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
	btrfs_start_workers(&fs_info->endio_meta_workers, 1);
	btrfs_start_workers(&fs_info->endio_meta_write_workers, 1);
	btrfs_start_workers(&fs_info->endio_write_workers, 1);
	btrfs_start_workers(&fs_info->enospc_workers, 1);

	fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super);
	fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages,
@@ -2012,6 +2018,7 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
	free_extent_buffer(chunk_root->node);
	free_extent_buffer(chunk_root->commit_root);
fail_sb_buffer:
	btrfs_stop_workers(&fs_info->generic_worker);
	btrfs_stop_workers(&fs_info->fixup_workers);
	btrfs_stop_workers(&fs_info->delalloc_workers);
	btrfs_stop_workers(&fs_info->workers);
@@ -2020,6 +2027,7 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
	btrfs_stop_workers(&fs_info->endio_meta_write_workers);
	btrfs_stop_workers(&fs_info->endio_write_workers);
	btrfs_stop_workers(&fs_info->submit_workers);
	btrfs_stop_workers(&fs_info->enospc_workers);
fail_iput:
	invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
	iput(fs_info->btree_inode);
@@ -2437,6 +2445,7 @@ int close_ctree(struct btrfs_root *root)

	iput(fs_info->btree_inode);

	btrfs_stop_workers(&fs_info->generic_worker);
	btrfs_stop_workers(&fs_info->fixup_workers);
	btrfs_stop_workers(&fs_info->delalloc_workers);
	btrfs_stop_workers(&fs_info->workers);
@@ -2445,6 +2454,7 @@ int close_ctree(struct btrfs_root *root)
	btrfs_stop_workers(&fs_info->endio_meta_write_workers);
	btrfs_stop_workers(&fs_info->endio_write_workers);
	btrfs_stop_workers(&fs_info->submit_workers);
	btrfs_stop_workers(&fs_info->enospc_workers);

	btrfs_close_devices(fs_info->fs_devices);
	btrfs_mapping_tree_free(&fs_info->mapping_tree);
Loading