summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArto Merilainen <amerilainen@nvidia.com>2014-07-14 16:43:37 +0300
committerWinnie Hsu <whsu@nvidia.com>2014-10-17 10:56:48 -0700
commit0f0a500c8528aa9432b1a00a3e148c6856d8eb0f (patch)
tree357b412b6d133f56d92b2a8746f799baccf4e3bc
parent6876f819d21ef127f98999e3f21d83ecd402956b (diff)
video: tegra: host: Fix channel refcount issues
This patch fixes few possible races in channel initialisation and deinitialisation: - If two channel initialisations were running concurrently for the same device and the first initialisation failed, we potentially gave an uninitialised channel to the second requester - If putchannel() triggered uninitialisation, we were still able to give the channel in getchannel(). Bug 200013323 Change-Id: I99fa726db99fbb98401d5703cc2572131907f726 (cherry picked from commit 4fb20fef4a9eaf222c3f6d4c83fcb495f207c478) Signed-off-by: Arto Merilainen <amerilainen@nvidia.com> Reviewed-on: http://git-master/r/498509 Reviewed-on: http://git-master/r/538725 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Shreshtha Sahu <ssahu@nvidia.com> Tested-by: Shreshtha Sahu <ssahu@nvidia.com> Reviewed-by: Venkat Moganty <vmoganty@nvidia.com> Reviewed-by: Shridhar Rasal <srasal@nvidia.com>
-rw-r--r--drivers/video/tegra/host/host1x/host1x_channel.c1
-rw-r--r--drivers/video/tegra/host/nvhost_channel.c54
-rw-r--r--drivers/video/tegra/host/nvhost_channel.h3
3 files changed, 28 insertions, 30 deletions
diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c
index 5b2b4998af92..011b523fdee9 100644
--- a/drivers/video/tegra/host/host1x/host1x_channel.c
+++ b/drivers/video/tegra/host/host1x/host1x_channel.c
@@ -526,7 +526,6 @@ static inline int hwctx_handler_init(struct nvhost_channel *ch)
static int host1x_channel_init(struct nvhost_channel *ch,
struct nvhost_master *dev)
{
- mutex_init(&ch->reflock);
mutex_init(&ch->submitlock);
ch->aperture = host1x_channel_aperture(dev->aperture, ch->chid);
diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c
index 9f0ca9e1268e..05a31d8c3934 100644
--- a/drivers/video/tegra/host/nvhost_channel.c
+++ b/drivers/video/tegra/host/nvhost_channel.c
@@ -130,7 +130,7 @@ int nvhost_channel_release(struct nvhost_device_data *pdata)
return 0;
}
/* Unmap channel from device and free all resources, deinit device */
-int nvhost_channel_unmap(struct nvhost_channel *ch)
+static int nvhost_channel_unmap_locked(struct nvhost_channel *ch)
{
struct nvhost_device_data *pdata;
struct nvhost_master *host;
@@ -145,12 +145,10 @@ int nvhost_channel_unmap(struct nvhost_channel *ch)
pdata = platform_get_drvdata(ch->dev);
host = nvhost_get_host(pdata->pdev);
- mutex_lock(&host->chlist_mutex);
max_channels = host->info.nb_channels;
if (ch->chid == NVHOST_INVALID_CHANNEL) {
dev_err(&host->dev->dev, "Freeing un-mapped channel\n");
- mutex_unlock(&host->chlist_mutex);
return 0;
}
if (ch->error_notifier_ref)
@@ -195,10 +193,9 @@ int nvhost_channel_unmap(struct nvhost_channel *ch)
ch->ctxhandler = NULL;
ch->cur_ctx = NULL;
ch->aperture = NULL;
+ ch->refcount = 0;
pdata->channels[ch->dev_chid] = NULL;
- mutex_unlock(&host->chlist_mutex);
-
return 0;
}
@@ -230,7 +227,7 @@ int nvhost_channel_map(struct nvhost_device_data *pdata,
}
ch = nvhost_check_channel(pdata);
if (ch)
- nvhost_getchannel(ch);
+ ch->refcount++;
mutex_unlock(&host->chlist_mutex);
*channel = ch;
return 0;
@@ -254,32 +251,27 @@ int nvhost_channel_map(struct nvhost_device_data *pdata,
/* Get channel from list and map to device */
ch = host->chlist[index];
- if (!ch) {
- dev_err(&host->dev->dev, "%s: No channel is free\n", __func__);
- mutex_unlock(&host->chlist_mutex);
- return -EBUSY;
- }
- if (ch->chid == NVHOST_INVALID_CHANNEL) {
- ch->dev = pdata->pdev;
- ch->chid = index;
- nvhost_channel_assign(pdata, ch);
- nvhost_set_chanops(ch);
- } else {
+ if (!ch || (ch->chid != NVHOST_INVALID_CHANNEL)) {
dev_err(&host->dev->dev, "%s: wrong channel map\n", __func__);
mutex_unlock(&host->chlist_mutex);
return -EINVAL;
}
+ ch->dev = pdata->pdev;
+ ch->chid = index;
+ nvhost_channel_assign(pdata, ch);
+ nvhost_set_chanops(ch);
+ set_bit(ch->chid, &host->allocated_channels);
+ ch->refcount = 1;
+
/* Initialize channel */
err = nvhost_channel_init(ch, host);
if (err) {
dev_err(&ch->dev->dev, "%s: channel init failed\n", __func__);
+ nvhost_channel_unmap_locked(ch);
mutex_unlock(&host->chlist_mutex);
- nvhost_channel_unmap(ch);
return err;
}
- nvhost_getchannel(ch);
- set_bit(ch->chid, &host->allocated_channels);
/* set next free channel */
if (index >= (max_channels - 1))
@@ -291,8 +283,8 @@ int nvhost_channel_map(struct nvhost_device_data *pdata,
err = pdata->init(ch->dev);
if (err) {
dev_err(&ch->dev->dev, "device init failed\n");
+ nvhost_channel_unmap_locked(ch);
mutex_unlock(&host->chlist_mutex);
- nvhost_channel_unmap(ch);
return err;
}
}
@@ -374,20 +366,28 @@ int nvhost_channel_submit(struct nvhost_job *job)
void nvhost_getchannel(struct nvhost_channel *ch)
{
- atomic_inc(&ch->refcount);
+ struct nvhost_device_data *pdata = platform_get_drvdata(ch->dev);
+ struct nvhost_master *host = nvhost_get_host(pdata->pdev);
+
+ mutex_lock(&host->chlist_mutex);
+ ch->refcount++;
+ mutex_unlock(&host->chlist_mutex);
}
void nvhost_putchannel(struct nvhost_channel *ch, int cnt)
{
- int ref;
+ struct nvhost_device_data *pdata = platform_get_drvdata(ch->dev);
+ struct nvhost_master *host = nvhost_get_host(pdata->pdev);
- ref = atomic_sub_return(cnt, &ch->refcount);
+ mutex_lock(&host->chlist_mutex);
+ ch->refcount -= cnt;
/* WARN on negative reference, with zero reference unmap channel*/
- if (!ref)
- nvhost_channel_unmap(ch);
- else if (ref < 0)
+ if (!ch->refcount)
+ nvhost_channel_unmap_locked(ch);
+ else if (ch->refcount < 0)
WARN_ON(1);
+ mutex_unlock(&host->chlist_mutex);
}
int nvhost_channel_suspend(struct nvhost_channel *ch)
diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h
index 84643a28aca8..026e7561b0dd 100644
--- a/drivers/video/tegra/host/nvhost_channel.h
+++ b/drivers/video/tegra/host/nvhost_channel.h
@@ -47,10 +47,9 @@ struct nvhost_channel_ops {
struct nvhost_channel {
struct nvhost_channel_ops ops;
- atomic_t refcount;
+ int refcount;
int chid;
int dev_chid;
- struct mutex reflock;
struct mutex submitlock;
void __iomem *aperture;
struct nvhost_hwctx *cur_ctx;