diff options
Diffstat (limited to 'gxp-platform.c')
-rw-r--r-- | gxp-platform.c | 338 |
1 files changed, 281 insertions, 57 deletions
diff --git a/gxp-platform.c b/gxp-platform.c index 96a3a76..35634c0 100644 --- a/gxp-platform.c +++ b/gxp-platform.c @@ -27,6 +27,7 @@ #endif #include "gxp.h" +#include "gxp-client.h" #include "gxp-debug-dump.h" #include "gxp-debugfs.h" #include "gxp-dma.h" @@ -66,7 +67,6 @@ static int gxp_open(struct inode *inode, struct file *file) struct gxp_client *client; struct gxp_dev *gxp = container_of(file->private_data, struct gxp_dev, misc_dev); - int ret = 0; client = gxp_client_create(gxp); if (IS_ERR(client)) @@ -74,19 +74,12 @@ static int gxp_open(struct inode *inode, struct file *file) file->private_data = client; - ret = gxp_wakelock_acquire(gxp); - if (ret) { - gxp_client_destroy(client); - file->private_data = NULL; - } - - return ret; + return 0; } static int gxp_release(struct inode *inode, struct file *file) { struct gxp_client *client = file->private_data; - struct gxp_dev *gxp; /* * If open failed and no client was created then no clean-up is needed. @@ -94,16 +87,12 @@ static int gxp_release(struct inode *inode, struct file *file) if (!client) return 0; - gxp = client->gxp; - /* * TODO (b/184572070): Unmap buffers and drop mailbox responses * belonging to the client */ gxp_client_destroy(client); - gxp_wakelock_release(gxp); - return 0; } @@ -133,11 +122,6 @@ static int gxp_map_buffer(struct gxp_client *client, if (copy_from_user(&ibuf, argp, sizeof(ibuf))) return -EFAULT; - phys_core_list = gxp_vd_virt_core_list_to_phys_core_list( - client, ibuf.virtual_core_list); - if (phys_core_list == 0) - return -EINVAL; - if (ibuf.size == 0) return -EINVAL; @@ -147,6 +131,23 @@ static int gxp_map_buffer(struct gxp_client *client, return -EINVAL; } + /* Caller must hold VIRTUAL_DEVICE wakelock */ + down_read(&client->semaphore); + + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "GXP_MAP_BUFFER requires the client hold a VIRTUAL_DEVICE wakelock\n"); + ret = -ENODEV; + goto out; + } + + phys_core_list = gxp_vd_virt_core_list_to_phys_core_list( + client->vd, ibuf.virtual_core_list); + if (phys_core_list == 0) { + ret = -EINVAL; + goto out; + } + #ifndef CONFIG_GXP_HAS_SYSMMU /* * TODO(b/193272602) On systems without a SysMMU, all attempts to map @@ -160,19 +161,23 @@ static int gxp_map_buffer(struct gxp_client *client, map = gxp_mapping_get_host(gxp, ibuf.host_address); if (map) { ibuf.device_address = map->device_address; - if (copy_to_user(argp, &ibuf, sizeof(ibuf))) - return -EFAULT; + if (copy_to_user(argp, &ibuf, sizeof(ibuf))) { + ret = -EFAULT; + goto out + } map->map_count++; - return ret; + goto out; } #endif map = gxp_mapping_create(gxp, phys_core_list, ibuf.host_address, ibuf.size, /*gxp_dma_flags=*/0, mapping_flags_to_dma_dir(ibuf.flags)); - if (IS_ERR(map)) - return PTR_ERR(map); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } ret = gxp_mapping_put(gxp, map); if (ret) @@ -185,12 +190,16 @@ static int gxp_map_buffer(struct gxp_client *client, goto error_remove; } +out: + up_read(&client->semaphore); + return ret; error_remove: gxp_mapping_remove(gxp, map); error_destroy: gxp_mapping_destroy(gxp, map); + up_read(&client->semaphore); devm_kfree(gxp->dev, (void *)map); return ret; } @@ -206,17 +215,32 @@ static int gxp_unmap_buffer(struct gxp_client *client, if (copy_from_user(&ibuf, argp, sizeof(ibuf))) return -EFAULT; + /* Caller must hold VIRTUAL_DEVICE wakelock */ + down_read(&client->semaphore); + + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "GXP_UNMAP_BUFFER requires the client hold a VIRTUAL_DEVICE wakelock\n"); + ret = -ENODEV; + goto out; + } + map = gxp_mapping_get(gxp, ibuf.device_address); - if (!map) - return -EINVAL; + if (!map) { + ret = -EINVAL; + goto out; + } WARN_ON(map->host_address != ibuf.host_address); if (--(map->map_count)) - return ret; + goto out; gxp_mapping_remove(gxp, map); gxp_mapping_destroy(gxp, map); +out: + up_read(&client->semaphore); + return ret; } @@ -226,16 +250,34 @@ static int gxp_sync_buffer(struct gxp_client *client, struct gxp_dev *gxp = client->gxp; struct gxp_sync_ioctl ibuf; struct gxp_mapping *map; + int ret; if (copy_from_user(&ibuf, argp, sizeof(ibuf))) return -EFAULT; + /* Caller must hold VIRTUAL_DEVICE wakelock */ + down_read(&client->semaphore); + + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "GXP_SYNC_BUFFER requires the client hold a VIRTUAL_DEVICE wakelock\n"); + ret = -ENODEV; + goto out; + } + map = gxp_mapping_get(gxp, ibuf.device_address); - if (!map) - return -EINVAL; + if (!map) { + ret = -EINVAL; + goto out; + } + + ret = gxp_mapping_sync(gxp, map, ibuf.offset, ibuf.size, + ibuf.flags == GXP_SYNC_FOR_CPU); - return gxp_mapping_sync(gxp, map, ibuf.offset, ibuf.size, - ibuf.flags == GXP_SYNC_FOR_CPU); +out: + up_read(&client->semaphore); + + return ret; } static int gxp_mailbox_command(struct gxp_client *client, @@ -254,26 +296,39 @@ static int gxp_mailbox_command(struct gxp_client *client, return -EFAULT; } - phys_core = gxp_vd_virt_core_to_phys_core(client, ibuf.virtual_core_id); + /* Caller must hold VIRTUAL_DEVICE wakelock */ + down_read(&client->semaphore); + + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "GXP_MAILBOX_COMMAND requires the client hold a VIRTUAL_DEVICE wakelock\n"); + ret = -ENODEV; + goto out; + } + + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, ibuf.virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Mailbox command failed: Invalid virtual core id (%u)\n", ibuf.virtual_core_id); - return -EINVAL; + ret = -EINVAL; + goto out; } if (!gxp_is_fw_running(gxp, phys_core)) { dev_err(gxp->dev, "Cannot process mailbox command for core %d when firmware isn't running\n", phys_core); - return -EINVAL; + ret = -EINVAL; + goto out; } if (gxp->mailbox_mgr == NULL || gxp->mailbox_mgr->mailboxes == NULL || gxp->mailbox_mgr->mailboxes[phys_core] == NULL) { dev_err(gxp->dev, "Mailbox not initialized for core %d\n", phys_core); - return -EIO; + ret = -EIO; + goto out; } /* Pack the command structure */ @@ -294,16 +349,20 @@ static int gxp_mailbox_command(struct gxp_client *client, if (ret) { dev_err(gxp->dev, "Failed to enqueue mailbox command (ret=%d)\n", ret); - return ret; + goto out; } ibuf.sequence_number = cmd.seq; if (copy_to_user(argp, &ibuf, sizeof(ibuf))) { dev_err(gxp->dev, "Failed to copy back sequence number!\n"); - return -EFAULT; + ret = -EFAULT; + goto out; } - return 0; +out: + up_read(&client->semaphore); + + return ret; } static int gxp_mailbox_response(struct gxp_client *client, @@ -315,22 +374,35 @@ static int gxp_mailbox_response(struct gxp_client *client, struct gxp_async_response *resp_ptr; int phys_core; unsigned long flags; + int ret = 0; if (copy_from_user(&ibuf, argp, sizeof(ibuf))) return -EFAULT; + /* Caller must hold VIRTUAL_DEVICE wakelock */ + down_read(&client->semaphore); + + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "GXP_MAILBOX_RESPONSE requires the client hold a VIRTUAL_DEVICE wakelock\n"); + ret = -ENODEV; + goto out; + } + virtual_core_id = ibuf.virtual_core_id; - phys_core = gxp_vd_virt_core_to_phys_core(client, virtual_core_id); + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Mailbox response failed: Invalid virtual core id (%u)\n", virtual_core_id); - return -EINVAL; + ret = -EINVAL; + goto out; } if (!gxp_is_fw_running(gxp, phys_core)) { dev_err(gxp->dev, "Cannot process mailbox response for core %d when firmware isn't running\n", phys_core); - return -EINVAL; + ret = -EINVAL; + goto out; } spin_lock_irqsave(&gxp->mailbox_resps_lock, flags); @@ -397,9 +469,12 @@ static int gxp_mailbox_response(struct gxp_client *client, kfree(resp_ptr); if (copy_to_user(argp, &ibuf, sizeof(ibuf))) - return -EFAULT; + ret = -EFAULT; - return 0; +out: + up_read(&client->semaphore); + + return ret; } static int gxp_get_specs(struct gxp_client *client, @@ -425,6 +500,7 @@ static int gxp_allocate_vd(struct gxp_client *client, { struct gxp_dev *gxp = client->gxp; struct gxp_virtual_device_ioctl ibuf; + struct gxp_virtual_device *vd; int ret = 0; if (copy_from_user(&ibuf, argp, sizeof(ibuf))) @@ -435,15 +511,26 @@ static int gxp_allocate_vd(struct gxp_client *client, return -EINVAL; } - down_write(&gxp->vd_semaphore); - if (client->vd_allocated) { - up_write(&gxp->vd_semaphore); + down_write(&client->semaphore); + + if (client->vd) { dev_err(gxp->dev, "Virtual device was already allocated for client\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } - ret = gxp_vd_allocate(client, ibuf.core_count); - up_write(&gxp->vd_semaphore); + vd = gxp_vd_allocate(gxp, ibuf.core_count); + if (IS_ERR(vd)) { + dev_err(gxp->dev, + "Failed to allocate virtual device for client (%ld)\n", + PTR_ERR(vd)); + goto out; + } + + client->vd = vd; + +out: + up_write(&client->semaphore); return ret; } @@ -474,7 +561,7 @@ gxp_etm_trace_start_command(struct gxp_client *client, if (ibuf.pc_match_mask_length > ETM_TRACE_PC_MATCH_MASK_LEN_MAX) return -EINVAL; - phys_core = gxp_vd_virt_core_to_phys_core(client, ibuf.virtual_core_id); + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, ibuf.virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Trace start failed: Invalid virtual core id (%u)\n", ibuf.virtual_core_id); @@ -501,7 +588,7 @@ static int gxp_etm_trace_sw_stop_command(struct gxp_client *client, return -EFAULT; - phys_core = gxp_vd_virt_core_to_phys_core(client, virtual_core_id); + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Trace stop via software trigger failed: Invalid virtual core id (%u)\n", virtual_core_id); @@ -527,7 +614,7 @@ static int gxp_etm_trace_cleanup_command(struct gxp_client *client, if (copy_from_user(&virtual_core_id, argp, sizeof(virtual_core_id))) return -EFAULT; - phys_core = gxp_vd_virt_core_to_phys_core(client, virtual_core_id); + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Trace cleanup failed: Invalid virtual core id (%u)\n", virtual_core_id); @@ -560,7 +647,7 @@ gxp_etm_get_trace_info_command(struct gxp_client *client, if (ibuf.type > 1) return -EINVAL; - phys_core = gxp_vd_virt_core_to_phys_core(client, ibuf.virtual_core_id); + phys_core = gxp_vd_virt_core_to_phys_core(client->vd, ibuf.virtual_core_id); if (phys_core < 0) { dev_err(gxp->dev, "Get trace info failed: Invalid virtual core id (%u)\n", ibuf.virtual_core_id); @@ -653,7 +740,7 @@ static int gxp_map_tpu_mbx_queue(struct gxp_client *client, virtual_core_list = ibuf.virtual_core_list; core_count = hweight_long(virtual_core_list); phys_core_list = gxp_vd_virt_core_list_to_phys_core_list( - client, virtual_core_list); + client->vd, virtual_core_list); if (!phys_core_list) { dev_err(gxp->dev, "%s: invalid virtual core list 0x%x\n", __func__, virtual_core_list); @@ -667,7 +754,7 @@ static int gxp_map_tpu_mbx_queue(struct gxp_client *client, if (!mbx_info) return -ENOMEM; - down_write(&gxp->vd_semaphore); + down_write(&client->semaphore); if (client->tpu_mbx_allocated) { dev_err(gxp->dev, "%s: Mappings already exist for TPU mailboxes\n", @@ -708,7 +795,7 @@ static int gxp_map_tpu_mbx_queue(struct gxp_client *client, client->tpu_mbx_allocated = true; error: - up_write(&gxp->vd_semaphore); + up_write(&client->semaphore); kfree(mbx_info); return ret; @@ -729,7 +816,7 @@ static int gxp_unmap_tpu_mbx_queue(struct gxp_client *client, if (copy_from_user(&ibuf, argp, sizeof(ibuf))) return -EFAULT; - down_write(&gxp->vd_semaphore); + down_write(&client->semaphore); if (!client->tpu_mbx_allocated) { dev_err(gxp->dev, "%s: No mappings exist for TPU mailboxes\n", @@ -753,7 +840,7 @@ static int gxp_unmap_tpu_mbx_queue(struct gxp_client *client, client->tpu_mbx_allocated = false; out: - up_write(&gxp->vd_semaphore); + up_write(&client->semaphore); return ret; #else @@ -814,6 +901,137 @@ static int gxp_read_global_counter(struct gxp_client *client, return 0; } +static int gxp_acquire_wake_lock(struct gxp_client *client, + struct gxp_acquire_wakelock_ioctl __user *argp) +{ + struct gxp_dev *gxp = client->gxp; + struct gxp_acquire_wakelock_ioctl ibuf; + bool acquired_block_wakelock = false; + int ret = 0; + + if (copy_from_user(&ibuf, argp, sizeof(ibuf))) + return -EFAULT; + + down_write(&client->semaphore); + + /* Acquire a BLOCK wakelock if requested */ + if (ibuf.components_to_wake & WAKELOCK_BLOCK) { + if (!client->has_block_wakelock) { + ret = gxp_wakelock_acquire(gxp); + acquired_block_wakelock = true; + } + + if (ret) { + dev_err(gxp->dev, + "Failed to acquire BLOCK wakelock for client (ret=%d)\n", + ret); + goto out; + } + + client->has_block_wakelock = true; + } + + /* Acquire a VIRTUAL_DEVICE wakelock if requested */ + if (ibuf.components_to_wake & WAKELOCK_VIRTUAL_DEVICE) { + if (!client->has_block_wakelock) { + dev_err(gxp->dev, + "Must hold BLOCK wakelock to acquire VIRTUAL_DEVICE wakelock\n"); + ret = -EINVAL; + goto out; + + } + + if (!client->has_vd_wakelock) { + down_write(&gxp->vd_semaphore); + ret = gxp_vd_start(client->vd); + up_write(&gxp->vd_semaphore); + } + + if (ret) { + dev_err(gxp->dev, + "Failed to acquire VIRTUAL_DEVICE wakelock for client (ret=%d)\n", + ret); + goto err_acquiring_vd_wl; + } + + client->has_vd_wakelock = true; + } + +out: + up_write(&client->semaphore); + + return ret; + +err_acquiring_vd_wl: + /* + * In a single call, if any wakelock acquisition fails, all of them do. + * If the client was acquiring both wakelocks and failed to acquire the + * VIRTUAL_DEVICE wakelock after successfully acquiring the BLOCK + * wakelock, then release it before returning the error code. + */ + if (acquired_block_wakelock) { + gxp_wakelock_release(gxp); + client->has_block_wakelock = false; + } + + up_write(&client->semaphore); + + return ret; +} + +static int gxp_release_wake_lock(struct gxp_client *client, __u32 __user *argp) +{ + struct gxp_dev *gxp = client->gxp; + u32 wakelock_components; + int ret = 0; + + if (copy_from_user(&wakelock_components, argp, + sizeof(wakelock_components))) + return -EFAULT; + + down_write(&client->semaphore); + + if (wakelock_components & WAKELOCK_VIRTUAL_DEVICE) { + if (!client->has_vd_wakelock) { + dev_err(gxp->dev, + "Client must hold a VIRTUAL_DEVICE wakelock to release one\n"); + ret = -ENODEV; + goto out; + } + + down_write(&gxp->vd_semaphore); + gxp_vd_stop(client->vd); + up_write(&gxp->vd_semaphore); + + client->has_vd_wakelock = false; + } + + if (wakelock_components & WAKELOCK_BLOCK) { + if (client->has_vd_wakelock) { + dev_err(gxp->dev, + "Client cannot release BLOCK wakelock while holding a VD wakelock\n"); + ret = -EBUSY; + goto out; + } + + if (!client->has_block_wakelock) { + dev_err(gxp->dev, + "Client must hold a BLOCK wakelock to release one\n"); + ret = -ENODEV; + goto out; + } + + gxp_wakelock_release(gxp); + + client->has_block_wakelock = false; + } + +out: + up_write(&client->semaphore); + + return ret; +} + static long gxp_ioctl(struct file *file, uint cmd, ulong arg) { struct gxp_client *client = file->private_data; @@ -875,6 +1093,12 @@ static long gxp_ioctl(struct file *file, uint cmd, ulong arg) case GXP_READ_GLOBAL_COUNTER: ret = gxp_read_global_counter(client, argp); break; + case GXP_ACQUIRE_WAKE_LOCK: + ret = gxp_acquire_wake_lock(client, argp); + break; + case GXP_RELEASE_WAKE_LOCK: + ret = gxp_release_wake_lock(client, argp); + break; default: ret = -ENOTTY; /* unknown command */ } |