mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
coe: fix driver reinsert
Fix CoE driver rmmod/insmode cleanup logic to avoid resource leacking. Jira CT26X-1892 Change-Id: I8539fd32965adf0fb302d7177a22fa1dd94c75d6 Signed-off-by: Igor Mitsyanko <imitsyanko@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3419639 GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: svcacv <svcacv@nvidia.com> Reviewed-by: Narendra Kondapalli <nkondapalli@nvidia.com> Tested-by: Raki Hassan <rakibulh@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
344aa664d2
commit
45423af499
@@ -321,6 +321,7 @@ static struct device *g_rtcpu_dev = NULL;
|
|||||||
static void *g_rx_descr_mem_area = NULL;
|
static void *g_rx_descr_mem_area = NULL;
|
||||||
static dma_addr_t g_rxdesc_mem_dma_rce;
|
static dma_addr_t g_rxdesc_mem_dma_rce;
|
||||||
static struct sg_table g_rxdesc_rce_sgt;
|
static struct sg_table g_rxdesc_rce_sgt;
|
||||||
|
static int32_t g_rx_descr_mem_refcount;
|
||||||
|
|
||||||
static inline struct coe_channel_state *coe_channel_arr_find_free(u32 * const arr_idx)
|
static inline struct coe_channel_state *coe_channel_arr_find_free(u32 * const arr_idx)
|
||||||
{
|
{
|
||||||
@@ -1556,6 +1557,8 @@ static int coe_alloc_rx_descr_mem_area(struct coe_state * const s)
|
|||||||
dev_err(&s->pdev->dev, "Multiple RCE CPUs not supported\n");
|
dev_err(&s->pdev->dev, "Multiple RCE CPUs not supported\n");
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
/* Memory already allocated, just increment reference count */
|
||||||
|
g_rx_descr_mem_refcount++;
|
||||||
} else {
|
} else {
|
||||||
g_rx_descr_mem_area = dma_alloc_coherent(s->rtcpu_dev,
|
g_rx_descr_mem_area = dma_alloc_coherent(s->rtcpu_dev,
|
||||||
alloc_size,
|
alloc_size,
|
||||||
@@ -1599,6 +1602,7 @@ static int coe_alloc_rx_descr_mem_area(struct coe_state * const s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_rtcpu_dev = s->rtcpu_dev;
|
g_rtcpu_dev = s->rtcpu_dev;
|
||||||
|
g_rx_descr_mem_refcount = 1;
|
||||||
|
|
||||||
dev_info(&s->pdev->dev, "Rx descr RCE addr=0x%llx len=%lu\n",
|
dev_info(&s->pdev->dev, "Rx descr RCE addr=0x%llx len=%lu\n",
|
||||||
g_rxdesc_mem_dma_rce, alloc_size);
|
g_rxdesc_mem_dma_rce, alloc_size);
|
||||||
@@ -1620,6 +1624,18 @@ static int coe_alloc_rx_descr_mem_area(struct coe_state * const s)
|
|||||||
pib_start_offset, pktinfo_size_per_mgbe);
|
pib_start_offset, pktinfo_size_per_mgbe);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&s->pdev->dev, "Failed to map Pktinfo ret=%d\n", ret);
|
dev_err(&s->pdev->dev, "Failed to map Pktinfo ret=%d\n", ret);
|
||||||
|
/* Decrement refcount on failure */
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
|
g_rx_descr_mem_refcount--;
|
||||||
|
if (g_rx_descr_mem_refcount <= 0) {
|
||||||
|
sg_free_table(&g_rxdesc_rce_sgt);
|
||||||
|
dma_free_coherent(g_rtcpu_dev, alloc_size,
|
||||||
|
g_rx_descr_mem_area, g_rxdesc_mem_dma_rce);
|
||||||
|
g_rx_descr_mem_area = NULL;
|
||||||
|
g_rtcpu_dev = NULL;
|
||||||
|
g_rx_descr_mem_refcount = 0;
|
||||||
|
}
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1642,6 +1658,27 @@ static int coe_alloc_rx_descr_mem_area(struct coe_state * const s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void coe_free_rx_descr_mem_area(void)
|
||||||
|
{
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
|
|
||||||
|
if (g_rx_descr_mem_area != NULL) {
|
||||||
|
g_rx_descr_mem_refcount--;
|
||||||
|
if (g_rx_descr_mem_refcount <= 0) {
|
||||||
|
const size_t alloc_size = COE_TOTAL_RXDESCR_MEM_SIZE;
|
||||||
|
|
||||||
|
sg_free_table(&g_rxdesc_rce_sgt);
|
||||||
|
dma_free_coherent(g_rtcpu_dev, alloc_size,
|
||||||
|
g_rx_descr_mem_area, g_rxdesc_mem_dma_rce);
|
||||||
|
g_rx_descr_mem_area = NULL;
|
||||||
|
g_rtcpu_dev = NULL;
|
||||||
|
g_rx_descr_mem_refcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t coe_mgbe_parse_dt_dmachans(struct coe_state * const s,
|
static int32_t coe_mgbe_parse_dt_dmachans(struct coe_state * const s,
|
||||||
u32 * const vm_chans,
|
u32 * const vm_chans,
|
||||||
size_t max_num_chans)
|
size_t max_num_chans)
|
||||||
@@ -1702,6 +1739,30 @@ static int32_t coe_mgbe_parse_dt_dmachans(struct coe_state * const s,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void coe_destroy_channels(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct coe_channel_state *ch;
|
||||||
|
u32 ch_id;
|
||||||
|
|
||||||
|
mutex_lock(&coe_channels_arr_lock);
|
||||||
|
for (ch_id = 0U; ch_id < ARRAY_SIZE(coe_channels_arr); ch_id++) {
|
||||||
|
ch = &coe_channels_arr[ch_id];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find all channel devices that are children of the platform
|
||||||
|
* device being removed and destroy them. This cleans up all
|
||||||
|
* channels, whether they were opened or not.
|
||||||
|
*/
|
||||||
|
if (ch->dev != NULL && ch->dev->parent == &pdev->dev) {
|
||||||
|
coe_channel_close(ch);
|
||||||
|
device_destroy(coe_channel_class, ch->devt);
|
||||||
|
ch->dev = NULL;
|
||||||
|
ch->devt = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&coe_channels_arr_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static int camrtc_coe_probe(struct platform_device *pdev)
|
static int camrtc_coe_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct coe_state *s;
|
struct coe_state *s;
|
||||||
@@ -1727,13 +1788,15 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
s->mgbe_dev = camrtc_coe_get_linked_device(dev,
|
s->mgbe_dev = camrtc_coe_get_linked_device(dev,
|
||||||
"nvidia,eth_controller", 0U);
|
"nvidia,eth_controller", 0U);
|
||||||
if (s->mgbe_dev == NULL) {
|
if (s->mgbe_dev == NULL) {
|
||||||
return -ENOENT;
|
ret = -ENOENT;
|
||||||
|
goto err_put_rtcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_coe_channels = coe_mgbe_parse_dt_dmachans(s, dma_chans_arr,
|
num_coe_channels = coe_mgbe_parse_dt_dmachans(s, dma_chans_arr,
|
||||||
ARRAY_SIZE(dma_chans_arr));
|
ARRAY_SIZE(dma_chans_arr));
|
||||||
if (num_coe_channels < 0) {
|
if (num_coe_channels < 0) {
|
||||||
return num_coe_channels;
|
ret = num_coe_channels;
|
||||||
|
goto err_put_devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, s);
|
platform_set_drvdata(pdev, s);
|
||||||
@@ -1746,7 +1809,7 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
ret = register_netdevice_notifier(&s->netdev_nb);
|
ret = register_netdevice_notifier(&s->netdev_nb);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(dev, "CoE failed to register notifier\n");
|
dev_err(dev, "CoE failed to register notifier\n");
|
||||||
return ret;
|
goto err_put_devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO take from DT? */
|
/* TODO take from DT? */
|
||||||
@@ -1763,7 +1826,8 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (s->rx_ring_size > COE_MGBE_MAX_RXDESC_NUM) {
|
if (s->rx_ring_size > COE_MGBE_MAX_RXDESC_NUM) {
|
||||||
dev_err(dev, "Invalid Rx ring size %u\n", s->rx_ring_size);
|
dev_err(dev, "Invalid Rx ring size %u\n", s->rx_ring_size);
|
||||||
return -ENOSPC;
|
ret = -ENOSPC;
|
||||||
|
goto err_unregister_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->rx_pktinfo_ring_size != 256U &&
|
if (s->rx_pktinfo_ring_size != 256U &&
|
||||||
@@ -1771,21 +1835,36 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
s->rx_pktinfo_ring_size != 2048U &&
|
s->rx_pktinfo_ring_size != 2048U &&
|
||||||
s->rx_pktinfo_ring_size != 4096U) {
|
s->rx_pktinfo_ring_size != 4096U) {
|
||||||
dev_err(dev, "Invalid pktinfo ring size %u\n", s->rx_pktinfo_ring_size);
|
dev_err(dev, "Invalid pktinfo ring size %u\n", s->rx_pktinfo_ring_size);
|
||||||
return -ENOSPC;
|
ret = -ENOSPC;
|
||||||
|
goto err_unregister_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->mgbe_id >= MAX_NUM_COE_DEVICES) {
|
if (s->mgbe_id >= MAX_NUM_COE_DEVICES) {
|
||||||
dev_err(dev, "Invalid MGBE ID %u\n", s->mgbe_id);
|
dev_err(dev, "Invalid MGBE ID %u\n", s->mgbe_id);
|
||||||
return -EBADFD;
|
ret = -EBADFD;
|
||||||
|
goto err_unregister_notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
|
list_for_each_entry(check_state, &coe_device_list, device_entry) {
|
||||||
|
if (s->mgbe_id == check_state->mgbe_id) {
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
dev_err(dev, "Device already exists for mgbe_id=%u\n",
|
||||||
|
s->mgbe_id);
|
||||||
|
ret = -EEXIST;
|
||||||
|
goto err_unregister_notifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list_add(&s->device_entry, &coe_device_list);
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
|
||||||
ret = coe_mgbe_init_pdmas(s);
|
ret = coe_mgbe_init_pdmas(s);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err_del_from_list;
|
||||||
|
|
||||||
ret = coe_alloc_rx_descr_mem_area(s);
|
ret = coe_alloc_rx_descr_mem_area(s);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err_del_from_list;
|
||||||
|
|
||||||
for (u32 ch = 0U; ch < num_coe_channels; ch++) {
|
for (u32 ch = 0U; ch < num_coe_channels; ch++) {
|
||||||
u32 arr_idx;
|
u32 arr_idx;
|
||||||
@@ -1798,17 +1877,18 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
if (chan == NULL) {
|
if (chan == NULL) {
|
||||||
dev_err(dev, "No free channel slots ch=%u\n", ch);
|
dev_err(dev, "No free channel slots ch=%u\n", ch);
|
||||||
mutex_unlock(&coe_channels_arr_lock);
|
mutex_unlock(&coe_channels_arr_lock);
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto err_destroy_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
chan->devt = MKDEV(coe_channel_major, arr_idx);
|
chan->devt = MKDEV(coe_channel_major, arr_idx);
|
||||||
chan->dev = device_create(coe_channel_class, NULL, chan->devt, NULL,
|
chan->dev = device_create(coe_channel_class, dev, chan->devt, NULL,
|
||||||
"coe-chan-%u", arr_idx);
|
"coe-chan-%u", arr_idx);
|
||||||
if (IS_ERR(chan->dev)) {
|
if (IS_ERR(chan->dev)) {
|
||||||
ret = PTR_ERR(chan->dev);
|
ret = PTR_ERR(chan->dev);
|
||||||
chan->dev = NULL;
|
chan->dev = NULL;
|
||||||
mutex_unlock(&coe_channels_arr_lock);
|
mutex_unlock(&coe_channels_arr_lock);
|
||||||
return ret;
|
goto err_destroy_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&coe_channels_arr_lock);
|
mutex_unlock(&coe_channels_arr_lock);
|
||||||
@@ -1833,53 +1913,54 @@ static int camrtc_coe_probe(struct platform_device *pdev)
|
|||||||
dma_chans_arr[ch], s->vdma2pdma_map[dma_chans_arr[ch]]);
|
dma_chans_arr[ch], s->vdma2pdma_map[dma_chans_arr[ch]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&coe_device_list_lock);
|
|
||||||
list_for_each_entry(check_state, &coe_device_list, device_entry) {
|
|
||||||
if (s->mgbe_id == check_state->mgbe_id) {
|
|
||||||
mutex_unlock(&coe_device_list_lock);
|
|
||||||
dev_err(dev, "Device already exists for mgbe_id=%u\n",
|
|
||||||
s->mgbe_id);
|
|
||||||
return -EEXIST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add(&s->device_entry, &coe_device_list);
|
|
||||||
mutex_unlock(&coe_device_list_lock);
|
|
||||||
|
|
||||||
dev_info(dev, "Camera Over Eth controller %s num_chans=%u IRQ=%u\n",
|
dev_info(dev, "Camera Over Eth controller %s num_chans=%u IRQ=%u\n",
|
||||||
dev_name(s->mgbe_dev), num_coe_channels, s->mgbe_irq_id);
|
dev_name(s->mgbe_dev), num_coe_channels, s->mgbe_irq_id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_destroy_channels:
|
||||||
|
coe_destroy_channels(pdev);
|
||||||
|
dma_unmap_sg(s->mgbe_dev, s->rx_pktinfo_mgbe_sgt.sgl,
|
||||||
|
s->rx_pktinfo_mgbe_sgt.orig_nents, DMA_BIDIRECTIONAL);
|
||||||
|
sg_free_table(&s->rx_pktinfo_mgbe_sgt);
|
||||||
|
/* Decrement global memory reference count on error */
|
||||||
|
coe_free_rx_descr_mem_area();
|
||||||
|
err_del_from_list:
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
|
list_del(&s->device_entry);
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
err_unregister_notifier:
|
||||||
|
unregister_netdevice_notifier(&s->netdev_nb);
|
||||||
|
err_put_devices:
|
||||||
|
put_device(s->mgbe_dev);
|
||||||
|
err_put_rtcpu:
|
||||||
|
put_device(s->rtcpu_dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int camrtc_coe_remove(struct platform_device *pdev)
|
static int camrtc_coe_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct coe_state * const s = platform_get_drvdata(pdev);
|
struct coe_state * const s = platform_get_drvdata(pdev);
|
||||||
struct coe_channel_state *ch;
|
|
||||||
struct coe_channel_state *ch_tmp;
|
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "tegra-camrtc-capture-coe remove\n");
|
dev_dbg(&pdev->dev, "tegra-camrtc-capture-coe remove\n");
|
||||||
|
|
||||||
|
coe_destroy_channels(pdev);
|
||||||
|
|
||||||
unregister_netdevice_notifier(&s->netdev_nb);
|
unregister_netdevice_notifier(&s->netdev_nb);
|
||||||
|
|
||||||
mutex_lock(&coe_channels_arr_lock);
|
|
||||||
|
|
||||||
list_for_each_entry_safe(ch, ch_tmp, &s->channels, list_entry) {
|
|
||||||
coe_channel_close(ch);
|
|
||||||
if (ch->dev != NULL)
|
|
||||||
device_destroy(coe_channel_class, ch->devt);
|
|
||||||
ch->dev = NULL;
|
|
||||||
ch->devt = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&coe_channels_arr_lock);
|
|
||||||
|
|
||||||
if (s->rx_pktinfo_mgbe_sgt.sgl != NULL) {
|
if (s->rx_pktinfo_mgbe_sgt.sgl != NULL) {
|
||||||
dma_unmap_sg(s->mgbe_dev, s->rx_pktinfo_mgbe_sgt.sgl,
|
dma_unmap_sg(s->mgbe_dev, s->rx_pktinfo_mgbe_sgt.sgl,
|
||||||
s->rx_pktinfo_mgbe_sgt.orig_nents, DMA_BIDIRECTIONAL);
|
s->rx_pktinfo_mgbe_sgt.orig_nents, DMA_BIDIRECTIONAL);
|
||||||
sg_free_table(&s->rx_pktinfo_mgbe_sgt);
|
sg_free_table(&s->rx_pktinfo_mgbe_sgt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
|
list_del(&s->device_entry);
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
|
||||||
|
/* Decrement reference count and free global memory if last device */
|
||||||
|
coe_free_rx_descr_mem_area();
|
||||||
|
|
||||||
if (s->mgbe_dev != NULL) {
|
if (s->mgbe_dev != NULL) {
|
||||||
put_device(s->mgbe_dev);
|
put_device(s->mgbe_dev);
|
||||||
s->mgbe_dev = NULL;
|
s->mgbe_dev = NULL;
|
||||||
@@ -1940,6 +2021,10 @@ static int __init capture_coe_init(void)
|
|||||||
|
|
||||||
static void __exit capture_coe_exit(void)
|
static void __exit capture_coe_exit(void)
|
||||||
{
|
{
|
||||||
|
platform_driver_unregister(&capture_coe_driver);
|
||||||
|
|
||||||
|
/* Clean up any remaining global resources if they still exist */
|
||||||
|
mutex_lock(&coe_device_list_lock);
|
||||||
if (g_rx_descr_mem_area != NULL) {
|
if (g_rx_descr_mem_area != NULL) {
|
||||||
const size_t alloc_size = COE_TOTAL_RXDESCR_MEM_SIZE;
|
const size_t alloc_size = COE_TOTAL_RXDESCR_MEM_SIZE;
|
||||||
|
|
||||||
@@ -1949,17 +2034,25 @@ static void __exit capture_coe_exit(void)
|
|||||||
g_rx_descr_mem_area = NULL;
|
g_rx_descr_mem_area = NULL;
|
||||||
g_rtcpu_dev = NULL;
|
g_rtcpu_dev = NULL;
|
||||||
}
|
}
|
||||||
|
/* Reset reference count for clean module reload */
|
||||||
|
g_rx_descr_mem_refcount = 0;
|
||||||
|
mutex_unlock(&coe_device_list_lock);
|
||||||
|
|
||||||
|
/* Clean up any remaining channel devices in the global array */
|
||||||
|
mutex_lock(&coe_channels_arr_lock);
|
||||||
for (u32 ch_id = 0U; ch_id < ARRAY_SIZE(coe_channels_arr); ch_id++) {
|
for (u32 ch_id = 0U; ch_id < ARRAY_SIZE(coe_channels_arr); ch_id++) {
|
||||||
struct coe_channel_state * const ch = &coe_channels_arr[ch_id];
|
struct coe_channel_state * const ch = &coe_channels_arr[ch_id];
|
||||||
|
|
||||||
if (ch->dev != NULL)
|
if (ch->dev != NULL) {
|
||||||
device_destroy(coe_channel_class, ch->devt);
|
device_destroy(coe_channel_class, ch->devt);
|
||||||
ch->dev = NULL;
|
ch->dev = NULL;
|
||||||
ch->devt = 0U;
|
ch->devt = 0U;
|
||||||
|
}
|
||||||
|
/* Reset all channel state for clean module reload */
|
||||||
|
memset(ch, 0, sizeof(*ch));
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&coe_channels_arr_lock);
|
||||||
|
|
||||||
platform_driver_unregister(&capture_coe_driver);
|
|
||||||
unregister_chrdev(coe_channel_major, "capture-coe-channel");
|
unregister_chrdev(coe_channel_major, "capture-coe-channel");
|
||||||
class_destroy(coe_channel_class);
|
class_destroy(coe_channel_class);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user