Commit de696716 authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Philipp Reisner
Browse files

drbd: Use interval tree for overlapping write request detection

parent ace652ac
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1019,6 +1019,9 @@ struct drbd_conf {
	struct hlist_head *tl_hash;
	unsigned int tl_hash_s;

	/* Interval tree of pending local write requests */
	struct rb_root write_requests;

	/* blocks to resync in this run [unit BM_BLOCK_SIZE] */
	unsigned long rs_total;
	/* number of resync blocks that failed in this run */
+1 −0
Original line number Diff line number Diff line
@@ -3473,6 +3473,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
	/* no need to lock access, we are still initializing this minor device. */
	if (!tl_init(mdev))
		goto out_no_tl;
	mdev->write_requests = RB_ROOT;

	mdev->app_reads_hash = kzalloc(APP_R_HSIZE*sizeof(void *), GFP_KERNEL);
	if (!mdev->app_reads_hash)
+18 −20
Original line number Diff line number Diff line
@@ -1733,9 +1733,6 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
		const int size = e->size;
		const int discard = test_bit(DISCARD_CONCURRENT, &mdev->flags);
		DEFINE_WAIT(wait);
		struct drbd_request *i;
		struct hlist_node *n;
		struct hlist_head *slot;
		int first;

		D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
@@ -1783,16 +1780,19 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned

		hlist_add_head(&e->collision, ee_hash_slot(mdev, sector));

#define OVERLAPS overlaps(i->i.sector, i->i.size, sector, size)
		slot = tl_hash_slot(mdev, sector);
		first = 1;
		for (;;) {
			struct drbd_interval *i;
			int have_unacked = 0;
			int have_conflict = 0;
			prepare_to_wait(&mdev->misc_wait, &wait,
				TASK_INTERRUPTIBLE);
			hlist_for_each_entry(i, n, slot, collision) {
				if (OVERLAPS) {

			i = drbd_find_overlap(&mdev->write_requests, sector, size);
			if (i) {
				struct drbd_request *req2 =
					container_of(i, struct drbd_request, i);

				/* only ALERT on first iteration,
				 * we may be woken up early... */
				if (first)
@@ -1800,13 +1800,11 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
					      "	new: %llus +%u; pending: %llus +%u\n",
					      current->comm, current->pid,
					      (unsigned long long)sector, size,
						      (unsigned long long)i->i.sector, i->i.size);
					if (i->rq_state & RQ_NET_PENDING)
					      (unsigned long long)req2->i.sector, req2->i.size);
				if (req2->rq_state & RQ_NET_PENDING)
					++have_unacked;
				++have_conflict;
			}
			}
#undef OVERLAPS
			if (!have_conflict)
				break;

+29 −27
Original line number Diff line number Diff line
@@ -135,7 +135,6 @@ static void _about_to_complete_local_write(struct drbd_conf *mdev,
	struct drbd_request *req)
{
	const unsigned long s = req->rq_state;
	struct drbd_request *i;
	struct drbd_epoch_entry *e;
	struct hlist_node *n;
	struct hlist_head *slot;
@@ -157,19 +156,21 @@ static void _about_to_complete_local_write(struct drbd_conf *mdev,
	if ((s & RQ_NET_DONE) && mdev->ee_hash != NULL) {
		const sector_t sector = req->i.sector;
		const int size = req->i.size;
		struct drbd_interval *i;

		/* ASSERT:
		 * there must be no conflicting requests, since
		 * they must have been failed on the spot */
#define OVERLAPS overlaps(sector, size, i->i.sector, i->i.size)
		slot = tl_hash_slot(mdev, sector);
		hlist_for_each_entry(i, n, slot, collision) {
			if (OVERLAPS) {

		i = drbd_find_overlap(&mdev->write_requests, sector, size);
		if (i) {
			struct drbd_request *req2 =
				container_of(i, struct drbd_request, i);

			dev_alert(DEV, "LOGIC BUG: completed: %p %llus +%u; "
			      "other: %p %llus +%u\n",
			      req, (unsigned long long)sector, size,
				      i, (unsigned long long)i->i.sector, i->i.size);
			}
			      i, (unsigned long long)req2->i.sector, req2->i.size);
		}

		/* maybe "wake" those conflicting epoch entries
@@ -184,7 +185,6 @@ static void _about_to_complete_local_write(struct drbd_conf *mdev,
		 *
		 * anyways, if we found one,
		 * we just have to do a wake_up.  */
#undef OVERLAPS
#define OVERLAPS overlaps(sector, size, e->sector, e->size)
		slot = ee_hash_slot(mdev, req->i.sector);
		hlist_for_each_entry(e, n, slot, collision) {
@@ -260,9 +260,11 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)

		/* remove the request from the conflict detection
		 * respective block_id verification hash */
		if (!hlist_unhashed(&req->collision))
		if (!hlist_unhashed(&req->collision)) {
			hlist_del(&req->collision);
		else
			if (!drbd_interval_empty(&req->i))
				drbd_remove_interval(&mdev->write_requests, &req->i);
		} else
			D_ASSERT((s & (RQ_NET_MASK & ~RQ_NET_DONE)) == 0);

		/* for writes we need to do some extra housekeeping */
@@ -324,7 +326,7 @@ static int _req_conflicts(struct drbd_request *req)
	struct drbd_conf *mdev = req->mdev;
	const sector_t sector = req->i.sector;
	const int size = req->i.size;
	struct drbd_request *i;
	struct drbd_interval *i;
	struct drbd_epoch_entry *e;
	struct hlist_node *n;
	struct hlist_head *slot;
@@ -339,24 +341,23 @@ static int _req_conflicts(struct drbd_request *req)
		goto out_no_conflict;
	BUG_ON(mdev->tl_hash == NULL);

#define OVERLAPS overlaps(i->i.sector, i->i.size, sector, size)
	slot = tl_hash_slot(mdev, sector);
	hlist_for_each_entry(i, n, slot, collision) {
		if (OVERLAPS) {
	i = drbd_find_overlap(&mdev->write_requests, sector, size);
	if (i) {
		struct drbd_request *req2 =
			container_of(i, struct drbd_request, i);

		dev_alert(DEV, "%s[%u] Concurrent local write detected! "
		      "[DISCARD L] new: %llus +%u; "
		      "pending: %llus +%u\n",
		      current->comm, current->pid,
		      (unsigned long long)sector, size,
			      (unsigned long long)i->i.sector, i->i.size);
		      (unsigned long long)req2->i.sector, req2->i.size);
		goto out_conflict;
	}
	}

	if (mdev->ee_hash_s) {
		/* now, check for overlapping requests with remote origin */
		BUG_ON(mdev->ee_hash == NULL);
#undef OVERLAPS
#define OVERLAPS overlaps(e->sector, e->size, sector, size)
		slot = ee_hash_slot(mdev, sector);
		hlist_for_each_entry(e, n, slot, collision) {
@@ -509,6 +510,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,

		hlist_add_head(&req->collision, tl_hash_slot(mdev, req->i.sector));
		/* corresponding hlist_del is in _req_may_be_done() */
		drbd_insert_interval(&mdev->write_requests, &req->i);

		/* NOTE
		 * In case the req ended up on the transfer log before being
+1 −0
Original line number Diff line number Diff line
@@ -275,6 +275,7 @@ static inline struct drbd_request *drbd_req_new(struct drbd_conf *mdev,
		req->i.sector     = bio_src->bi_sector;
		req->i.size      = bio_src->bi_size;
		INIT_HLIST_NODE(&req->collision);
		drbd_clear_interval(&req->i);
		INIT_LIST_HEAD(&req->tl_requests);
		INIT_LIST_HEAD(&req->w.list);
	}