diff options
author | yixuanjiang <yixuanjiang@google.com> | 2021-12-15 14:28:53 +0800 |
---|---|---|
committer | Roger Liao <rogerliao@google.com> | 2022-05-10 05:44:58 +0800 |
commit | c59744779c7c4587a74a50c9acd13926eb6475bc (patch) | |
tree | 271dc6ff3fd98cfca74afa6ff84dbea895f67bd4 /aoc.c | |
parent | d2a8bfc8a8e306edd59ac0e8adcaecf9ee45911b (diff) | |
download | aoc-c59744779c7c4587a74a50c9acd13926eb6475bc.tar.gz |
aoc: add protect on service read/write timeout
Prevent NULL pointer during aoc SSR
Block free flow when service write read active
Bug: 209403953
Signed-off-by: yixuanjiang <yixuanjiang@google.com>
Change-Id: Ib0a0f4082018fc237cdd789c7dfba3cee03d760b
Diffstat (limited to 'aoc.c')
-rw-r--r-- | aoc.c | 47 |
1 files changed, 26 insertions, 21 deletions
@@ -135,7 +135,7 @@ struct aoc_prvdata { void *map_handler_ctx; struct delayed_work monitor_work; - bool aoc_process_active; + atomic_t aoc_process_active; struct device *dev; struct iommu_domain *domain; @@ -1073,7 +1073,6 @@ EXPORT_SYMBOL_GPL(aoc_service_read); ssize_t aoc_service_read_timeout(struct aoc_service_dev *dev, uint8_t *buffer, size_t count, long timeout) { - const struct device *parent; struct aoc_prvdata *prvdata; aoc_service *service; @@ -1087,16 +1086,19 @@ ssize_t aoc_service_read_timeout(struct aoc_service_dev *dev, uint8_t *buffer, if (dev->dead) return -ENODEV; - mutex_lock(&aoc_service_lock); + if (!aoc_platform_device) + return -ENODEV; + + prvdata = platform_get_drvdata(aoc_platform_device); + if (!prvdata) + return -ENODEV; - if (aoc_state != AOC_STATE_ONLINE) { + atomic_inc(&prvdata->aoc_process_active); + if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) { ret = -EBUSY; goto err; } - parent = dev->dev.parent; - prvdata = dev_get_drvdata(parent); - service_number = dev->service_index; service = service_at_index(prvdata, dev->service_index); @@ -1147,7 +1149,7 @@ ssize_t aoc_service_read_timeout(struct aoc_service_dev *dev, uint8_t *buffer, &msg_size); err: - mutex_unlock(&aoc_service_lock); + atomic_dec(&prvdata->aoc_process_active); if (ret < 0) return ret; @@ -1232,7 +1234,6 @@ EXPORT_SYMBOL_GPL(aoc_service_write); ssize_t aoc_service_write_timeout(struct aoc_service_dev *dev, const uint8_t *buffer, size_t count, long timeout) { - const struct device *parent; struct aoc_prvdata *prvdata; aoc_service *service; @@ -1246,15 +1247,19 @@ ssize_t aoc_service_write_timeout(struct aoc_service_dev *dev, const uint8_t *bu if (dev->dead) return -ENODEV; - mutex_lock(&aoc_service_lock); - if (aoc_state != AOC_STATE_ONLINE) { - ret = -ENODEV; + if (!aoc_platform_device) + return -ENODEV; + + prvdata = platform_get_drvdata(aoc_platform_device); + if (!prvdata) + return -ENODEV; + + atomic_inc(&prvdata->aoc_process_active); + if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) { + ret = -EBUSY; goto err; } - parent = dev->dev.parent; - prvdata = dev_get_drvdata(parent); - service_number = dev->service_index; service = service_at_index(prvdata, service_number); @@ -1304,7 +1309,7 @@ ssize_t aoc_service_write_timeout(struct aoc_service_dev *dev, const uint8_t *bu signal_aoc(prvdata->mbox_channels[interrupt].channel); err: - mutex_unlock(&aoc_service_lock); + atomic_dec(&prvdata->aoc_process_active); if (ret < 0) return ret; @@ -2338,8 +2343,8 @@ static void aoc_take_offline(struct aoc_prvdata *prvdata) pr_notice("taking aoc offline\n"); aoc_state = AOC_STATE_OFFLINE; - /* wait until aoc_process_services finish */ - while (prvdata->aoc_process_active); + /* wait until aoc_process or service write/read finish */ + while (!!atomic_read(&prvdata->aoc_process_active)); bus_for_each_dev(&aoc_bus_type, NULL, NULL, aoc_remove_device); @@ -2375,11 +2380,11 @@ static void aoc_process_services(struct aoc_prvdata *prvdata, int offset) int services; int i; + atomic_inc(&prvdata->aoc_process_active); + if (aoc_state != AOC_STATE_ONLINE || work_busy(&prvdata->watchdog_work)) goto exit; - prvdata->aoc_process_active = true; - services = aoc_num_services(); for (i = 0; i < services; i++) { service_dev = service_dev_at_index(prvdata, i); @@ -2400,7 +2405,7 @@ static void aoc_process_services(struct aoc_prvdata *prvdata, int offset) } } exit: - prvdata->aoc_process_active = false; + atomic_dec(&prvdata->aoc_process_active); } void aoc_set_map_handler(struct aoc_service_dev *dev, aoc_map_handler handler, |