|
@@ -633,6 +633,28 @@ static bool throtl_schedule_next_dispatch(struct throtl_service_queue *sq,
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static inline void throtl_start_new_slice_with_credit(struct throtl_grp *tg,
|
|
|
+ bool rw, unsigned long start)
|
|
|
+{
|
|
|
+ tg->bytes_disp[rw] = 0;
|
|
|
+ tg->io_disp[rw] = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Previous slice has expired. We must have trimmed it after last
|
|
|
+ * bio dispatch. That means since start of last slice, we never used
|
|
|
+ * that bandwidth. Do try to make use of that bandwidth while giving
|
|
|
+ * credit.
|
|
|
+ */
|
|
|
+ if (time_after_eq(start, tg->slice_start[rw]))
|
|
|
+ tg->slice_start[rw] = start;
|
|
|
+
|
|
|
+ tg->slice_end[rw] = jiffies + throtl_slice;
|
|
|
+ throtl_log(&tg->service_queue,
|
|
|
+ "[%c] new slice with credit start=%lu end=%lu jiffies=%lu",
|
|
|
+ rw == READ ? 'R' : 'W', tg->slice_start[rw],
|
|
|
+ tg->slice_end[rw], jiffies);
|
|
|
+}
|
|
|
+
|
|
|
static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw)
|
|
|
{
|
|
|
tg->bytes_disp[rw] = 0;
|
|
@@ -992,6 +1014,16 @@ static void tg_update_disptime(struct throtl_grp *tg)
|
|
|
tg->flags &= ~THROTL_TG_WAS_EMPTY;
|
|
|
}
|
|
|
|
|
|
+static void start_parent_slice_with_credit(struct throtl_grp *child_tg,
|
|
|
+ struct throtl_grp *parent_tg, bool rw)
|
|
|
+{
|
|
|
+ if (throtl_slice_used(parent_tg, rw)) {
|
|
|
+ throtl_start_new_slice_with_credit(parent_tg, rw,
|
|
|
+ child_tg->slice_start[rw]);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw)
|
|
|
{
|
|
|
struct throtl_service_queue *sq = &tg->service_queue;
|
|
@@ -1020,6 +1052,7 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw)
|
|
|
*/
|
|
|
if (parent_tg) {
|
|
|
throtl_add_bio_tg(bio, &tg->qnode_on_parent[rw], parent_tg);
|
|
|
+ start_parent_slice_with_credit(tg, parent_tg, rw);
|
|
|
} else {
|
|
|
throtl_qnode_add_bio(bio, &tg->qnode_on_parent[rw],
|
|
|
&parent_sq->queued[rw]);
|