diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 764e9d9c0830c152de4c78f1f56a3d8f6e40c7a1..97326458b340e89ca5cca7d590dad07c463fddb3 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -50,6 +50,11 @@ struct ashmem_area { struct file *file; size_t size; unsigned long prot_mask; +#ifdef CONFIG_MEM_PURGEABLE + bool is_purgeable; + atomic_t ref_count; + bool purged; +#endif }; /** @@ -275,7 +280,11 @@ static int ashmem_open(struct inode *inode, struct file *file) memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN); asma->prot_mask = PROT_MASK; file->private_data = asma; - +#ifdef CONFIG_MEM_PURGEABLE + asma->is_purgeable = false; + atomic_set(&asma->ref_count, 1); + asma->purged = false; +#endif return 0; } @@ -506,6 +515,11 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) get_file(f); atomic_inc(&ashmem_shrink_inflight); range->purged = ASHMEM_WAS_PURGED; +#ifdef CONFIG_MEM_PURGEABLE + if(range->asma->is_purgeable) { + range->asma->purged = true; + } +#endif lru_del(range); freed += range_size(range); @@ -647,7 +661,15 @@ static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend, { struct ashmem_range *range, *next; int ret = ASHMEM_NOT_PURGED; - +#ifdef CONFIG_MEM_PURGEABLE + if (asma && asma->is_purgeable) { + atomic_inc(&asma->ref_count); + if (atomic_read(&asma->ref_count) > 1) { + ret = PM_SUCCESS; + return ret; + } + } +#endif list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) { /* moved past last applicable page; we can short circuit */ if (range_before_page(range, pgstart)) @@ -716,6 +738,17 @@ static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend, struct ashmem_range *range, *next; unsigned int purged = ASHMEM_NOT_PURGED; +#ifdef CONFIG_MEM_PURGEABLE + if (asma && asma->is_purgeable) { + if (atomic_read(&asma->ref_count) > 0 && !atomic_dec_and_test(&asma->ref_count)) { + return PM_SUCCESS; + } + if (atomic_read(&asma->ref_count) < 0) { + atomic_set(&asma->ref_count, 0); + } + } +#endif + restart: list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) { /* short circuit: this is our insertion point */ @@ -752,7 +785,11 @@ static int ashmem_get_pin_status(struct ashmem_area *asma, size_t pgstart, { struct ashmem_range *range; int ret = ASHMEM_IS_PINNED; - +#ifdef CONFIG_MEM_PURGEABLE + if(asma && asma->is_purgeable) { + return atomic_read(&asma->ref_count); + } +#endif list_for_each_entry(range, &asma->unpinned_list, unpinned) { if (range_before_page(range, pgstart)) break; @@ -785,6 +822,13 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, mutex_lock(&ashmem_mutex); wait_event(ashmem_shrink_wait, !atomic_read(&ashmem_shrink_inflight)); +#ifdef CONFIG_MEM_PURGEABLE + if (asma && asma->is_purgeable) { + pin.offset = 0; + pin.len = PAGE_ALIGN(asma->size); + } +#endif + if (!asma->file) goto out_unlock; @@ -823,12 +867,34 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, return ret; } - +#ifdef CONFIG_MEM_PURGEABLE +static long purgeable_ashmem_cmd(struct ashmem_area *asma, unsigned int cmd) +{ + int ret = -EINVAL; + if(asma == NULL || !asma->is_purgeable) { + return ret; + } + mutex_lock(&ashmem_mutex); + switch (cmd) { + case ASHMEM_GET_PURGEABLE: + ret = asma->is_purgeable; + break; + case PURGEABLE_ASHMEM_IS_PURGED: + ret = asma->purged; + break; + case PURGEABLE_ASHMEM_REBUILD_SUCCESS: + asma->purged = false; + ret = PM_SUCCESS; + break; + } + mutex_unlock(&ashmem_mutex); + return ret; +} +#endif static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ashmem_area *asma = file->private_data; long ret = -ENOTTY; - switch (cmd) { case ASHMEM_SET_NAME: ret = set_name(asma, (void __user *)arg); @@ -870,8 +936,22 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ashmem_shrink_scan(&ashmem_shrinker, &sc); } break; +#ifdef CONFIG_MEM_PURGEABLE + case ASHMEM_SET_PURGEABLE: + mutex_lock(&ashmem_mutex); + if(asma) { + asma->is_purgeable = true; + ret = PM_SUCCESS; + } + mutex_unlock(&ashmem_mutex); + break; + case ASHMEM_GET_PURGEABLE: + case PURGEABLE_ASHMEM_IS_PURGED: + case PURGEABLE_ASHMEM_REBUILD_SUCCESS: + ret = purgeable_ashmem_cmd(asma, cmd); + break; +#endif } - return ret; } diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h index 5442e0019dcd933213e17d95297ad6c4dba47007..296b7cf14d5e6fefa5dfadeeeb89046685e76d57 100644 --- a/drivers/staging/android/uapi/ashmem.h +++ b/drivers/staging/android/uapi/ashmem.h @@ -24,6 +24,13 @@ #define ASHMEM_IS_UNPINNED 0 #define ASHMEM_IS_PINNED 1 +#ifdef CONFIG_MEM_PURGEABLE +#define PM_SUCCESS 0 +#define PM_FAIL 1 +#define ASHMEM_NOT_PURGEABLE 0 +#define ASHMEM_WAS_PURGEABLE 1 +#endif + struct ashmem_pin { __u32 offset; /* offset into region, in bytes, page-aligned */ __u32 len; /* length forward from offset, in bytes, page-aligned */ @@ -42,4 +49,11 @@ struct ashmem_pin { #define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9) #define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) +#ifdef CONFIG_MEM_PURGEABLE +#define ASHMEM_SET_PURGEABLE _IO(__ASHMEMIOC, 11) +#define ASHMEM_GET_PURGEABLE _IO(__ASHMEMIOC, 12) +#define PURGEABLE_ASHMEM_IS_PURGED _IO(__ASHMEMIOC, 13) +#define PURGEABLE_ASHMEM_REBUILD_SUCCESS _IO(__ASHMEMIOC, 14) +#endif + #endif /* _UAPI_LINUX_ASHMEM_H */