mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
video: rockchip: mpp: add iommu fault handle
when iommu fault, dump mem region and register. Change-Id: Ifa5f175c6f0d8f7688c2d65a4423d975c37fe1af Signed-off-by: Ding Wei <leo.ding@rock-chips.com>
This commit is contained in:
@@ -1186,14 +1186,15 @@ int mpp_translate_reg_address(struct mpp_session *session,
|
||||
|
||||
mem_region = mpp_task_attach_fd(task, usr_fd);
|
||||
if (IS_ERR(mem_region)) {
|
||||
mpp_debug(DEBUG_IOMMU, "reg[%3d]: %08x failed\n",
|
||||
tbl[i], reg[tbl[i]]);
|
||||
mpp_debug(DEBUG_IOMMU, "reg[%3d]: %08x fd %d failed\n",
|
||||
tbl[i], reg[tbl[i]], usr_fd);
|
||||
return PTR_ERR(mem_region);
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_IOMMU,
|
||||
"reg[%3d]: %d => %pad, offset %10d, size %lx\n",
|
||||
tbl[i], usr_fd, &mem_region->iova,
|
||||
offset, mem_region->len);
|
||||
mem_region->reg_idx = tbl[i];
|
||||
mpp_debug(DEBUG_IOMMU, "reg[%3d]: %3d => %pad + offset %10d\n",
|
||||
tbl[i], usr_fd, &mem_region->iova, offset);
|
||||
reg[tbl[i]] = mem_region->iova + offset;
|
||||
}
|
||||
|
||||
@@ -1332,6 +1333,51 @@ int mpp_task_finalize(struct mpp_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_iommu_handle(struct iommu_domain *iommu,
|
||||
struct device *iommu_dev,
|
||||
unsigned long iova,
|
||||
int status, void *arg)
|
||||
{
|
||||
u32 i, s, e;
|
||||
struct mpp_mem_region *mem = NULL, *n;
|
||||
struct mpp_dev *mpp = (struct mpp_dev *)arg;
|
||||
struct mpp_task *task = mpp->cur_task;
|
||||
|
||||
dev_err(mpp->dev, "fault addr 0x%08lx status %x\n", iova, status);
|
||||
|
||||
if (!task)
|
||||
return -EIO;
|
||||
|
||||
if (!list_empty(&task->mem_region_list)) {
|
||||
/* dump mem region */
|
||||
mpp_err("--- dump mem region ---\n");
|
||||
list_for_each_entry_safe(mem, n,
|
||||
&task->mem_region_list,
|
||||
reg_link) {
|
||||
mpp_err("reg[%3d]: %pad, size %lx\n",
|
||||
mem->reg_idx, &mem->iova, mem->len);
|
||||
}
|
||||
} else {
|
||||
dev_err(mpp->dev, "no memory region mapped\n");
|
||||
}
|
||||
|
||||
/* dump register */
|
||||
s = mpp->var->hw_info->reg_start;
|
||||
e = mpp->var->hw_info->reg_end;
|
||||
mpp_err("--- dump register ---\n");
|
||||
for (i = s; i <= e; i++) {
|
||||
u32 reg = i * sizeof(u32);
|
||||
|
||||
mpp_err("reg[%3d]: %08x\n",
|
||||
i, readl_relaxed(mpp->reg_base + reg));
|
||||
}
|
||||
|
||||
if (mpp->iommu_info->hdl)
|
||||
mpp->iommu_info->hdl(iommu, iommu_dev, iova, status, arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The device will do more probing work after this */
|
||||
int mpp_dev_probe(struct mpp_dev *mpp,
|
||||
struct platform_device *pdev)
|
||||
@@ -1411,6 +1457,12 @@ int mpp_dev_probe(struct mpp_dev *mpp,
|
||||
if (ret)
|
||||
goto failed_init;
|
||||
}
|
||||
/* set iommu fault handler */
|
||||
if (!IS_ERR(mpp->iommu_info))
|
||||
iommu_set_fault_handler(mpp->iommu_info->domain,
|
||||
mpp_iommu_handle, mpp);
|
||||
|
||||
/* read hardware id */
|
||||
if (hw_info->reg_id >= 0) {
|
||||
if (mpp->hw_ops->power_on)
|
||||
mpp->hw_ops->power_on(mpp);
|
||||
|
||||
@@ -195,6 +195,8 @@ struct mpp_dev {
|
||||
atomic_t total_running;
|
||||
/* task for work queue */
|
||||
struct workqueue_struct *workq;
|
||||
/* current task in running */
|
||||
struct mpp_task *cur_task;
|
||||
/* set session max buffers */
|
||||
u32 session_max_buffers;
|
||||
struct mpp_taskqueue *queue;
|
||||
|
||||
@@ -64,6 +64,7 @@ struct mpp_iommu_info {
|
||||
struct iommu_domain *domain;
|
||||
struct iommu_group *group;
|
||||
struct mpp_rk_iommu *iommu;
|
||||
iommu_fault_handler_t hdl;
|
||||
};
|
||||
|
||||
struct mpp_dma_session *
|
||||
|
||||
@@ -185,7 +185,6 @@ struct rkvdec_dev {
|
||||
enum RKVDEC_STATE state;
|
||||
unsigned long aux_iova;
|
||||
struct page *aux_page;
|
||||
struct rkvdec_task *current_task;
|
||||
/* regulator and devfreq */
|
||||
struct regulator *vdd;
|
||||
struct devfreq *devfreq;
|
||||
@@ -901,7 +900,7 @@ static int rkvdec_run(struct mpp_dev *mpp,
|
||||
mpp_write_req(mpp, task->reg, s, e, reg_en);
|
||||
}
|
||||
/* init current task */
|
||||
dec->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Flush the register before the start the device */
|
||||
wmb();
|
||||
mpp_write(mpp, RKVDEC_REG_INT_EN,
|
||||
@@ -961,17 +960,17 @@ static int rkvdec_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct rkvdec_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
struct rkvdec_dev *dec = to_rkvdec_dev(mpp);
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = dec->current_task;
|
||||
if (!task) {
|
||||
dev_err(dec->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
mpp_time_diff(&task->mpp_task);
|
||||
dec->current_task = NULL;
|
||||
mpp_time_diff(mpp_task);
|
||||
mpp->cur_task = NULL;
|
||||
task = to_rkvdec_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
switch (dec->state) {
|
||||
case RKVDEC_STATE_NORMAL:
|
||||
@@ -987,7 +986,6 @@ static int rkvdec_isr(struct mpp_dev *mpp)
|
||||
if (err_mask & task->irq_status)
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_task_finish(mpp_task->session, mpp_task);
|
||||
|
||||
mpp_debug_leave();
|
||||
@@ -1204,25 +1202,8 @@ static int rkvdec_3328_iommu_hdl(struct iommu_domain *iommu,
|
||||
int status, void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = (struct device *)arg;
|
||||
struct platform_device *pdev = NULL;
|
||||
struct rkvdec_dev *dec = NULL;
|
||||
struct mpp_dev *mpp = NULL;
|
||||
|
||||
pdev = container_of(dev, struct platform_device, dev);
|
||||
if (!pdev) {
|
||||
dev_err(dev, "invalid platform_device\n");
|
||||
ret = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dec = platform_get_drvdata(pdev);
|
||||
if (!dec) {
|
||||
dev_err(dev, "invalid device instance\n");
|
||||
ret = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
mpp = &dec->mpp;
|
||||
struct mpp_dev *mpp = (struct mpp_dev *)arg;
|
||||
struct rkvdec_dev *dec = to_rkvdec_dev(mpp);
|
||||
|
||||
/*
|
||||
* defeat workaround, invalidate address generated when rk322x
|
||||
@@ -1246,7 +1227,6 @@ static int rkvdec_3328_iommu_hdl(struct iommu_domain *iommu,
|
||||
dec->aux_iova = page_iova;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1346,9 +1326,7 @@ static int rkvdec_3328_init(struct mpp_dev *mpp)
|
||||
goto done;
|
||||
}
|
||||
dec->aux_iova = 0;
|
||||
iommu_set_fault_handler(mpp->iommu_info->domain,
|
||||
rkvdec_3328_iommu_hdl,
|
||||
mpp->dev);
|
||||
mpp->iommu_info->hdl = rkvdec_3328_iommu_hdl;
|
||||
|
||||
ret = rkvdec_devfreq_init(mpp);
|
||||
done:
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
/* The maximum registers number of all the version */
|
||||
#define RKVENC_REG_L1_NUM 780
|
||||
#define RKVENC_REG_L2_NUM 200
|
||||
#define RKVENC_REG_START_INDEX 0
|
||||
#define RKVENC_REG_END_INDEX 131
|
||||
/* rkvenc register info */
|
||||
#define RKVENC_REG_NUM 112
|
||||
#define RKVENC_REG_HW_ID_INDEX 0
|
||||
@@ -159,10 +161,6 @@ struct rkvenc_dev {
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct reset_control *rst_core;
|
||||
|
||||
void *current_task;
|
||||
|
||||
struct mpp_dma_buffer *buffer;
|
||||
};
|
||||
|
||||
struct link_table_elem {
|
||||
@@ -176,6 +174,8 @@ static struct mpp_hw_info rkvenc_hw_info = {
|
||||
.reg_num = RKVENC_REG_NUM,
|
||||
.reg_id = RKVENC_REG_HW_ID_INDEX,
|
||||
.reg_en = RKVENC_ENC_START_INDEX,
|
||||
.reg_start = RKVENC_REG_START_INDEX,
|
||||
.reg_end = RKVENC_REG_END_INDEX,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -370,7 +370,6 @@ static int rkvenc_read_req_l2(struct mpp_dev *mpp,
|
||||
static int rkvenc_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct rkvenc_task *task = to_rkvenc_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
@@ -409,7 +408,7 @@ static int rkvenc_run(struct mpp_dev *mpp,
|
||||
}
|
||||
}
|
||||
/* init current task */
|
||||
enc->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Flush the register before the start the device */
|
||||
wmb();
|
||||
mpp_write(mpp, RKVENC_ENC_START_BASE, task->reg[reg_en]);
|
||||
@@ -446,21 +445,18 @@ static int rkvenc_irq(struct mpp_dev *mpp)
|
||||
static int rkvenc_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
struct rkvenc_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = (struct rkvenc_task *)enc->current_task;
|
||||
if (!task) {
|
||||
dev_err(enc->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
enc->current_task = NULL;
|
||||
mpp->cur_task = NULL;
|
||||
task = to_rkvenc_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", task->irq_status);
|
||||
|
||||
if (task->irq_status & RKVENC_INT_ERROR_BITS) {
|
||||
|
||||
@@ -127,7 +127,6 @@ struct vdpu_dev {
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vdpu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vdpu_v1_hw_info = {
|
||||
@@ -399,14 +398,10 @@ static int vdpu_run(struct mpp_dev *mpp,
|
||||
{
|
||||
u32 i;
|
||||
u32 reg_en;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vdpu_task(mpp_task);
|
||||
dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VDPU1_REG_CLR_CACHE_BASE, 1);
|
||||
/* set registers for hardware */
|
||||
@@ -419,7 +414,7 @@ static int vdpu_run(struct mpp_dev *mpp,
|
||||
mpp_write_req(mpp, task->reg, s, e, reg_en);
|
||||
}
|
||||
/* init current task */
|
||||
dec->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Flush the register before the start the device */
|
||||
wmb();
|
||||
mpp_write(mpp, VDPU1_REG_DEC_INT_EN,
|
||||
@@ -610,8 +605,6 @@ static int vdpu_probe_width(struct mpp_dev *mpp,
|
||||
case VDPU1_ID_9190:
|
||||
width = VDPU1_GET_WIDTH(task->reg[VDPU1_RGE_WIDTH_INDEX]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return width;
|
||||
@@ -687,19 +680,16 @@ static int vdpu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = dec->current_task;
|
||||
if (!task) {
|
||||
dev_err(dec->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
dec->current_task = NULL;
|
||||
mpp->cur_task = NULL;
|
||||
task = to_vdpu_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
@@ -113,7 +113,6 @@ struct vdpu_dev {
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vdpu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vdpu_v2_hw_info = {
|
||||
@@ -352,14 +351,10 @@ static int vdpu_run(struct mpp_dev *mpp,
|
||||
{
|
||||
u32 i;
|
||||
u32 reg_en;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vdpu_task(mpp_task);
|
||||
dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VDPU2_REG_CLR_CACHE_BASE, 1);
|
||||
/* set registers for hardware */
|
||||
@@ -372,7 +367,7 @@ static int vdpu_run(struct mpp_dev *mpp,
|
||||
mpp_write_req(mpp, task->reg, s, e, reg_en);
|
||||
}
|
||||
/* init current task */
|
||||
dec->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Flush the registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VDPU2_REG_DEC_EN,
|
||||
@@ -598,19 +593,16 @@ static int vdpu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = dec->current_task;
|
||||
if (!task) {
|
||||
dev_err(dec->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
dec->current_task = NULL;
|
||||
mpp->cur_task = NULL;
|
||||
task = to_vdpu_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
@@ -95,8 +95,6 @@ struct vepu_dev {
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
|
||||
struct vepu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vepu_v1_hw_info = {
|
||||
@@ -268,14 +266,10 @@ static int vepu_run(struct mpp_dev *mpp,
|
||||
{
|
||||
u32 i;
|
||||
u32 reg_en;
|
||||
struct vepu_task *task = NULL;
|
||||
struct vepu_dev *enc = NULL;
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vepu_task(mpp_task);
|
||||
enc = to_vepu_dev(mpp);
|
||||
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VEPU1_REG_CLR_CACHE_BASE, 1);
|
||||
/* set registers for hardware */
|
||||
@@ -292,7 +286,7 @@ static int vepu_run(struct mpp_dev *mpp,
|
||||
mpp_write_req(mpp, task->reg, s, e, reg_en);
|
||||
}
|
||||
/* init current task */
|
||||
enc->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Last, flush start registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VEPU1_REG_ENC_EN,
|
||||
@@ -318,19 +312,16 @@ static int vepu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vepu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = enc->current_task;
|
||||
if (!task) {
|
||||
dev_err(enc->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
enc->current_task = NULL;
|
||||
mpp->cur_task = NULL;
|
||||
task = to_vepu_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
@@ -103,7 +103,6 @@ struct vepu_dev {
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vepu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vepu_v2_hw_info = {
|
||||
@@ -276,16 +275,10 @@ static int vepu_run(struct mpp_dev *mpp,
|
||||
{
|
||||
u32 i;
|
||||
u32 reg_en;
|
||||
struct vepu_task *task = NULL;
|
||||
struct vepu_dev *enc = NULL;
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vepu_task(mpp_task);
|
||||
enc = to_vepu_dev(mpp);
|
||||
|
||||
/* FIXME: spin lock here */
|
||||
enc->current_task = task;
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VEPU2_REG_CLR_CACHE_BASE, 1);
|
||||
|
||||
@@ -302,7 +295,7 @@ static int vepu_run(struct mpp_dev *mpp,
|
||||
mpp_write_req(mpp, task->reg, s, e, reg_en);
|
||||
}
|
||||
/* init current task */
|
||||
enc->current_task = task;
|
||||
mpp->cur_task = mpp_task;
|
||||
/* Last, flush the registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VEPU2_REG_ENC_EN,
|
||||
@@ -328,19 +321,16 @@ static int vepu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vepu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct mpp_task *mpp_task = mpp->cur_task;
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = enc->current_task;
|
||||
if (!task) {
|
||||
dev_err(enc->mpp.dev, "no current task\n");
|
||||
if (!mpp_task) {
|
||||
dev_err(mpp->dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
enc->current_task = NULL;
|
||||
mpp->cur_task = NULL;
|
||||
task = to_vepu_task(mpp_task);
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
Reference in New Issue
Block a user