block: row: Prevent starvation of regular priority by high priority

At the moment all REGULAR and LOW priority requests are starved as long as
there are HIGH priority requests to dispatch.
This patch prevents the above starvation by setting a starvation limit the
REGULAR\LOW priority requests can tolerate.

Change-Id: Ibe24207982c2c55d75c0b0230f67e013d1106017
Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
diff --git a/block/row-iosched.c b/block/row-iosched.c
index 3baec8c..8fc71ee 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -158,6 +158,20 @@
 };
 
 /**
+ * struct starvation_data - data for starvation management
+ * @starvation_limit:	number of times this priority class
+ *			can tolerate being starved
+ * @starvation_counter:	number of requests from higher
+ *			priority classes that were dispatched while this
+ *			priority request were pending
+ *
+ */
+struct starvation_data {
+	int				starvation_limit;
+	int				starvation_counter;
+};
+
+/**
  * struct row_queue - Per block device rqueue structure
  * @dispatch_queue:	dispatch rqueue
  * @row_queues:		array of priority request queues
@@ -170,6 +184,8 @@
  *			complete.
  * @pending_urgent_rq:	pointer to the pending urgent request
  * @last_served_ioprio_class: I/O priority class that was last dispatched from
+ * @reg_prio_starvation: starvation data for REGULAR priority queues
+ * @low_prio_starvation: starvation data for LOW priority queues
  * @cycle_flags:	used for marking unserved queueus
  *
  */
@@ -183,6 +199,12 @@
 	bool				urgent_in_flight;
 	struct request			*pending_urgent_rq;
 	int				last_served_ioprio_class;
+
+#define	ROW_REG_STARVATION_TOLLERANCE	50
+	struct starvation_data		reg_prio_starvation;
+#define	ROW_LOW_STARVATION_TOLLERANCE	1000
+	struct starvation_data		low_prio_starvation;
+
 	unsigned int			cycle_flags;
 };
 
@@ -258,6 +280,42 @@
 	return HRTIMER_NORESTART;
 }
 
