Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6db52745 authored by Sayali Lokhande's avatar Sayali Lokhande Committed by Subhash Jadavani
Browse files

scsi: ufs: Fix race condition in ufs qcom debugfs driver



In function ufs_qcom_dbg_testbus_cfg_write(), the global
variable ufs_qcom_host (host) is not protected by lock.
In function ufs_qcom_testbug_config(), we are checking this
variable in switch case and there is possibility of race
condition while accessing host variable in both of these
functions. This change fixes the possible race scenario
using spin_lock on host_lock.

Change-Id: I4e3fa1c3b80b92a648965371e12e52352cf80ce5
Signed-off-by: default avatarSayali Lokhande <sayalil@codeaurora.org>
parent cc8c86a2
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, Linux Foundation. All rights reserved.
 * Copyright (c) 2015,2017, Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -117,6 +117,9 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file,
	int ret = 0;
	int major;
	int minor;
	unsigned long flags;
	struct ufs_hba *hba = host->hba;


	ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE,
		&buff_pos, ubuf, cnt);
@@ -142,8 +145,15 @@ static ssize_t ufs_qcom_dbg_testbus_cfg_write(struct file *file,
		goto out;
	}

	if (!ufs_qcom_testbus_cfg_is_ok(host, major, minor)) {
		ret = -EPERM;
		goto out;
	}

	spin_lock_irqsave(hba->host->host_lock, flags);
	host->testbus.select_major = (u8)major;
	host->testbus.select_minor = (u8)minor;
	spin_unlock_irqrestore(hba->host->host_lock, flags);

	/*
	 * Sanity check of the {major, minor} tuple is done in the
+24 −16
Original line number Diff line number Diff line
@@ -2466,12 +2466,13 @@ static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host)
	host->testbus.select_minor = 37;
}

static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host,
		u8 select_major, u8 select_minor)
{
	if (host->testbus.select_major >= TSTBUS_MAX) {
	if (select_major >= TSTBUS_MAX) {
		dev_err(host->hba->dev,
			"%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n",
			__func__, host->testbus.select_major);
			__func__, select_major);
		return false;
	}

@@ -2480,10 +2481,10 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
	 * mappings of select_minor, since there is no harm in
	 * configuring a non-existent select_minor
	 */
	if (host->testbus.select_minor > 0xFF) {
	if (select_minor > 0xFF) {
		dev_err(host->hba->dev,
			"%s: 0x%05X is not a legal testbus option\n",
			__func__, host->testbus.select_minor);
			__func__, select_minor);
		return false;
	}

@@ -2497,16 +2498,16 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
 */
int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
{
	int reg;
	int offset;
	int reg = 0;
	int offset, ret = 0, testbus_sel_offset = 19;
	u32 mask = TEST_BUS_SUB_SEL_MASK;
	unsigned long flags;
	struct ufs_hba *hba;

	if (!host)
		return -EINVAL;

	if (!ufs_qcom_testbus_cfg_is_ok(host))
		return -EPERM;

	hba = host->hba;
	spin_lock_irqsave(hba->host->host_lock, flags);
	switch (host->testbus.select_major) {
	case TSTBUS_UAWM:
		reg = UFS_TEST_BUS_CTRL_0;
@@ -2565,20 +2566,27 @@ int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
	}
	mask <<= offset;

	spin_unlock_irqrestore(hba->host->host_lock, flags);
	if (reg) {
		ufshcd_rmwl(host->hba, TEST_BUS_SEL,
		    (u32)host->testbus.select_major << 19,
		    (u32)host->testbus.select_major << testbus_sel_offset,
		    REG_UFS_CFG1);
		ufshcd_rmwl(host->hba, mask,
		    (u32)host->testbus.select_minor << offset,
		    reg);
	} else {
		dev_err(hba->dev, "%s: Problem setting minor\n", __func__);
		ret = -EINVAL;
		goto out;
	}
	ufs_qcom_enable_test_bus(host);
	/*
	 * Make sure the test bus configuration is
	 * committed before returning.
	 */
	mb();

	return 0;
out:
	return ret;
}

static void ufs_qcom_testbus_read(struct ufs_hba *hba)
+3 −1
Original line number Diff line number Diff line
@@ -100,7 +100,7 @@ enum {
/* bit definitions for REG_UFS_CFG1 register */
#define QUNIPRO_SEL	UFS_BIT(0)
#define TEST_BUS_EN		BIT(18)
#define TEST_BUS_SEL		GENMASK(22, 19)
#define TEST_BUS_SEL		0x780000
#define UFS_REG_TEST_BUS_EN	BIT(30)

/* bit definitions for REG_UFS_CFG2 register */
@@ -390,6 +390,8 @@ ufs_qcom_get_debug_reg_offset(struct ufs_qcom_host *host, u32 reg)
#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)

bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, u8 select_major,
		u8 select_minor);
int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
		void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs,