static void writecache_writeback()

in dm-writecache.c [1919:2087]


static void writecache_writeback(struct work_struct *work)
{
	struct dm_writecache *wc = container_of(work, struct dm_writecache, writeback_work);
	struct blk_plug plug;
	struct wc_entry *f, *g, *e = NULL;
	struct rb_node *node, *next_node;
	struct list_head skipped;
	struct writeback_list wbl;
	unsigned long n_walked;

	if (!WC_MODE_PMEM(wc)) {
		/* Wait for any active kcopyd work on behalf of ssd writeback */
		dm_kcopyd_client_flush(wc->dm_kcopyd);
	}

	if (likely(wc->pause != 0)) {
		while (1) {
			unsigned long idle;
			if (unlikely(wc->cleaner) || unlikely(wc->writeback_all) ||
			    unlikely(dm_suspended(wc->ti)))
				break;
			idle = dm_iot_idle_time(&wc->iot);
			if (idle >= wc->pause)
				break;
			idle = wc->pause - idle;
			if (idle > HZ)
				idle = HZ;
			schedule_timeout_idle(idle);
		}
	}

	wc_lock(wc);
restart:
	if (writecache_has_error(wc)) {
		wc_unlock(wc);
		return;
	}

	if (unlikely(wc->writeback_all)) {
		if (writecache_wait_for_writeback(wc))
			goto restart;
	}

	if (wc->overwrote_committed) {
		writecache_wait_for_ios(wc, WRITE);
	}

	n_walked = 0;
	INIT_LIST_HEAD(&skipped);
	INIT_LIST_HEAD(&wbl.list);
	wbl.size = 0;
	while (!list_empty(&wc->lru) &&
	       (wc->writeback_all ||
		wc->freelist_size + wc->writeback_size <= wc->freelist_low_watermark ||
		(jiffies - container_of(wc->lru.prev, struct wc_entry, lru)->age >=
		 wc->max_age - wc->max_age / MAX_AGE_DIV))) {

		n_walked++;
		if (unlikely(n_walked > WRITEBACK_LATENCY) &&
		    likely(!wc->writeback_all)) {
			if (likely(!dm_suspended(wc->ti)))
				queue_work(wc->writeback_wq, &wc->writeback_work);
			break;
		}

		if (unlikely(wc->writeback_all)) {
			if (unlikely(!e)) {
				writecache_flush(wc);
				e = container_of(rb_first(&wc->tree), struct wc_entry, rb_node);
			} else
				e = g;
		} else
			e = container_of(wc->lru.prev, struct wc_entry, lru);
		BUG_ON(e->write_in_progress);
		if (unlikely(!writecache_entry_is_committed(wc, e))) {
			writecache_flush(wc);
		}
		node = rb_prev(&e->rb_node);
		if (node) {
			f = container_of(node, struct wc_entry, rb_node);
			if (unlikely(read_original_sector(wc, f) ==
				     read_original_sector(wc, e))) {
				BUG_ON(!f->write_in_progress);
				list_move(&e->lru, &skipped);
				cond_resched();
				continue;
			}
		}
		wc->writeback_size++;
		list_move(&e->lru, &wbl.list);
		wbl.size++;
		e->write_in_progress = true;
		e->wc_list_contiguous = 1;

		f = e;

		while (1) {
			next_node = rb_next(&f->rb_node);
			if (unlikely(!next_node))
				break;
			g = container_of(next_node, struct wc_entry, rb_node);
			if (unlikely(read_original_sector(wc, g) ==
			    read_original_sector(wc, f))) {
				f = g;
				continue;
			}
			if (read_original_sector(wc, g) !=
			    read_original_sector(wc, f) + (wc->block_size >> SECTOR_SHIFT))
				break;
			if (unlikely(g->write_in_progress))
				break;
			if (unlikely(!writecache_entry_is_committed(wc, g)))
				break;

			if (!WC_MODE_PMEM(wc)) {
				if (g != f + 1)
					break;
			}

			n_walked++;
			//if (unlikely(n_walked > WRITEBACK_LATENCY) && likely(!wc->writeback_all))
			//	break;

			wc->writeback_size++;
			list_move(&g->lru, &wbl.list);
			wbl.size++;
			g->write_in_progress = true;
			g->wc_list_contiguous = BIO_MAX_VECS;
			f = g;
			e->wc_list_contiguous++;
			if (unlikely(e->wc_list_contiguous == BIO_MAX_VECS)) {
				if (unlikely(wc->writeback_all)) {
					next_node = rb_next(&f->rb_node);
					if (likely(next_node))
						g = container_of(next_node, struct wc_entry, rb_node);
				}
				break;
			}
		}
		cond_resched();
	}

	if (!list_empty(&skipped)) {
		list_splice_tail(&skipped, &wc->lru);
		/*
		 * If we didn't do any progress, we must wait until some
		 * writeback finishes to avoid burning CPU in a loop
		 */
		if (unlikely(!wbl.size))
			writecache_wait_for_writeback(wc);
	}

	wc_unlock(wc);

	blk_start_plug(&plug);

	if (WC_MODE_PMEM(wc))
		__writecache_writeback_pmem(wc, &wbl);
	else
		__writecache_writeback_ssd(wc, &wbl);

	blk_finish_plug(&plug);

	if (unlikely(wc->writeback_all)) {
		wc_lock(wc);
		while (writecache_wait_for_writeback(wc));
		wc_unlock(wc);
	}
}