blob: 77e4d0d97d22b73dd78e45ffc4326449d0183088 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Implementation of the user-space ashmem API for devices, which have our
19 * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
20 * used by the simulator.
21 */
22
Mark Salyzync2d8aad2016-02-02 08:05:54 -080023#include <errno.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080024#include <fcntl.h>
Mark Salyzyn1186f3a2016-02-02 08:21:32 -080025#include <pthread.h>
Mark Salyzync2d8aad2016-02-02 08:05:54 -080026#include <string.h>
27#include <sys/ioctl.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <unistd.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080031
32#include <linux/ashmem.h>
Mark Salyzync2d8aad2016-02-02 08:05:54 -080033
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080034#include <cutils/ashmem.h>
35
Mark Salyzync2d8aad2016-02-02 08:05:54 -080036#define ASHMEM_DEVICE "/dev/ashmem"
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080037
Mark Salyzyn1186f3a2016-02-02 08:21:32 -080038/* ashmem identity */
39static dev_t __ashmem_rdev;
40/*
41 * If we trigger a signal handler in the middle of locked activity and the
42 * signal handler calls ashmem, we could get into a deadlock state.
43 */
44static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
45
46/* logistics of getting file descriptor for ashmem */
47static int __ashmem_open_locked()
48{
49 int ret;
50 struct stat st;
51
52 int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
53 if (fd < 0) {
54 return fd;
55 }
56
57 ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
58 if (ret < 0) {
59 int save_errno = errno;
60 close(fd);
61 errno = save_errno;
62 return ret;
63 }
64 if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
65 close(fd);
66 errno = ENOTTY;
67 return -1;
68 }
69
70 __ashmem_rdev = st.st_rdev;
71 return fd;
72}
73
74static int __ashmem_open()
75{
76 int fd;
77
78 pthread_mutex_lock(&__ashmem_lock);
79 fd = __ashmem_open_locked();
80 pthread_mutex_unlock(&__ashmem_lock);
81
82 return fd;
83}
84
85/* Make sure file descriptor references ashmem, negative number means false */
86static int __ashmem_is_ashmem(int fd)
87{
88 dev_t rdev;
89 struct stat st;
90
91 if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
92 return -1;
93 }
94
95 if (S_ISCHR(st.st_mode) && st.st_rdev) {
96 pthread_mutex_lock(&__ashmem_lock);
97 rdev = __ashmem_rdev;
98 if (rdev) {
99 pthread_mutex_unlock(&__ashmem_lock);
100 } else {
101 int fd = __ashmem_open_locked();
102 if (fd < 0) {
103 pthread_mutex_unlock(&__ashmem_lock);
104 return -1;
105 }
106 rdev = __ashmem_rdev;
107 pthread_mutex_unlock(&__ashmem_lock);
108
109 close(fd);
110 }
111
112 if (st.st_rdev == rdev) {
113 return 0;
114 }
115 }
116
117 errno = ENOTTY;
118 return -1;
119}
120
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800121/*
122 * ashmem_create_region - creates a new ashmem region and returns the file
123 * descriptor, or <0 on error
124 *
125 * `name' is an optional label to give the region (visible in /proc/pid/maps)
126 * `size' is the size of the region, in page-aligned bytes
127 */
128int ashmem_create_region(const char *name, size_t size)
129{
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800130 int ret, save_errno;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800131
Mark Salyzyn1186f3a2016-02-02 08:21:32 -0800132 int fd = __ashmem_open();
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800133 if (fd < 0) {
134 return fd;
135 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800136
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800137 if (name) {
138 char buf[ASHMEM_NAME_LEN] = {0};
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800139
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800140 strlcpy(buf, name, sizeof(buf));
141 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
142 if (ret < 0) {
143 goto error;
144 }
145 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800146
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800147 ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
148 if (ret < 0) {
149 goto error;
150 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800151
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800152 return fd;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800153
154error:
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800155 save_errno = errno;
156 close(fd);
157 errno = save_errno;
158 return ret;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800159}
160
161int ashmem_set_prot_region(int fd, int prot)
162{
Mark Salyzyn1186f3a2016-02-02 08:21:32 -0800163 int ret = __ashmem_is_ashmem(fd);
164 if (ret < 0) {
165 return ret;
166 }
167
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800168 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800169}
170
171int ashmem_pin_region(int fd, size_t offset, size_t len)
172{
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800173 struct ashmem_pin pin = { offset, len };
Mark Salyzyn1186f3a2016-02-02 08:21:32 -0800174
175 int ret = __ashmem_is_ashmem(fd);
176 if (ret < 0) {
177 return ret;
178 }
179
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800180 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800181}
182
183int ashmem_unpin_region(int fd, size_t offset, size_t len)
184{
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800185 struct ashmem_pin pin = { offset, len };
Mark Salyzyn1186f3a2016-02-02 08:21:32 -0800186
187 int ret = __ashmem_is_ashmem(fd);
188 if (ret < 0) {
189 return ret;
190 }
191
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800192 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800193}
Bjorn Bringert7be52b12009-06-02 00:41:09 +0100194
195int ashmem_get_size_region(int fd)
196{
Mark Salyzyn1186f3a2016-02-02 08:21:32 -0800197 int ret = __ashmem_is_ashmem(fd);
198 if (ret < 0) {
199 return ret;
200 }
201
Mark Salyzync2d8aad2016-02-02 08:05:54 -0800202 return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
Bjorn Bringert7be52b12009-06-02 00:41:09 +0100203}