mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
misc: bcm-vk: reset_pid support
Add reset support via ioctl. Kill user processes that are open when VK card is reset. If a particular PID has issued the reset request do not kill that process as it issued the ioctl. Co-developed-by: Desmond Yan <desmond.yan@broadcom.com> Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Desmond Yan <desmond.yan@broadcom.com> Signed-off-by: Scott Branden <scott.branden@broadcom.com> Link: https://lore.kernel.org/r/20210120175827.14820-11-scott.branden@broadcom.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
111d746bb4
commit
d63d658f74
@ -468,6 +468,7 @@ irqreturn_t bcm_vk_msgq_irqhandler(int irq, void *dev_id);
|
||||
irqreturn_t bcm_vk_notf_irqhandler(int irq, void *dev_id);
|
||||
int bcm_vk_msg_init(struct bcm_vk *vk);
|
||||
void bcm_vk_msg_remove(struct bcm_vk *vk);
|
||||
void bcm_vk_drain_msg_on_reset(struct bcm_vk *vk);
|
||||
int bcm_vk_sync_msgq(struct bcm_vk *vk, bool force_sync);
|
||||
void bcm_vk_blk_drv_access(struct bcm_vk *vk);
|
||||
s32 bcm_to_h_msg_dequeue(struct bcm_vk *vk);
|
||||
|
@ -504,7 +504,9 @@ void bcm_vk_blk_drv_access(struct bcm_vk *vk)
|
||||
int i;
|
||||
|
||||
/*
|
||||
* kill all the apps
|
||||
* kill all the apps except for the process that is resetting.
|
||||
* If not called during reset, reset_pid will be 0, and all will be
|
||||
* killed.
|
||||
*/
|
||||
spin_lock(&vk->ctx_lock);
|
||||
|
||||
@ -515,10 +517,12 @@ void bcm_vk_blk_drv_access(struct bcm_vk *vk)
|
||||
struct bcm_vk_ctx *ctx;
|
||||
|
||||
list_for_each_entry(ctx, &vk->pid_ht[i].head, node) {
|
||||
dev_dbg(&vk->pdev->dev,
|
||||
"Send kill signal to pid %d\n",
|
||||
ctx->pid);
|
||||
kill_pid(find_vpid(ctx->pid), SIGKILL, 1);
|
||||
if (ctx->pid != vk->reset_pid) {
|
||||
dev_dbg(&vk->pdev->dev,
|
||||
"Send kill signal to pid %d\n",
|
||||
ctx->pid);
|
||||
kill_pid(find_vpid(ctx->pid), SIGKILL, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&vk->ctx_lock);
|
||||
@ -1001,6 +1005,49 @@ err_idx:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_vk_reset_successful(struct bcm_vk *vk)
|
||||
{
|
||||
struct device *dev = &vk->pdev->dev;
|
||||
u32 fw_status, reset_reason;
|
||||
int ret = -EAGAIN;
|
||||
|
||||
/*
|
||||
* Reset could be triggered when the card in several state:
|
||||
* i) in bootROM
|
||||
* ii) after boot1
|
||||
* iii) boot2 running
|
||||
*
|
||||
* i) & ii) - no status bits will be updated. If vkboot1
|
||||
* runs automatically after reset, it will update the reason
|
||||
* to be unknown reason
|
||||
* iii) - reboot reason match + deinit done.
|
||||
*/
|
||||
fw_status = vkread32(vk, BAR_0, VK_BAR_FWSTS);
|
||||
/* immediate exit if interface goes down */
|
||||
if (BCM_VK_INTF_IS_DOWN(fw_status)) {
|
||||
dev_err(dev, "PCIe Intf Down!\n");
|
||||
goto reset_exit;
|
||||
}
|
||||
|
||||
reset_reason = (fw_status & VK_FWSTS_RESET_REASON_MASK);
|
||||
if ((reset_reason == VK_FWSTS_RESET_MBOX_DB) ||
|
||||
(reset_reason == VK_FWSTS_RESET_UNKNOWN))
|
||||
ret = 0;
|
||||
|
||||
/*
|
||||
* if some of the deinit bits are set, but done
|
||||
* bit is not, this is a failure if triggered while boot2 is running
|
||||
*/
|
||||
if ((fw_status & VK_FWSTS_DEINIT_TRIGGERED) &&
|
||||
!(fw_status & VK_FWSTS_RESET_DONE))
|
||||
ret = -EAGAIN;
|
||||
|
||||
reset_exit:
|
||||
dev_dbg(dev, "FW status = 0x%x ret %d\n", fw_status, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bcm_to_v_reset_doorbell(struct bcm_vk *vk, u32 db_val)
|
||||
{
|
||||
vkwrite32(vk, db_val, BAR_0, VK_BAR0_RESET_DB_BASE);
|
||||
@ -1010,12 +1057,16 @@ static int bcm_vk_trigger_reset(struct bcm_vk *vk)
|
||||
{
|
||||
u32 i;
|
||||
u32 value, boot_status;
|
||||
bool is_stdalone, is_boot2;
|
||||
static const u32 bar0_reg_clr_list[] = { BAR_OS_UPTIME,
|
||||
BAR_INTF_VER,
|
||||
BAR_CARD_VOLTAGE,
|
||||
BAR_CARD_TEMPERATURE,
|
||||
BAR_CARD_PWR_AND_THRE };
|
||||
|
||||
/* clean up before pressing the door bell */
|
||||
bcm_vk_drain_msg_on_reset(vk);
|
||||
vkwrite32(vk, 0, BAR_1, VK_BAR1_MSGQ_DEF_RDY);
|
||||
/* make tag '\0' terminated */
|
||||
vkwrite32(vk, 0, BAR_1, VK_BAR1_BOOT1_VER_TAG);
|
||||
|
||||
@ -1026,6 +1077,11 @@ static int bcm_vk_trigger_reset(struct bcm_vk *vk)
|
||||
for (i = 0; i < VK_BAR1_SOTP_REVID_MAX; i++)
|
||||
vkwrite32(vk, 0, BAR_1, VK_BAR1_SOTP_REVID_ADDR(i));
|
||||
|
||||
memset(&vk->card_info, 0, sizeof(vk->card_info));
|
||||
memset(&vk->peerlog_info, 0, sizeof(vk->peerlog_info));
|
||||
memset(&vk->proc_mon_info, 0, sizeof(vk->proc_mon_info));
|
||||
memset(&vk->alert_cnts, 0, sizeof(vk->alert_cnts));
|
||||
|
||||
/*
|
||||
* When boot request fails, the CODE_PUSH_OFFSET stays persistent.
|
||||
* Allowing us to debug the failure. When we call reset,
|
||||
@ -1046,17 +1102,103 @@ static int bcm_vk_trigger_reset(struct bcm_vk *vk)
|
||||
}
|
||||
vkwrite32(vk, value, BAR_0, BAR_CODEPUSH_SBL);
|
||||
|
||||
/* special reset handling */
|
||||
is_stdalone = boot_status & BOOT_STDALONE_RUNNING;
|
||||
is_boot2 = (boot_status & BOOT_STATE_MASK) == BOOT2_RUNNING;
|
||||
if (vk->peer_alert.flags & ERR_LOG_RAMDUMP) {
|
||||
/*
|
||||
* if card is in ramdump mode, it is hitting an error. Don't
|
||||
* reset the reboot reason as it will contain valid info that
|
||||
* is important - simply use special reset
|
||||
*/
|
||||
vkwrite32(vk, VK_BAR0_RESET_RAMPDUMP, BAR_0, VK_BAR_FWSTS);
|
||||
return VK_BAR0_RESET_RAMPDUMP;
|
||||
} else if (is_stdalone && !is_boot2) {
|
||||
dev_info(&vk->pdev->dev, "Hard reset on Standalone mode");
|
||||
bcm_to_v_reset_doorbell(vk, VK_BAR0_RESET_DB_HARD);
|
||||
return VK_BAR0_RESET_DB_HARD;
|
||||
}
|
||||
|
||||
/* reset fw_status with proper reason, and press db */
|
||||
vkwrite32(vk, VK_FWSTS_RESET_MBOX_DB, BAR_0, VK_BAR_FWSTS);
|
||||
bcm_to_v_reset_doorbell(vk, VK_BAR0_RESET_DB_SOFT);
|
||||
|
||||
/* clear other necessary registers records */
|
||||
/* clear other necessary registers and alert records */
|
||||
for (i = 0; i < ARRAY_SIZE(bar0_reg_clr_list); i++)
|
||||
vkwrite32(vk, 0, BAR_0, bar0_reg_clr_list[i]);
|
||||
memset(&vk->host_alert, 0, sizeof(vk->host_alert));
|
||||
memset(&vk->peer_alert, 0, sizeof(vk->peer_alert));
|
||||
/* clear 4096 bits of bitmap */
|
||||
bitmap_clear(vk->bmap, 0, VK_MSG_ID_BITMAP_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long bcm_vk_reset(struct bcm_vk *vk, struct vk_reset __user *arg)
|
||||
{
|
||||
struct device *dev = &vk->pdev->dev;
|
||||
struct vk_reset reset;
|
||||
int ret = 0;
|
||||
u32 ramdump_reset;
|
||||
int special_reset;
|
||||
|
||||
if (copy_from_user(&reset, arg, sizeof(struct vk_reset)))
|
||||
return -EFAULT;
|
||||
|
||||
/* check if any download is in-progress, if so return error */
|
||||
if (test_and_set_bit(BCM_VK_WQ_DWNLD_PEND, vk->wq_offload) != 0) {
|
||||
dev_err(dev, "Download operation pending - skip reset.\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ramdump_reset = vk->peer_alert.flags & ERR_LOG_RAMDUMP;
|
||||
dev_info(dev, "Issue Reset %s\n",
|
||||
ramdump_reset ? "in ramdump mode" : "");
|
||||
|
||||
/*
|
||||
* The following is the sequence of reset:
|
||||
* - send card level graceful shut down
|
||||
* - wait enough time for VK to handle its business, stopping DMA etc
|
||||
* - kill host apps
|
||||
* - Trigger interrupt with DB
|
||||
*/
|
||||
bcm_vk_send_shutdown_msg(vk, VK_SHUTDOWN_GRACEFUL, 0, 0);
|
||||
|
||||
spin_lock(&vk->ctx_lock);
|
||||
if (!vk->reset_pid) {
|
||||
vk->reset_pid = task_pid_nr(current);
|
||||
} else {
|
||||
dev_err(dev, "Reset already launched by process pid %d\n",
|
||||
vk->reset_pid);
|
||||
ret = -EACCES;
|
||||
}
|
||||
spin_unlock(&vk->ctx_lock);
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
|
||||
bcm_vk_blk_drv_access(vk);
|
||||
special_reset = bcm_vk_trigger_reset(vk);
|
||||
|
||||
/*
|
||||
* Wait enough time for card os to deinit
|
||||
* and populate the reset reason.
|
||||
*/
|
||||
msleep(BCM_VK_DEINIT_TIME_MS);
|
||||
|
||||
if (special_reset) {
|
||||
/* if it is special ramdump reset, return the type to user */
|
||||
reset.arg2 = special_reset;
|
||||
if (copy_to_user(arg, &reset, sizeof(reset)))
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
ret = bcm_vk_reset_successful(vk);
|
||||
}
|
||||
|
||||
err_exit:
|
||||
clear_bit(BCM_VK_WQ_DWNLD_PEND, vk->wq_offload);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long bcm_vk_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret = -EINVAL;
|
||||
@ -1075,6 +1217,10 @@ static long bcm_vk_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
ret = bcm_vk_load_image(vk, argp);
|
||||
break;
|
||||
|
||||
case VK_IOCTL_RESET:
|
||||
ret = bcm_vk_reset(vk, argp);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -209,6 +209,15 @@ static struct bcm_vk_ctx *bcm_vk_get_ctx(struct bcm_vk *vk, const pid_t pid)
|
||||
|
||||
spin_lock(&vk->ctx_lock);
|
||||
|
||||
/* check if it is in reset, if so, don't allow */
|
||||
if (vk->reset_pid) {
|
||||
dev_err(&vk->pdev->dev,
|
||||
"No context allowed during reset by pid %d\n",
|
||||
vk->reset_pid);
|
||||
|
||||
goto in_reset_exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vk->ctx); i++) {
|
||||
if (!vk->ctx[i].in_use) {
|
||||
vk->ctx[i].in_use = true;
|
||||
@ -237,6 +246,7 @@ static struct bcm_vk_ctx *bcm_vk_get_ctx(struct bcm_vk *vk, const pid_t pid)
|
||||
init_waitqueue_head(&ctx->rd_wq);
|
||||
|
||||
all_in_use_exit:
|
||||
in_reset_exit:
|
||||
spin_unlock(&vk->ctx_lock);
|
||||
|
||||
return ctx;
|
||||
@ -381,6 +391,12 @@ static void bcm_vk_drain_all_pend(struct device *dev,
|
||||
num, ctx->idx);
|
||||
}
|
||||
|
||||
void bcm_vk_drain_msg_on_reset(struct bcm_vk *vk)
|
||||
{
|
||||
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_v_msg_chan, NULL);
|
||||
bcm_vk_drain_all_pend(&vk->pdev->dev, &vk->to_h_msg_chan, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to sync up the messages queue info that is provided by BAR1
|
||||
*/
|
||||
@ -712,13 +728,22 @@ static int bcm_vk_handle_last_sess(struct bcm_vk *vk, const pid_t pid,
|
||||
|
||||
/*
|
||||
* don't send down or do anything if message queue is not initialized
|
||||
* and if it is the reset session, clear it.
|
||||
*/
|
||||
if (!bcm_vk_drv_access_ok(vk))
|
||||
if (!bcm_vk_drv_access_ok(vk)) {
|
||||
if (vk->reset_pid == pid)
|
||||
vk->reset_pid = 0;
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "No more sessions, shut down pid %d\n", pid);
|
||||
|
||||
rc = bcm_vk_send_shutdown_msg(vk, VK_SHUTDOWN_PID, pid, q_num);
|
||||
/* only need to do it if it is not the reset process */
|
||||
if (vk->reset_pid != pid)
|
||||
rc = bcm_vk_send_shutdown_msg(vk, VK_SHUTDOWN_PID, pid, q_num);
|
||||
else
|
||||
/* put reset_pid to 0 if it is exiting last session */
|
||||
vk->reset_pid = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -1122,6 +1147,17 @@ ssize_t bcm_vk_write(struct file *p_file,
|
||||
int dir;
|
||||
struct _vk_data *data;
|
||||
|
||||
/*
|
||||
* check if we are in reset, if so, no buffer transfer is
|
||||
* allowed and return error.
|
||||
*/
|
||||
if (vk->reset_pid) {
|
||||
dev_dbg(dev, "No Transfer allowed during reset, pid %d.\n",
|
||||
ctx->pid);
|
||||
rc = -EACCES;
|
||||
goto write_free_msgid;
|
||||
}
|
||||
|
||||
num_planes = entry->to_v_msg[0].cmd & VK_CMD_PLANES_MASK;
|
||||
if ((entry->to_v_msg[0].cmd & VK_CMD_MASK) == VK_CMD_DOWNLOAD)
|
||||
dir = DMA_FROM_DEVICE;
|
||||
|
Loading…
Reference in New Issue
Block a user