• Kemeng Shi's avatar
    blk-throttle: simpfy low limit reached check in throtl_tg_can_upgrade · a4d508e3
    Kemeng Shi authored
    Commit c79892c5 ("blk-throttle: add upgrade logic for LIMIT_LOW
    state") added upgrade logic for low limit and methioned that
    1. "To determine if a cgroup exceeds its limitation, we check if the cgroup
    has pending request. Since cgroup is throttled according to the limit,
    pending request means the cgroup reaches the limit."
    2. "If a cgroup has limit set for both read and write, we consider the
    combination of them for upgrade. The reason is read IO and write IO can
    interfere with each other. If we do the upgrade based in one direction IO,
    the other direction IO could be severly harmed."
    Besides, we also determine that cgroup reaches low limit if low limit is 0,
    see comment in throtl_tg_can_upgrade.
    
    Collect the information above, the desgin of upgrade check is as following:
    1.The low limit is reached if limit is zero or io is already queued.
    2.Cgroup will pass upgrade check if low limits of READ and WRITE are both
    reached.
    
    Simpfy the check code described above to removce repeat check and improve
    readability. There is no functional change.
    
    Detail equivalence proof is as following:
    All replaced conditions to return true are as following:
    condition 1
      (!read_limit && !write_limit)
    condition 2
      read_limit && sq->nr_queued[READ] &&
      (!write_limit || sq->nr_queued[WRITE])
    condition 3
      write_limit && sq->nr_queued[WRITE] &&
      (!read_limit || sq->nr_queued[READ])
    
    Transferring condition 2 as following:
      (read_limit && sq->nr_queued[READ]) &&
      (!write_limit || sq->nr_queued[WRITE])
    is equivalent to
      (read_limit && sq->nr_queued[READ]) &&
      (!write_limit || (write_limit && sq->nr_queued[WRITE]))
    is equivalent to
    condition 2.1
      (read_limit && sq->nr_queued[READ] &&
      !write_limit) ||
    condition 2.2
      (read_limit && sq->nr_queued[READ] &&
      (write_limit && sq->nr_queued[WRITE]))
    
    Transferring condition 3 as following:
      write_limit && sq->nr_queued[WRITE] &&
      (!read_limit || sq->nr_queued[READ])
    is equivalent to
      (write_limit && sq->nr_queued[WRITE]) &&
      (!read_limit || (read_limit && sq->nr_queued[READ]))
    is equivalent to
    condition 3.1
      ((write_limit && sq->nr_queued[WRITE]) &&
      !read_limit) ||
    condition 3.2
      ((write_limit && sq->nr_queued[WRITE]) &&
      (read_limit && sq->nr_queued[READ]))
    
    Condition 3.2 is the same as condition 2.2, so all conditions we get to
    return are as following:
      (!read_limit && !write_limit) (1)
      (!read_limit && (write_limit && sq->nr_queued[WRITE])) (3.1)
      ((read_limit && sq->nr_queued[READ]) && !write_limit) (2.1)
      ((write_limit && sq->nr_queued[WRITE]) &&
      (read_limit && sq->nr_queued[READ])) (2.2)
    
    As we can extract conditions "(a1 || a2) && (b1 || b2)" to:
    a1 && b1
    a1 && b2
    a2 && b1
    ab && b2
    
    Considering that:
    a1 = !read_limit
    a2 = read_limit && sq->nr_queued[READ]
    b1 = !write_limit
    b2 = write_limit && sq->nr_queued[WRITE]
    
    We can pack replaced conditions to
      (!read_limit || (read_limit && sq->nr_queued[READ])) &&
      (!write_limit || (write_limit && sq->nr_queued[WRITE]))
    which is equivalent to
      (!read_limit || sq->nr_queued[READ]) &&
      (!write_limit || sq->nr_queued[WRITE])
    Reported-by: default avatarkernel test robot <lkp@intel.com>
    Acked-by: default avatarTejun Heo <tj@kernel.org>
    Signed-off-by: default avatarKemeng Shi <shikemeng@huawei.com>
    Link: https://lore.kernel.org/r/20221205115709.251489-6-shikemeng@huaweicloud.comSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
    a4d508e3
blk-throttle.c 66.8 KB