OpenCloudOS-Kernel/drivers/scsi/linkdata/ps3stor/ps3_qos.c

4044 lines
122 KiB
C

#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/err.h>
#include <linux/jiffies.h>
#include <linux/version.h>
#include <scsi/scsi_host.h>
#include "ps3_ioc_manager.h"
#include "ps3_util.h"
#include "ps3_types.h"
#include "ps3_instance_manager.h"
#include "ps3_htp_def.h"
#include "ps3_scsih.h"
#include "ps3_mgr_cmd.h"
#include "ps3_driver_log.h"
#include "ps3_htp_dev.h"
#include "ps3_scsih_cmd_parse.h"
#include "ps3_scsi_cmd_err.h"
#include "ps3_r1x_write_lock.h"
#include "ps3_cmd_statistics.h"
#include "ps3_scsih_raid_engine.h"
#include "ps3_ioc_state.h"
#define PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr) ((qos_pd_mgr)->vd_id > 0)
#define PS3_QOS_JBOD_PD_WAITQ_ID(qos_pd_mgr) ((qos_pd_mgr)->waitq_cnt - 1)
#define PS3_QOS_VD_HDD_DELAY_THD_MS 500
#define PS3_QOS_VD_SDD_DELAY_THD_MS 1
static inline Bool ps3_tfifo_depth_get(struct ps3_instance *instance,
U64 *depth)
{
Bool ret = PS3_TRUE;
PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, ps3TfifoDepth, *depth);
if (*depth == U64_MAX) {
LOG_INFO("host_no:%u read reg ps3TfifoDepth failed!\n",
PS3_HOST(instance));
*depth = 0;
ret = PS3_FALSE;
goto l_out;
}
*depth &= 0xffff;
l_out:
return ret;
}
static inline Bool ps3_cmdq_depth_get(struct ps3_instance *instance,
U64 *depth)
{
Bool ret = PS3_TRUE;
PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, ps3CmdQueueDepth, *depth);
if (*depth == U64_MAX) {
LOG_INFO("host_no:%u read reg ps3CmdQueueDepth failed!\n",
PS3_HOST(instance));
*depth = 0;
ret = PS3_FALSE;
goto l_out;
}
*depth &= 0xffff;
l_out:
return ret;
}
static inline Bool ps3_mgrq_depth_get(struct ps3_instance *instance,
U64 *depth)
{
Bool ret = PS3_TRUE;
PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, ps3MgrQueueDepth, *depth);
if (*depth == U64_MAX) {
LOG_INFO("host_no:%u read reg ps3MgrQueueDepth failed!\n",
PS3_HOST(instance));
*depth = 0;
ret = PS3_FALSE;
goto l_out;
}
*depth &= 0xffff;
l_out:
return ret;
}
static inline struct ps3_qos_pd_mgr *ps3_qos_pd_mgr_get(struct ps3_instance *instance, U16 disk_id)
{
return &instance->qos_context.pd_ctx.qos_pd_mgrs[disk_id];
}
static inline bool ps3_is_nvme_direct_cmd(struct ps3_qos_pd_mgr *qos_pd_mgr, struct ps3_cmd *cmd)
{
return (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD &&
cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK);
}
static void ps3_qos_update_pd_quota(struct ps3_cmd *cmd)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 i = 0;
U16 disk_id = 0;
S32 pd_used_quota = 0;
ps3_atomic32 *pd_rsc = NULL;
for (i = 0; i < cmd->target_pd_count; i++) {
if (cmd->target_pd[i].get_quota) {
disk_id = cmd->target_pd[i].flat_disk_id;
qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id);
if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) {
pd_rsc = &qos_pd_mgr->direct_used_quota;
} else {
pd_rsc = &qos_pd_mgr->pd_used_quota;
}
pd_used_quota = ps3_atomic_dec_return(pd_rsc);
LOG_DEBUG("update pd quota. host_no:%u t_id:0x%llx CFID:%u direct:%u pid:%u used_quota:%d\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, cmd->cmd_word.direct,
disk_id, pd_used_quota);
break;
}
}
}
static inline struct ps3_qos_vd_mgr *ps3_qos_vd_mgr_get(struct ps3_cmd *cmd)
{
U16 disk_id = 0;
U16 flat_disk_id = 0;
struct ps3_instance *instance = cmd->instance;
if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) {
disk_id = PS3_VDID(&cmd->io_attr.vd_entry->diskPos);
flat_disk_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_id);
return &instance->qos_context.vd_ctx.qos_vd_mgrs[flat_disk_id];
} else {
return &instance->qos_context.vd_ctx.qos_vd_mgrs[instance->qos_context.max_vd_count];
}
}
static inline struct ps3_qos_vd_mgr *ps3_qos_vd_mgr_get_by_id(struct ps3_instance *instance,
U16 disk_id)
{
struct ps3_qos_vd_context *qos_vd_ctx = NULL;
U16 flat_disk_id = 0;
qos_vd_ctx = &instance->qos_context.vd_ctx;
flat_disk_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_id);
return &qos_vd_ctx->qos_vd_mgrs[flat_disk_id];
}
static void ps3_qos_update_vd_quota(struct ps3_cmd *cmd)
{
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) {
qos_vd_mgr = ps3_qos_vd_mgr_get(cmd);
ps3_atomic_inc(&qos_vd_mgr->vd_quota);
}
}
Bool ps3_qos_enable(struct ps3_instance *instance)
{
return instance->qos_context.qos_switch;
}
static void ps3_qos_cmd_resend_fail(struct ps3_cmd *cmd, S32 ret)
{
struct ps3_scsi_priv_data *pri_data = NULL;
struct ps3_instance *instance = cmd->instance;
struct scsi_cmnd *s_cmd = cmd->scmd;
if (ret == -PS3_RECOVERED || ret == -PS3_RETRY) {
s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_RESET);
} else {
s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT);
}
if (cmd->is_got_r1x == 1) {
pri_data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata;
ps3_r1x_write_unlock(&pri_data->lock_mgr, cmd);
}
LOG_INFO_IN_IRQ(instance, "t_id:0x%llx hno:%u tag:%d cmd send err ret:%d\n",
cmd->trace_id, PS3_HOST(instance), cmd->index, ret);
ps3_scsi_dma_unmap(cmd);
s_cmd = cmd->scmd;
PS3_DEV_IO_START_ERR_INC(instance, cmd);
PS3_IO_OUTSTAND_DEC(cmd->instance, s_cmd);
PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd);
PS3_VD_OUTSTAND_DEC(cmd->instance, s_cmd);
PS3_DEV_BUSY_DEC(s_cmd);
ps3_scsi_cmd_free(cmd);
SCMD_IO_DONE(s_cmd);
}
struct qos_wait_queue *ps3_qos_cmd_waitq_get(struct ps3_qos_tg_context *qos_tg_ctx, struct ps3_cmd *cmd)
{
struct qos_wait_queue *wait_q = NULL;
U16 disk_id = 0;
struct ps3_qos_context *qos_ctx = NULL;
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
wait_q = &qos_tg_ctx->mgr_cmd_wait_q;
} else {
if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) {
disk_id = get_offset_of_vdid(PS3_VDID_OFFSET(cmd->instance),
PS3_VDID(&cmd->io_attr.vd_entry->diskPos));
wait_q = &qos_tg_ctx->vd_cmd_waitqs[disk_id];
} else {
qos_ctx = &cmd->instance->qos_context;
wait_q = &qos_tg_ctx->vd_cmd_waitqs[qos_ctx->max_vd_count];
}
}
return wait_q;
}
static void ps3_hba_qos_cmd_update(struct ps3_cmd *cmd)
{
struct ps3_qos_tg_context *qos_tg_ctx = &cmd->instance->qos_context.tg_ctx;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_share_used) >= 0) {
ps3_atomic_inc(&qos_tg_ctx->share_free_cnt);
} else {
ps3_atomic_inc(&qos_tg_ctx->mgr_share_used);
ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt);
}
} else {
ps3_qos_update_pd_quota(cmd);
if (cmd->cmd_word.direct != PS3_CMDWORD_DIRECT_OK) {
qos_vd_mgr = ps3_qos_vd_mgr_get(cmd);
if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) {
ps3_atomic_inc(&qos_vd_mgr->vd_quota);
}
if (ps3_atomic_dec_return(&qos_vd_mgr->share_cmd_used) >= 0) {
ps3_atomic_inc(&qos_tg_ctx->share_free_cnt);
} else {
ps3_atomic_inc(&qos_vd_mgr->share_cmd_used);
ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt);
}
}
}
LOG_DEBUG("qos cmd update. host_no:%u dev_t:%u share:%u\n",
PS3_HOST(cmd->instance), cmd->io_attr.dev_type,
ps3_atomic_read(&qos_tg_ctx->share_free_cnt));
}
static inline Bool ps3_qos_share_cmdword_dec(struct ps3_qos_tg_context *qos_tg_ctx)
{
Bool can_get = PS3_FALSE;
if (ps3_atomic_dec_return(&qos_tg_ctx->share_free_cnt) >= 0) {
can_get = PS3_TRUE;
} else {
ps3_atomic_inc(&qos_tg_ctx->share_free_cnt);
can_get = PS3_FALSE;
}
return can_get;
}
static Bool ps3_qos_share_cmdword_get(struct ps3_qos_tg_context *qos_tg_ctx,
struct ps3_cmd *cmd, struct qos_wait_queue *wait_q)
{
Bool can_get = PS3_FALSE;
ULong flag = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
can_get = ps3_qos_share_cmdword_dec(qos_tg_ctx);
if (!can_get) {
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
list_add_tail(&cmd->qos_list, &wait_q->wait_list);
wait_q->count++;
qos_tg_ctx->total_wait_cmd_cnt++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_FRAME;
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
LOG_DEBUG("insert qos tag waitq.host_no:%u:t_id:0x%llx CFID:%u diskid:%u waitq:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
cmd->io_attr.disk_id, wait_q->count);
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_TAG_QUEUE);
} else {
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
ps3_atomic_inc(&qos_tg_ctx->mgr_share_used);
} else {
qos_vd_mgr = ps3_qos_vd_mgr_get(cmd);
ps3_atomic_inc(&qos_vd_mgr->share_cmd_used);
}
}
return can_get;
}
static bool ps3_qos_mgr_cmdword_get(struct ps3_cmd *cmd)
{
bool can_get = PS3_FALSE;
struct ps3_instance *instance = cmd->instance;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_free_cnt) >= 0) {
can_get = PS3_TRUE;
} else {
ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt);
can_get = PS3_FALSE;
}
return can_get;
}
Bool ps3_qos_vd_cmdword_get(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
qos_vd_mgr = ps3_qos_vd_mgr_get(cmd);
if (ps3_atomic_dec_return(&qos_vd_mgr->exclusive_cmd_cnt) >= 0) {
can_get = PS3_TRUE;
} else {
ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt);
can_get = PS3_FALSE;
}
return can_get;
}
Bool ps3_qos_exclusive_cmdword_get(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
can_get = ps3_qos_mgr_cmdword_get(cmd);
} else {
can_get = ps3_qos_vd_cmdword_get(cmd);
}
return can_get;
}
Bool ps3_qos_tg_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
struct ps3_instance *instance = cmd->instance;
ULong flag = 0;
struct qos_wait_queue *cmd_wait_q = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
PS3_QOS_STAT_START(instance, cmd, PS3_QOS_TAG_PRO);
cmd_wait_q = ps3_qos_cmd_waitq_get(qos_tg_ctx, cmd);
if (cmd_wait_q->count == 0) {
can_get = ps3_qos_exclusive_cmdword_get(cmd);
if (can_get){
goto out;
}
}
if (likely(qos_tg_ctx->total_wait_cmd_cnt == 0)) {
can_get = ps3_qos_share_cmdword_get(qos_tg_ctx, cmd, cmd_wait_q);
} else {
INJECT_START(PS3_ERR_IJ_QOS_WAIT_TAG_WAITQ_CLEAR, cmd->instance)
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
if (qos_tg_ctx->total_wait_cmd_cnt > 0){
list_add_tail(&cmd->qos_list, &cmd_wait_q->wait_list);
cmd_wait_q->count++;
qos_tg_ctx->total_wait_cmd_cnt++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_FRAME;
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
can_get = PS3_FALSE;
LOG_DEBUG("insert qos tag waitq.host_no:%u:t_id:0x%llx CFID:%u diskid:%u waitq:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index,
cmd->io_attr.disk_id, cmd_wait_q->count);
PS3_QOS_STAT_START(instance, cmd, PS3_QOS_TAG_QUEUE);
} else {
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
can_get = ps3_qos_share_cmdword_get(qos_tg_ctx, cmd, cmd_wait_q);
}
}
out:
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_PRO);
return can_get;
}
static inline bool ps3_qos_pd_quota_add(struct qos_wait_queue *waitq)
{
Bool can_get = PS3_TRUE;
if (ps3_atomic_inc_return(waitq->used_rsc) > *waitq->free_rsc) {
ps3_atomic_dec(waitq->used_rsc);
can_get = PS3_FALSE;
}
return can_get;
}
static void ps3_qos_pd_in_q(struct ps3_cmd *cmd, struct qos_wait_queue *waitq,
U16 pd_idx) {
ULong flag = 0;
ps3_spin_lock_irqsave(waitq->rsc_lock, &flag);
list_add_tail(&cmd->qos_list, &waitq->wait_list);
waitq->count++;
(*waitq->total_waited_cnt)++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_PD;
cmd->first_over_quota_pd_idx = pd_idx;
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_PD_QUEUE);
}
static struct qos_wait_queue* ps3_qos_pd_waitq_get(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct ps3_cmd *cmd)
{
U16 que_id = 0;
struct ps3_instance *instance = NULL;
instance = cmd->instance;
if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) {
que_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance),
PS3_VDID(&cmd->io_attr.vd_entry->diskPos));
} else {
if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) {
que_id = 0;
} else {
que_id = instance->qos_context.max_vd_count;
}
}
return &qos_pd_mgr->waitqs[que_id];
}
static Bool ps3_qos_pd_quota_get(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct ps3_cmd *cmd, U16 pd_idx, struct qos_wait_queue *waitq)
{
Bool can_get = PS3_FALSE;
can_get = ps3_qos_pd_quota_add(waitq);
if (!can_get) {
ps3_qos_pd_in_q(cmd, waitq, pd_idx);
LOG_DEBUG("insert qos pd quota waitq.host_no:%u:t_id:0x%llx CFID:%u waitq[%u,%u] did[%u,%u]\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
waitq->id, waitq->count, qos_pd_mgr->disk_id, pd_idx);
} else {
cmd->target_pd[pd_idx].get_quota = PS3_TRUE;
}
return can_get;
}
static bool ps3_qos_pd_quota_req(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct ps3_cmd *cmd, U16 pd_idx)
{
bool can_get = PS3_FALSE;
ULong flag = 0;
struct qos_wait_queue *waitq = NULL;
waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd);
if (likely((*waitq->total_waited_cnt) == 0)) {
can_get = ps3_qos_pd_quota_get(qos_pd_mgr, cmd, pd_idx, waitq);
} else {
INJECT_START(PS3_ERR_IJ_QOS_WAIT_PD_WAITQ_CLEAR, cmd->instance)
ps3_spin_lock_irqsave(waitq->rsc_lock, &flag);
if ((*waitq->total_waited_cnt) > 0) {
list_add_tail(&cmd->qos_list, &waitq->wait_list);
waitq->count++;
(*waitq->total_waited_cnt)++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_PD;
cmd->first_over_quota_pd_idx = pd_idx;
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
can_get = PS3_FALSE;
LOG_DEBUG("insert qos pd quota waitq.host_no:%u:t_id:0x%llx CFID:%u waitq[%u,%u] did[%u,%u]\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
waitq->id, waitq->count, qos_pd_mgr->disk_id, pd_idx);
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_PD_QUEUE);
} else {
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
can_get = ps3_qos_pd_quota_get(qos_pd_mgr, cmd, pd_idx, waitq);
}
}
return can_get;
}
static bool ps3_qos_pd_quota_check(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct ps3_cmd *cmd)
{
bool can_get = PS3_FALSE;
ULong flag = 0;
struct qos_wait_queue *waitq = NULL;
waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd);
if (likely((*waitq->total_waited_cnt) == 0)) {
can_get = ps3_qos_pd_quota_add(waitq);
} else {
INJECT_START(PS3_ERR_IJ_QOS_WAIT_PD_WAITQ_CLEAR_2, cmd->instance)
ps3_spin_lock_irqsave(waitq->rsc_lock, &flag);
if ((*waitq->total_waited_cnt) > 0) {
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
can_get = PS3_FALSE;
} else {
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
can_get = ps3_qos_pd_quota_add(waitq);
}
}
return can_get;
}
static inline bool ps3_qos_vd_quota_dec(struct ps3_qos_vd_mgr *qos_vd_mgr)
{
Bool can_get = PS3_FALSE;
if (ps3_atomic_dec_return(&qos_vd_mgr->vd_quota) >= 0) {
can_get = PS3_TRUE;
} else {
ps3_atomic_inc(&qos_vd_mgr->vd_quota);
can_get = PS3_FALSE;
}
return can_get;
}
static void ps3_qos_insert_vd_quota_waitq(struct ps3_qos_vd_mgr *qos_vd_mgr, struct ps3_cmd *cmd)
{
ULong flag = 0;
struct qos_wait_queue *wait_q = NULL;
wait_q = &qos_vd_mgr->vd_quota_wait_q;
ps3_spin_lock_irqsave(wait_q->rsc_lock, &flag);
list_add_tail(&cmd->qos_list, &wait_q->wait_list);
wait_q->count++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_VD;
ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag);
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_VD_QUEUE);
LOG_DEBUG("insert qos vd quota waitq.host_no:%u:tid:0x%llx CFID:%u diskid:%u waitq:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
cmd->io_attr.disk_id, wait_q->count);
}
static bool ps3_qos_vd_quota_get_waitq(struct ps3_qos_vd_mgr *qos_vd_mgr, struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
can_get = ps3_qos_vd_quota_dec(qos_vd_mgr);
if (!can_get) {
ps3_qos_insert_vd_quota_waitq(qos_vd_mgr, cmd);
}
return can_get;
}
static bool ps3_qos_vd_quota_check(struct ps3_cmd *cmd)
{
bool can_get = PS3_FALSE;
ULong flag = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct qos_wait_queue *wait_q = NULL;
qos_vd_mgr = ps3_qos_vd_mgr_get(cmd);
wait_q = &qos_vd_mgr->vd_quota_wait_q;
if (likely(wait_q->count == 0)){
can_get = ps3_qos_vd_quota_get_waitq(qos_vd_mgr, cmd);
} else {
INJECT_START(PS3_ERR_IJ_QOS_WAIT_VD_WAITQ_CLEAR, cmd->instance)
ps3_spin_lock_irqsave(wait_q->rsc_lock, &flag);
if (wait_q->count > 0) {
list_add_tail(&cmd->qos_list, &wait_q->wait_list);
wait_q->count++;
cmd->qos_waitq_flag = PS3_QOS_CMD_IN_VD;
ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag);
can_get = PS3_FALSE;
LOG_DEBUG("insert qos vd quota waitq.host_no:%u:tid:0x%llx CFID:%u "
"diskid:%u waitq:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
cmd->io_attr.disk_id, wait_q->count);
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_VD_QUEUE);
} else {
ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag);
can_get = ps3_qos_vd_quota_get_waitq(qos_vd_mgr, cmd);
}
}
return can_get;
}
static inline Bool ps3_qos_pd_notify_judge(struct ps3_qos_pd_mgr *qos_pd_mgr)
{
Bool can_notify = PS3_FALSE;
can_notify = qos_pd_mgr->total_wait_cmd_cnt &&
(ps3_atomic_read(&qos_pd_mgr->pd_used_quota) < qos_pd_mgr->pd_quota);
if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr) || can_notify) {
goto _out;
} else {
can_notify = qos_pd_mgr->total_waited_direct_cmd &&
(ps3_atomic_read(&qos_pd_mgr->direct_used_quota) < qos_pd_mgr->direct_quota);
}
_out:
INJECT_START(PS3_ERR_IJ_QOS_NOT_AWAKE_PD, &can_notify)
return can_notify;
}
static Bool ps3_qos_single_pd_notify(struct ps3_qos_pd_context *qos_pd_ctx,
struct ps3_qos_pd_mgr *qos_pd_mgr)
{
struct workqueue_struct *workq = NULL;
Bool notified = PS3_FALSE;
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) {
if (ps3_qos_pd_notify_judge(qos_pd_mgr)) {
workq = qos_pd_ctx->work_queues[qos_pd_mgr->workq_id];
queue_work(workq, &qos_pd_mgr->resend_work);
notified = PS3_TRUE;
}
}
return notified;
}
static void ps3_qos_pd_notify(struct ps3_instance *instance)
{
U16 id = 0;
struct ps3_qos_pd_context *qos_pd_ctx = NULL;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
qos_pd_ctx = &instance->qos_context.pd_ctx;
for (id = 1; id <= instance->qos_context.max_pd_count; id++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, id);
ps3_qos_single_pd_notify(qos_pd_ctx, qos_pd_mgr);
}
}
static Bool ps3_qos_single_vd_notify(struct ps3_qos_vd_context *qos_vd_ctx,
struct ps3_qos_vd_mgr *qos_vd_mgr)
{
struct workqueue_struct *workq = NULL;
Bool notify = PS3_FALSE;
if (qos_vd_mgr->valid) {
if (qos_vd_mgr->vd_quota_wait_q.count > 0 &&
ps3_atomic_read(&qos_vd_mgr->vd_quota) > 0) {
workq = qos_vd_ctx->work_queues[qos_vd_mgr->workq_id];
queue_work(workq, &qos_vd_mgr->resend_work);
notify = PS3_TRUE;
}
}
return notify;
}
static U8 ps3_qos_vd_notify(struct ps3_instance *instance)
{
U8 notify = PS3_FALSE;
U16 id = 0;
struct ps3_qos_vd_context *qos_vd_ctx = NULL;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
qos_vd_ctx = &instance->qos_context.vd_ctx;
for (id = 1; id <= instance->qos_context.max_vd_count; id++) {
qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[id];
if (ps3_qos_single_vd_notify(qos_vd_ctx, qos_vd_mgr)) {
notify = PS3_TRUE;
}
}
return notify;
}
static Bool ps3_qos_tag_rsc_available(struct ps3_instance *instance)
{
Bool rsc_avail = PS3_FALSE;
U16 i = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
if (ps3_atomic_read(&qos_tg_ctx->share_free_cnt) > 0) {
rsc_avail = PS3_TRUE;
goto _lout;
}
if (qos_tg_ctx->mgr_cmd_wait_q.count > 0 &&
ps3_atomic_read(&qos_tg_ctx->mgr_free_cnt) > 0) {
rsc_avail = PS3_TRUE;
goto _lout;
}
for (i = 1; i <= instance->qos_context.max_vd_count; i++) {
qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i];
if (ps3_atomic_read(&qos_vd_mgr->exclusive_cmd_cnt) > 0 &&
qos_tg_ctx->vd_cmd_waitqs[i].count > 0) {
rsc_avail = PS3_TRUE;
break;
}
}
_lout:
INJECT_START(PS3_ERR_IJ_QOS_NOT_AWAKE_TAG, &rsc_avail);
return rsc_avail;
}
static Bool ps3_qos_tg_notify(struct ps3_instance *instance)
{
Bool notified = PS3_FALSE;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
if (qos_tg_ctx->total_wait_cmd_cnt > 0 &&
ps3_qos_tag_rsc_available(instance)) {
queue_work(qos_tg_ctx->work_queue, &qos_tg_ctx->resend_work);
notified = PS3_TRUE;
}
return notified;
}
Bool ps3_qos_all_pd_rc_get(struct ps3_cmd *cmd)
{
cmd->target_pd[cmd->first_over_quota_pd_idx].get_quota = PS3_TRUE;
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_QUEUE);
LOG_DEBUG("resend cmd judge:host_no:%u t_id:0x%llx cmd:%u cmd_t:%u dev_t:%u ret[%u,%u]\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, cmd->cmd_word.type, cmd->io_attr.dev_type,
cmd->first_over_quota_pd_idx, 1);
return PS3_TRUE;
}
static void ps3_vd_quota_waitq_clean( struct ps3_qos_vd_mgr *qos_vd_mgr,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag);
if (qos_vd_mgr->vd_quota_wait_q.count > 0) {
list_for_each_entry_safe(cmd, cmd_next, &qos_vd_mgr->vd_quota_wait_q.wait_list, qos_list) {
if (priv_data == NULL ||
priv_data == scsi_device_private_data(cmd->scmd)) {
list_del(&cmd->qos_list);
qos_vd_mgr->vd_quota_wait_q.count--;
LOG_DEBUG("qos clean vd quota waitq. hno:%u t_id:0x%llx cmd:%d vd_id:%u\n",
PS3_HOST(qos_vd_mgr->instance), cmd->trace_id, cmd->index, qos_vd_mgr->id);
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
}
}
ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag);
LOG_FILE_INFO("clean qos device vd quota waitq host_no:%u diskid:%u\n",
PS3_HOST(qos_vd_mgr->instance), qos_vd_mgr->id);
}
static void ps3_qos_vd_quota_waitq_resend(struct ps3_qos_vd_mgr *qos_vd_mgr)
{
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
ULong flag = 0;
S32 ret = PS3_SUCCESS;
Bool waitq_cleared = PS3_FALSE;
struct ps3_instance *instance = qos_vd_mgr->instance;
ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag);
if (qos_vd_mgr->vd_quota_wait_q.count > 0
&& ps3_atomic_read(&qos_vd_mgr->vd_quota) > 0) {
list_for_each_entry_safe(cmd, cmd_next, &qos_vd_mgr->vd_quota_wait_q.wait_list, qos_list) {
if (ps3_atomic_dec_return(&qos_vd_mgr->vd_quota) < 0) {
ps3_atomic_inc(&qos_vd_mgr->vd_quota);
break;
}
list_del(&cmd->qos_list);
qos_vd_mgr->vd_quota_wait_q.count--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_VD_QUEUE);
if (ps3_qos_tg_decision(cmd)) {
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_cmd_update(instance, cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
cmd->qos_waitq_flag = 0;
LOG_DEBUG("qos vd quota waitq resend:host_no:%u vid:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), qos_vd_mgr->id, cmd->trace_id, cmd->index, cmd->cmd_word.type);
}
}
}
if (qos_vd_mgr->vd_quota_wait_q.count == 0) {
waitq_cleared = PS3_TRUE;
}
}
ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag);
if (waitq_cleared) {
LOG_DEBUG("qos vd quota waitq cleared.host_no:%u vd:%u\n",
PS3_HOST(instance), qos_vd_mgr->id);
}
qos_vd_mgr->last_sched_jiffies = jiffies;
ps3_qos_pd_notify(instance);
}
static void ps3_qos_vd_resend_work(struct work_struct *work)
{
struct ps3_qos_vd_mgr *qos_vd_mgr =
ps3_container_of(work, struct ps3_qos_vd_mgr, resend_work);
ps3_qos_vd_quota_waitq_resend(qos_vd_mgr);
}
static void ps3_qos_waitq_clean(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct qos_wait_queue *waitq, S32 resp_status)
{
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
ps3_spin_lock_irqsave(waitq->rsc_lock, &flag);
while (waitq->count > 0) {
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
LOG_DEBUG("qos clean pd waitq. hno:%u t_id:0x%llx CFID:%d pid:%u que_id:%u\n",
PS3_HOST(qos_pd_mgr->instance), cmd->trace_id, cmd->index,
qos_pd_mgr->disk_id, waitq->id);
list_del(&cmd->qos_list);
waitq->count--;
(*waitq->total_waited_cnt)--;
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
}
void ps3_pd_quota_waitq_clear_all(struct ps3_qos_pd_mgr *qos_pd_mgr,
S32 resp_status)
{
U16 i = 0;
struct qos_wait_queue *waitq = NULL;
for (i = 0; i < qos_pd_mgr->waitq_cnt; i++) {
waitq = &qos_pd_mgr->waitqs[i];
ps3_qos_waitq_clean(qos_pd_mgr, waitq, resp_status);
}
LOG_DEBUG("clear all qos pd quota waitq host_no:%u did:%u vid:%u\n",
PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, qos_pd_mgr->vd_id);
}
void ps3_pd_quota_waitq_clean(struct ps3_qos_pd_mgr *qos_pd_mgr,
U16 que_id, S32 resp_status)
{
struct qos_wait_queue *waitq = NULL;
if (que_id == 0) {
ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status);
} else {
waitq = &qos_pd_mgr->waitqs[que_id];
ps3_qos_waitq_clean(qos_pd_mgr, waitq, resp_status);
}
LOG_DEBUG("clean qos pd quota waitq host_no:%u did:%u vid:%u\n",
PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, qos_pd_mgr->vd_id);
}
static Bool ps3_hba_qos_pd_resend_check(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) {
if (!ps3_qos_vd_quota_check(cmd)) {
goto _out;
}
}
can_get = ps3_qos_tg_decision(cmd);
_out:
return can_get;
}
static Bool ps3_qos_pd_resend_check(struct ps3_instance *instance, struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
if (cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) {
cmd->target_pd[cmd->first_over_quota_pd_idx].get_quota = PS3_TRUE;
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_QUEUE);
can_get = PS3_TRUE;
} else {
if (!ps3_qos_all_pd_rc_get(cmd)) {
goto _out;
}
can_get = instance->qos_context.opts.qos_pd_resend_check(cmd);
}
_out:
return can_get;
}
static Bool ps3_qos_vd_seq_check(struct ps3_cmd *cmd)
{
struct PS3VDEntry *vd_entry = NULL;
U16 vd_id = 0;
Bool seq_changed = PS3_FALSE;
INJECT_START(PS3_ERR_IJ_QOS_FORCE_VD_SEQ_CHANGE, cmd);
if (cmd->cmd_word.direct == PS3_CMDWORD_DIRECT_ADVICE) {
vd_id = PS3_VDID(&cmd->io_attr.vd_entry->diskPos);
vd_entry = ps3_dev_mgr_lookup_vd_info_by_id(cmd->instance, vd_id);
if (!vd_entry || vd_entry->virtDiskSeq !=
cmd->req_frame->hwReq.reqHead.virtDiskSeq) {
seq_changed = PS3_TRUE;
LOG_DEBUG("qos pd waitq vd seq check:host_no:%u"
"t_id:0x%llx CFID:%u seq[%u %u]\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
cmd->req_frame->hwReq.reqHead.virtDiskSeq,
(!vd_entry) ? 0 : vd_entry->virtDiskSeq);
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd,
SCSI_STATUS_TASK_ABORTED, PS3_FALSE);
}
}
return seq_changed;
}
static U32 ps3_qos_pd_resend_cmd(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct qos_wait_queue *waitq, S32 count)
{
S32 ret = PS3_SUCCESS;
struct ps3_cmd *cmd = NULL;
struct ps3_instance *instance = qos_pd_mgr->instance;
U32 can_send = 0;
U32 que_cnt = 0;
S32 avail_rsc = 0;
S32 used_rsc = ps3_atomic_read(waitq->used_rsc);
if (used_rsc < *waitq->free_rsc) {
avail_rsc = *waitq->free_rsc - used_rsc;
}
can_send = PS3_MIN(avail_rsc, count);
que_cnt = waitq->count;
while(can_send > 0 && waitq->count > 0) {
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
list_del(&cmd->qos_list);
waitq->count--;
(*waitq->total_waited_cnt)--;
if (ps3_qos_vd_seq_check(cmd)) {
continue;
}
can_send--;
ps3_atomic_inc(waitq->used_rsc);
if (ps3_qos_pd_resend_check(instance, cmd)) {
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_cmd_update(instance, cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
cmd->qos_waitq_flag = 0;
LOG_DEBUG("qos pd quota waitq resend:host_no:%u qid:%u pid:%u"
"t_id:0x%llx CFID:%u resend[%u %u]\n",
PS3_HOST(instance), waitq->id, qos_pd_mgr->disk_id,
cmd->trace_id, cmd->index, waitq->has_resend, waitq->can_resend);
}
}
}
return (que_cnt - waitq->count);
}
static void ps3_qos_pd_waitq_resend(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct qos_wait_queue *waitq )
{
ULong flag = 0;
Bool waitq_cleared = PS3_FALSE;
if (waitq->count > 0) {
ps3_spin_lock_irqsave(waitq->rsc_lock, &flag);
if (ps3_atomic_read(waitq->used_rsc) < *waitq->free_rsc) {
ps3_qos_pd_resend_cmd(qos_pd_mgr, waitq, waitq->count);
}
if (*waitq->total_waited_cnt == 0) {
waitq_cleared = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag);
if (waitq_cleared) {
LOG_DEBUG("qos pd quota waitq is cleard. host_no:%u did:%u qid:%u\n",
PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, waitq->id);
}
}
}
static void ps3_qos_pd_jbod_resend(struct ps3_qos_pd_mgr *qos_pd_mgr)
{
struct ps3_instance *instance = qos_pd_mgr->instance;
struct qos_wait_queue *waitq = NULL;
U16 que_id = 0;
if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) {
waitq = &qos_pd_mgr->waitqs[0];
ps3_qos_pd_waitq_resend(qos_pd_mgr, waitq);
}
que_id = instance->qos_context.max_vd_count;
waitq = &qos_pd_mgr->waitqs[que_id];
ps3_qos_pd_waitq_resend(qos_pd_mgr, waitq);
}
void ps3_qos_pd_waitq_ratio_update(struct ps3_qos_pd_mgr *qos_pd_mgr)
{
U16 min_waitq_cnt = 0;
U16 i = 0;
struct qos_wait_queue *waitq = NULL;
ULong min_cmd_jiffies = 0;
struct ps3_cmd *cmd = NULL;
U16 poll_que_id = 1;
if (qos_pd_mgr->poll_que_id != qos_pd_mgr->poll_start_que_id) {
return;
}
waitq = &qos_pd_mgr->waitqs[qos_pd_mgr->poll_que_id];
if (waitq->has_resend > 0) {
return;
}
for (i = 1; i < qos_pd_mgr->waitq_cnt - 1; i++) {
waitq = &qos_pd_mgr->waitqs[i];
if (waitq->count > 0) {
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
if (min_waitq_cnt == 0) {
min_waitq_cnt = waitq->count;
min_cmd_jiffies = cmd->scmd->jiffies_at_alloc;
} else {
if (min_waitq_cnt > waitq->count) {
min_waitq_cnt = waitq->count;
}
if (min_cmd_jiffies > cmd->scmd->jiffies_at_alloc) {
min_cmd_jiffies = cmd->scmd->jiffies_at_alloc;
poll_que_id = i;
}
}
}
}
for (i = 1; i < qos_pd_mgr->waitq_cnt - 1; i++) {
waitq = &qos_pd_mgr->waitqs[i];
if (waitq->count > 0) {
waitq->can_resend = waitq->count/min_waitq_cnt > 0 ?
waitq->count/min_waitq_cnt : 1;
waitq->has_resend = 0;
}
}
qos_pd_mgr->poll_que_id = poll_que_id;
qos_pd_mgr->poll_start_que_id = poll_que_id;
LOG_DEBUG("qos pd ratio update:host_no:%u pid:%u first:%u\n",
PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, poll_que_id);
}
static void ps3_qos_pd_vd_member_resend(struct ps3_qos_pd_mgr *qos_pd_mgr)
{
ULong flag = 0;
struct ps3_instance *instance = qos_pd_mgr->instance;
struct qos_wait_queue *waitq = NULL;
ULong timeout_jiffies = 0;
Bool waitq_cleared = PS3_FALSE;
Bool vd_expired = PS3_FALSE;
U32 pd_wait_count = 0;
ps3_spin_lock_irqsave(&qos_pd_mgr->rc_lock, &flag);
pd_wait_count = qos_pd_mgr->waitqs[instance->qos_context.max_vd_count].count;
while ((ps3_atomic_read(&qos_pd_mgr->pd_used_quota) <
qos_pd_mgr->pd_quota) && qos_pd_mgr->total_wait_cmd_cnt > pd_wait_count) {
ps3_qos_pd_waitq_ratio_update(qos_pd_mgr);
waitq = &qos_pd_mgr->waitqs[qos_pd_mgr->poll_que_id];
if (waitq->count == 0) {
goto _next_waitq;
}
if (waitq->has_resend > 0) {
if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_SAS_HDD ||
qos_pd_mgr->dev_type == PS3_DEV_TYPE_SATA_HDD) {
timeout_jiffies = waitq->last_sched_jiffies +
msecs_to_jiffies(PS3_QOS_VD_HDD_DELAY_THD_MS);
} else {
timeout_jiffies = waitq->last_sched_jiffies +
msecs_to_jiffies(PS3_QOS_VD_SDD_DELAY_THD_MS);
}
vd_expired = time_after(jiffies, timeout_jiffies);
INJECT_START(PS3_ERR_IJ_QOS_PD_RESEND_NOT_EXPIRED, &vd_expired)
if (vd_expired) {
LOG_DEBUG("go to next vd waitq for delay:host_no:%u qid:%u pid:%u",
PS3_HOST(instance), waitq->id, qos_pd_mgr->disk_id);
goto _next_waitq;
}
}
waitq->last_sched_jiffies = jiffies;
waitq->has_resend += ps3_qos_pd_resend_cmd(qos_pd_mgr, waitq, waitq->can_resend - waitq->has_resend);
if (waitq->has_resend >= waitq->can_resend) {
goto _next_waitq;
}
continue;
_next_waitq:
waitq->has_resend = 0;
if (++qos_pd_mgr->poll_que_id == instance->qos_context.max_vd_count) {
qos_pd_mgr->poll_que_id = 1;
}
}
if (qos_pd_mgr->total_wait_cmd_cnt == 0) {
waitq_cleared = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(&qos_pd_mgr->rc_lock, flag);
if (waitq_cleared) {
LOG_DEBUG("qos pd quota waitq is cleard. host_no:%u diskid:%u\n",
PS3_HOST(instance), qos_pd_mgr->disk_id);
}
}
static void ps3_qos_pd_resend_work(struct work_struct *work)
{
struct ps3_qos_pd_mgr *qos_pd_mgr =
ps3_container_of(work, struct ps3_qos_pd_mgr, resend_work);
ps3_qos_pd_jbod_resend(qos_pd_mgr);
INJECT_START(PS3_ERR_IJ_QOS_WAIT_VD_SEND, qos_pd_mgr)
ps3_qos_pd_vd_member_resend(qos_pd_mgr);
qos_pd_mgr->last_sched_jiffies = jiffies;
return;
}
static void ps3_poll_vd_cmd_waitq(struct ps3_qos_tg_context *qos_tg_ctx)
{
struct qos_wait_queue *waitq = NULL;
U32 free_cnt = 0;
U8 vd_id = 0;
S32 ret = PS3_SUCCESS;
struct ps3_cmd *cmd = NULL;
U32 sent_count = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_instance *instance = qos_tg_ctx->instance;
U32 mgrq_cnt = qos_tg_ctx->mgr_cmd_wait_q.count;
INJECT_START(PS3_ERR_IJ_QOS_SET_SHARE_COUNT, instance)
free_cnt = ps3_atomic_read(&qos_tg_ctx->share_free_cnt);
vd_id = qos_tg_ctx->poll_vd_id;
while (free_cnt > sent_count && qos_tg_ctx->total_wait_cmd_cnt > mgrq_cnt) {
waitq = &qos_tg_ctx->vd_cmd_waitqs[vd_id];
if (waitq->count > 0) {
qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[vd_id];
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
list_del(&cmd->qos_list);
cmd->qos_waitq_flag = 0;
waitq->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE);
if (ps3_qos_vd_seq_check(cmd)) {
continue;
}
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
ps3_atomic_inc(&qos_vd_mgr->share_cmd_used);
++sent_count;
LOG_DEBUG("qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, cmd->cmd_word.type);
}
}
if (++vd_id > instance->qos_context.max_vd_count) {
vd_id = 1;
}
}
ps3_atomic_sub(sent_count, &qos_tg_ctx->share_free_cnt);
LOG_DEBUG("resend vd cmd waitq. host_no:%u poll_vd_id:%u sent:%u\n",
PS3_HOST(instance), vd_id, sent_count);
qos_tg_ctx->poll_vd_id = vd_id;
}
static void ps3_mgr_cmd_waitq_resend(struct ps3_qos_tg_context *qos_tg_ctx)
{
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct qos_wait_queue *wait_q = NULL;
struct ps3_instance *instance = qos_tg_ctx->instance;
S32 ret = PS3_SUCCESS;
U32 free_cnt = ps3_atomic_read(&qos_tg_ctx->share_free_cnt);
U32 sent_count = 0;
wait_q = &qos_tg_ctx->mgr_cmd_wait_q;
if (wait_q->count > 0 && free_cnt > 0) {
list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, qos_list) {
list_del(&cmd->qos_list);
cmd->qos_waitq_flag = 0;
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE);
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
LOG_DEBUG("qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, cmd->cmd_word.type);
ps3_atomic_inc(&qos_tg_ctx->mgr_share_used);
if(++sent_count == free_cnt) {
break;
}
}
}
ps3_atomic_sub(sent_count, &qos_tg_ctx->share_free_cnt);
}
}
static void ps3_qos_tg_share_resend(struct ps3_qos_tg_context *qos_tg_ctx)
{
ps3_mgr_cmd_waitq_resend(qos_tg_ctx);
ps3_poll_vd_cmd_waitq(qos_tg_ctx);
}
static void ps3_qos_tg_exclusive_resend(struct ps3_qos_tg_context *qos_tg_ctx)
{
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct qos_wait_queue *waitq = NULL;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
S32 ret = PS3_SUCCESS;
U8 id = 0;
struct ps3_instance *instance = qos_tg_ctx->instance;
waitq = &qos_tg_ctx->mgr_cmd_wait_q;
if (waitq->count > 0 &&
ps3_atomic_read(&qos_tg_ctx->mgr_free_cnt) > 0) {
list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, qos_list) {
if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_free_cnt) < 0) {
ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt);
break;
}
list_del(&cmd->qos_list);
cmd->qos_waitq_flag = 0;
waitq->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE);
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_cmd_resend_fail(cmd, ret);
ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt);
} else {
LOG_DEBUG("qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, cmd->cmd_word.type);
}
}
}
for (id = 1; id <= instance->qos_context.max_vd_count; id++) {
waitq = &qos_tg_ctx->vd_cmd_waitqs[id];
qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[id];
if (waitq->count > 0 &&
ps3_atomic_read(&qos_vd_mgr->exclusive_cmd_cnt) > 0) {
list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, qos_list) {
if (ps3_atomic_dec_return(&qos_vd_mgr->exclusive_cmd_cnt) < 0) {
ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt);
break;
}
list_del(&cmd->qos_list);
cmd->qos_waitq_flag = 0;
waitq->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE);
if (ps3_qos_vd_seq_check(cmd)) {
ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt);
continue;
}
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt);
} else {
LOG_DEBUG("qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, cmd->cmd_word.type);
}
}
}
}
}
static void ps3_qos_tg_waitq_resend(struct ps3_qos_tg_context *qos_tg_ctx)
{
ULong flag = 0;
Bool waitq_cleard = PS3_FALSE;
struct ps3_instance *instance = qos_tg_ctx->instance;
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
ps3_qos_tg_share_resend(qos_tg_ctx);
ps3_qos_tg_exclusive_resend(qos_tg_ctx);
if (qos_tg_ctx->total_wait_cmd_cnt == 0) {
waitq_cleard = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
if (waitq_cleard) {
LOG_DEBUG("hno:%u resend all tag waited cmds\n",
PS3_HOST(instance));
}
if (!ps3_qos_vd_notify(instance)) {
ps3_qos_pd_notify(instance);
}
qos_tg_ctx->last_sched_jiffies = jiffies;
}
static void ps3_qos_tg_resend_work(struct work_struct *work)
{
struct ps3_qos_tg_context *qos_tg_ctx =
ps3_container_of(work, struct ps3_qos_tg_context, resend_work);
ps3_qos_tg_waitq_resend(qos_tg_ctx);
}
U32 g_ps3_qos_cmdq_depth = PS3_QOS_CMDQ_DEPTH;
U32 g_ps3_qos_mgrq_depth = PS3_QOS_MGRQ_DEPTH;
U32 g_ps3_qos_hdd_pd_quota = PS3_QOS_DEFAULT_PD_QUOTA;
U32 g_ps3_qos_ssd_pd_quota = PS3_QOS_DEFAULT_PD_QUOTA;
U32 g_ps3_qos_sas_pd_quota = PS3_QOS_SAS_PD_QUOTA;
U32 g_ps3_qos_nvme_pd_quota = PS3_QOS_NVME_DIRECT_QUOTA;
U32 g_ps3_qos_nvme_member_quota = PS3_QOS_NVME_MEMBER_QUOTA;
U32 g_ps3_qos_hba_nvme_normal_quota = PS3_QOS_HBA_NVME_NORMAL_QUOTA;
U32 g_ps3_qos_raid_nvme_normal_quota = PS3_QOS_RAID_NVME_NORMAL_QUOTA;
U32 g_ps3_qos_vd_quota = PS3_QOS_FUNC1_JBOD_VD_QUOTA;
U32 g_ps3_qos_max_cmds = PS3_QOS_HBA_MAX_CMD;
U32 g_ps3_qos_jbod_exclusive = PS3_QOS_JBOD_EXCLUSIVE_CMD_COUNT;
U32 g_ps3_qos_vd_exclusive = PS3_QOS_VD_EXCLUSIVE_CMD_COUNT;
U32 g_ps3_qos_mgr_exclusive = QOS_MGR_EXCLUSIVE_CMD_COUNT;
#define PS3_QOS_MAX_WORKQ_NAME_LENGTH 32
static S32 ps3_qos_pd_context_init(struct ps3_instance *instance)
{
S32 ret = PS3_SUCCESS;
U16 i = 0;
U16 j = 0;
U16 k = 0;
struct ps3_qos_pd_context *qos_pd_ctx = NULL;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = {0};
U16 max_pd_count = 0;
struct qos_wait_queue *waitq = NULL;
U32 cpu_num = 0;
qos_pd_ctx = &instance->qos_context.pd_ctx;
if (ps3_is_last_func(instance)) {
cpu_num = num_online_cpus();
if (instance->irq_context.is_balance_current_perf_mode) {
INJECT_START(PS3_ERR_IJ_SET_HIGH_IOPS_CHANNEL_IN_QOS, instance);
if (cpu_num > instance->irq_context.high_iops_msix_vectors
+ PS3_QOS_FUNC1_PD_WORKQ_COUNT) {
qos_pd_ctx->workq_count =
cpu_num - instance->irq_context.high_iops_msix_vectors;
} else {
qos_pd_ctx->workq_count = PS3_QOS_FUNC1_PD_WORKQ_COUNT;
}
} else {
qos_pd_ctx->workq_count = PS3_QOS_FUNC1_PD_WORKQ_COUNT;
}
} else {
qos_pd_ctx->workq_count = PS3_QOS_FUNC0_PD_WORKQ_COUNT;
}
max_pd_count = instance->qos_context.max_pd_count;
qos_pd_ctx->qos_pd_mgrs = (struct ps3_qos_pd_mgr *)ps3_vzalloc(instance,
(max_pd_count + 1) * sizeof(struct ps3_qos_pd_mgr));
INJECT_START(PS3_ERR_IJ_QOS_PD_INIT_FAIL_2, instance)
if (qos_pd_ctx->qos_pd_mgrs == NULL ) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_pd_mgrs\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _out;
}
memset(qos_pd_ctx->qos_pd_mgrs, 0, sizeof(struct ps3_qos_pd_mgr) * (max_pd_count + 1));
for (i = 1; i <= max_pd_count; i++) {
qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[i];
INIT_WORK(&qos_pd_mgr->resend_work, ps3_qos_pd_resend_work);
ps3_spin_lock_init(&qos_pd_mgr->rc_lock);
ps3_spin_lock_init(&qos_pd_mgr->direct_rsc_lock);
ps3_spin_lock_init(&qos_pd_mgr->adjust_quota_lock);
qos_pd_mgr->workq_id = 0;
qos_pd_mgr->clearing = PS3_FALSE;
qos_pd_mgr->poll_cmd_cnt = 1;
qos_pd_mgr->poll_que_id = 1;
qos_pd_mgr->poll_start_que_id = 1;
ps3_atomic_set(&qos_pd_mgr->processing_cnt, 0);
qos_pd_mgr->pd_init_quota = 0;
qos_pd_mgr->instance = instance;
qos_pd_mgr->total_wait_cmd_cnt = 0;
qos_pd_mgr->total_waited_direct_cmd = 0;
qos_pd_mgr->waitq_cnt = instance->qos_context.max_vd_count + 1;
qos_pd_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc(instance,
qos_pd_mgr->waitq_cnt * sizeof(struct qos_wait_queue));
INJECT_START(PS3_ERR_IJ_QOS_PD_INIT_FAIL_3, qos_pd_ctx)
if (qos_pd_mgr->waitqs == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for pd waitqs\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _release_pd_waitq;
}
for (j = 0; j < qos_pd_mgr->waitq_cnt; j++) {
waitq = &qos_pd_mgr->waitqs[j];
waitq->count = 0;
waitq->id = j;
waitq->can_resend = 0;
waitq->has_resend = 0;
INIT_LIST_HEAD(&waitq->wait_list);
if (j == 0) {
waitq->free_rsc = &qos_pd_mgr->direct_quota;
waitq->used_rsc = &qos_pd_mgr->direct_used_quota;
waitq->rsc_lock = &qos_pd_mgr->direct_rsc_lock;
waitq->total_waited_cnt = &qos_pd_mgr->total_waited_direct_cmd;
} else {
waitq->free_rsc = &qos_pd_mgr->pd_quota;
waitq->used_rsc = &qos_pd_mgr->pd_used_quota;
waitq->rsc_lock = &qos_pd_mgr->rc_lock;
waitq->total_waited_cnt = &qos_pd_mgr->total_wait_cmd_cnt;
}
}
}
qos_pd_ctx->work_queues = (struct workqueue_struct **)ps3_vzalloc(instance,
(qos_pd_ctx->workq_count) * sizeof(struct workqueue_struct *));
INJECT_START(PS3_ERR_IJ_QOS_PD_INIT_FAIL_4, instance)
if (qos_pd_ctx->work_queues == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for work_queues\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _release_pd_mgr;
}
memset(qos_pd_ctx->work_queues, 0, (qos_pd_ctx->workq_count) * sizeof(struct workqueue_struct *));
for (j = 0; j < qos_pd_ctx->workq_count; j++) {
snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "pd_wq_f%u_%u",
ps3_get_pci_function(instance->pdev), j);
qos_pd_ctx->work_queues[j] = create_singlethread_workqueue(workq_name);
INJECT_START(PS3_ERR_IJ_QOS_PD_INIT_FAIL_1, qos_pd_ctx)
if (qos_pd_ctx->work_queues[j] == NULL) {
LOG_ERROR("qos pd workq:%u create failed\n", j);
ret = -PS3_FAILED;
goto _destroy_created_workqueue;
}
}
LOG_INFO("hno:%u: qos pd context init success\n", PS3_HOST(instance));
goto _out;
_destroy_created_workqueue:
for (k = 0; k < j; k++) {
destroy_workqueue(qos_pd_ctx->work_queues[k]);
qos_pd_ctx->work_queues[k] = NULL;
}
ps3_vfree(instance, qos_pd_ctx->work_queues);
qos_pd_ctx->work_queues = NULL;
_release_pd_waitq:
for (k = 1; k < i; k++) {
qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[k];
if (qos_pd_mgr->waitqs) {
ps3_vfree(instance, qos_pd_mgr->waitqs);
qos_pd_mgr->waitqs = NULL;
}
}
_release_pd_mgr:
ps3_vfree(instance, qos_pd_ctx->qos_pd_mgrs);
qos_pd_ctx->qos_pd_mgrs = NULL;
_out:
return ret;
}
static void ps3_qos_pd_context_exit(struct ps3_instance *instance)
{
U16 i = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct ps3_qos_pd_context *qos_pd_ctx = NULL;
struct workqueue_struct *wq = NULL;
qos_pd_ctx = &instance->qos_context.pd_ctx;
if (qos_pd_ctx->qos_pd_mgrs != NULL) {
for (i = 1; i <= instance->qos_context.max_pd_count; i++) {
qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[i];
cancel_work_sync(&qos_pd_mgr->resend_work);
ps3_vfree(instance, qos_pd_mgr->waitqs);
qos_pd_mgr->waitqs = NULL;
}
ps3_vfree(instance, qos_pd_ctx->qos_pd_mgrs);
qos_pd_ctx->qos_pd_mgrs = NULL;
}
if (qos_pd_ctx->work_queues != NULL) {
for (i = 0; i < qos_pd_ctx->workq_count; i++) {
wq = qos_pd_ctx->work_queues[i];
flush_workqueue(wq);
destroy_workqueue(wq);
qos_pd_ctx->work_queues[i] = NULL;
}
ps3_vfree(instance, qos_pd_ctx->work_queues);
qos_pd_ctx->work_queues = NULL;
}
}
static S32 ps3_qos_vd_context_init(struct ps3_instance *instance)
{
S32 ret = PS3_SUCCESS;
U16 i = 0;
U16 j = 0;
struct ps3_qos_vd_context *qos_vd_ctx = NULL;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = {0};
U16 max_vd_count = 0;
U16 jbod_vd_quota = 0;
qos_vd_ctx = &instance->qos_context.vd_ctx;
if (ps3_is_last_func(instance)) {
qos_vd_ctx->vd_exclusive_cnt = g_ps3_qos_vd_exclusive;
qos_vd_ctx->jbod_exclusive_cnt = g_ps3_qos_jbod_exclusive;
} else {
qos_vd_ctx->vd_exclusive_cnt = 0;
qos_vd_ctx->jbod_exclusive_cnt = 0;
}
jbod_vd_quota = instance->qos_context.tg_ctx.share + qos_vd_ctx->jbod_exclusive_cnt;
max_vd_count = instance->qos_context.max_vd_count;
qos_vd_ctx->qos_vd_mgrs = (struct ps3_qos_vd_mgr *)ps3_vzalloc(instance,
(max_vd_count + 1) * sizeof(struct ps3_qos_vd_mgr));
INJECT_START(PS3_ERR_IJ_QOS_VD_INIT_FAIL_1, instance)
if (qos_vd_ctx->qos_vd_mgrs == NULL ) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_vd_mgrs\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _out;
}
memset(qos_vd_ctx->qos_vd_mgrs, 0, sizeof(struct ps3_qos_vd_mgr) * (max_vd_count + 1));
for (i = 1; i <= max_vd_count; i++) {
qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i];
qos_vd_mgr->instance = instance;
ps3_spin_lock_init(&qos_vd_mgr->rsc_lock);
INIT_LIST_HEAD(&qos_vd_mgr->vd_quota_wait_q.wait_list);
qos_vd_mgr->vd_quota_wait_q.rsc_lock = &qos_vd_mgr->rsc_lock;
qos_vd_mgr->vd_quota_wait_q.count = 0;
INIT_WORK(&qos_vd_mgr->resend_work, ps3_qos_vd_resend_work);
qos_vd_mgr->workq_id = 0;
qos_vd_mgr->id = i;
if (i == max_vd_count) {
ps3_atomic_set(&qos_vd_mgr->vd_quota, jbod_vd_quota);
ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, qos_vd_ctx->jbod_exclusive_cnt);
qos_vd_mgr->valid = PS3_TRUE;
} else {
ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, qos_vd_ctx->vd_exclusive_cnt);
}
ps3_atomic_set(&qos_vd_mgr->share_cmd_used, 0);
}
qos_vd_ctx->workq_count = 1;
qos_vd_ctx->work_queues = (struct workqueue_struct **)ps3_vzalloc(instance,
(qos_vd_ctx->workq_count) * sizeof(struct workqueue_struct *));
INJECT_START(PS3_ERR_IJ_QOS_VD_INIT_FAIL_2, instance)
if (qos_vd_ctx->work_queues == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for work_queues\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _release_vd_mgr;
}
memset(qos_vd_ctx->work_queues, 0, (qos_vd_ctx->workq_count) * sizeof(struct workqueue_struct *));
for (i = 0; i < qos_vd_ctx->workq_count; i++) {
snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "qos_vd_wq_f%u_%u",
ps3_get_pci_function(instance->pdev), i);
qos_vd_ctx->work_queues[i] = create_singlethread_workqueue(workq_name);
INJECT_START(PS3_ERR_IJ_QOS_VD_INIT_FAIL_3, instance)
if (qos_vd_ctx->work_queues[i] == NULL) {
LOG_ERROR("qos vd workq:%u create failed\n", i);
ret = -PS3_FAILED;
goto _destroy_created_workqueue;
}
}
LOG_INFO("hno:%u: qos vd context init success\n", PS3_HOST(instance));
goto _out;
_destroy_created_workqueue:
for (j = 0; j < i; j++) {
destroy_workqueue(qos_vd_ctx->work_queues[j]);
qos_vd_ctx->work_queues[j] = NULL;
}
ps3_vfree(instance, qos_vd_ctx->work_queues);
qos_vd_ctx->work_queues = NULL;
_release_vd_mgr:
ps3_vfree(instance, qos_vd_ctx->qos_vd_mgrs);
qos_vd_ctx->qos_vd_mgrs = NULL;
_out:
return ret;
}
static void ps3_qos_vd_context_exit(struct ps3_instance *instance)
{
U16 i = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_qos_vd_context *qos_vd_ctx = NULL;
struct workqueue_struct *wq = NULL;
qos_vd_ctx = &instance->qos_context.vd_ctx;
if (qos_vd_ctx->qos_vd_mgrs != NULL) {
for (i = 1; i <= instance->qos_context.max_vd_count; i++) {
qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i];
cancel_work_sync(&qos_vd_mgr->resend_work);
}
ps3_vfree(instance, qos_vd_ctx->qos_vd_mgrs);
qos_vd_ctx->qos_vd_mgrs = NULL;
}
if (qos_vd_ctx->work_queues != NULL) {
for (i = 0; i < qos_vd_ctx->workq_count; i++) {
wq = qos_vd_ctx->work_queues[i];
flush_workqueue(wq);
destroy_workqueue(wq);
}
ps3_vfree(instance, qos_vd_ctx->work_queues);
qos_vd_ctx->work_queues = NULL;
}
}
static S32 ps3_qos_tg_context_init(struct ps3_instance *instance)
{
struct ps3_qos_tg_context *qos_tg_ctx = NULL;
S32 ret = PS3_SUCCESS;
U64 tfifo_depth = 0;
U16 i = 0;
struct qos_wait_queue *wait_q = NULL;
char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = {0};
U32 tag_exclusive = 0;
ps3_tfifo_depth_get(instance, &tfifo_depth);
INJECT_START(PS3_ERR_IJ_QOS_FORCE_TFIFO_ZERO_DEPTH, &tfifo_depth)
if (tfifo_depth == 0) {
ret = -PS3_FAILED;
LOG_ERROR("hno:%u:tfifo_depth is invalid func:%u\n",
PS3_HOST(instance),
ps3_get_pci_function(instance->pdev));
goto out;
}
qos_tg_ctx = &instance->qos_context.tg_ctx;
qos_tg_ctx->instance = instance;
qos_tg_ctx->high_pri_exclusive_cnt = QOS_HIGH_PRI_EXCLUSIVE_CMD_COUNT;
qos_tg_ctx->mgr_exclusive_cnt = g_ps3_qos_mgr_exclusive;
if (!ps3_ioc_multi_func_support(instance)) {
tag_exclusive = qos_tg_ctx->mgr_exclusive_cnt + qos_tg_ctx->high_pri_exclusive_cnt +
g_ps3_qos_jbod_exclusive + instance->ctrl_info.maxVdCount * g_ps3_qos_vd_exclusive;
} else {
qos_tg_ctx->high_pri_exclusive_cnt = qos_tg_ctx->high_pri_exclusive_cnt >> 1;
qos_tg_ctx->mgr_exclusive_cnt = qos_tg_ctx->mgr_exclusive_cnt >> 1;
tag_exclusive = qos_tg_ctx->high_pri_exclusive_cnt + qos_tg_ctx->mgr_exclusive_cnt;
if (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_1) {
tag_exclusive += (g_ps3_qos_jbod_exclusive + instance->ctrl_info.maxVdCount * g_ps3_qos_vd_exclusive);
}
}
if (tfifo_depth > tag_exclusive) {
qos_tg_ctx->share = tfifo_depth - tag_exclusive;
} else {
qos_tg_ctx->share = tag_exclusive;
}
ps3_atomic_set(&qos_tg_ctx->mgr_free_cnt, qos_tg_ctx->mgr_exclusive_cnt);
ps3_atomic_set(&qos_tg_ctx->share_free_cnt, qos_tg_ctx->share);
ps3_atomic_set(&qos_tg_ctx->mgr_share_used, 0);
ps3_spin_lock_init(&qos_tg_ctx->lock);
qos_tg_ctx->poll_vd_id = 1;
wait_q = &qos_tg_ctx->mgr_cmd_wait_q;
INIT_LIST_HEAD(&wait_q->wait_list);
qos_tg_ctx->vd_cmd_waitqs = (struct qos_wait_queue *)ps3_vzalloc(instance,
(instance->qos_context.max_vd_count + 1) * sizeof(struct qos_wait_queue));
INJECT_START(PS3_ERR_IJ_QOS_TAG_INIT_FAIL_1, qos_tg_ctx)
if (qos_tg_ctx->vd_cmd_waitqs == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_context\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto out;
}
memset(qos_tg_ctx->vd_cmd_waitqs, 0, (instance->qos_context.max_vd_count + 1) * sizeof(struct qos_wait_queue));
for (i = 1; i < instance->qos_context.max_vd_count + 1; i++) {
wait_q = &qos_tg_ctx->vd_cmd_waitqs[i];
INIT_LIST_HEAD(&wait_q->wait_list);
}
INIT_WORK(&qos_tg_ctx->resend_work, ps3_qos_tg_resend_work);
snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "qos_tag_wq_f%u", ps3_get_pci_function(instance->pdev));
qos_tg_ctx->work_queue = create_singlethread_workqueue(workq_name);
INJECT_START(PS3_ERR_IJ_QOS_TAG_INIT_FAIL_2, qos_tg_ctx)
if (qos_tg_ctx->work_queue == NULL) {
LOG_ERROR("qos tag workq:%u create failed\n", i);
ret = -PS3_FAILED;
goto release_vd_cmd_waitqs;
}
LOG_INFO("hno:%u func:%u: qos tg context init success tfifo:%llu\n",
PS3_HOST(instance), ps3_get_pci_function(instance->pdev), tfifo_depth);
goto out;
release_vd_cmd_waitqs:
ps3_vfree(instance, qos_tg_ctx->vd_cmd_waitqs);
qos_tg_ctx->vd_cmd_waitqs = NULL;
out:
return ret;
}
static void ps3_qos_tg_context_exit(struct ps3_instance *instance)
{
struct ps3_qos_tg_context *qos_tg_ctx = NULL;
qos_tg_ctx = &instance->qos_context.tg_ctx;
if (qos_tg_ctx->work_queue != NULL) {
cancel_work_sync(&qos_tg_ctx->resend_work);
flush_workqueue(qos_tg_ctx->work_queue);
destroy_workqueue(qos_tg_ctx->work_queue);
qos_tg_ctx->work_queue = NULL;
}
if (qos_tg_ctx->vd_cmd_waitqs != NULL) {
ps3_vfree(instance, qos_tg_ctx->vd_cmd_waitqs);
qos_tg_ctx->vd_cmd_waitqs = NULL;
}
}
S32 ps3_hba_qos_init(struct ps3_instance *instance)
{
S32 ret = PS3_SUCCESS;
ret = ps3_qos_tg_context_init(instance);
if (ret != PS3_SUCCESS) {
goto _out;
}
ret = ps3_qos_vd_context_init(instance);
if (ret != PS3_SUCCESS) {
goto _tg_ctx_exit;
}
ret = ps3_qos_pd_context_init(instance);
if (ret != PS3_SUCCESS) {
goto _vd_ctx_exit;
}
goto _out;
_vd_ctx_exit:
ps3_qos_vd_context_exit(instance);
_tg_ctx_exit:
ps3_qos_tg_context_exit(instance);
_out:
return ret;
}
void ps3_hba_qos_exit(struct ps3_instance *instance)
{
ps3_qos_tg_context_exit(instance);
ps3_qos_vd_context_exit(instance);
ps3_qos_pd_context_exit(instance);
LOG_INFO("hba qos exit. host_no:%u\n", PS3_HOST(instance));
}
static Bool ps3_qos_vd_member_insert(struct ps3_cmd *cmd)
{
U8 i = 0;
U16 disk_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct qos_wait_queue *waitq = NULL;
Bool in_queue = PS3_FALSE;
for (i = 0; i < cmd->target_pd_count; i++) {
disk_id = cmd->target_pd[i].flat_disk_id;
qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id);
ps3_atomic_inc(&qos_pd_mgr->processing_cnt);
INJECT_START(PS3_ERR_IJ_QOS_VD_MEMBER_CLEARING, qos_pd_mgr);
if (unlikely(qos_pd_mgr->clearing)) {
LOG_DEBUG("qos vd member is clearing .host_no:%u:t_id:0x%llx CFID:%u did[%u %u]\n",
PS3_HOST(qos_pd_mgr->instance), cmd->trace_id, cmd->index,
qos_pd_mgr->disk_id, qos_pd_mgr->vd_id);
ps3_atomic_dec(&qos_pd_mgr->processing_cnt);
} else {
in_queue = PS3_TRUE;
break;
}
}
if (in_queue) {
waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd);
ps3_qos_pd_in_q(cmd, waitq, i);
ps3_atomic_dec(&qos_pd_mgr->processing_cnt);
LOG_DEBUG("insert qos pd quota waitq.host_no:%u:t_id:0x%llx"
" CFID:%u waitq[%u,%u] did[%u %u]\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index,
waitq->id, waitq->count, qos_pd_mgr->disk_id, i);
}
return in_queue;
}
static Bool ps3_qos_pd_quota_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 disk_id = 0;
U16 i = 0;
struct scsi_device *sdev = NULL;
S32 pd_quota = 0;
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_PD_PRO);
if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) {
ps3_qos_cmd_member_pd_calc(cmd);
for (i = 0; i < cmd->target_pd_count; i++) {
disk_id = cmd->target_pd[i].flat_disk_id;
qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id);
if (ps3_qos_pd_quota_check(qos_pd_mgr, cmd)) {
cmd->target_pd[i].get_quota = PS3_TRUE;
can_get = PS3_TRUE;
break;
}
}
if (cmd->target_pd_count == 0) {
can_get = PS3_TRUE;
}
if (!can_get) {
if (!ps3_qos_vd_member_insert(cmd)) {
can_get = PS3_TRUE;
}
}
} else {
disk_id = cmd->io_attr.disk_id;
qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id);
if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) {
pd_quota = qos_pd_mgr->direct_quota;
} else {
pd_quota = qos_pd_mgr->pd_quota;
}
sdev = cmd->scmd->device;
if (pd_quota >= sdev->queue_depth) {
can_get = PS3_TRUE;
} else {
ps3_qos_cmd_member_pd_calc(cmd);
can_get = ps3_qos_pd_quota_req(qos_pd_mgr, cmd, 0);
}
}
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_PRO);
return can_get;
}
static Bool ps3_qos_vd_quota_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_TRUE;
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_VD_PRO);
if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD){
can_get = ps3_qos_vd_quota_check(cmd);
}
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_VD_PRO);
return can_get;
}
static inline Bool ps3_qos_dev_valid(struct ps3_cmd *cmd)
{
Bool valid = PS3_TRUE;
struct ps3_scsi_priv_data* priv_data = NULL;
priv_data = scsi_device_private_data(cmd->scmd);
if (priv_data->dev_deling) {
LOG_INFO_LIM("qos dev is deling. host_no:%u chanel:%u id:%u\n",
PS3_HOST(cmd->instance),
PS3_SDEV_CHANNEL(cmd->scmd->device),
PS3_SDEV_TARGET(cmd->scmd->device));
valid = PS3_FALSE;
}
return valid;
}
static S32 ps3_qos_pre_check(struct ps3_instance *instance, struct ps3_cmd *cmd)
{
S32 ret = PS3_SUCCESS;
INJECT_START(PS3_ERR_IJ_FORCE_TASK_MGR_BUSY, instance)
if (instance->task_manager_host_busy) {
LOG_INFO_LIM("hno:%u task_manager_host_busy\n",
PS3_HOST(instance));
ret = -PS3_RETRY;
goto l_out;
}
INJECT_START(PS3_ERR_IJ_FORCE_INS_UNLOAD, instance)
if (instance->is_probe_finish && !instance->state_machine.is_load) {
LOG_INFO_LIM("hno:%u instance state not is_load\n", PS3_HOST(instance));
ret = -PS3_FAILED;
goto l_out;
}
if (!ps3_qos_dev_valid(cmd)){
ret = -PS3_FAILED;
}
INJECT_START(PS3_ERR_IJ_FORCE_INSTANCE_UNNORMAL, instance)
if (!ps3_is_instance_state_normal(instance, PS3_TRUE)) {
LOG_INFO_LIM("hno:%u instance state is not normal\n", PS3_HOST(instance));
ret = -PS3_RECOVERED;
goto l_out;
}
l_out:
return ret;
}
Bool ps3_hba_qos_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) {
can_get = ps3_qos_pd_quota_decision(cmd);
if (!can_get ||
cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) {
goto _out;
}
can_get = ps3_qos_vd_quota_decision(cmd);
if (!can_get) {
goto _out;
}
}
can_get = ps3_qos_tg_decision(cmd);
_out:
return can_get;
}
void ps3_hba_qos_waitq_notify(struct ps3_instance *instance)
{
if (!ps3_qos_tg_notify(instance)) {
if (!ps3_qos_vd_notify(instance)) {
ps3_qos_pd_notify(instance);
}
}
}
static Bool ps3_pd_quota_waiq_abort(struct ps3_cmd *cmd)
{
ULong lock_flag_pd = 0;
U16 disk_id = 0;
Bool found = PS3_FALSE;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct qos_wait_queue *waitq = NULL;
U16 index = 0;
for (index = cmd->first_over_quota_pd_idx;
index < cmd->target_pd_count; index++) {
disk_id = cmd->target_pd[index].flat_disk_id;
qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id);
waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd);
ps3_spin_lock_irqsave(waitq->rsc_lock, &lock_flag_pd);
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD &&
index == cmd->first_over_quota_pd_idx) {
list_del(&cmd->qos_list);
waitq->count--;
(*waitq->total_waited_cnt)--;
ps3_spin_unlock_irqrestore(waitq->rsc_lock, lock_flag_pd);
found = PS3_TRUE;
break;
}
ps3_spin_unlock_irqrestore(waitq->rsc_lock, lock_flag_pd);
LOG_INFO("abort pd waitq. t_id:0x%llx did[%u,%u] qid:%u found:%u\n",
cmd->trace_id, index, disk_id, waitq->id, found);
}
return found;
}
static bool ps3_vd_quota_waiq_abort(struct ps3_cmd *aborted_cmd)
{
ULong flag = 0;
Bool found = PS3_FALSE;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
qos_vd_mgr = ps3_qos_vd_mgr_get(aborted_cmd);
ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag);
if (aborted_cmd->qos_waitq_flag == PS3_QOS_CMD_IN_VD) {
list_del(&aborted_cmd->qos_list);
qos_vd_mgr->vd_quota_wait_q.count--;
found = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag);
return found;
}
bool ps3_cmd_waitq_abort(struct ps3_cmd *aborted_cmd)
{
ULong lock_flag_mgr = 0;
struct ps3_instance *instance = NULL;
Bool found = PS3_FALSE;
struct qos_wait_queue *cmd_wait_q = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = NULL;
instance = aborted_cmd->instance;
qos_tg_ctx = &instance->qos_context.tg_ctx;
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &lock_flag_mgr);
if (aborted_cmd->qos_waitq_flag == PS3_QOS_CMD_IN_FRAME) {
cmd_wait_q = ps3_qos_cmd_waitq_get(qos_tg_ctx, aborted_cmd);
list_del(&aborted_cmd->qos_list);
cmd_wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
found = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, lock_flag_mgr);
return found;
}
static Bool ps3_hba_qos_waitq_abort(struct ps3_cmd *cmd)
{
Bool found = PS3_FALSE;
if (cmd->qos_waitq_flag == 0) {
goto out;
}
if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) {
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD) {
found = ps3_pd_quota_waiq_abort(cmd);
if (found) {
goto out;
}
}
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_VD) {
found = ps3_vd_quota_waiq_abort(cmd);
if (found) {
ps3_qos_update_pd_quota(cmd);
goto out;
}
}
}
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_FRAME) {
found = ps3_cmd_waitq_abort(cmd);
if (found) {
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
}
}
out:
return found;
}
static void ps3_vd_cmd_waitq_clean(struct ps3_instance *instance, U16 disk_id, int result)
{
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
struct qos_wait_queue *wait_q = &qos_tg_ctx->vd_cmd_waitqs[disk_id];
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
if (wait_q->count > 0) {
list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, qos_list) {
LOG_DEBUG("qos clean vd cmd waitq. hno:%u t_id:0x%llx cmd:%d vd_id:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, disk_id);
list_del(&cmd->qos_list);
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, result, PS3_FALSE);
}
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
}
static void ps3_jbod_cmd_waitq_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, int result)
{
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
struct qos_wait_queue *wait_q = &qos_tg_ctx->vd_cmd_waitqs
[instance->qos_context.max_vd_count];
U16 disk_id = priv_data->disk_pos.diskDev.ps3Dev.virtDiskID;
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
if (wait_q->count > 0) {
list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, qos_list) {
if (scsi_device_private_data(cmd->scmd) == priv_data) {
LOG_DEBUG("qos clean jbod cmd waitq. hno:%u t_id:0x%llx cmd:%d dev_t:%u diskid:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index,
priv_data->dev_type, disk_id);
list_del(&cmd->qos_list);
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, result, PS3_FALSE);
}
}
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
}
static void ps3_mgr_cmd_waitq_clean(struct ps3_instance *instance, struct ps3_scsi_priv_data *priv_data, int result)
{
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
struct qos_wait_queue *wait_q = &qos_tg_ctx->mgr_cmd_wait_q;
U16 disk_id = priv_data->disk_pos.diskDev.ps3Dev.virtDiskID;
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
if (wait_q->count > 0) {
list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, qos_list) {
if (cmd->scmd && scsi_device_private_data(cmd->scmd) == priv_data) {
LOG_DEBUG("qos clean mgr cmd waitq. hno:%u t_id:0x%llx cmd:%d dev_t:%u diskid:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index,
priv_data->dev_type, disk_id);
list_del(&cmd->qos_list);
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, result, PS3_FALSE);
}
}
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
}
static void ps3_hba_qos_vd_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
U16 vd_id = 0;
U16 pd_id = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), PS3_VDID(&priv_data->disk_pos));
qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, PS3_VDID(&priv_data->disk_pos));
for (pd_id = 1; pd_id <= instance->qos_context.max_pd_count; pd_id++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1 &&
qos_pd_mgr->vd_id == vd_id) {
ps3_pd_quota_waitq_clean(qos_pd_mgr, vd_id, resp_status);
cancel_work_sync(&qos_pd_mgr->resend_work);
}
}
ps3_vd_quota_waitq_clean(qos_vd_mgr, NULL, resp_status);
cancel_work_sync(&qos_vd_mgr->resend_work);
ps3_vd_cmd_waitq_clean(instance, vd_id, resp_status);
ps3_mgr_cmd_waitq_clean(instance, priv_data, resp_status);
LOG_FILE_INFO("qos clean vd. host_no:%u type:%u disk_id:%u\n",
PS3_HOST(instance), priv_data->dev_type, vd_id);
}
#define PS3_QOS_JBOD_VD_MGR(instance) \
(&instance->qos_context.vd_ctx.qos_vd_mgrs[instance->qos_context.max_vd_count])
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,9,0) && LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0))
void ps3_linx80_vd_member_change(struct ps3_instance *instance, struct ps3_pd_entry *pd_entry)
{
U16 pd_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
if (!PS3_QOS_INITED(instance)) {
goto _out;
}
pd_id = PS3_PDID(&pd_entry->disk_pos);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
if (ps3_atomic_read(&qos_pd_mgr->valid) != PS3_TRUE) {
goto _out;
}
ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, PS3_STATUS_VD_MEMBER_OFFLINE);
cancel_work_sync(&qos_pd_mgr->resend_work);
LOG_INFO("linx80 update pd qos rsc. host_no:%u pd_id:%u dev_type:%u\n",
PS3_HOST(instance), pd_id, pd_entry->dev_type);
_out:
return;
}
#endif
static void ps3_hba_qos_pd_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
U16 disk_id = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
disk_id = PS3_PDID(&priv_data->disk_pos);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id);
ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, resp_status);
cancel_work_sync(&qos_pd_mgr->resend_work);
qos_vd_mgr = PS3_QOS_JBOD_VD_MGR(instance);
ps3_vd_quota_waitq_clean(qos_vd_mgr, priv_data, resp_status);
ps3_jbod_cmd_waitq_clean(instance, priv_data, resp_status);
ps3_mgr_cmd_waitq_clean(instance, priv_data, resp_status);
LOG_FILE_INFO("qos clean pd. host_no:%u diskid:%u\n",
PS3_HOST(instance), disk_id);
}
static void ps3_qos_wait_io_end(struct ps3_instance *instance)
{
U32 i = 0;
struct ps3_cmd *cmd = NULL;
LOG_DEBUG("host_no:%u wait cmd in qos flighting start\n",
PS3_HOST(instance));
for (i = 0; i < instance->cmd_context.max_scsi_cmd_count; i++) {
cmd = instance->cmd_context.cmd_buf[i];
while (cmd->qos_processing) {
ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS);
}
}
LOG_DEBUG("host_no:%u wait cmd in qos flighting end\n",
PS3_HOST(instance));
}
void ps3_qos_device_clean(struct ps3_instance *instance, struct ps3_scsi_priv_data *priv_data,
S32 resp_status)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
ps3_qos_wait_io_end(instance);
if (priv_data->dev_type == PS3_DEV_TYPE_VD) {
instance->qos_context.opts.qos_vd_clean(instance, priv_data, resp_status);
} else {
instance->qos_context.opts.qos_pd_clean(instance, priv_data, resp_status);
}
}
static inline void ps3_qos_dev_end(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data)
{
U32 i = 0;
struct ps3_cmd *cmd = NULL;
struct scsi_cmnd *scmd = NULL;
struct scsi_device *sdev = NULL;
U32 softChan = PS3_CHANNEL(&priv_data->disk_pos);
U32 devID = PS3_TARGET(&priv_data->disk_pos);
LOG_DEBUG("host_no:%u dev wait cmd in qos flighting start\n",
PS3_HOST(instance));
for (i = 0; i < instance->cmd_context.max_scsi_cmd_count; i++) {
cmd = instance->cmd_context.cmd_buf[i];
if (likely(!cmd->qos_processing)) {
continue;
}
scmd = cmd->scmd;
if (scmd == NULL) {
continue;
}
sdev = scmd->device;
if (sdev == NULL) {
continue;
}
if (PS3_SDEV_CHANNEL(sdev) == softChan &&
PS3_SDEV_TARGET(sdev) == devID) {
LOG_DEBUG("cmd is flighting. host_no:%u CFID:%u\n",
PS3_HOST(instance), cmd->index);
while (cmd->qos_processing) {
ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS);
}
}
}
LOG_DEBUG("host_no:%u dev wait cmd in qos flighting end\n",
PS3_HOST(instance));
}
void ps3_qos_disk_del(struct ps3_instance *instance, struct ps3_scsi_priv_data *priv_data)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 disk_id = 0;
S32 resp_status = PS3_STATUS_DEVICE_NOT_FOUND;
if (!PS3_QOS_INITED(instance)) {
return;
}
priv_data->dev_deling = PS3_TRUE;
wmb();
disk_id = PS3_PDID(&priv_data->disk_pos);
LOG_FILE_INFO("qos disk del. host_no:%u dev_t:%u diskid:%u\n",
PS3_HOST(instance), priv_data->dev_type, disk_id);
if (priv_data->dev_type == PS3_DEV_TYPE_VD) {
ps3_qos_dev_end(instance, priv_data);
instance->qos_context.opts.qos_vd_clean(instance, priv_data, resp_status);
} else {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id);
ps3_qos_dev_end(instance, priv_data);
if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) {
resp_status = PS3_STATUS_VD_MEMBER_OFFLINE;
qos_pd_mgr->clearing = PS3_TRUE;
wmb();
LOG_INFO("host_no:%u wait cmd in qos vd member flighting count:%d did:%u\n",
PS3_HOST(instance),
ps3_atomic_read(&qos_pd_mgr->processing_cnt), disk_id);
while (ps3_atomic_read(&qos_pd_mgr->processing_cnt) > 0) {
ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS);
}
}
instance->qos_context.opts.qos_pd_clean(instance, priv_data, resp_status);
}
}
void ps3_qos_vd_member_del(struct ps3_instance *instance, struct PS3DiskDevPos *dev_pos)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 disk_id = 0;
disk_id = PS3_PDID(dev_pos);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id);
LOG_INFO("qos disk del. host_no:%u did:%u vid:%u\n",
PS3_HOST(instance), disk_id, qos_pd_mgr->vd_id);
if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) {
ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, PS3_STATUS_VD_MEMBER_OFFLINE);
}
}
void ps3_hba_qos_waitq_clear_all(struct ps3_instance *instance, S32 resp_status)
{
ULong flag = 0;
U16 i = 0;
struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct qos_wait_queue *wait_q = NULL;
struct ps3_cmd *cmd = NULL;
for (i = 1; i <= instance->qos_context.max_pd_count; i++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) {
ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status);
cancel_work_sync(&qos_pd_mgr->resend_work);
}
}
for (i = 1; i <= instance->qos_context.max_vd_count; i++) {
qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i];
if (qos_vd_mgr->valid) {
ps3_vd_quota_waitq_clean(qos_vd_mgr, NULL, resp_status);
cancel_work_sync(&qos_vd_mgr->resend_work);
}
}
ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag);
for (i = 1; i <= instance->qos_context.max_vd_count; i++) {
wait_q = &qos_tg_ctx->vd_cmd_waitqs[i];
while (wait_q->count > 0) {
cmd = list_first_entry(&wait_q->wait_list, struct ps3_cmd, qos_list);
LOG_DEBUG("qos clear tg vd waitq. hno:%u qid:%u t_id:0x%llx cmd:%d\n",
PS3_HOST(instance), i, cmd->trace_id, cmd->index);
list_del(&cmd->qos_list);
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
ps3_qos_update_vd_quota(cmd);
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
}
wait_q = &qos_tg_ctx->mgr_cmd_wait_q;
while (wait_q->count > 0) {
cmd = list_first_entry(&wait_q->wait_list, struct ps3_cmd, qos_list);
LOG_DEBUG("qos clear tg mgr waitq. hno:%u t_id:0x%llx cmd:%d\n",
PS3_HOST(instance), cmd->trace_id, cmd->index);
list_del(&cmd->qos_list);
wait_q->count--;
qos_tg_ctx->total_wait_cmd_cnt--;
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag);
cancel_work_sync(&qos_tg_ctx->resend_work);
LOG_INFO("host_no:%u:clear all qos waitq\n", PS3_HOST(instance));
}
static inline U16 ps3_qos_pd_workq_id_get(struct ps3_qos_pd_context *qos_pd_ctx)
{
return ps3_atomic_inc_return(&qos_pd_ctx->workq_id_cnt) % qos_pd_ctx->workq_count;
}
void ps3_qos_pd_rsc_init(struct ps3_qos_pd_mgr *qos_pd_mgr, struct ps3_pd_entry *pd_entry)
{
struct ps3_qos_pd_context *pd_ctx = NULL;
struct PS3QosInfo *qos_cfg_info = NULL;
qos_cfg_info = &qos_pd_mgr->instance->ctrl_info.qosInfo;
qos_pd_mgr->pd_quota = pd_entry->normal_quota;
qos_pd_mgr->direct_quota = pd_entry->direct_quota;
qos_pd_mgr->dev_type = pd_entry->dev_type;
switch (pd_entry->dev_type) {
case PS3_DEV_TYPE_SAS_HDD:
if (qos_pd_mgr->pd_quota == 0) {
qos_pd_mgr->pd_quota = qos_cfg_info->sasHddQuota > 0 ?
qos_cfg_info->sasHddQuota : g_ps3_qos_sas_pd_quota;
}
qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10);
break;
case PS3_DEV_TYPE_SAS_SSD:
if (qos_pd_mgr->pd_quota == 0) {
qos_pd_mgr->pd_quota = qos_cfg_info->sasSsdQuota > 0 ?
qos_cfg_info->sasSsdQuota : g_ps3_qos_sas_pd_quota;
}
qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10);
break;
case PS3_DEV_TYPE_SATA_HDD:
if (qos_pd_mgr->pd_quota == 0) {
qos_pd_mgr->pd_quota = qos_cfg_info->sataHddQuota > 0 ?
qos_cfg_info->sataHddQuota : g_ps3_qos_hdd_pd_quota;
}
qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10);
break;
case PS3_DEV_TYPE_SATA_SSD:
if (qos_pd_mgr->pd_quota == 0) {
qos_pd_mgr->pd_quota = qos_cfg_info->sataSsdQuota > 0 ?
qos_cfg_info->sataSsdQuota : g_ps3_qos_ssd_pd_quota;
}
qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10);
break;
case PS3_DEV_TYPE_NVME_SSD:
pd_ctx = &qos_pd_mgr->instance->qos_context.pd_ctx;
if (qos_pd_mgr->pd_quota == 0) {
qos_pd_mgr->pd_quota = qos_cfg_info->nvmeNormalQuota > 0 ?
qos_cfg_info->nvmeNormalQuota : pd_ctx->nvme_normal_quota;
}
if (qos_pd_mgr->direct_quota == 0) {
qos_pd_mgr->direct_quota = qos_cfg_info->nvmeDirectQuota > 0 ?
qos_cfg_info->nvmeDirectQuota : g_ps3_qos_nvme_pd_quota;
}
qos_pd_mgr->pd_init_quota = qos_pd_mgr->direct_quota;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->direct_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->direct_quota - (qos_pd_mgr->direct_quota / 10);
break;
}
LOG_DEBUG("qos pd rsc init. hno:%u type:%u normal_quota:%u direct_quota:%u\n",
PS3_HOST(qos_pd_mgr->instance), pd_entry->dev_type,
pd_entry->normal_quota, pd_entry->direct_quota);
}
void ps3_qos_adjust_pd_rsc(struct scsi_device *sdev,
struct ps3_instance *instance, S32 reason)
{
S32 *quota = NULL;
ULong flag = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
if (!PS3_QOS_INITED(instance) ||
(PS3_SDEV_PRI_DATA(sdev) == NULL) ||
(PS3_SDEV_PRI_DATA(sdev)->dev_type == PS3_DEV_TYPE_VD)) {
goto l_out;
}
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, PS3_PDID(&PS3_SDEV_PRI_DATA(sdev)->disk_pos));
switch (PS3_SDEV_PRI_DATA(sdev)->dev_type) {
case PS3_DEV_TYPE_SAS_HDD:
case PS3_DEV_TYPE_SAS_SSD:
case PS3_DEV_TYPE_SATA_HDD:
case PS3_DEV_TYPE_SATA_SSD:
quota = &qos_pd_mgr->pd_quota;
break;
case PS3_DEV_TYPE_NVME_SSD:
quota = &qos_pd_mgr->direct_quota;
break;
default:
goto l_out;
}
if ((*quota > sdev->queue_depth && reason == PS3_QOS_QUOTA_ADJUST_QFULL) ||
((*quota == qos_pd_mgr->adjust_max_quota ||
time_after(sdev->last_queue_full_time + sdev->queue_ramp_up_period, jiffies) ||
time_after(sdev->last_queue_ramp_up + sdev->queue_ramp_up_period, jiffies))
&& reason == PS3_QOS_QUOTA_ADJUST_UP)) {
goto l_out;
}
if (reason == PS3_QOS_QUOTA_ADJUST_UP) {
ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag);
if (*quota < qos_pd_mgr->adjust_max_quota) {
(*quota)++;
}
} else if (reason == PS3_QOS_QUOTA_ADJUST_QFULL) {
ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag);
if (*quota > qos_pd_mgr->adjust_min_quota) {
(*quota)--;
}
} else if (reason == PS3_QOS_QUOTA_ADJUST_DEFULAT) {
ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag);
*quota = qos_pd_mgr->adjust_max_quota;
} else {
goto l_out;
}
ps3_spin_unlock_irqrestore(&qos_pd_mgr->adjust_quota_lock, flag);
LOG_INFO_IN_IRQ(instance,"hno:%u dev[%u:%u] qos quota change to [%d] reason [%d]\n",
PS3_HOST(instance), sdev->channel, sdev->id, *quota, reason);
l_out:
return;
}
struct ps3_qos_pd_mgr* ps3_qos_pd_mgr_init(struct ps3_instance *instance, struct ps3_pd_entry *pd_entry)
{
U16 pd_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
if (!PS3_QOS_INITED(instance)) {
goto _out;
}
pd_id = PS3_PDID(&pd_entry->disk_pos);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
if ((ps3_atomic_add_unless(&qos_pd_mgr->valid, 1, 1) != 0) ||
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,9,0) && LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0))
((ps3_sas_is_support_smp(instance)) && (!ps3_check_pd_is_vd_member(pd_entry->config_flag))) ||
(pd_entry->config_flag == MIC_PD_STATE_JBOD)) {
#else
(pd_entry->config_flag == MIC_PD_STATE_JBOD)) {
#endif
if (pd_entry->config_flag == MIC_PD_STATE_JBOD) {
ps3_qos_vd_member_del(instance, &pd_entry->disk_pos);
}
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
qos_pd_mgr->disk_id = pd_id;
qos_pd_mgr->vd_id = 0;
qos_pd_mgr->workq_id = ps3_qos_pd_workq_id_get(&instance->qos_context.pd_ctx);
qos_pd_mgr->clearing = PS3_FALSE;
LOG_INFO_IN_IRQ(instance, "host_no:%u pd_id:%u dev_type:%u device qos init\n",
PS3_HOST(instance), pd_id, qos_pd_mgr->dev_type);
}
_out:
return qos_pd_mgr;
}
void ps3_qos_pd_mgr_reset(struct ps3_instance *instance, U16 pd_id)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
if (!PS3_QOS_INITED(instance)) {
return;
}
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
qos_pd_mgr->clearing = PS3_FALSE;
}
void ps3_qos_vd_attr_change(struct ps3_instance *instance,
struct PS3VDEntry *vd_entry_old, struct PS3VDEntry *vd_entry)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 change_type = PS3_QOS_VD_TYPE_CHANGE_INVALID;
U16 i = 0;
U16 j = 0;
U16 pd_id = 0;
if (!PS3_QOS_INITED(instance) || vd_entry->isNvme) {
goto l_out;
}
if (!vd_entry->isSsd) {
change_type = PS3_QOS_VD_TYPE_CHANGE_TO_HDD;
} else {
change_type = PS3_QOS_VD_TYPE_CHANGE_TO_SSD;
}
for (i = 0; i < vd_entry->spanCount; i++) {
for (j = 0; j < vd_entry->span[i].spanPdNum; j++) {
pd_id = vd_entry->span[i].extent[j].phyDiskID.ps3Dev.phyDiskID;
if (pd_id <= PS3_MAX_PD_COUNT(instance)) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
switch (change_type) {
case PS3_QOS_VD_TYPE_CHANGE_TO_SSD:
qos_pd_mgr->pd_quota = qos_pd_mgr->adjust_max_quota;
break;
case PS3_QOS_VD_TYPE_CHANGE_TO_HDD:
if (vd_entry_old != NULL) {
qos_pd_mgr->pd_quota = instance->cmd_context.max_scsi_cmd_count;
} else {
qos_pd_mgr->pd_quota = qos_pd_mgr->adjust_max_quota;
}
break;
default:
break;
}
}
}
}
LOG_INFO_IN_IRQ(instance, "host_no:%u qos vd[%u:%u:%u] attr change:%u complete\n",
PS3_HOST(instance), PS3_CHANNEL(&vd_entry->diskPos),
PS3_TARGET(&vd_entry->diskPos), PS3_VDID(&vd_entry->diskPos), change_type);
l_out:
return;
}
void ps3_qos_vd_member_change(struct ps3_instance *instance, struct ps3_pd_entry *pd_entry,
struct scsi_device *sdev, Bool is_vd_member)
{
U16 pd_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct ps3_scsi_priv_data *priv_data = NULL;
if (!PS3_QOS_INITED(instance)) {
goto _out;
}
pd_id = PS3_PDID(&pd_entry->disk_pos);
ps3_mutex_lock(&instance->dev_context.dev_priv_lock);
priv_data = PS3_SDEV_PRI_DATA(sdev);
if (priv_data != NULL) {
if (is_vd_member) {
ps3_hba_qos_pd_clean(instance, priv_data, PS3_STATUS_DEVICE_NOT_FOUND);
ps3_mutex_unlock(&instance->dev_context.dev_priv_lock);
} else {
ps3_hba_qos_pd_clean(instance, priv_data, PS3_STATUS_VD_MEMBER_OFFLINE);
ps3_mutex_unlock(&instance->dev_context.dev_priv_lock);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
qos_pd_mgr->vd_id = 0;
}
} else {
ps3_mutex_unlock(&instance->dev_context.dev_priv_lock);
}
LOG_INFO("update pd qos rsc. host_no:%u pd_id:%u dev_type:%u is_vd_member:%u\n",
PS3_HOST(instance), pd_id, pd_entry->dev_type, is_vd_member);
_out:
return;
}
void ps3_hba_qos_vd_init(struct ps3_instance *instance, struct PS3VDEntry *vd_entry)
{
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
U16 vd_id = 0;
vd_id = PS3_VDID(&vd_entry->diskPos);
qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, vd_id);
ps3_atomic_set(&qos_vd_mgr->vd_quota, g_ps3_qos_hdd_pd_quota * vd_entry->physDrvCnt);
qos_vd_mgr->vd_entry = vd_entry;
qos_vd_mgr->valid = PS3_TRUE;
}
static void ps3_qos_r1x_member_init(struct ps3_qos_pd_mgr *qos_pd_mgr,
struct ps3_pd_entry *pd_entry, U16 judge_type)
{
switch (judge_type) {
case PS3_IS_SSD_ODD_R1X_VD:
if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) {
qos_pd_mgr->direct_quota = qos_pd_mgr->pd_init_quota >> 1;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->direct_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->direct_quota - (qos_pd_mgr->direct_quota / 10);
} else {
qos_pd_mgr->pd_quota = qos_pd_mgr->pd_init_quota >> 1;
qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota;
qos_pd_mgr->adjust_min_quota = qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10);
}
break;
case PS3_IS_SSD_EVEN_R1X_VD:
case PS3_IS_VALID_R1X_VD:
if (qos_pd_mgr->vd_id > 0) {
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
}
break;
case PS3_IS_HDD_R1X_VD:
default:
break;
}
}
void ps3_qos_vd_init(struct ps3_instance *instance, struct PS3VDEntry *vd_entry)
{
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
U16 vd_id = 0;
U16 pd_id = 0;
struct ps3_pd_entry *pd_entry = NULL;
U16 i, j = 0;
U16 judge_type = PS3_IS_VALID_R1X_VD;
if(!PS3_QOS_INITED(instance)) {
return;
}
vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance),
PS3_VDID(&vd_entry->diskPos));
judge_type = ps3_odd_r1x_judge(vd_entry);
for (i = 0; i < vd_entry->spanCount; i++) {
for (j = 0; j < vd_entry->span[i].spanPdNum; j++) {
pd_id = vd_entry->span[i].extent[j].phyDiskID.ps3Dev.phyDiskID;
if (pd_id <= PS3_MAX_PD_COUNT(instance)) {
pd_entry = ps3_dev_mgr_lookup_pd_info_by_id(instance, pd_id);
if (pd_entry) {
qos_pd_mgr = ps3_qos_pd_mgr_init(instance, pd_entry);
if (unlikely(qos_pd_mgr->dev_type != pd_entry->dev_type)) {
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
if (!vd_entry->isSsd) {
qos_pd_mgr->pd_quota = instance->cmd_context.max_scsi_cmd_count;
}
}
ps3_qos_r1x_member_init(qos_pd_mgr, pd_entry, judge_type);
if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) {
if (qos_pd_mgr->pd_quota > qos_pd_mgr->direct_quota) {
qos_pd_mgr->pd_quota = qos_pd_mgr->direct_quota;
}
}
qos_pd_mgr->vd_id = vd_id;
}
}
}
}
if (instance->qos_context.opts.qos_vd_init) {
instance->qos_context.opts.qos_vd_init(instance, vd_entry);
}
LOG_INFO_IN_IRQ(instance, "host_no:%u disk_pos[%u:%u:%u] device qos init\n",
PS3_HOST(instance), PS3_CHANNEL(&vd_entry->diskPos),
PS3_TARGET(&vd_entry->diskPos), vd_id);
}
void ps3_hba_qos_vd_reset(struct ps3_instance *instance, U16 disk_id)
{
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, disk_id);
qos_vd_mgr->valid = PS3_FALSE;
}
void ps3_qos_vd_reset(struct ps3_instance *instance, U16 disk_id)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
if (instance->qos_context.opts.qos_vd_reset) {
instance->qos_context.opts.qos_vd_reset(instance, disk_id);
}
}
static void ps3_qos_pd_notify_timeout(struct ps3_instance *instance)
{
struct ps3_qos_pd_context *qos_pd_ctx = NULL;
U16 i = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
ULong timeout_jiffies = 0;
qos_pd_ctx = &instance->qos_context.pd_ctx;
for (i = 1; i <= instance->qos_context.max_pd_count; i++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i);
timeout_jiffies = qos_pd_mgr->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ;
if (time_after(jiffies, timeout_jiffies)) {
if (ps3_qos_single_pd_notify(qos_pd_ctx, qos_pd_mgr)) {
LOG_INFO("awake qos pd quota waitq by poll. host_no:%u vid:%u pid:%u\n",
PS3_HOST(instance), qos_pd_mgr->vd_id, qos_pd_mgr->disk_id);
}
}
}
}
static Bool ps3_qos_vd_notify_timeout(struct ps3_instance *instance)
{
struct ps3_qos_vd_context *qos_vd_ctx = NULL;
U16 i = 0;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
ULong timeout_jiffies = 0;
Bool notified = PS3_FALSE;
for (i = 1; i <= instance->qos_context.max_vd_count; i++) {
qos_vd_ctx = &instance->qos_context.vd_ctx;
qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i];
timeout_jiffies = qos_vd_mgr->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ;
if (time_after(jiffies, timeout_jiffies)) {
if (ps3_qos_single_vd_notify(qos_vd_ctx, qos_vd_mgr)) {
notified = PS3_TRUE;
LOG_INFO("awake qos vd quota waitq by poll. host_no:%u vid:%u\n",
PS3_HOST(instance), qos_vd_mgr->id);
}
}
}
return notified;
}
static Bool ps3_qos_tg_notify_timeout(struct ps3_instance *instance)
{
struct ps3_qos_tg_context *qos_tg_ctx = NULL;
Bool notified = PS3_FALSE;
ULong timeout_jiffies = 0;
qos_tg_ctx = &instance->qos_context.tg_ctx;
timeout_jiffies = qos_tg_ctx->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ;
if (qos_tg_ctx->total_wait_cmd_cnt &&
ps3_qos_tag_rsc_available(instance) &&
time_after(jiffies, timeout_jiffies)) {
queue_work(qos_tg_ctx->work_queue, &qos_tg_ctx->resend_work);
notified = PS3_TRUE;
LOG_INFO("awake qos cmd waitq by poll. host_no:%u\n",
PS3_HOST(instance));
}
return notified;
}
void ps3_hba_qos_waitq_poll(struct ps3_instance *instance)
{
if (!ps3_qos_tg_notify_timeout(instance)) {
if (!ps3_qos_vd_notify_timeout(instance)) {
ps3_qos_pd_notify_timeout(instance);
}
}
}
void ps3_qos_waitq_poll(struct ps3_instance *instance)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
if (++instance->qos_context.poll_count == PS3_QOS_POLL_INTERVAL) {
instance->qos_context.poll_count = 0;
instance->qos_context.opts.qos_waitq_poll(instance);
}
}
static void ps3_hba_qos_reset(struct ps3_instance *instance)
{
struct ps3_qos_context *qos_ctx = &instance->qos_context;
struct ps3_qos_vd_mgr *qos_vd_mgr = NULL;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct ps3_qos_tg_context *qos_tg_ctx = NULL;
U16 i = 0;
struct ps3_pd_entry *pd_entry = NULL;
qos_tg_ctx = &instance->qos_context.tg_ctx;
ps3_atomic_set(&qos_tg_ctx->mgr_free_cnt, qos_tg_ctx->mgr_exclusive_cnt);
ps3_atomic_set(&qos_tg_ctx->mgr_share_used, 0);
ps3_atomic_set(&qos_tg_ctx->share_free_cnt, qos_tg_ctx->share);
for (i = 1; i <= qos_ctx->max_vd_count; i++) {
qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i];
if (i == qos_ctx->max_vd_count) {
ps3_atomic_set(&qos_vd_mgr->vd_quota, g_ps3_qos_vd_quota);
ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, g_ps3_qos_jbod_exclusive);
} else {
ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, g_ps3_qos_vd_exclusive);
}
ps3_atomic_set(&qos_vd_mgr->share_cmd_used, 0);
}
qos_ctx->pd_ctx.nvme_normal_quota = g_ps3_qos_hba_nvme_normal_quota;
for (i = 1; i <= qos_ctx->max_pd_count; i++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) {
pd_entry = ps3_dev_mgr_lookup_pd_info_by_id(instance, i);
if (pd_entry != NULL) {
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
}
}
}
}
void ps3_qos_close(struct ps3_instance *instance)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
ps3_qos_wait_io_end(instance);
instance->qos_context.opts.qos_waitq_clear(instance,
PS3_SCSI_RESULT_HOST_STATUS(DID_SOFT_ERROR));
LOG_INFO("qos close. host_no:%u\n", PS3_HOST(instance));
}
void ps3_qos_open(struct ps3_instance *instance)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
instance->qos_context.opts.qos_reset(instance);
}
static U8 ps3_qos_waitq_flag_get(struct ps3_qos_softq_mgr *softq_mgr)
{
U8 waitq_flag = 0;
switch (softq_mgr->id) {
case PS3_QOS_MGRQ:
waitq_flag = PS3_QOS_CMD_IN_MGR;
break;
case PS3_QOS_CMDQ_0:
waitq_flag = PS3_QOS_CMD_IN_CMDQ0;
break;
case PS3_QOS_CMDQ_1:
waitq_flag = PS3_QOS_CMD_IN_CMDQ1;
break;
case PS3_QOS_CMDQ_2:
waitq_flag = PS3_QOS_CMD_IN_CMDQ2;
break;
case PS3_QOS_CMDQ_3:
waitq_flag = PS3_QOS_CMD_IN_CMDQ3;
break;
default:
LOG_ERROR_IN_IRQ(softq_mgr->instance, "invalid softq id. host_no:%u id:%u\n",
PS3_HOST(softq_mgr->instance), softq_mgr->id);
}
return waitq_flag;
}
static Bool ps3_qos_cq_rc_get(struct ps3_cmd *cmd,
struct ps3_qos_softq_mgr *softq_mgr, struct qos_wait_queue *wait_q)
{
Bool can_get = PS3_FALSE;
ULong flag = 0;
if (ps3_atomic_dec_return(&softq_mgr->free_cnt) >= 0) {
can_get = PS3_TRUE;
} else {
ps3_atomic_inc(&softq_mgr->free_cnt);
}
if (!can_get) {
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
list_add_tail(&cmd->qos_list, &wait_q->wait_list);
wait_q->count++;
softq_mgr->total_wait_cmd_cnt++;
cmd->qos_waitq_flag = ps3_qos_waitq_flag_get(softq_mgr);
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
LOG_DEBUG("insert qos cq waitq.host_no:%u:t_id:0x%llx "
"CFID:%u que_id:%u diskid:%u waitq:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, softq_mgr->id,
cmd->io_attr.disk_id, wait_q->count);
}
return can_get;
}
static struct qos_wait_queue* ps3_qos_cq_waitq_get(struct ps3_cmd *cmd,
struct ps3_qos_softq_mgr *softq_mgr)
{
U16 waitq_id = 0;
struct qos_wait_queue *waitq = NULL;
if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) {
if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) {
waitq_id = get_offset_of_vdid(PS3_VDID_OFFSET(cmd->instance),
PS3_VDID(&cmd->io_attr.vd_entry->diskPos));
} else {
waitq_id = cmd->instance->qos_context.max_vd_count;
}
waitq = &softq_mgr->waitqs[waitq_id];
} else {
waitq = &softq_mgr->waitqs[0];
}
return waitq;
}
static Bool ps3_qos_cq_rc_check(struct ps3_cmd *cmd,
struct ps3_qos_softq_mgr *softq_mgr)
{
Bool can_get = PS3_FALSE;
ULong flag = 0;
struct qos_wait_queue *waitq = NULL;
waitq = ps3_qos_cq_waitq_get(cmd, softq_mgr);
if (likely(softq_mgr->total_wait_cmd_cnt == 0)) {
can_get = ps3_qos_cq_rc_get(cmd, softq_mgr, waitq);
} else {
INJECT_START(PS3_ERR_IJ_QOS_WAIT_SOFT_WAITQ_CLEAR, cmd->instance)
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
if (softq_mgr->total_wait_cmd_cnt > 0) {
list_add_tail(&cmd->qos_list, &waitq->wait_list);
waitq->count++;
softq_mgr->total_wait_cmd_cnt++;
cmd->qos_waitq_flag = ps3_qos_waitq_flag_get(softq_mgr);
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
can_get = PS3_FALSE;
LOG_DEBUG("insert qos cq waitq.host_no:%u:t_id:0x%llx CFID:%u "
"que_id:%u diskid:%u waitq:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, softq_mgr->id,
cmd->io_attr.disk_id, waitq->count);
} else {
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
can_get = ps3_qos_cq_rc_get(cmd, softq_mgr, waitq);
}
}
return can_get;
}
static inline struct ps3_qos_softq_mgr *ps3_qos_cmdq_mgr_get(struct ps3_instance *instance, U16 index)
{
return &instance->qos_context.cq_ctx.cmdqs[index];
}
static void ps3_qos_update_target_cmdq(struct ps3_cmd *cmd)
{
U8 que_id = 0;
U8 qmask = 0;
cmd->cmdq_count = 0;
qmask = cmd->cmd_word.qMask;
while (que_id < cmd->instance->ctrl_info.vdQueueNum) {
if (qmask & (1 << que_id)) {
cmd->cmdq_info[cmd->cmdq_count].que_id = que_id;
if (++cmd->cmdq_count == PS3_QOS_MAX_CMDQ_ONE_CMD) {
break;
}
}
que_id++;
}
}
static Bool ps3_qos_cq_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_TRUE;
struct ps3_instance *instance = cmd->instance;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
U16 cmdq_index = 0;
U8 i = 0;
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_MGR_PRO);
softq_mgr = &instance->qos_context.cq_ctx.mgrq;
can_get = ps3_qos_cq_rc_check(cmd, softq_mgr);
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_MGR_PRO);
if (!can_get) {
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_MGR_QUEUE);
}
} else {
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_CMD_PRO);
ps3_qos_update_target_cmdq(cmd);
for (i = 0; i < cmd->cmdq_count; i++) {
cmdq_index = cmd->cmdq_info[i].que_id;
softq_mgr = ps3_qos_cmdq_mgr_get(instance, cmdq_index);
can_get = ps3_qos_cq_rc_check(cmd, softq_mgr);
if (can_get) {
cmd->cmdq_info[i].get_rc = PS3_TRUE;
} else {
can_get = PS3_FALSE;
break;
}
}
PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_CMD_PRO);
if (!can_get) {
PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_CMD_QUEUE);
}
}
return can_get;
}
Bool ps3_raid_qos_decision(struct ps3_cmd *cmd)
{
Bool can_get = PS3_FALSE;
if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) {
can_get = ps3_qos_pd_quota_decision(cmd);
if (!can_get ||
cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) {
goto _out;
}
}
can_get = ps3_qos_cq_decision(cmd);
_out:
return can_get;
}
static void ps3_qos_update_cmdq_rc(struct ps3_cmd *cmd)
{
U8 i = 0;
U8 index = 0;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
S32 cmdq_free = 0;
for (i = 0; i < cmd->cmdq_count; i++) {
index = cmd->cmdq_info[i].que_id;
softq_mgr = ps3_qos_cmdq_mgr_get(cmd->instance, index);
cmdq_free = ps3_atomic_inc_return(&softq_mgr->free_cnt);
LOG_DEBUG("update cmdq rc. host_no:%u t_id:0x%llx CFID:%u rc[%u,%d] dev_t:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, softq_mgr->id, cmdq_free,
cmd->io_attr.dev_type);
}
}
static void ps3_qos_update_mgrq_rc(struct ps3_cmd *cmd)
{
struct ps3_qos_cq_context *qos_cq_ctx = &cmd->instance->qos_context.cq_ctx;
S32 free_cnt = 0;
free_cnt = ps3_atomic_inc_return(&qos_cq_ctx->mgrq.free_cnt);
LOG_DEBUG("update mgrq rc. host_no:%u t_id:0x%llx CFID:%u rc:%d\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, free_cnt);
}
static void ps3_raid_qos_cmd_update(struct ps3_cmd *cmd)
{
if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) {
ps3_qos_update_mgrq_rc(cmd);
} else {
ps3_qos_update_pd_quota(cmd);
ps3_qos_update_cmdq_rc(cmd);
}
}
void ps3_qos_mgrq_resend(struct ps3_qos_softq_mgr *softq_mgr)
{
S32 ret = PS3_SUCCESS;
ULong flag = 0;
struct ps3_cmd *cmd = NULL;
struct ps3_instance *instance = softq_mgr->instance;
struct qos_wait_queue *waitq = &softq_mgr->waitqs[0];
Bool waitq_cleared = PS3_FALSE;
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
while (waitq->count > 0) {
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
if (ps3_atomic_dec_return(&softq_mgr->free_cnt) < 0) {
ps3_atomic_inc(&softq_mgr->free_cnt);
break;
}
list_del(&cmd->qos_list);
waitq->count--;
softq_mgr->total_wait_cmd_cnt--;
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_MGR_QUEUE);
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_raid_qos_cmd_update(cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
cmd->qos_waitq_flag = 0;
LOG_DEBUG("raid qos mgr waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, cmd->cmd_word.type);
}
}
if (softq_mgr->total_wait_cmd_cnt == 0) {
waitq_cleared = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
if (waitq_cleared) {
LOG_DEBUG("hno:%u resend all mgrq waited cmds\n",
PS3_HOST(instance));
}
softq_mgr->last_sched_jiffies = jiffies;
}
static void ps3_qos_mgrq_resend_work(struct work_struct *work)
{
struct ps3_qos_softq_mgr *softq_mgr =
ps3_container_of(work, struct ps3_qos_softq_mgr, resend_work);
ps3_qos_mgrq_resend(softq_mgr);
return;
}
static S32 ps3_qos_mgrq_init(struct ps3_instance *instance, struct ps3_qos_softq_mgr *softq_mgr)
{
S32 ret = PS3_SUCCESS;
U32 mgrq_depth = 0;
U32 high_mgr_cmd = 0;
char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = {0};
if (!ps3_ioc_multi_func_support(instance)) {
high_mgr_cmd = PS3_QOS_HIGH_PRI_MGR_CMD_COUNT;
} else {
high_mgr_cmd = PS3_QOS_HIGH_PRI_MGR_CMD_COUNT >> 1;
}
mgrq_depth = instance->qos_context.cq_ctx.mgrq_depth;
if (mgrq_depth <= high_mgr_cmd) {
ret = -PS3_FAILED;
LOG_ERROR("hno:%u:mgrq_depth is too small func:%u depth:%u\n",
PS3_HOST(softq_mgr->instance), ps3_get_pci_function(instance->pdev),
mgrq_depth);
goto _out;
}
softq_mgr->id = PS3_QOS_MGRQ;
softq_mgr->instance = instance;
ps3_spin_lock_init(&softq_mgr->rc_lock);
ps3_atomic_set(&softq_mgr->free_cnt, mgrq_depth - high_mgr_cmd);
softq_mgr->waitq_cnt = 1;
softq_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc(instance, sizeof(struct qos_wait_queue));
INJECT_START(PS3_ERR_IJ_QOS_MGRQ_INIT_FAIL_1, softq_mgr)
if (softq_mgr->waitqs == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for waitqs\n",
PS3_HOST(softq_mgr->instance));
ret = -PS3_FAILED;
goto _out;
}
INIT_LIST_HEAD(&softq_mgr->waitqs[0].wait_list);
softq_mgr->waitqs[0].count = 0;
INIT_WORK(&softq_mgr->resend_work, ps3_qos_mgrq_resend_work);
softq_mgr->total_wait_cmd_cnt = 0;
softq_mgr->last_sched_jiffies = 0;
snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "mgrq_wq_f%u",
ps3_get_pci_function(instance->pdev));
softq_mgr->work_queue = create_singlethread_workqueue(workq_name);
INJECT_START(PS3_ERR_IJ_QOS_MGRQ_INIT_FAIL_2, softq_mgr)
if (softq_mgr->work_queue == NULL) {
LOG_ERROR("qos mgr workq create failed\n");
ret = -PS3_FAILED;
goto _release_waitq;
}
LOG_INFO("hno:%u:mgrq init success func:%u depth:%u\n",
PS3_HOST(softq_mgr->instance), ps3_get_pci_function(instance->pdev),
mgrq_depth);
goto _out;
_release_waitq:
ps3_vfree(instance, softq_mgr->waitqs);
softq_mgr->waitqs = NULL;
_out:
return ret;
}
static Bool ps3_qos_softq_notify(struct ps3_qos_softq_mgr *softq_mgr)
{
Bool notified = PS3_FALSE;
if (softq_mgr->total_wait_cmd_cnt > 0 &&
ps3_atomic_read(&softq_mgr->free_cnt) > 0) {
queue_work(softq_mgr->work_queue, &softq_mgr->resend_work);
notified = PS3_TRUE;
}
return notified;
}
static Bool ps3_qos_cq_notify(struct ps3_instance *instance)
{
U8 i = 0;
Bool notified = PS3_FALSE;
struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
softq_mgr = &qos_cq_ctx->mgrq;
ps3_qos_softq_notify(softq_mgr);
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
softq_mgr = ps3_qos_cmdq_mgr_get(instance, i);
if (ps3_qos_softq_notify(softq_mgr)) {
notified = PS3_TRUE;
}
}
return notified;
}
void ps3_raid_qos_waitq_notify(struct ps3_instance *instance)
{
if (!ps3_qos_cq_notify(instance)) {
ps3_qos_pd_notify(instance);
}
}
static Bool ps3_qos_cmdq_resend_judge(struct ps3_qos_softq_mgr *softq_mgr, struct ps3_cmd *cmd)
{
Bool can_get = PS3_TRUE;
struct ps3_instance *instance = NULL;
struct ps3_qos_softq_mgr *next_softq_mgr = NULL;
instance = cmd->instance;
if (cmd->cmdq_count == 1) {
cmd->cmdq_info[0].get_rc = PS3_TRUE;
} else {
if (cmd->cmdq_info[0].que_id == softq_mgr->id) {
cmd->cmdq_info[0].get_rc = PS3_TRUE;
next_softq_mgr = ps3_qos_cmdq_mgr_get(instance, cmd->cmdq_info[1].que_id);
can_get = ps3_qos_cq_rc_check(cmd, next_softq_mgr);
if (can_get) {
cmd->cmdq_info[1].get_rc = PS3_TRUE;
}
} else {
cmd->cmdq_info[1].get_rc = PS3_TRUE;
}
}
if (can_get) {
PS3_QOS_STAT_END(instance, cmd, PS3_QOS_CMD_QUEUE);
}
LOG_DEBUG("raid resend cmdq judge:host_no:%u t_id:0x%llx CFID:%u cmdq_id:%u ret:%u\n",
PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, softq_mgr->id, can_get);
return can_get;
}
static void ps3_qos_resend_multi_cmd(struct ps3_qos_softq_mgr *softq_mgr,
struct qos_wait_queue *waitq, S32 count)
{
S32 ret = PS3_SUCCESS;
struct ps3_cmd *cmd = NULL;
struct ps3_instance *instance = softq_mgr->instance;
U32 can_send = 0;
can_send = PS3_MIN(ps3_atomic_read(&softq_mgr->free_cnt), count);
while(waitq->count > 0 && can_send > 0) {
cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, qos_list);
list_del(&cmd->qos_list);
softq_mgr->total_wait_cmd_cnt--;
waitq->count--;
if (ps3_qos_vd_seq_check(cmd)) {
continue;
}
can_send--;
ps3_atomic_dec(&softq_mgr->free_cnt);
if (ps3_qos_cmdq_resend_judge(softq_mgr, cmd)) {
ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE);
if (unlikely(ret != PS3_SUCCESS)) {
ps3_raid_qos_cmd_update(cmd);
ps3_qos_cmd_resend_fail(cmd, ret);
} else {
cmd->qos_waitq_flag = 0;
LOG_DEBUG("raid qos cmdq waitq resend:host_no:%u t_id:0x%llx CFID:%u qid:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, waitq->id);
}
}
}
}
static void ps3_qos_cmdq_resend(struct ps3_qos_softq_mgr *softq_mgr)
{
ULong flag = 0;
struct ps3_instance *instance = softq_mgr->instance;
struct qos_wait_queue *waitq = NULL;
U16 cur_que_id = softq_mgr->poll_que_id;
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
while (ps3_atomic_read(&softq_mgr->free_cnt) > 0
&& softq_mgr->total_wait_cmd_cnt > 0) {
waitq = &softq_mgr->waitqs[cur_que_id];
if (waitq->count > 0) {
ps3_qos_resend_multi_cmd(softq_mgr, waitq, softq_mgr->poll_cmd_cnt);
}
if (++cur_que_id > instance->qos_context.max_vd_count) {
cur_que_id = 1;
}
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
softq_mgr->poll_que_id = cur_que_id;
softq_mgr->last_sched_jiffies = jiffies;
ps3_qos_pd_notify(instance);
}
static void ps3_qos_cmdq_resend_work(struct work_struct *work)
{
struct ps3_qos_softq_mgr *softq_mgr =
ps3_container_of(work, struct ps3_qos_softq_mgr, resend_work);
ps3_qos_cmdq_resend(softq_mgr);
return;
}
static S32 ps3_qos_cmdq_init(struct ps3_instance *instance, struct ps3_qos_softq_mgr *softq_mgr)
{
S32 ret = PS3_SUCCESS;
U16 i = 0;
struct qos_wait_queue *waitq = NULL;
char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = {0};
U32 cmdq_depth = 0;
cmdq_depth = instance->qos_context.cq_ctx.cmdq_depth;
softq_mgr->instance = instance;
ps3_spin_lock_init(&softq_mgr->rc_lock);
ps3_atomic_set(&softq_mgr->free_cnt, cmdq_depth);
softq_mgr->waitq_cnt = instance->qos_context.max_vd_count + 1;
softq_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc(instance,
softq_mgr->waitq_cnt * sizeof(struct qos_wait_queue));
INJECT_START(PS3_ERR_IJ_QOS_CMDQ_INIT_FAIL_1, softq_mgr)
if (softq_mgr->waitqs == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for waitqs\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _out;
}
memset(softq_mgr->waitqs, 0, (instance->qos_context.max_vd_count + 1) * sizeof(struct qos_wait_queue));
for (i = 1; i < softq_mgr->waitq_cnt; i++) {
waitq = &softq_mgr->waitqs[i];
waitq->id = i;
INIT_LIST_HEAD(&waitq->wait_list);
waitq->count = 0;
}
softq_mgr->poll_cmd_cnt = PS3_QOS_POLL_CMD_COUNT;
softq_mgr->poll_que_id = 1;
INIT_WORK(&softq_mgr->resend_work, ps3_qos_cmdq_resend_work);
softq_mgr->total_wait_cmd_cnt = 0;
softq_mgr->last_sched_jiffies = 0;
snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "cmdq_wq_f%u_%u",
ps3_get_pci_function(instance->pdev), softq_mgr->id);
softq_mgr->work_queue = create_singlethread_workqueue(workq_name);
INJECT_START(PS3_ERR_IJ_QOS_CMDQ_INIT_FAIL_2, softq_mgr)
if (softq_mgr->work_queue == NULL) {
LOG_ERROR("qos mgr workq create failed\n");
ret = -PS3_FAILED;
goto _release_waitq;
}
LOG_INFO("hno:%u:cmdq init success func:%u depth:%u id:%u\n",
PS3_HOST(softq_mgr->instance), ps3_get_pci_function(instance->pdev),
cmdq_depth, softq_mgr->id);
goto _out;
_release_waitq:
ps3_vfree(instance, softq_mgr->waitqs);
softq_mgr->waitqs = NULL;
_out:
return ret;
}
static void ps3_qos_softq_exit(struct ps3_instance *instance, struct ps3_qos_softq_mgr *softq_mgr)
{
cancel_work_sync(&softq_mgr->resend_work);
flush_workqueue(softq_mgr->work_queue);
destroy_workqueue(softq_mgr->work_queue);
ps3_vfree(instance, softq_mgr->waitqs);
softq_mgr->waitqs = NULL;
}
static S32 ps3_qos_cq_context_init(struct ps3_instance *instance)
{
S32 ret = PS3_SUCCESS;
U16 i = 0;
U16 j = 0;
struct ps3_qos_cq_context *qos_cq_ctx = NULL;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
U64 mgrq_depth = 0;
U64 cmdq_depth = 0;
qos_cq_ctx = &instance->qos_context.cq_ctx;
ps3_mgrq_depth_get(instance, &mgrq_depth);
INJECT_START(PS3_ERR_IJ_QOS_FORCE_MGRQ_ZERO_DEPTH, &mgrq_depth)
if (mgrq_depth == 0) {
ret = -PS3_FAILED;
LOG_ERROR("hno:%u:mgrq_depth is invalid func:%u\n",
PS3_HOST(instance),ps3_get_pci_function(instance->pdev));
goto _out;
}
qos_cq_ctx->mgrq_depth = mgrq_depth;
ps3_cmdq_depth_get(instance, &cmdq_depth);
INJECT_START(PS3_ERR_IJ_QOS_FORCE_CMDQ_ZERO_DEPTH, &cmdq_depth)
if (cmdq_depth == 0) {
ret = -PS3_FAILED;
LOG_ERROR("hno:%u:cmdq_depth is invalid func:%u\n",
PS3_HOST(instance),
ps3_get_pci_function(instance->pdev));
goto _out;
}
qos_cq_ctx->cmdq_depth = cmdq_depth;
if (ps3_qos_mgrq_init(instance, &qos_cq_ctx->mgrq) != PS3_SUCCESS) {
ret = -PS3_FAILED;
goto _out;
}
qos_cq_ctx->cmdq_cnt = instance->ctrl_info.vdQueueNum;
qos_cq_ctx->cmdqs = (struct ps3_qos_softq_mgr *)ps3_vzalloc(instance,
qos_cq_ctx->cmdq_cnt * sizeof(struct ps3_qos_softq_mgr));
if (qos_cq_ctx->cmdqs == NULL) {
LOG_ERROR("hno:%u:Failed to kcalloc memory for cmdqs\n",
PS3_HOST(instance));
ret = -PS3_FAILED;
goto _mgrq_exit;
}
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
softq_mgr = &qos_cq_ctx->cmdqs[i];
softq_mgr->id = i;
if (ps3_qos_cmdq_init(instance, softq_mgr) != PS3_SUCCESS) {
ret = -PS3_FAILED;
goto _cmdq_exit;
}
}
LOG_INFO("hno:%u: qos cq context init success cmdq_cnt:%u\n",
PS3_HOST(instance), qos_cq_ctx->cmdq_cnt);
goto _out;
_cmdq_exit:
for (j = 0; j < i; j++) {
softq_mgr = &qos_cq_ctx->cmdqs[j];
ps3_qos_softq_exit(instance, softq_mgr);
}
ps3_vfree(instance, qos_cq_ctx->cmdqs);
qos_cq_ctx->cmdqs = NULL;
_mgrq_exit:
ps3_qos_softq_exit(instance, &qos_cq_ctx->mgrq);
_out:
return ret;
}
static void ps3_qos_cq_context_exit(struct ps3_instance *instance)
{
struct ps3_qos_cq_context *qos_cq_ctx = NULL;
U16 i = 0;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
qos_cq_ctx = &instance->qos_context.cq_ctx;
if (qos_cq_ctx->cmdqs != NULL) {
ps3_qos_softq_exit(instance, &qos_cq_ctx->mgrq);
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
softq_mgr = &qos_cq_ctx->cmdqs[i];
ps3_qos_softq_exit(instance, softq_mgr);
}
ps3_vfree(instance, qos_cq_ctx->cmdqs);
qos_cq_ctx->cmdqs = NULL;
}
LOG_INFO("hno:%u: qos cq context exit success\n", PS3_HOST(instance));
}
S32 ps3_raid_qos_init(struct ps3_instance *instance)
{
S32 ret = PS3_SUCCESS;
ret = ps3_qos_cq_context_init(instance);
if (ret != PS3_SUCCESS) {
goto _out;
}
ret = ps3_qos_pd_context_init(instance);
if (ret != PS3_SUCCESS) {
goto _cq_ctx_exit;
}
goto _out;
_cq_ctx_exit:
ps3_qos_cq_context_exit(instance);
_out:
return ret;
}
static Bool ps3_qos_softq_abort(struct ps3_qos_softq_mgr *softq_mgr,
struct ps3_cmd *cmd, U8 waitq_flag)
{
ULong lock_flag = 0;
Bool found = PS3_FALSE;
struct qos_wait_queue *waitq = NULL;
waitq = ps3_qos_cq_waitq_get(cmd, softq_mgr);
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &lock_flag);
if (cmd->qos_waitq_flag == waitq_flag) {
list_del(&cmd->qos_list);
waitq->count--;
softq_mgr->total_wait_cmd_cnt--;
found = PS3_TRUE;
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, lock_flag);
return found;
}
static void ps3_qos_peer_cmdq_rc_update(struct ps3_cmd *cmd)
{
U8 i = 0;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
for (i = 0; i < cmd->cmdq_count; i++) {
if (cmd->cmdq_info[i].get_rc) {
softq_mgr = ps3_qos_cmdq_mgr_get(cmd->instance, cmd->cmdq_info[i].que_id);
ps3_atomic_inc(&softq_mgr->free_cnt);
}
}
}
static Bool ps3_qos_cmdq_abort(struct ps3_cmd *cmd)
{
U8 i = 0;
U8 waitq_flag = 0;
Bool found = PS3_FALSE;
struct ps3_qos_softq_mgr *qos_softq_mgr = NULL;
for (i = 0; i < cmd->cmdq_count; i++) {
qos_softq_mgr = ps3_qos_cmdq_mgr_get(cmd->instance, cmd->cmdq_info[i].que_id);
waitq_flag = ps3_qos_waitq_flag_get(qos_softq_mgr);
if (cmd->qos_waitq_flag == waitq_flag) {
found = ps3_qos_softq_abort(qos_softq_mgr, cmd, waitq_flag);
if (found) {
break;
}
}
}
return found;
}
Bool ps3_raid_qos_waitq_abort(struct ps3_cmd *cmd)
{
Bool found = PS3_FALSE;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
if (cmd->qos_waitq_flag == 0) {
goto _out;
}
if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) {
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD) {
found = ps3_pd_quota_waiq_abort(cmd);
if (found) {
goto _out;
}
}
found = ps3_qos_cmdq_abort(cmd);
if (found) {
ps3_qos_update_pd_quota(cmd);
ps3_qos_peer_cmdq_rc_update(cmd);
}
} else {
if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_MGR) {
softq_mgr = &cmd->instance->qos_context.cq_ctx.mgrq;
found = ps3_qos_softq_abort(softq_mgr, cmd, PS3_QOS_CMD_IN_MGR);
}
}
_out:
return found;
}
static void ps3_qos_mgrq_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
struct ps3_qos_softq_mgr *softq_mgr = NULL;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct qos_wait_queue *waitq = NULL;
ULong flag = 0;
softq_mgr = &instance->qos_context.cq_ctx.mgrq;
waitq = &softq_mgr->waitqs[0];
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
if (waitq->count > 0) {
list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, qos_list) {
if (priv_data == NULL ||
priv_data == scsi_device_private_data(cmd->scmd)) {
LOG_DEBUG("qos clean mgrq. hno:%u t_id:0x%llx CFID:%d\n",
PS3_HOST(instance), cmd->trace_id, cmd->index);
list_del(&cmd->qos_list);
waitq->count--;
softq_mgr->total_wait_cmd_cnt--;
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
}
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
if (priv_data == NULL) {
cancel_work_sync(&softq_mgr->resend_work);
}
}
static void ps3_qos_cmdq_clean(struct ps3_instance *instance, U16 disk_id,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
U8 i = 0;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct qos_wait_queue *waitq = NULL;
ULong flag = 0;
struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx;
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
softq_mgr = ps3_qos_cmdq_mgr_get(instance, i);
waitq = &softq_mgr->waitqs[disk_id];
if (waitq->count > 0) {
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, qos_list) {
if (priv_data == NULL ||
priv_data == scsi_device_private_data(cmd->scmd)) {
LOG_DEBUG("qos clean cmdq. hno:%u t_id:0x%llx CFID:%d cmdq:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, softq_mgr->id);
list_del(&cmd->qos_list);
waitq->count--;
softq_mgr->total_wait_cmd_cnt--;
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
}
}
}
static void ps3_qos_cmdq_clear(struct ps3_instance *instance, S32 resp_status)
{
U16 i = 0;
U16 j = 0;
struct ps3_qos_softq_mgr *softq_mgr = NULL;
struct ps3_cmd *cmd = NULL;
struct ps3_cmd *cmd_next = NULL;
struct qos_wait_queue *waitq = NULL;
ULong flag = 0;
struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx;
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
softq_mgr = ps3_qos_cmdq_mgr_get(instance, i);
for (j = 1; j <= instance->qos_context.max_vd_count; j++) {
waitq = &softq_mgr->waitqs[j];
if (waitq->count > 0) {
ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag);
list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, qos_list) {
LOG_DEBUG("qos clean cmdq. hno:%u t_id:0x%llx CFID:%d cmdq:%u\n",
PS3_HOST(instance), cmd->trace_id, cmd->index, softq_mgr->id);
list_del(&cmd->qos_list);
waitq->count--;
softq_mgr->total_wait_cmd_cnt--;
ps3_qos_update_pd_quota(cmd);
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, PS3_FALSE);
}
ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag);
}
}
cancel_work_sync(&softq_mgr->resend_work);
}
}
static void ps3_raid_qos_pd_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
U16 pd_id = 0;
U16 vd_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
pd_id = PS3_PDID(&priv_data->disk_pos);
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, resp_status);
if (!PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) {
vd_id = instance->qos_context.max_vd_count;
ps3_qos_cmdq_clean(instance, vd_id, priv_data, resp_status);
ps3_qos_mgrq_clean(instance, priv_data, resp_status);
}
LOG_INFO("qos clean vd. host_no:%u did:%u vid:%u\n",
PS3_HOST(instance), pd_id, vd_id);
}
static void ps3_raid_qos_vd_clean(struct ps3_instance *instance,
struct ps3_scsi_priv_data *priv_data, S32 resp_status)
{
U16 vd_id = 0;
U16 pd_id = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), PS3_VDID(&priv_data->disk_pos));
for (pd_id = 1; pd_id <= instance->qos_context.max_pd_count; pd_id++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1 && PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) {
ps3_pd_quota_waitq_clean(qos_pd_mgr, vd_id, resp_status);
}
}
ps3_qos_cmdq_clean(instance, vd_id, NULL, resp_status);
ps3_qos_mgrq_clean(instance, priv_data, resp_status);
LOG_FILE_INFO("qos clean vd. host_no:%u type:%u disk_id:%u\n",
PS3_HOST(instance), priv_data->dev_type, vd_id);
}
void ps3_raid_qos_waitq_clear_all(struct ps3_instance *instance, S32 resp_status)
{
U16 i = 0;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
for (i = 1; i <= instance->qos_context.max_pd_count; i++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) {
ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status);
cancel_work_sync(&qos_pd_mgr->resend_work);
}
}
ps3_qos_cmdq_clear(instance, resp_status);
ps3_qos_mgrq_clean(instance, NULL, resp_status);
LOG_INFO("host_no:%u:clear all qos waitq\n",
PS3_HOST(instance));
}
static Bool ps3_qos_softq_notify_timeout(struct ps3_qos_softq_mgr *qos_softq_mgr)
{
Bool notified = PS3_FALSE;
ULong timeout_jiffies = 0;
timeout_jiffies = qos_softq_mgr->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ;
if (qos_softq_mgr->total_wait_cmd_cnt &&
ps3_atomic_read(&qos_softq_mgr->free_cnt) > 0 &&
time_after(jiffies, timeout_jiffies)) {
queue_work(qos_softq_mgr->work_queue, &qos_softq_mgr->resend_work);
notified = PS3_TRUE;
LOG_INFO("awake qos softq by poll. host_no:%u q_id:%u\n",
PS3_HOST(qos_softq_mgr->instance), qos_softq_mgr->id);
}
return notified;
}
static Bool ps3_qos_cq_notify_timeout(struct ps3_instance *instance)
{
struct ps3_qos_cq_context *qos_cq_ctx = NULL;
struct ps3_qos_softq_mgr *qos_softq_mgr = NULL;
Bool notified = PS3_FALSE;
U8 i = 0;
qos_cq_ctx = &instance->qos_context.cq_ctx;
ps3_qos_softq_notify_timeout(&qos_cq_ctx->mgrq);
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
qos_softq_mgr = &qos_cq_ctx->cmdqs[i];
if (ps3_qos_softq_notify_timeout(qos_softq_mgr)) {
notified = PS3_TRUE;
}
}
return notified;
}
void ps3_raid_qos_waitq_poll(struct ps3_instance *instance)
{
if (!ps3_qos_cq_notify_timeout(instance)) {
ps3_qos_pd_notify_timeout(instance);
}
}
static void ps3_raid_qos_reset(struct ps3_instance *instance)
{
struct ps3_qos_context *qos_ctx = &instance->qos_context;
struct ps3_qos_pd_mgr *qos_pd_mgr = NULL;
struct ps3_qos_cq_context *qos_cq_ctx = NULL;
struct ps3_qos_softq_mgr *qos_softq_mgr = NULL;
U16 i = 0;
struct ps3_pd_entry *pd_entry = NULL;
qos_cq_ctx = &instance->qos_context.cq_ctx;
qos_softq_mgr = &qos_cq_ctx->mgrq;
ps3_atomic_set(&qos_softq_mgr->free_cnt,
qos_ctx->cq_ctx.mgrq_depth - (PS3_QOS_HIGH_PRI_MGR_CMD_COUNT >> 1));
for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) {
qos_softq_mgr = &qos_cq_ctx->cmdqs[i];
ps3_atomic_set(&qos_softq_mgr->free_cnt, qos_ctx->cq_ctx.cmdq_depth);
}
qos_ctx->pd_ctx.nvme_normal_quota = g_ps3_qos_raid_nvme_normal_quota;
for (i = 1; i <= qos_ctx->max_pd_count; i++) {
qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i);
if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) {
pd_entry = ps3_dev_mgr_lookup_pd_info_by_id(instance, i);
if (pd_entry != NULL) {
ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry);
}
}
}
}
static Bool ps3_raid_qos_pd_resend_check(struct ps3_cmd *cmd)
{
return ps3_qos_cq_decision(cmd);
}
void ps3_raid_qos_exit(struct ps3_instance *instance)
{
ps3_qos_cq_context_exit(instance);
ps3_qos_pd_context_exit(instance);
LOG_INFO("raid qos exit. hno:%u\n", PS3_HOST(instance));
}
void ps3_hba_qos_prepare(struct ps3_instance *instance)
{
instance->qos_context.opts.qos_init = ps3_hba_qos_init;
instance->qos_context.opts.qos_exit = ps3_hba_qos_exit;
instance->qos_context.opts.qos_decision = ps3_hba_qos_decision;
instance->qos_context.opts.qos_cmd_update = ps3_hba_qos_cmd_update;
instance->qos_context.opts.qos_waitq_notify = ps3_hba_qos_waitq_notify;
instance->qos_context.opts.qos_pd_resend_check = ps3_hba_qos_pd_resend_check;
instance->qos_context.opts.qos_waitq_abort = ps3_hba_qos_waitq_abort;
instance->qos_context.opts.qos_vd_clean = ps3_hba_qos_vd_clean;
instance->qos_context.opts.qos_pd_clean = ps3_hba_qos_pd_clean;
instance->qos_context.opts.qos_waitq_poll = ps3_hba_qos_waitq_poll;
instance->qos_context.opts.qos_waitq_clear = ps3_hba_qos_waitq_clear_all;
instance->qos_context.opts.qos_reset = ps3_hba_qos_reset;
instance->qos_context.opts.qos_vd_init = ps3_hba_qos_vd_init;
instance->qos_context.opts.qos_vd_reset = ps3_hba_qos_vd_reset;
instance->qos_context.qos_switch = 1;
instance->qos_context.pd_ctx.nvme_normal_quota = g_ps3_qos_hba_nvme_normal_quota;
}
void ps3_raid_qos_prepare(struct ps3_instance *instance)
{
instance->qos_context.opts.qos_init = ps3_raid_qos_init;
instance->qos_context.opts.qos_exit = ps3_raid_qos_exit;
instance->qos_context.opts.qos_decision = ps3_raid_qos_decision;
instance->qos_context.opts.qos_cmd_update = ps3_raid_qos_cmd_update;
instance->qos_context.opts.qos_waitq_notify = ps3_raid_qos_waitq_notify;
instance->qos_context.opts.qos_pd_resend_check = ps3_raid_qos_pd_resend_check;
instance->qos_context.opts.qos_waitq_abort = ps3_raid_qos_waitq_abort;
instance->qos_context.opts.qos_vd_clean = ps3_raid_qos_vd_clean;
instance->qos_context.opts.qos_pd_clean = ps3_raid_qos_pd_clean;
instance->qos_context.opts.qos_waitq_poll = ps3_raid_qos_waitq_poll;
instance->qos_context.opts.qos_waitq_clear = ps3_raid_qos_waitq_clear_all;
instance->qos_context.opts.qos_reset = ps3_raid_qos_reset;
instance->qos_context.opts.qos_vd_init = NULL;
instance->qos_context.opts.qos_vd_reset = NULL;
instance->qos_context.qos_switch = 1;
instance->qos_context.pd_ctx.nvme_normal_quota = g_ps3_qos_raid_nvme_normal_quota;
}
S32 ps3_qos_decision(struct ps3_cmd *cmd)
{
S32 ret = PS3_SUCCESS;
Bool can_get = PS3_TRUE;
struct ps3_instance *instance = NULL;
cmd->qos_processing = PS3_TRUE;
wmb();
instance = cmd->instance;
if (!ps3_qos_enable(cmd->instance) || !PS3_QOS_INITED(instance)) {
goto _exit;
}
ret = ps3_qos_pre_check(instance, cmd);
if (ret != PS3_SUCCESS) {
goto _exit;
}
can_get = instance->qos_context.opts.qos_decision(cmd);
if (!can_get) {
ret = -PS3_IN_QOS_Q;
PS3_QOS_CMD_INC(instance);
}
_exit:
cmd->qos_processing = PS3_FALSE;
return ret;
}
void ps3_qos_cmd_update(struct ps3_instance *instance, struct ps3_cmd *cmd)
{
if (!ps3_qos_enable(instance) || !PS3_QOS_INITED(instance)) {
return;
}
instance->qos_context.opts.qos_cmd_update(cmd);
}
void ps3_qos_waitq_notify(struct ps3_instance *instance)
{
if (!ps3_qos_enable(instance) || !PS3_QOS_INITED(instance)) {
return;
}
instance->qos_context.opts.qos_waitq_notify(instance);
}
Bool ps3_qos_waitq_abort(struct ps3_cmd *cmd)
{
Bool found = PS3_FALSE;
if (!PS3_QOS_INITED(cmd->instance)) {
goto out;
}
found = cmd->instance->qos_context.opts.qos_waitq_abort(cmd);
LOG_INFO("task abort cmd in qos waitq t_id:0x%llx CFID:%u flag:%u ret:%u\n",
cmd->trace_id, cmd->index, cmd->qos_waitq_flag, found);
if (found) {
ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, SCSI_STATUS_TASK_ABORTED, PS3_FALSE);
}
out:
return found;
}
void ps3_qos_hard_reset(struct ps3_instance *instance)
{
S32 resp_status = 0;
if (ps3_pci_err_recovery_get(instance)) {
resp_status = PS3_STATUS_PCI_RECOVERY;
} else {
resp_status = PS3_STATUS_HOST_RESET;
}
ps3_qos_waitq_clear_all(instance, resp_status);
}
void ps3_qos_waitq_clear_all(struct ps3_instance *instance, S32 resp_status)
{
if (!PS3_QOS_INITED(instance)) {
return;
}
ps3_qos_wait_io_end(instance);
instance->qos_context.opts.qos_waitq_clear(instance, resp_status);
}
S32 ps3_qos_init(struct ps3_instance *instance)
{
struct ps3_qos_context *qos_ctx = &instance->qos_context;
S32 ret = PS3_SUCCESS;
qos_ctx->max_vd_count = instance->ctrl_info.maxVdCount + 1;
qos_ctx->max_pd_count = instance->ctrl_info.maxPdCount;
qos_ctx->inited = 0;
LOG_INFO("hno:%u ps3 qos info:tfifo:%u sas_pd_quota[%u %u] sata_pd_quota[%u %u] nvme_quota[%u %u %u]\n",
PS3_HOST(instance), instance->ctrl_info.qosInfo.tfifoDepth,
instance->ctrl_info.qosInfo.sasHddQuota,
instance->ctrl_info.qosInfo.sasSsdQuota,
instance->ctrl_info.qosInfo.sataHddQuota,
instance->ctrl_info.qosInfo.sataSsdQuota,
instance->ctrl_info.qosInfo.nvmeVdQuota,
instance->ctrl_info.qosInfo.nvmeDirectQuota,
instance->ctrl_info.qosInfo.nvmeNormalQuota);
if (qos_ctx->opts.qos_init) {
if (qos_ctx->opts.qos_init(instance) == PS3_SUCCESS) {
qos_ctx->inited = 1;
LOG_INFO("hno:%u:ps3 qos init success, vd_count:%u pd_count:%u\n",
PS3_HOST(instance), qos_ctx->max_vd_count, qos_ctx->max_pd_count);
} else {
ret = -PS3_FAILED;
}
}
return ret;
}
void ps3_qos_exit(struct ps3_instance *instance)
{
if (instance->qos_context.opts.qos_exit) {
instance->qos_context.opts.qos_exit(instance);
instance->qos_context.inited = 0;
}
}