+/*
+ * row_regular_req_pending() - Check if there are REGULAR priority requests
+ *				 Pending in scheduler
+ * @rd:		pointer to struct row_data
+ *
+ * Returns True if there are REGULAR priority requests in scheduler queues.
+ *		False, otherwise.
+ */
+static inline bool row_regular_req_pending(struct row_data *rd)
+{
+	int i;
+
+	for (i = ROWQ_REG_PRIO_IDX; i < ROWQ_LOW_PRIO_IDX; i++)
+		if (!list_empty(&rd->row_queues[i].fifo))
+			return true;
+	return false;
+}
+
+/*
+ * row_low_req_pending() - Check if there are LOW priority requests
+ *				 Pending in scheduler
+ * @rd:		pointer to struct row_data
+ *
+ * Returns True if there are LOW priority requests in scheduler queues.
+ *		False, otherwise.
+ */
+static inline bool row_low_req_pending(struct row_data *rd)
+{
+	int i;
+
+	for (i = ROWQ_LOW_PRIO_IDX; i < ROWQ_MAX_PRIO; i++)
+		if (!list_empty(&rd->row_queues[i].fifo))
+			return true;
+	return false;
+}
+
 /******************* Elevator callback functions *********************/
 
 /*
@@ -272,6 +330,7 @@
 	struct row_data *rd = (struct row_data *)q->elevator->elevator_data;
 	struct row_queue *rqueue = RQ_ROWQ(rq);
 	s64 diff_ms;
+	bool queue_was_empty = list_empty(&rqueue->fifo);
 
 	list_add_tail(&rq->queuelist, &rqueue->fifo);
 	rd->nr_reqs[rq_data_dir(rq)]++;
@@ -316,7 +375,8 @@
 	    !rd->pending_urgent_rq && !rd->urgent_in_flight) {
 		/* Handle High Priority queues */
 		if (rqueue->prio < ROWQ_REG_PRIO_IDX &&
-		    rd->last_served_ioprio_class != IOPRIO_CLASS_RT) {
+		    rd->last_served_ioprio_class != IOPRIO_CLASS_RT &&
+		    queue_was_empty) {
 			row_log_rowq(rd, rqueue->prio,
 				"added (high prio) urgent request");
 			rq->cmd_flags |= REQ_URGENT;
@@ -472,12 +532,21 @@
 	row_log_rowq(rd, rqueue->prio,
 		" Dispatched request %p nr_disp = %d", rq,
 		rqueue->nr_dispatched);
-	if (rqueue->prio < ROWQ_REG_PRIO_IDX)
+	if (rqueue->prio < ROWQ_REG_PRIO_IDX) {
 		rd->last_served_ioprio_class = IOPRIO_CLASS_RT;
-	else if (rqueue->prio < ROWQ_LOW_PRIO_IDX)
+		if (row_regular_req_pending(rd))
+			rd->reg_prio_starvation.starvation_counter++;
+		if (row_low_req_pending(rd))
+			rd->low_prio_starvation.starvation_counter++;
+	} else if (rqueue->prio < ROWQ_LOW_PRIO_IDX) {
 		rd->last_served_ioprio_class = IOPRIO_CLASS_BE;
-	else
+		rd->reg_prio_starvation.starvation_counter = 0;
+		if (row_low_req_pending(rd))
+			rd->low_prio_starvation.starvation_counter++;
+	} else {
 		rd->last_served_ioprio_class = IOPRIO_CLASS_IDLE;
+		rd->low_prio_starvation.starvation_counter = 0;
+	}
 }
 
 /*
@@ -517,7 +586,18 @@
 				rd->rd_idle_data.idling_queue_idx =
 					ROWQ_MAX_PRIO;
 			}
-			ret = IOPRIO_CLASS_RT;
+
+			if (row_regular_req_pending(rd) &&
+			    (rd->reg_prio_starvation.starvation_counter >=
+			     rd->reg_prio_starvation.starvation_limit))
+				ret = IOPRIO_CLASS_BE;
+			else if (row_low_req_pending(rd) &&
+			    (rd->low_prio_starvation.starvation_counter >=
+			     rd->low_prio_starvation.starvation_limit))
+				ret = IOPRIO_CLASS_IDLE;
+			else
+				ret = IOPRIO_CLASS_RT;
+
 			goto done;
 		}
 	}
@@ -546,7 +626,12 @@
 			    !force && row_queues_def[i].idling_enabled)
 				goto initiate_idling;
 		} else {
-			ret = IOPRIO_CLASS_BE;
+			if (row_low_req_pending(rd) &&
+			    (rd->low_prio_starvation.starvation_counter >=
+			     rd->low_prio_starvation.starvation_limit))
+				ret = IOPRIO_CLASS_IDLE;
+			else
+				ret = IOPRIO_CLASS_BE;
 			goto done;
 		}
 	}
@@ -716,6 +801,10 @@
 			ktime_set(0, 0);
 	}
 
+	rdata->reg_prio_starvation.starvation_limit =
+			ROW_REG_STARVATION_TOLLERANCE;
+	rdata->low_prio_starvation.starvation_limit =
+			ROW_LOW_STARVATION_TOLLERANCE;
 	/*
 	 * Currently idling is enabled only for READ queues. If we want to
 	 * enable it for write queues also, note that idling frequency will
@@ -890,6 +979,10 @@
 	rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0);
 SHOW_FUNCTION(row_rd_idle_data_show, rowd->rd_idle_data.idle_time_ms, 0);
 SHOW_FUNCTION(row_rd_idle_data_freq_show, rowd->rd_idle_data.freq_ms, 0);
+SHOW_FUNCTION(row_reg_starv_limit_show,
+	rowd->reg_prio_starvation.starvation_limit, 0);
+SHOW_FUNCTION(row_low_starv_limit_show,
+	rowd->low_prio_starvation.starvation_limit, 0);
 #undef SHOW_FUNCTION
 
 #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV)			\
@@ -932,6 +1025,12 @@
 			1, INT_MAX, 0);
 STORE_FUNCTION(row_rd_idle_data_freq_store, &rowd->rd_idle_data.freq_ms,
 			1, INT_MAX, 0);
+STORE_FUNCTION(row_reg_starv_limit_store,
+			&rowd->reg_prio_starvation.starvation_limit,
+			1, INT_MAX, 0);
+STORE_FUNCTION(row_low_starv_limit_store,
+			&rowd->low_prio_starvation.starvation_limit,
+			1, INT_MAX, 0);
 
 #undef STORE_FUNCTION
 
@@ -949,6 +1048,8 @@
 	ROW_ATTR(lp_swrite_quantum),
 	ROW_ATTR(rd_idle_data),
 	ROW_ATTR(rd_idle_data_freq),
+	ROW_ATTR(reg_starv_limit),
+	ROW_ATTR(low_starv_limit),
 	__ATTR_NULL
 };