├── series ├── README ├── dev_ns__adopt_the_led_subsystem ├── dev_ns__adopt_the_backlight-lcd_subsystem ├── dev_ns__adopt_the_android_logger_driver ├── dev_ns__adopt_the_android_binder_driver ├── dev_ns__adopt_the_android_alarm-dev_driver ├── dev_ns__adopt_the_input_subsystem ├── COPYING ├── dev_ns__introduce_device_namespaces └── dev_ns__adopt_the_framebuffer_subsystem /series: -------------------------------------------------------------------------------- 1 | dev_ns__introduce_device_namespaces 2 | dev_ns__adopt_the_android_alarm-dev_driver 3 | dev_ns__adopt_the_framebuffer_subsystem 4 | dev_ns__adopt_the_input_subsystem 5 | dev_ns__adopt_the_android_binder_driver 6 | dev_ns__adopt_the_android_logger_driver 7 | dev_ns__adopt_the_led_subsystem 8 | dev_ns__adopt_the_backlight-lcd_subsystem 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Device namespaces are an extension to existing Linux kernel namespaces, 2 | developed by Cellrox as part of its Thinvisor technology for lightweight 3 | virtualization for mobile devices. 4 | 5 | Device namespaces are based on development done at Columbia University. Certain 6 | portions are copyrighted by Columbia University. 7 | 8 | Device namespaces create the illusion, for processes inside a namespace, that 9 | they interact exclusively with a set of device drivers, hiding the fact that 10 | there are other namespaces interacting with the same set of device drivers. 11 | 12 | Device namespaces also introduce the concept of an active namespace, which is 13 | a namespace with access to real devices/peripherals and with which the user 14 | interacts, and the concept of switching namespaces to allow users to interact 15 | with multiple namespaces, one at a time. 16 | 17 | For more information, see https://github.com/Cellrox/devns-patches/wiki 18 | 19 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_led_subsystem: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the led subsystem 2 | 3 | Adds device namespace awareness to LED subsystem - so changes to the brightness 4 | setttings can only be made from an active namespace. 5 | 6 | Changelog: 7 | [16-Aug-2013] v1 - initial version 8 | 9 | Change-Id: I7efbf17c9ef102b9b167b4fdec867bd76023f0bd 10 | Signed-off-by: Oren Laadan 11 | 12 | --- 13 | drivers/leds/led-class.c | 7 +++++++ 14 | 1 file changed, 7 insertions(+) 15 | 16 | diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c 17 | index 5bff843..d1c9f22 100644 18 | --- a/drivers/leds/led-class.c 19 | +++ b/drivers/leds/led-class.c 20 | @@ -19,6 +19,7 @@ 21 | #include 22 | #include 23 | #include 24 | +#include 25 | #include "leds.h" 26 | 27 | static struct class *leds_class; 28 | @@ -55,6 +56,12 @@ static ssize_t led_brightness_store(struct device *dev, 29 | if (count == size) { 30 | ret = count; 31 | 32 | + if (!is_active_dev_ns(current_dev_ns())) { 33 | + printk(KERN_INFO "led_brightness: not setting %s to %ld from inactive container.\n", 34 | + dev_name(dev), state); 35 | + return ret; 36 | + } 37 | + 38 | if (state == LED_OFF) 39 | led_trigger_remove(led_cdev); 40 | led_set_brightness(led_cdev, state); 41 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_backlight-lcd_subsystem: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the backlight/lcd subsystem 2 | 3 | Adds device namespace awareness to backlight and lcd subsystem - so that 4 | changes to the brightness/power/contrast setttings can only be made from an 5 | active namespace. 6 | 7 | Changelog: 8 | [16-Aug-2013] v1 - initial version 9 | 10 | Change-Id: Ib1b68a50240ca6116620eed74e31407c7b9d1bd4 11 | Signed-off-by: Oren Laadan 12 | 13 | --- 14 | drivers/video/backlight/backlight.c | 21 +++++++++++++++++++++ 15 | drivers/video/backlight/lcd.c | 17 +++++++++++++++++ 16 | 2 files changed, 38 insertions(+) 17 | 18 | diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c 19 | index bf5b1ec..1b95c8d 100644 20 | --- a/drivers/video/backlight/backlight.c 21 | +++ b/drivers/video/backlight/backlight.c 22 | @@ -14,6 +14,7 @@ 23 | #include 24 | #include 25 | #include 26 | +#include 27 | 28 | #ifdef CONFIG_PMAC_BACKLIGHT 29 | #include 30 | @@ -121,6 +122,14 @@ static ssize_t backlight_store_power(struct device *dev, 31 | return rc; 32 | 33 | rc = -ENXIO; 34 | + 35 | + if (!is_active_dev_ns(current_dev_ns())) { 36 | + printk(KERN_INFO "%s: not setting %s power to %ld from inactive devns\n", 37 | + __func__, dev_name(dev), power); 38 | + rc = count; 39 | + goto out; 40 | + } 41 | + 42 | mutex_lock(&bd->ops_lock); 43 | if (bd->ops) { 44 | pr_debug("backlight: set power to %lu\n", power); 45 | @@ -132,6 +141,7 @@ static ssize_t backlight_store_power(struct device *dev, 46 | } 47 | mutex_unlock(&bd->ops_lock); 48 | 49 | + out: 50 | return rc; 51 | } 52 | 53 | @@ -156,6 +166,16 @@ static ssize_t backlight_store_brightness(struct device *dev, 54 | 55 | rc = -ENXIO; 56 | 57 | + if (!is_active_dev_ns(current_dev_ns())) { 58 | + printk(KERN_INFO "%s: not setting %s brightness to %ld from inactive devns\n", 59 | + __func__, dev_name(dev), brightness); 60 | + rc = count; 61 | + /* generate the backlight event in case user space was relying 62 | + * on this to happen 63 | + */ 64 | + goto out; 65 | + } 66 | + 67 | mutex_lock(&bd->ops_lock); 68 | if (bd->ops) { 69 | if (brightness > bd->props.max_brightness) 70 | @@ -172,6 +192,7 @@ static ssize_t backlight_store_brightness(struct device *dev, 71 | 72 | backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); 73 | 74 | + out: 75 | return rc; 76 | } 77 | 78 | diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c 79 | index 79c1b0d..831b539 100644 80 | --- a/drivers/video/backlight/lcd.c 81 | +++ b/drivers/video/backlight/lcd.c 82 | @@ -14,6 +14,7 @@ 83 | #include 84 | #include 85 | #include 86 | +#include 87 | 88 | #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ 89 | defined(CONFIG_LCD_CLASS_DEVICE_MODULE)) 90 | @@ -104,6 +105,13 @@ static ssize_t lcd_store_power(struct device *dev, 91 | if (rc) 92 | return rc; 93 | 94 | + if (!is_active_dev_ns(current_dev_ns())) { 95 | + printk(KERN_INFO "%s: not setting %s power to %d from inactive devns\n", 96 | + __func__, dev_name(dev), power); 97 | + rc = count; 98 | + goto out; 99 | + } 100 | + 101 | mutex_lock(&ld->ops_lock); 102 | if (ld->ops && ld->ops->set_power) { 103 | pr_debug("lcd: set power to %lu\n", power); 104 | @@ -112,6 +120,7 @@ static ssize_t lcd_store_power(struct device *dev, 105 | } 106 | mutex_unlock(&ld->ops_lock); 107 | 108 | + out: 109 | return rc; 110 | } 111 | 112 | @@ -140,6 +149,13 @@ static ssize_t lcd_store_contrast(struct device *dev, 113 | if (rc) 114 | return rc; 115 | 116 | + if (!is_active_dev_ns(current_dev_ns())) { 117 | + printk(KERN_INFO "%s: not setting %s contrast to %d from inactive devn.\n", 118 | + __func__, dev_name(dev), contrast); 119 | + rc = count; 120 | + goto out; 121 | + } 122 | + 123 | mutex_lock(&ld->ops_lock); 124 | if (ld->ops && ld->ops->set_contrast) { 125 | pr_debug("lcd: set contrast to %lu\n", contrast); 126 | @@ -148,6 +164,7 @@ static ssize_t lcd_store_contrast(struct device *dev, 127 | } 128 | mutex_unlock(&ld->ops_lock); 129 | 130 | + out: 131 | return rc; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_android_logger_driver: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the android logger driver 2 | 3 | Adds device namespace info to android logger entries and filters entries 4 | read by the device namespace in which the reader runs - so a process would 5 | only see logs from its own namespace. 6 | 7 | Adds an ioctl to android logger to select from which namespace log entries 8 | should be read. This is useful to allow host to filter events from specific 9 | namespace when reading from /dev/log/events. 10 | 11 | Changelog: 12 | [11-Sep-2013] v2 - fix compile and crashes with !CONFIG_DEV_NS 13 | fix crash when !COFNIG_DEV_NS [report: Jeremy Andrus] 14 | [16-Aug-2013] v1 - initial version 15 | 16 | Change-Id: I09d4c5009c18d94d69cdd785e46c76d756477767 17 | Signed-off-by: Amir Goldstein 18 | Signed-off-by: Oren Laadan 19 | 20 | --- 21 | drivers/staging/android/logger.c | 130 +++++++++++++++++++++++++++++++-------- 22 | drivers/staging/android/logger.h | 36 ++++++++++- 23 | 2 files changed, 139 insertions(+), 27 deletions(-) 24 | 25 | diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c 26 | index eb3d4ca..c606dad 100644 27 | --- a/drivers/staging/android/logger.c 28 | +++ b/drivers/staging/android/logger.c 29 | @@ -25,6 +25,7 @@ 30 | #include 31 | #include 32 | #include 33 | +#include 34 | #include "logger.h" 35 | 36 | #include 37 | @@ -47,6 +48,13 @@ struct logger_log { 38 | size_t size; /* size of the log */ 39 | }; 40 | 41 | +/* logger_ver - returns reader ABI compatible version */ 42 | +#define logger_compat_ver(v) ((v) & 0x00FF) 43 | +/* logger_ns_ver - returns reader ns aware version */ 44 | +#define logger_ns_ver(v) ((v) | 0x0100) 45 | +/* logger_ns_info - returns true iff reader should get ns info */ 46 | +#define logger_ns_info(v) ((v) & 0x0100) 47 | + 48 | /* 49 | * struct logger_reader - a logging device open for reading 50 | * 51 | @@ -59,6 +67,7 @@ struct logger_reader { 52 | size_t r_off; /* current read head offset */ 53 | bool r_all; /* reader can read all entries */ 54 | int r_ver; /* reader ABI version */ 55 | + pid_t r_initpid; /* filter by devns initpid */ 56 | }; 57 | 58 | /* logger_offset - returns index 'n' into the log via (optimized) modulus */ 59 | @@ -133,10 +142,17 @@ static __u32 get_entry_msg_len(struct logger_log *log, size_t off) 60 | 61 | static size_t get_user_hdr_len(int ver) 62 | { 63 | - if (ver < 2) 64 | - return sizeof(struct user_logger_entry_compat); 65 | - else 66 | + switch (ver) { 67 | + case logger_compat_ver(1): 68 | + return sizeof(struct user_logger_entry_compat_v1); 69 | + case logger_ns_ver(1): 70 | + return sizeof(struct user_logger_entry_compat_ns_v1); 71 | + case logger_compat_ver(2): 72 | + return sizeof(struct user_logger_entry_compat_v2); 73 | + case logger_ns_ver(2): 74 | + default: 75 | return sizeof(struct logger_entry); 76 | + } 77 | } 78 | 79 | static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, 80 | @@ -144,9 +160,15 @@ static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, 81 | { 82 | void *hdr; 83 | size_t hdr_len; 84 | - struct user_logger_entry_compat v1; 85 | - 86 | - if (ver < 2) { 87 | + struct user_logger_entry_compat_ns_v1 v1; 88 | + 89 | + if (logger_compat_ver(ver) < 2) { 90 | + if (logger_ns_info(ver)) { 91 | + v1.ns_initpid = entry->ns_initpid; 92 | + v1.ns_pid = entry->ns_pid; 93 | + v1.ns_tid = entry->ns_tid; 94 | + memcpy(v1.ns_tag, entry->ns_tag, DEV_NS_TAG_LEN); 95 | + } 96 | v1.len = entry->len; 97 | v1.__pad = 0; 98 | v1.pid = entry->pid; 99 | @@ -154,12 +176,15 @@ static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, 100 | v1.sec = entry->sec; 101 | v1.nsec = entry->nsec; 102 | hdr = &v1; 103 | - hdr_len = sizeof(struct user_logger_entry_compat); 104 | } else { 105 | hdr = entry; 106 | - hdr_len = sizeof(struct logger_entry); 107 | } 108 | 109 | + hdr_len = get_user_hdr_len(ver); 110 | + if (!logger_ns_info(ver)) 111 | + /* skip the ns info header part */ 112 | + hdr = &((struct logger_entry *)hdr)->len; 113 | + 114 | return copy_to_user(buf, hdr, hdr_len); 115 | } 116 | 117 | @@ -216,12 +241,15 @@ static ssize_t do_read_log_to_user(struct logger_log *log, 118 | } 119 | 120 | /* 121 | - * get_next_entry_by_uid - Starting at 'off', returns an offset into 122 | + * get_next_entry_by_ns_uid - Starting at 'off', returns an offset into 123 | * 'log->buffer' which contains the first entry readable by 'euid' 124 | + * in namespace of 'initpid' 125 | */ 126 | -static size_t get_next_entry_by_uid(struct logger_log *log, 127 | - size_t off, uid_t euid) 128 | +static size_t get_next_entry_by_ns_uid(struct logger_log *log, 129 | + size_t off, pid_t initpid, bool all) 130 | { 131 | + uid_t euid = current_euid(); 132 | + 133 | while (off != log->w_off) { 134 | struct logger_entry *entry; 135 | struct logger_entry scratch; 136 | @@ -229,7 +257,8 @@ static size_t get_next_entry_by_uid(struct logger_log *log, 137 | 138 | entry = get_entry_header(log, off, &scratch); 139 | 140 | - if (entry->euid == euid) 141 | + if ((all || entry->euid == euid) && 142 | + (!initpid || entry->ns_initpid == initpid)) 143 | return off; 144 | 145 | next_len = sizeof(struct logger_entry) + entry->len; 146 | @@ -289,9 +318,9 @@ start: 147 | 148 | mutex_lock(&log->mutex); 149 | 150 | - if (!reader->r_all) 151 | - reader->r_off = get_next_entry_by_uid(log, 152 | - reader->r_off, current_euid()); 153 | + if (!reader->r_all || reader->r_initpid) 154 | + reader->r_off = get_next_entry_by_ns_uid(log, 155 | + reader->r_off, reader->r_initpid, reader->r_all); 156 | 157 | /* is there still something to read or did we race? */ 158 | if (unlikely(log->w_off == reader->r_off)) { 159 | @@ -447,6 +476,7 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, 160 | unsigned long nr_segs, loff_t ppos) 161 | { 162 | struct logger_log *log = file_get_log(iocb->ki_filp); 163 | + struct dev_namespace *dev_ns = current_dev_ns(); 164 | size_t orig = log->w_off; 165 | struct logger_entry header; 166 | struct timespec now; 167 | @@ -454,8 +484,13 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, 168 | 169 | now = current_kernel_time(); 170 | 171 | - header.pid = current->tgid; 172 | - header.tid = current->pid; 173 | + header.ns_initpid = dev_ns_init_pid(dev_ns); 174 | + get_dev_ns_tag(header.ns_tag, dev_ns); 175 | + 176 | + header.ns_pid = task_tgid_nr(current); 177 | + header.ns_tid = task_pid_nr(current); 178 | + header.pid = task_tgid_vnr(current); 179 | + header.tid = task_pid_vnr(current); 180 | header.sec = now.tv_sec; 181 | header.nsec = now.tv_nsec; 182 | header.euid = current_euid(); 183 | @@ -527,16 +562,31 @@ static int logger_open(struct inode *inode, struct file *file) 184 | 185 | if (file->f_mode & FMODE_READ) { 186 | struct logger_reader *reader; 187 | + struct dev_namespace *dev_ns = current_dev_ns(); 188 | + 189 | + if (file->f_flags & O_DIRECT && !is_init_dev_ns(dev_ns)) 190 | + return -EPERM; 191 | 192 | reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL); 193 | if (!reader) 194 | return -ENOMEM; 195 | 196 | reader->log = log; 197 | - reader->r_ver = 1; 198 | + reader->r_ver = logger_compat_ver(1); 199 | reader->r_all = in_egroup_p(inode->i_gid) || 200 | capable(CAP_SYSLOG); 201 | 202 | + if (file->f_flags & O_DIRECT) { 203 | + /* open in raw mode from root ns without filtering */ 204 | + /* but clear flag before __dentry_open() sees it */ 205 | + reader->r_initpid = 0; 206 | + reader->r_ver = logger_ns_ver(1); 207 | + file->f_flags &= ~O_DIRECT; 208 | + } else { 209 | + /* open logger in initpid filtering mode */ 210 | + reader->r_initpid = dev_ns_init_pid(dev_ns); 211 | + } 212 | + 213 | INIT_LIST_HEAD(&reader->list); 214 | 215 | mutex_lock(&log->mutex); 216 | @@ -596,9 +646,9 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) 217 | poll_wait(file, &log->wq, wait); 218 | 219 | mutex_lock(&log->mutex); 220 | - if (!reader->r_all) 221 | - reader->r_off = get_next_entry_by_uid(log, 222 | - reader->r_off, current_euid()); 223 | + if (!reader->r_all || reader->r_initpid) 224 | + reader->r_off = get_next_entry_by_ns_uid(log, 225 | + reader->r_off, reader->r_initpid, reader->r_all); 226 | 227 | if (log->w_off != reader->r_off) 228 | ret |= POLLIN | POLLRDNORM; 229 | @@ -616,7 +666,11 @@ static long logger_set_version(struct logger_reader *reader, void __user *arg) 230 | if ((version < 1) || (version > 2)) 231 | return -EINVAL; 232 | 233 | - reader->r_ver = version; 234 | + if (reader->r_initpid) 235 | + reader->r_ver = logger_compat_ver(version); 236 | + else 237 | + reader->r_ver = logger_ns_ver(version); 238 | + 239 | return 0; 240 | } 241 | 242 | @@ -651,9 +705,10 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 243 | } 244 | reader = file->private_data; 245 | 246 | - if (!reader->r_all) 247 | - reader->r_off = get_next_entry_by_uid(log, 248 | - reader->r_off, current_euid()); 249 | + if (!reader->r_all || reader->r_initpid) 250 | + reader->r_off = get_next_entry_by_ns_uid(log, 251 | + reader->r_off, reader->r_initpid, 252 | + reader->r_all); 253 | 254 | if (log->w_off != reader->r_off) 255 | ret = get_user_hdr_len(reader->r_ver) + 256 | @@ -677,7 +732,7 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 257 | break; 258 | } 259 | reader = file->private_data; 260 | - ret = reader->r_ver; 261 | + ret = logger_compat_ver(reader->r_ver); 262 | break; 263 | case LOGGER_SET_VERSION: 264 | if (!(file->f_mode & FMODE_READ)) { 265 | @@ -687,6 +742,29 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 266 | reader = file->private_data; 267 | ret = logger_set_version(reader, argp); 268 | break; 269 | + case LOGGER_SET_DEV_NS_FILTER: 270 | + if (!(file->f_mode & FMODE_READ)) { 271 | + ret = -EBADF; 272 | + break; 273 | + } 274 | + if (!is_init_dev_ns(current_dev_ns())) { 275 | + ret = -EPERM; 276 | + break; 277 | + } 278 | + if ((pid_t) arg < 0) { /* 0 for no filtering */ 279 | + ret = -EINVAL; 280 | + break; 281 | + } 282 | + reader = file->private_data; 283 | + reader->r_initpid = (pid_t) arg; 284 | + if (arg) 285 | + reader->r_ver = logger_compat_ver(reader->r_ver); 286 | + else 287 | + reader->r_ver = logger_ns_ver(reader->r_ver); 288 | + pr_info("logger: set ns filter to %lu, ver=%x\n", 289 | + arg, reader->r_ver); 290 | + ret = 0; 291 | + break; 292 | } 293 | 294 | mutex_unlock(&log->mutex); 295 | diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h 296 | index 3f612a3b..3607aeb 100644 297 | --- a/drivers/staging/android/logger.h 298 | +++ b/drivers/staging/android/logger.h 299 | @@ -25,7 +25,7 @@ 300 | * This structure is returned to userspace unless the caller requests 301 | * an upgrade to a newer ABI version. 302 | */ 303 | -struct user_logger_entry_compat { 304 | +struct user_logger_entry_compat_v1 { 305 | __u16 len; /* length of the payload */ 306 | __u16 __pad; /* no matter what, we get 2 bytes of padding */ 307 | __s32 pid; /* generating process's pid */ 308 | @@ -40,7 +40,40 @@ struct user_logger_entry_compat { 309 | * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) 310 | * is called with version >= 2 311 | */ 312 | +struct user_logger_entry_compat_v2 { 313 | + __u16 len; /* length of the payload */ 314 | + __u16 hdr_size; /* sizeof(struct logger_entry_v2) */ 315 | + __s32 pid; /* generating process's pid */ 316 | + __s32 tid; /* generating process's tid */ 317 | + __s32 sec; /* seconds since Epoch */ 318 | + __s32 nsec; /* nanoseconds */ 319 | + uid_t euid; /* effective UID of logger */ 320 | + char msg[0]; /* the entry's payload */ 321 | +}; 322 | + 323 | +#define DEV_NS_TAG_LEN 4 /* see dev_namespace.h */ 324 | + 325 | +/* The structure for version 1 of the namespace-aware logger_entry ABI */ 326 | +struct user_logger_entry_compat_ns_v1 { 327 | + __s32 ns_initpid;/* generating process's device ns initpid */ 328 | + __s32 ns_pid; /* generating process's real pid */ 329 | + __s32 ns_tid; /* generating process's real tid */ 330 | + char ns_tag[DEV_NS_TAG_LEN]; /* device ns identifier */ 331 | + __u16 len; /* length of the payload */ 332 | + __u16 __pad; /* no matter what, we get 2 bytes of padding */ 333 | + __s32 pid; /* generating process's pid */ 334 | + __s32 tid; /* generating process's tid */ 335 | + __s32 sec; /* seconds since Epoch */ 336 | + __s32 nsec; /* nanoseconds */ 337 | + char msg[0]; /* the entry's payload */ 338 | +}; 339 | + 340 | +/* The structure for version 2 of the namespace-aware logger_entry ABI */ 341 | struct logger_entry { 342 | + __s32 ns_initpid; /* generating process's devns initpid */ 343 | + __s32 ns_pid; /* generating process's real pid */ 344 | + __s32 ns_tid; /* generating process's real tid */ 345 | + char ns_tag[DEV_NS_TAG_LEN]; /* device ns identifier */ 346 | __u16 len; /* length of the payload */ 347 | __u16 hdr_size; /* sizeof(struct logger_entry_v2) */ 348 | __s32 pid; /* generating process's pid */ 349 | @@ -66,5 +99,6 @@ struct logger_entry { 350 | #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ 351 | #define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ 352 | #define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ 353 | +#define LOGGER_SET_DEV_NS_FILTER _IO(__LOGGERIO, 10) /* devns filter */ 354 | 355 | #endif /* _LINUX_LOGGER_H */ 356 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_android_binder_driver: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the android binder driver 2 | 3 | Adds device namespace info to Android binder to isolate binder contexts between 4 | device namespaces. 5 | 6 | In binder, the first process to call BINDER_SET_CONTEXT_MGR ioctl becomes the 7 | manager with context 0, and thereafter IPC is realized through binder hanldes 8 | obtained from the manager. 9 | 10 | Isolation builds on this concept. We associate a separate binder state with 11 | each device namespace such that each namespace has its own context manager. 12 | Binder users within a namespace interact only with the context manager there. 13 | This suffices because binder does not allow IPC not via the context manager. 14 | 15 | Changelog: 16 | [16-Aug-2013] v1 - initial version 17 | 18 | Change-Id: Idac7e12eba602dbd2534b5ce9f86c36ac2ec3e0f 19 | Signed-off-by: Oren Laadan 20 | 21 | --- 22 | drivers/staging/android/binder.c | 235 +++++++++++++++++++++++++++++++-------- 23 | 1 file changed, 187 insertions(+), 48 deletions(-) 24 | 25 | diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c 26 | index f1364a8..21d8537 100644 27 | --- a/drivers/staging/android/binder.c 28 | +++ b/drivers/staging/android/binder.c 29 | @@ -34,6 +34,7 @@ 30 | #include 31 | #include 32 | #include 33 | +#include 34 | 35 | #include "binder.h" 36 | #include "binder_trace.h" 37 | @@ -42,17 +43,72 @@ static DEFINE_MUTEX(binder_main_lock); 38 | static DEFINE_MUTEX(binder_deferred_lock); 39 | static DEFINE_MUTEX(binder_mmap_lock); 40 | 41 | -static HLIST_HEAD(binder_procs); 42 | static HLIST_HEAD(binder_deferred_list); 43 | -static HLIST_HEAD(binder_dead_nodes); 44 | 45 | static struct dentry *binder_debugfs_dir_entry_root; 46 | static struct dentry *binder_debugfs_dir_entry_proc; 47 | -static struct binder_node *binder_context_mgr_node; 48 | -static uid_t binder_context_mgr_uid = -1; 49 | -static int binder_last_id; 50 | static struct workqueue_struct *binder_deferred_workqueue; 51 | 52 | +struct binder_dev_ns { 53 | + struct binder_node *context_mgr_node; 54 | + uid_t context_mgr_uid; 55 | + int last_id; 56 | + 57 | + struct hlist_head procs; 58 | + struct hlist_head dead_nodes; 59 | + 60 | + struct dev_ns_info dev_ns_info; 61 | +}; 62 | + 63 | +static void binder_ns_initialize(struct binder_dev_ns *binder_ns) 64 | +{ 65 | + INIT_HLIST_HEAD(&binder_ns->procs); 66 | + INIT_HLIST_HEAD(&binder_ns->dead_nodes); 67 | + 68 | + binder_ns->context_mgr_uid = -1; 69 | +} 70 | + 71 | + 72 | +#ifdef CONFIG_DEV_NS 73 | + 74 | +/* binder_ns_id, get_binder_ns(), get_binder_ns_cur(), put_binder_ns() */ 75 | +DEFINE_DEV_NS_INFO(binder) 76 | + 77 | +static struct dev_ns_info *binder_ns_create(struct dev_namespace *dev_ns) 78 | +{ 79 | + struct binder_dev_ns *binder_ns; 80 | + 81 | + binder_ns = kzalloc(sizeof(*binder_ns), GFP_KERNEL); 82 | + if (!binder_ns) 83 | + return ERR_PTR(-ENOMEM); 84 | + 85 | + binder_ns_initialize(binder_ns); 86 | + 87 | + return &binder_ns->dev_ns_info; 88 | +} 89 | + 90 | +static void binder_ns_release(struct dev_ns_info *dev_ns_info) 91 | +{ 92 | + struct binder_dev_ns *binder_ns; 93 | + 94 | + binder_ns = container_of(dev_ns_info, struct binder_dev_ns, 95 | + dev_ns_info); 96 | + kfree(binder_ns); 97 | +} 98 | + 99 | +static struct dev_ns_ops binder_ns_ops = { 100 | + .create = binder_ns_create, 101 | + .release = binder_ns_release, 102 | +}; 103 | + 104 | +#else 105 | + 106 | +/* init_binder_ns, get_binder_ns_cur(), put_binder_ns() */ 107 | +DEFINE_DEV_NS_INIT(binder) 108 | + 109 | +#endif /* CONFIG_DEVICE_NS */ 110 | + 111 | + 112 | #define BINDER_DEBUG_ENTRY(name) \ 113 | static int binder_##name##_open(struct inode *inode, struct file *file) \ 114 | { \ 115 | @@ -298,6 +354,8 @@ struct binder_proc { 116 | void *buffer; 117 | ptrdiff_t user_buffer_offset; 118 | 119 | + struct binder_dev_ns *binder_ns; 120 | + 121 | struct list_head buffers; 122 | struct rb_root free_buffers; 123 | struct rb_root allocated_buffers; 124 | @@ -991,7 +1049,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, 125 | binder_stats_created(BINDER_STAT_NODE); 126 | rb_link_node(&node->rb_node, parent, p); 127 | rb_insert_color(&node->rb_node, &proc->nodes); 128 | - node->debug_id = ++binder_last_id; 129 | + node->debug_id = ++proc->binder_ns->last_id; 130 | node->proc = proc; 131 | node->ptr = ptr; 132 | node->cookie = cookie; 133 | @@ -1012,7 +1070,7 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, 134 | if (internal) { 135 | if (target_list == NULL && 136 | node->internal_strong_refs == 0 && 137 | - !(node == binder_context_mgr_node && 138 | + !(node == node->proc->binder_ns->context_mgr_node && 139 | node->has_strong_ref)) { 140 | printk(KERN_ERR "binder: invalid inc strong " 141 | "node for %d\n", node->debug_id); 142 | @@ -1126,13 +1184,13 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, 143 | if (new_ref == NULL) 144 | return NULL; 145 | binder_stats_created(BINDER_STAT_REF); 146 | - new_ref->debug_id = ++binder_last_id; 147 | + new_ref->debug_id = ++proc->binder_ns->last_id; 148 | new_ref->proc = proc; 149 | new_ref->node = node; 150 | rb_link_node(&new_ref->rb_node_node, parent, p); 151 | rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); 152 | 153 | - new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; 154 | + new_ref->desc = (node == proc->binder_ns->context_mgr_node) ? 0 : 1; 155 | for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { 156 | ref = rb_entry(n, struct binder_ref, rb_node_desc); 157 | if (ref->desc > new_ref->desc) 158 | @@ -1478,7 +1536,7 @@ static void binder_transaction(struct binder_proc *proc, 159 | } 160 | target_node = ref->node; 161 | } else { 162 | - target_node = binder_context_mgr_node; 163 | + target_node = proc->binder_ns->context_mgr_node; 164 | if (target_node == NULL) { 165 | return_error = BR_DEAD_REPLY; 166 | goto err_no_context_mgr_node; 167 | @@ -1540,7 +1598,7 @@ static void binder_transaction(struct binder_proc *proc, 168 | } 169 | binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); 170 | 171 | - t->debug_id = ++binder_last_id; 172 | + t->debug_id = ++proc->binder_ns->last_id; 173 | e->debug_id = t->debug_id; 174 | 175 | if (reply) 176 | @@ -1862,10 +1920,10 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, 177 | if (get_user(target, (uint32_t __user *)ptr)) 178 | return -EFAULT; 179 | ptr += sizeof(uint32_t); 180 | - if (target == 0 && binder_context_mgr_node && 181 | + if (target == 0 && proc->binder_ns->context_mgr_node && 182 | (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { 183 | ref = binder_get_ref_for_node(proc, 184 | - binder_context_mgr_node); 185 | + proc->binder_ns->context_mgr_node); 186 | if (ref->desc != target) { 187 | binder_user_error("binder: %d:" 188 | "%d tried to acquire " 189 | @@ -2765,7 +2823,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 190 | } 191 | break; 192 | case BINDER_SET_CONTEXT_MGR: 193 | - if (binder_context_mgr_node != NULL) { 194 | + if (proc->binder_ns->context_mgr_node != NULL) { 195 | printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n"); 196 | ret = -EBUSY; 197 | goto err; 198 | @@ -2773,26 +2831,28 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 199 | ret = security_binder_set_context_mgr(proc->tsk); 200 | if (ret < 0) 201 | goto err; 202 | - if (binder_context_mgr_uid != -1) { 203 | - if (binder_context_mgr_uid != current->cred->euid) { 204 | + if (proc->binder_ns->context_mgr_uid != -1) { 205 | + if (proc->binder_ns->context_mgr_uid != 206 | + current->cred->euid) { 207 | printk(KERN_ERR "binder: BINDER_SET_" 208 | "CONTEXT_MGR bad uid %d != %d\n", 209 | current->cred->euid, 210 | - binder_context_mgr_uid); 211 | + proc->binder_ns->context_mgr_uid); 212 | ret = -EPERM; 213 | goto err; 214 | } 215 | } else 216 | - binder_context_mgr_uid = current->cred->euid; 217 | - binder_context_mgr_node = binder_new_node(proc, NULL, NULL); 218 | - if (binder_context_mgr_node == NULL) { 219 | + proc->binder_ns->context_mgr_uid = current->cred->euid; 220 | + proc->binder_ns->context_mgr_node = 221 | + binder_new_node(proc, NULL, NULL); 222 | + if (proc->binder_ns->context_mgr_node == NULL) { 223 | ret = -ENOMEM; 224 | goto err; 225 | } 226 | - binder_context_mgr_node->local_weak_refs++; 227 | - binder_context_mgr_node->local_strong_refs++; 228 | - binder_context_mgr_node->has_strong_ref = 1; 229 | - binder_context_mgr_node->has_weak_ref = 1; 230 | + proc->binder_ns->context_mgr_node->local_weak_refs++; 231 | + proc->binder_ns->context_mgr_node->local_strong_refs++; 232 | + proc->binder_ns->context_mgr_node->has_strong_ref = 1; 233 | + proc->binder_ns->context_mgr_node->has_weak_ref = 1; 234 | break; 235 | case BINDER_THREAD_EXIT: 236 | binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n", 237 | @@ -2954,13 +3014,22 @@ err_bad_arg: 238 | static int binder_open(struct inode *nodp, struct file *filp) 239 | { 240 | struct binder_proc *proc; 241 | + struct binder_dev_ns *binder_ns; 242 | 243 | binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n", 244 | current->group_leader->pid, current->pid); 245 | 246 | + binder_ns = get_binder_ns_cur(); 247 | + if (!binder_ns) 248 | + return -ENOMEM; 249 | + 250 | proc = kzalloc(sizeof(*proc), GFP_KERNEL); 251 | - if (proc == NULL) 252 | + if (proc == NULL) { 253 | + put_binder_ns(binder_ns); 254 | return -ENOMEM; 255 | + } 256 | + 257 | + proc->binder_ns = binder_ns; 258 | get_task_struct(current); 259 | proc->tsk = current; 260 | INIT_LIST_HEAD(&proc->todo); 261 | @@ -2970,7 +3039,7 @@ static int binder_open(struct inode *nodp, struct file *filp) 262 | binder_lock(__func__); 263 | 264 | binder_stats_created(BINDER_STAT_PROC); 265 | - hlist_add_head(&proc->proc_node, &binder_procs); 266 | + hlist_add_head(&proc->proc_node, &binder_ns->procs); 267 | proc->pid = current->group_leader->pid; 268 | INIT_LIST_HEAD(&proc->delivered_death); 269 | filp->private_data = proc; 270 | @@ -3035,11 +3104,12 @@ static void binder_deferred_release(struct binder_proc *proc) 271 | BUG_ON(proc->files); 272 | 273 | hlist_del(&proc->proc_node); 274 | - if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) { 275 | + if (proc->binder_ns->context_mgr_node && 276 | + proc->binder_ns->context_mgr_node->proc == proc) { 277 | binder_debug(BINDER_DEBUG_DEAD_BINDER, 278 | "binder_release: %d context_mgr_node gone\n", 279 | proc->pid); 280 | - binder_context_mgr_node = NULL; 281 | + proc->binder_ns->context_mgr_node = NULL; 282 | } 283 | 284 | threads = 0; 285 | @@ -3067,7 +3137,8 @@ static void binder_deferred_release(struct binder_proc *proc) 286 | node->proc = NULL; 287 | node->local_strong_refs = 0; 288 | node->local_weak_refs = 0; 289 | - hlist_add_head(&node->dead_node, &binder_dead_nodes); 290 | + hlist_add_head(&node->dead_node, 291 | + &proc->binder_ns->dead_nodes); 292 | 293 | hlist_for_each_entry(ref, pos, &node->refs, node_entry) { 294 | incoming_refs++; 295 | @@ -3145,6 +3216,7 @@ static void binder_deferred_release(struct binder_proc *proc) 296 | proc->pid, threads, nodes, incoming_refs, outgoing_refs, 297 | active_transactions, buffers, page_count); 298 | 299 | + put_binder_ns(proc->binder_ns); 300 | kfree(proc); 301 | } 302 | 303 | @@ -3515,62 +3587,119 @@ static void print_binder_proc_stats(struct seq_file *m, 304 | } 305 | 306 | 307 | -static int binder_state_show(struct seq_file *m, void *unused) 308 | +static void __binder_state_show(struct dev_ns_info *dev_ns_info, void *data) 309 | { 310 | + struct binder_dev_ns *binder_ns; 311 | + struct seq_file *m = data; 312 | struct binder_proc *proc; 313 | struct hlist_node *pos; 314 | struct binder_node *node; 315 | - int do_lock = !binder_debug_no_lock; 316 | + char str[64]; 317 | 318 | - if (do_lock) 319 | - binder_lock(__func__); 320 | + binder_ns = container_of(dev_ns_info, struct binder_dev_ns, 321 | + dev_ns_info); 322 | 323 | - seq_puts(m, "binder state:\n"); 324 | + snprintf(str, sizeof(str), 325 | + "binder state (0x%p):\n", dev_ns_info->dev_ns); 326 | + seq_puts(m, str); 327 | 328 | - if (!hlist_empty(&binder_dead_nodes)) 329 | + if (!hlist_empty(&binder_ns->dead_nodes)) 330 | seq_puts(m, "dead nodes:\n"); 331 | - hlist_for_each_entry(node, pos, &binder_dead_nodes, dead_node) 332 | + hlist_for_each_entry(node, pos, &binder_ns->dead_nodes, dead_node) 333 | print_binder_node(m, node); 334 | - 335 | - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) 336 | + hlist_for_each_entry(proc, pos, &binder_ns->procs, proc_node) 337 | print_binder_proc(m, proc, 1); 338 | +} 339 | + 340 | +static int binder_state_show(struct seq_file *m, void *unused) 341 | +{ 342 | + int do_lock = !binder_debug_no_lock; 343 | + 344 | + if (do_lock) 345 | + binder_lock(__func__); 346 | + 347 | +#ifdef CONFIG_DEV_NS 348 | + loop_dev_ns_info(binder_ns_id, m, __binder_state_show); 349 | +#else 350 | + __binder_state_show(&init_binder_ns.dev_ns_info, m); 351 | +#endif 352 | + 353 | if (do_lock) 354 | binder_unlock(__func__); 355 | return 0; 356 | } 357 | 358 | -static int binder_stats_show(struct seq_file *m, void *unused) 359 | +static void __binder_stats_show(struct dev_ns_info *dev_ns_info, void *data) 360 | { 361 | + struct binder_dev_ns *binder_ns; 362 | + struct seq_file *m = data; 363 | struct binder_proc *proc; 364 | struct hlist_node *pos; 365 | + char str[64]; 366 | + 367 | + binder_ns = container_of(dev_ns_info, struct binder_dev_ns, 368 | + dev_ns_info); 369 | + 370 | + snprintf(str, sizeof(str), 371 | + "binder stats (0x%p):\n", dev_ns_info->dev_ns); 372 | + seq_puts(m, str); 373 | + 374 | + print_binder_stats(m, "", &binder_stats); 375 | + hlist_for_each_entry(proc, pos, &binder_ns->procs, proc_node) 376 | + print_binder_proc_stats(m, proc); 377 | +} 378 | + 379 | +static int binder_stats_show(struct seq_file *m, void *unused) 380 | +{ 381 | int do_lock = !binder_debug_no_lock; 382 | 383 | if (do_lock) 384 | binder_lock(__func__); 385 | 386 | - seq_puts(m, "binder stats:\n"); 387 | - 388 | - print_binder_stats(m, "", &binder_stats); 389 | +#ifdef CONFIG_DEV_NS 390 | + loop_dev_ns_info(binder_ns_id, m, __binder_stats_show); 391 | +#else 392 | + __binder_stats_show(&init_binder_ns.dev_ns_info, m); 393 | +#endif 394 | 395 | - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) 396 | - print_binder_proc_stats(m, proc); 397 | if (do_lock) 398 | binder_unlock(__func__); 399 | return 0; 400 | } 401 | 402 | -static int binder_transactions_show(struct seq_file *m, void *unused) 403 | +static void __binder_transaction_show(struct dev_ns_info *dev_ns_info, 404 | + void *data) 405 | { 406 | + struct binder_dev_ns *binder_ns; 407 | + struct seq_file *m = data; 408 | struct binder_proc *proc; 409 | struct hlist_node *pos; 410 | + char str[64]; 411 | + 412 | + binder_ns = container_of(dev_ns_info, struct binder_dev_ns, 413 | + dev_ns_info); 414 | + 415 | + snprintf(str, sizeof(str), 416 | + "binder transactions (0x%p):\n", dev_ns_info->dev_ns); 417 | + seq_puts(m, str); 418 | + 419 | + hlist_for_each_entry(proc, pos, &binder_ns->procs, proc_node) 420 | + print_binder_proc(m, proc, 0); 421 | +} 422 | + 423 | +static int binder_transactions_show(struct seq_file *m, void *unused) 424 | +{ 425 | int do_lock = !binder_debug_no_lock; 426 | 427 | if (do_lock) 428 | binder_lock(__func__); 429 | 430 | - seq_puts(m, "binder transactions:\n"); 431 | - hlist_for_each_entry(proc, pos, &binder_procs, proc_node) 432 | - print_binder_proc(m, proc, 0); 433 | +#ifdef CONFIG_DEV_NS 434 | + loop_dev_ns_info(binder_ns_id, m, __binder_transaction_show); 435 | +#else 436 | + __binder_transaction_show(&init_binder_ns.dev_ns_info, m); 437 | +#endif 438 | + 439 | if (do_lock) 440 | binder_unlock(__func__); 441 | return 0; 442 | @@ -3644,6 +3773,16 @@ static int __init binder_init(void) 443 | if (!binder_deferred_workqueue) 444 | return -ENOMEM; 445 | 446 | +#ifdef CONFIG_DEV_NS 447 | + ret = DEV_NS_REGISTER(binder, "binder"); 448 | + if (ret < 0) { 449 | + destroy_workqueue(binder_deferred_workqueue); 450 | + return ret; 451 | + } 452 | +#else 453 | + binder_ns_initialize(&init_binder_ns); 454 | +#endif 455 | + 456 | binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); 457 | if (binder_debugfs_dir_entry_root) 458 | binder_debugfs_dir_entry_proc = debugfs_create_dir("proc", 459 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_android_alarm-dev_driver: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the android alarm-dev driver 2 | 3 | Add device namespace logic to Android alarm-dev driver - so processes in a 4 | namespace would only see alarms in its own namespace. 5 | 6 | Place per-namespace data into struct alarm_dev_ns, and convert the driver 7 | to operate on the current process's context. 8 | 9 | The exception is an ioctl call with ANDROID_ALARM_SET_RTC, in which case 10 | the fact that the RTC setting changes must be communicated to all of the 11 | contexts (device namespaces) so it takes effect in each namespace. This is 12 | by calling the new propagate_alarm_time_change(). 13 | 14 | Changelog: 15 | [16-Aug-2013] v1 - initial version 16 | 17 | Change-Id: I968989e23268d9510f40cf90b7c3366c03d4a226 18 | Signed-off-by: Oren Laadan 19 | Signed-off-by: Amir Goldstein 20 | 21 | --- 22 | drivers/staging/android/alarm-dev.c | 315 +++++++++++++++++++++++++++--------- 23 | 1 file changed, 236 insertions(+), 79 deletions(-) 24 | 25 | diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c 26 | index e001fe5..ff2505b 100644 27 | --- a/drivers/staging/android/alarm-dev.c 28 | +++ b/drivers/staging/android/alarm-dev.c 29 | @@ -24,12 +24,41 @@ 30 | #include 31 | #include 32 | #include 33 | +#include 34 | +#include 35 | #include "android_alarm.h" 36 | 37 | #define ANDROID_ALARM_PRINT_INFO (1U << 0) 38 | #define ANDROID_ALARM_PRINT_IO (1U << 1) 39 | #define ANDROID_ALARM_PRINT_INT (1U << 2) 40 | 41 | +struct alarm_dev_ns; 42 | + 43 | +struct devalarm { 44 | + union { 45 | + struct hrtimer hrt; 46 | + struct alarm alrm; 47 | + } u; 48 | + enum android_alarm_type type; 49 | + 50 | + struct alarm_dev_ns *alarm_ns; 51 | +}; 52 | + 53 | +struct alarm_dev_ns { 54 | + int alarm_opened; 55 | + spinlock_t alarm_slock; 56 | + struct wake_lock alarm_wake_lock; 57 | + wait_queue_head_t alarm_wait_queue; 58 | + uint32_t alarm_pending; 59 | + uint32_t alarm_enabled; 60 | + uint32_t wait_pending; 61 | + 62 | + struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; 63 | + char wakelock_name[32]; 64 | + 65 | + struct dev_ns_info dev_ns_info; 66 | +}; 67 | + 68 | static int debug_mask = ANDROID_ALARM_PRINT_INFO; 69 | module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); 70 | 71 | @@ -48,23 +77,100 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); 72 | #define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ 73 | #define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) 74 | 75 | -static int alarm_opened; 76 | -static DEFINE_SPINLOCK(alarm_slock); 77 | -static struct wake_lock alarm_wake_lock; 78 | -static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); 79 | -static uint32_t alarm_pending; 80 | -static uint32_t alarm_enabled; 81 | -static uint32_t wait_pending; 82 | +static void devalarm_alarms_init(struct devalarm *alarms); 83 | 84 | -struct devalarm { 85 | - union { 86 | - struct hrtimer hrt; 87 | - struct alarm alrm; 88 | - } u; 89 | - enum android_alarm_type type; 90 | +static void alarm_ns_initialize(struct alarm_dev_ns *alarm_ns) 91 | +{ 92 | + alarm_ns->alarm_slock = __SPIN_LOCK_UNLOCKED(alarm_ns->alarm_slock); 93 | + init_waitqueue_head(&alarm_ns->alarm_wait_queue); 94 | + 95 | + alarm_ns->alarm_pending = 0; 96 | + alarm_ns->alarm_enabled = 0; 97 | + alarm_ns->wait_pending = 0; 98 | + 99 | + devalarm_alarms_init(alarm_ns->alarms); 100 | + 101 | +#ifdef CONFIG_HAS_WAKELOCK 102 | +#ifdef CONFIG_DEV_NS 103 | + /* encode device-namespace into wakelock to ensure uniqueness */ 104 | + sprintf(alarm_ns->wakelock_name, "alarm[ns:%d]", 105 | + dev_ns_init_pid(current_dev_ns())); 106 | +#else 107 | + sprintf(alarm_ns->wakelock_name, "alarm"); 108 | +#endif 109 | + 110 | + wake_lock_init(&alarm_ns->alarm_wake_lock, WAKE_LOCK_SUSPEND, 111 | + alarm_ns->wakelock_name); 112 | +#endif 113 | +} 114 | + 115 | +static void alarm_ns_destroy(struct alarm_dev_ns *alarm_ns) 116 | +{ 117 | +#ifdef CONFIG_HAS_WAKELOCK 118 | + wake_lock_destroy(&alarm_ns->alarm_wake_lock); 119 | +#endif 120 | +} 121 | + 122 | +#ifdef CONFIG_DEV_NS 123 | + 124 | +/* alarm_ns_id, get_alarm_ns(), get_alarm_ns_cur(), put_alarm_ns() */ 125 | +DEFINE_DEV_NS_INFO(alarm) 126 | + 127 | +static struct dev_ns_info *alarm_ns_create(struct dev_namespace *dev_ns) 128 | +{ 129 | + struct alarm_dev_ns *alarm_ns; 130 | + 131 | + alarm_ns = kzalloc(sizeof(*alarm_ns), GFP_KERNEL); 132 | + if (!alarm_ns) 133 | + return ERR_PTR(-ENOMEM); 134 | + 135 | + alarm_ns_initialize(alarm_ns); 136 | + 137 | + return &alarm_ns->dev_ns_info; 138 | +} 139 | + 140 | +static void alarm_ns_release(struct dev_ns_info *dev_ns_info) 141 | +{ 142 | + struct alarm_dev_ns *alarm_ns; 143 | + 144 | + alarm_ns = container_of(dev_ns_info, struct alarm_dev_ns, dev_ns_info); 145 | + alarm_ns_destroy(alarm_ns); 146 | + kfree(alarm_ns); 147 | +} 148 | + 149 | +/* 150 | + * If the RTC is set in any namespace (via alarm_set_rtc), make sure 151 | + * the remaining namespaces are udpates suitably. 152 | + */ 153 | + 154 | +static void update_alarm_time_change(struct alarm_dev_ns *alarm_ns); 155 | + 156 | +static void propagate_alarm_time_change_func(struct dev_ns_info *dev_ns_info, 157 | + void *unused) 158 | +{ 159 | + struct alarm_dev_ns *alarm_ns; 160 | + 161 | + alarm_ns = container_of(dev_ns_info, struct alarm_dev_ns, dev_ns_info); 162 | + update_alarm_time_change(alarm_ns); 163 | +} 164 | + 165 | +static void propagate_alarm_time_change(void) 166 | +{ 167 | + loop_dev_ns_info(alarm_ns_id, NULL, propagate_alarm_time_change_func); 168 | +} 169 | + 170 | + 171 | +static struct dev_ns_ops alarm_ns_ops = { 172 | + .create = alarm_ns_create, 173 | + .release = alarm_ns_release, 174 | }; 175 | 176 | -static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; 177 | +#else 178 | + 179 | +/* init_alarm_ns, get_alarm_ns(), get_alarm_ns_cur(), put_alarm_ns() */ 180 | +DEFINE_DEV_NS_INIT(alarm) 181 | + 182 | +#endif /* CONFIG_DEV_NS */ 183 | 184 | 185 | static int is_wakeup(enum android_alarm_type type) 186 | @@ -103,6 +209,15 @@ static void devalarm_cancel(struct devalarm *alrm) 187 | hrtimer_cancel(&alrm->u.hrt); 188 | } 189 | 190 | +static void update_alarm_time_change(struct alarm_dev_ns *alarm_ns) 191 | +{ 192 | + unsigned long flags; 193 | + 194 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 195 | + alarm_ns->alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; 196 | + wake_up(&alarm_ns->alarm_wait_queue); 197 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 198 | +} 199 | 200 | static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 201 | { 202 | @@ -115,38 +230,50 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 203 | struct rtc_device *rtc_dev; 204 | enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); 205 | uint32_t alarm_type_mask = 1U << alarm_type; 206 | + struct alarm_dev_ns *alarm_ns; 207 | + bool opened = false; 208 | 209 | if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) 210 | return -EINVAL; 211 | 212 | + alarm_ns = get_alarm_ns_cur(); 213 | + if (!alarm_ns) 214 | + return -ENOMEM; 215 | + 216 | if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { 217 | - if ((file->f_flags & O_ACCMODE) == O_RDONLY) 218 | - return -EPERM; 219 | + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { 220 | + rv = -EPERM; 221 | + goto err1; 222 | + } 223 | + 224 | if (file->private_data == NULL && 225 | cmd != ANDROID_ALARM_SET_RTC) { 226 | - spin_lock_irqsave(&alarm_slock, flags); 227 | - if (alarm_opened) { 228 | - spin_unlock_irqrestore(&alarm_slock, flags); 229 | - return -EBUSY; 230 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 231 | + if (alarm_ns->alarm_opened) { 232 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, 233 | + flags); 234 | + rv = -EBUSY; 235 | + goto err1; 236 | } 237 | - alarm_opened = 1; 238 | + opened = true; 239 | + alarm_ns->alarm_opened = 1; 240 | file->private_data = (void *)1; 241 | - spin_unlock_irqrestore(&alarm_slock, flags); 242 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 243 | } 244 | } 245 | 246 | switch (ANDROID_ALARM_BASE_CMD(cmd)) { 247 | case ANDROID_ALARM_CLEAR(0): 248 | - spin_lock_irqsave(&alarm_slock, flags); 249 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 250 | pr_alarm(IO, "alarm %d clear\n", alarm_type); 251 | - devalarm_try_to_cancel(&alarms[alarm_type]); 252 | - if (alarm_pending) { 253 | - alarm_pending &= ~alarm_type_mask; 254 | - if (!alarm_pending && !wait_pending) 255 | - wake_unlock(&alarm_wake_lock); 256 | + devalarm_try_to_cancel(&alarm_ns->alarms[alarm_type]); 257 | + if (alarm_ns->alarm_pending) { 258 | + alarm_ns->alarm_pending &= ~alarm_type_mask; 259 | + if (!alarm_ns->alarm_pending && !alarm_ns->wait_pending) 260 | + wake_unlock(&alarm_ns->alarm_wake_lock); 261 | } 262 | - alarm_enabled &= ~alarm_type_mask; 263 | - spin_unlock_irqrestore(&alarm_slock, flags); 264 | + alarm_ns->alarm_enabled &= ~alarm_type_mask; 265 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 266 | break; 267 | 268 | case ANDROID_ALARM_SET_OLD: 269 | @@ -166,33 +293,34 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 270 | goto err1; 271 | } 272 | from_old_alarm_set: 273 | - spin_lock_irqsave(&alarm_slock, flags); 274 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 275 | pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, 276 | new_alarm_time.tv_sec, new_alarm_time.tv_nsec); 277 | - alarm_enabled |= alarm_type_mask; 278 | - devalarm_start(&alarms[alarm_type], 279 | + alarm_ns->alarm_enabled |= alarm_type_mask; 280 | + devalarm_start(&alarm_ns->alarms[alarm_type], 281 | timespec_to_ktime(new_alarm_time)); 282 | - spin_unlock_irqrestore(&alarm_slock, flags); 283 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 284 | if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) 285 | && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) 286 | break; 287 | /* fall though */ 288 | case ANDROID_ALARM_WAIT: 289 | - spin_lock_irqsave(&alarm_slock, flags); 290 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 291 | pr_alarm(IO, "alarm wait\n"); 292 | - if (!alarm_pending && wait_pending) { 293 | - wake_unlock(&alarm_wake_lock); 294 | - wait_pending = 0; 295 | + if (!alarm_ns->alarm_pending && alarm_ns->wait_pending) { 296 | + wake_unlock(&alarm_ns->alarm_wake_lock); 297 | + alarm_ns->wait_pending = 0; 298 | } 299 | - spin_unlock_irqrestore(&alarm_slock, flags); 300 | - rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); 301 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 302 | + rv = wait_event_interruptible(alarm_ns->alarm_wait_queue, 303 | + alarm_ns->alarm_pending); 304 | if (rv) 305 | goto err1; 306 | - spin_lock_irqsave(&alarm_slock, flags); 307 | - rv = alarm_pending; 308 | - wait_pending = 1; 309 | - alarm_pending = 0; 310 | - spin_unlock_irqrestore(&alarm_slock, flags); 311 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 312 | + rv = alarm_ns->alarm_pending; 313 | + alarm_ns->wait_pending = 1; 314 | + alarm_ns->alarm_pending = 0; 315 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 316 | break; 317 | case ANDROID_ALARM_SET_RTC: 318 | if (copy_from_user(&new_rtc_time, (void __user *)arg, 319 | @@ -207,10 +335,10 @@ from_old_alarm_set: 320 | goto err1; 321 | if (rtc_dev) 322 | rv = rtc_set_time(rtc_dev, &new_rtc_tm); 323 | - spin_lock_irqsave(&alarm_slock, flags); 324 | - alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; 325 | - wake_up(&alarm_wait_queue); 326 | - spin_unlock_irqrestore(&alarm_slock, flags); 327 | + update_alarm_time_change(alarm_ns); 328 | +#ifdef CONFIG_DEV_NS 329 | + propagate_alarm_time_change(); 330 | +#endif 331 | if (rv < 0) 332 | goto err1; 333 | break; 334 | @@ -241,6 +369,8 @@ from_old_alarm_set: 335 | goto err1; 336 | } 337 | err1: 338 | + if (!opened) 339 | + put_alarm_ns(alarm_ns); 340 | return rv; 341 | } 342 | 343 | @@ -254,32 +384,39 @@ static int alarm_release(struct inode *inode, struct file *file) 344 | { 345 | int i; 346 | unsigned long flags; 347 | + struct alarm_dev_ns *alarm_ns; 348 | + 349 | + alarm_ns = get_alarm_ns_cur(); 350 | 351 | - spin_lock_irqsave(&alarm_slock, flags); 352 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 353 | if (file->private_data != 0) { 354 | for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { 355 | uint32_t alarm_type_mask = 1U << i; 356 | - if (alarm_enabled & alarm_type_mask) { 357 | + if (alarm_ns->alarm_enabled & alarm_type_mask) { 358 | pr_alarm(INFO, "alarm_release: clear alarm, " 359 | "pending %d\n", 360 | - !!(alarm_pending & alarm_type_mask)); 361 | - alarm_enabled &= ~alarm_type_mask; 362 | + !!(alarm_ns->alarm_pending & 363 | + alarm_type_mask)); 364 | + alarm_ns->alarm_enabled &= ~alarm_type_mask; 365 | } 366 | - spin_unlock_irqrestore(&alarm_slock, flags); 367 | - devalarm_cancel(&alarms[i]); 368 | - spin_lock_irqsave(&alarm_slock, flags); 369 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 370 | + devalarm_cancel(&alarm_ns->alarms[i]); 371 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 372 | } 373 | - if (alarm_pending | wait_pending) { 374 | - if (alarm_pending) 375 | + if (alarm_ns->alarm_pending | alarm_ns->wait_pending) { 376 | + if (alarm_ns->alarm_pending) 377 | pr_alarm(INFO, "alarm_release: clear " 378 | - "pending alarms %x\n", alarm_pending); 379 | - wake_unlock(&alarm_wake_lock); 380 | - wait_pending = 0; 381 | - alarm_pending = 0; 382 | + "pending alarms %x\n", 383 | + alarm_ns->alarm_pending); 384 | + wake_unlock(&alarm_ns->alarm_wake_lock); 385 | + alarm_ns->wait_pending = 0; 386 | + alarm_ns->alarm_pending = 0; 387 | } 388 | - alarm_opened = 0; 389 | + alarm_ns->alarm_opened = 0; 390 | + put_alarm_ns(alarm_ns); /* drop reference from open time */ 391 | } 392 | - spin_unlock_irqrestore(&alarm_slock, flags); 393 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 394 | + put_alarm_ns(alarm_ns); 395 | return 0; 396 | } 397 | 398 | @@ -287,16 +424,17 @@ static void devalarm_triggered(struct devalarm *alarm) 399 | { 400 | unsigned long flags; 401 | uint32_t alarm_type_mask = 1U << alarm->type; 402 | + struct alarm_dev_ns *alarm_ns = alarm->alarm_ns; 403 | 404 | pr_alarm(INT, "devalarm_triggered type %d\n", alarm->type); 405 | - spin_lock_irqsave(&alarm_slock, flags); 406 | - if (alarm_enabled & alarm_type_mask) { 407 | - wake_lock_timeout(&alarm_wake_lock, 5 * HZ); 408 | - alarm_enabled &= ~alarm_type_mask; 409 | - alarm_pending |= alarm_type_mask; 410 | - wake_up(&alarm_wait_queue); 411 | + spin_lock_irqsave(&alarm_ns->alarm_slock, flags); 412 | + if (alarm_ns->alarm_enabled & alarm_type_mask) { 413 | + wake_lock_timeout(&alarm_ns->alarm_wake_lock, 5 * HZ); 414 | + alarm_ns->alarm_enabled &= ~alarm_type_mask; 415 | + alarm_ns->alarm_pending |= alarm_type_mask; 416 | + wake_up(&alarm_ns->alarm_wait_queue); 417 | } 418 | - spin_unlock_irqrestore(&alarm_slock, flags); 419 | + spin_unlock_irqrestore(&alarm_ns->alarm_slock, flags); 420 | } 421 | 422 | 423 | @@ -331,15 +469,12 @@ static struct miscdevice alarm_device = { 424 | .fops = &alarm_fops, 425 | }; 426 | 427 | -static int __init alarm_dev_init(void) 428 | +static void devalarm_alarms_init(struct devalarm *alarms) 429 | { 430 | - int err; 431 | + struct alarm_dev_ns *alarm_ns = 432 | + container_of(alarms, struct alarm_dev_ns, alarms[0]); 433 | int i; 434 | 435 | - err = misc_register(&alarm_device); 436 | - if (err) 437 | - return err; 438 | - 439 | alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm, 440 | ALARM_REALTIME, devalarm_alarmhandler); 441 | hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt, 442 | @@ -353,19 +488,41 @@ static int __init alarm_dev_init(void) 443 | 444 | for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { 445 | alarms[i].type = i; 446 | + alarms[i].alarm_ns = alarm_ns; 447 | if (!is_wakeup(i)) 448 | alarms[i].u.hrt.function = devalarm_hrthandler; 449 | } 450 | 451 | - wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); 452 | +} 453 | 454 | +static int __init alarm_dev_init(void) 455 | +{ 456 | + int err; 457 | + 458 | + err = misc_register(&alarm_device); 459 | + if (err) 460 | + return err; 461 | + 462 | +#ifdef CONFIG_DEV_NS 463 | + err = DEV_NS_REGISTER(alarm, "alarm"); 464 | + if (err < 0) { 465 | + misc_deregister(&alarm_device); 466 | + return err; 467 | + } 468 | +#else 469 | + alarm_ns_initialize(&init_alarm_ns); 470 | +#endif 471 | return 0; 472 | } 473 | 474 | static void __exit alarm_dev_exit(void) 475 | { 476 | misc_deregister(&alarm_device); 477 | - wake_lock_destroy(&alarm_wake_lock); 478 | +#ifdef CONFIG_DEV_NS 479 | + DEV_NS_UNREGISTER(alarm); 480 | +#else 481 | + alarm_ns_destroy(&init_alarm_ns); 482 | +#endif 483 | } 484 | 485 | module_init(alarm_dev_init); 486 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_input_subsystem: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the input subsystem 2 | 3 | Adds device namespace logic to the input subsystem to multiplex inputs events 4 | between device namespaces (evdev, mousedev). 5 | 6 | Keep track of the device namespace of clients, and deliver input events only to 7 | those clients that belong to an active (foreground) namespace. Thus, listeners 8 | in background namespaces are oblivious to such input. 9 | 10 | With evdev, input grab depends on the device namespace context. Only clients in 11 | an active namespace can really grab an input, and from an active namespace, grab 12 | operates as usual. From a background namespace, input grab is done virtually: 13 | if no other clients in same namespace have the grab, the operation succeeds and 14 | the client is marked as having the grab, but no action is taken. When another 15 | namepace becomes active, grabs are swapped: current real grabs are undone and 16 | instead become virtual, and virtual grabs are forced and become real. 17 | 18 | Changelog: 19 | [16-Aug-2013] v1 - initial version 20 | 21 | Change-Id: Ie6a6d192cfaabeb7b06cc5d12a5ac0576c4a45d0 22 | Signed-off-by: Oren Laadan 23 | Signed-off-by: Amir Goldstein 24 | 25 | --- 26 | drivers/input/evdev.c | 275 +++++++++++++++++++++++++++++++++++++++++++++-- 27 | drivers/input/mousedev.c | 20 ++++ 28 | 2 files changed, 289 insertions(+), 6 deletions(-) 29 | 30 | diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c 31 | index a937438..b452b64 100644 32 | --- a/drivers/input/evdev.c 33 | +++ b/drivers/input/evdev.c 34 | @@ -8,7 +8,23 @@ 35 | * the Free Software Foundation. 36 | */ 37 | 38 | +#ifdef CONFIG_DEV_NS 39 | +#define DEBUG 40 | +#define DEV_NS_EVDEV_DEBUG 41 | + 42 | +#ifdef DEV_NS_EVDEV_DEBUG 43 | +#define pr_fmt(fmt) \ 44 | + "[%d] devns:evdev [%s:%d]: " fmt, \ 45 | + current->pid, __func__, __LINE__ 46 | +#else 47 | +#define pr_fmt(fmt) \ 48 | + "[%d] devns:evdev: " fmt, current->pid 49 | +#endif 50 | +#endif /* CONFIG_DEV_NS */ 51 | + 52 | +#ifndef pr_fmt 53 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 54 | +#endif 55 | 56 | #define EVDEV_MINOR_BASE 64 57 | #define EVDEV_MINORS 32 58 | @@ -24,6 +40,10 @@ 59 | #include 60 | #include 61 | #include 62 | +#ifdef CONFIG_DEV_NS 63 | +#include 64 | +#endif 65 | + 66 | #include "input-compat.h" 67 | 68 | struct evdev { 69 | @@ -39,6 +59,10 @@ struct evdev { 70 | bool exist; 71 | }; 72 | 73 | +#ifdef CONFIG_DEV_NS 74 | +struct evdev_dev_ns; 75 | +#endif 76 | + 77 | struct evdev_client { 78 | unsigned int head; 79 | unsigned int tail; 80 | @@ -46,7 +70,12 @@ struct evdev_client { 81 | spinlock_t buffer_lock; /* protects access to buffer, head and tail */ 82 | struct wake_lock wake_lock; 83 | bool use_wake_lock; 84 | - char name[28]; 85 | + char name[44]; 86 | +#ifdef CONFIG_DEV_NS 87 | + struct evdev_dev_ns *evdev_ns; 88 | + struct list_head list; 89 | + bool grab; 90 | +#endif 91 | struct fasync_struct *fasync; 92 | struct evdev *evdev; 93 | struct list_head node; 94 | @@ -58,6 +87,161 @@ struct evdev_client { 95 | static struct evdev *evdev_table[EVDEV_MINORS]; 96 | static DEFINE_MUTEX(evdev_table_mutex); 97 | 98 | +/* 99 | + * Multiplex inputs events between device namespaces (evdev, mousedev). 100 | + * 101 | + * Keep track of the device namespace of clients, and deliver input events 102 | + * only to those clients that belong to an active (foreground) namespace. 103 | + * Thus, listeners in background namespaces are oblivious to such input. 104 | + * 105 | + * With evdev, input grab depends on the device namespace context. Only 106 | + * clients in an active namespace can really grab an input, and from an active 107 | + * namespace, grab operates as usual. From a background namespace, input grab 108 | + * is done virtually: if no other clients in same namespace have the grab, the 109 | + * operation succeeds and the client is marked as having the grab, but no 110 | + * action is taken. When another namepace becomes active, grabs are swapped: 111 | + * current real grabs are undone and instead become virtual, and virtual grabs 112 | + * are forced and become real. 113 | + */ 114 | + 115 | +#ifdef CONFIG_DEV_NS 116 | +struct evdev_dev_ns { 117 | + struct mutex mutex; 118 | + struct list_head clients; 119 | + struct dev_ns_info dev_ns_info; 120 | +}; 121 | + 122 | +/* evdev_ns_id, get_evdev_ns(), get_evdev_ns_cur(), put_evdev_ns() */ 123 | +DEFINE_DEV_NS_INFO(evdev) 124 | + 125 | +/* indicate whether an evdev client is in the foreground */ 126 | +static bool evdev_client_is_active(struct evdev_client *client) 127 | +{ 128 | + return is_active_dev_ns(client->evdev_ns->dev_ns_info.dev_ns); 129 | +} 130 | + 131 | +static struct notifier_block evdev_ns_switch_notifier; 132 | +static int evdev_grab(struct evdev *evdev, struct evdev_client *client); 133 | +static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client); 134 | + 135 | +/* evdev_ns helpers */ 136 | +static struct dev_ns_info *evdev_devns_create(struct dev_namespace *dev_ns) 137 | +{ 138 | + struct evdev_dev_ns *evdev_ns; 139 | + struct dev_ns_info *dev_ns_info; 140 | + 141 | + evdev_ns = kzalloc(sizeof(*evdev_ns), GFP_KERNEL); 142 | + if (!evdev_ns) 143 | + return ERR_PTR(-ENOMEM); 144 | + 145 | + mutex_init(&evdev_ns->mutex); 146 | + INIT_LIST_HEAD(&evdev_ns->clients); 147 | + 148 | + pr_info("new evdev_dev_ns %p (d %p)\n", evdev_ns, dev_ns); 149 | + 150 | + dev_ns_info = &evdev_ns->dev_ns_info; 151 | + 152 | + dev_ns_info->nb = evdev_ns_switch_notifier; 153 | + dev_ns_register_notify(dev_ns, &dev_ns_info->nb); 154 | + 155 | + return dev_ns_info; 156 | +} 157 | + 158 | +static void evdev_devns_release(struct dev_ns_info *dev_ns_info) 159 | +{ 160 | + struct evdev_dev_ns *evdev_ns; 161 | + 162 | + evdev_ns = container_of(dev_ns_info, struct evdev_dev_ns, dev_ns_info); 163 | + 164 | + pr_info("del evdev_dev_ns %p (d %p)\n", evdev_ns, dev_ns_info->dev_ns); 165 | + dev_ns_unregister_notify(dev_ns_info->dev_ns, &dev_ns_info->nb); 166 | + 167 | + kfree(evdev_ns); 168 | +} 169 | + 170 | +static struct dev_ns_ops evdev_ns_ops = { 171 | + .create = evdev_devns_create, 172 | + .release = evdev_devns_release, 173 | +}; 174 | + 175 | +static int evdev_ns_track_client(struct evdev_client *client) 176 | +{ 177 | + struct evdev_dev_ns *evdev_ns; 178 | + 179 | + evdev_ns = get_evdev_ns_cur(); 180 | + if (!evdev_ns) 181 | + return -ENOMEM; 182 | + 183 | + pr_info("track new client 0x%p in evdev_ns 0x%p (dev_ns 0x%p)\n", 184 | + client, evdev_ns, evdev_ns->dev_ns_info.dev_ns); 185 | + 186 | + client->evdev_ns = evdev_ns; 187 | + client->grab = false; 188 | + 189 | + mutex_lock(&evdev_ns->mutex); 190 | + list_add(&client->list, &evdev_ns->clients); 191 | + mutex_unlock(&evdev_ns->mutex); 192 | + 193 | + return 0; 194 | +} 195 | + 196 | +static void evdev_ns_untrack_client(struct evdev_client *client) 197 | +{ 198 | + struct evdev_dev_ns *evdev_ns; 199 | + 200 | + evdev_ns = client->evdev_ns; 201 | + 202 | + pr_info("untrack client 0x%p in evdev_ns 0x%p (dev_ns 0x%p)\n", 203 | + client, evdev_ns, evdev_ns->dev_ns_info.dev_ns); 204 | + 205 | + mutex_lock(&evdev_ns->mutex); 206 | + list_del(&client->list); 207 | + mutex_unlock(&evdev_ns->mutex); 208 | + 209 | + put_evdev_ns(evdev_ns); 210 | +} 211 | + 212 | +/* dev_ns and respective fb_dev_ns protected by caller */ 213 | +static int evdev_ns_switch_callback(struct notifier_block *self, 214 | + unsigned long action, void *data) 215 | +{ 216 | + struct dev_namespace *dev_ns = data; 217 | + struct evdev_dev_ns *evdev_ns; 218 | + struct evdev_client *client; 219 | + 220 | + evdev_ns = find_evdev_ns(dev_ns); 221 | + WARN(evdev_ns == NULL, "devns 0x%p: no matching evdev_ns\n", dev_ns); 222 | + 223 | + mutex_lock(&evdev_ns->mutex); 224 | + switch (action) { 225 | + case DEV_NS_EVENT_ACTIVATE: 226 | + list_for_each_entry(client, &evdev_ns->clients, list) 227 | + { 228 | + mutex_lock(&client->evdev->mutex); 229 | + if (client->grab) 230 | + evdev_grab(client->evdev, client); 231 | + mutex_unlock(&client->evdev->mutex); 232 | + } 233 | + break; 234 | + case DEV_NS_EVENT_DEACTIVATE: 235 | + list_for_each_entry(client, &evdev_ns->clients, list) 236 | + { 237 | + mutex_lock(&client->evdev->mutex); 238 | + if (client->evdev->grab == client) 239 | + evdev_ungrab(client->evdev, client); 240 | + mutex_unlock(&client->evdev->mutex); 241 | + } 242 | + break; 243 | + } 244 | + mutex_unlock(&evdev_ns->mutex); 245 | + return 0; 246 | +} 247 | + 248 | +static struct notifier_block evdev_ns_switch_notifier = { 249 | + .notifier_call = evdev_ns_switch_callback, 250 | +}; 251 | +#endif /* CONFIG_DEV_NS */ 252 | + 253 | static void evdev_pass_event(struct evdev_client *client, 254 | struct input_event *event, 255 | ktime_t mono, ktime_t real) 256 | @@ -123,8 +307,13 @@ static void evdev_event(struct input_handle *handle, 257 | if (client) 258 | evdev_pass_event(client, &event, time_mono, time_real); 259 | else 260 | - list_for_each_entry_rcu(client, &evdev->client_list, node) 261 | + list_for_each_entry_rcu(client, &evdev->client_list, node) { 262 | +#ifdef CONFIG_DEV_NS 263 | + if (!evdev_client_is_active(client)) 264 | + continue; 265 | +#endif 266 | evdev_pass_event(client, &event, time_mono, time_real); 267 | + } 268 | 269 | rcu_read_unlock(); 270 | 271 | @@ -181,6 +370,9 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client) 272 | if (error) 273 | return error; 274 | 275 | +#ifdef CONFIG_DEV_NS 276 | + client->grab = true; 277 | +#endif 278 | rcu_assign_pointer(evdev->grab, client); 279 | 280 | return 0; 281 | @@ -191,6 +383,9 @@ static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) 282 | if (evdev->grab != client) 283 | return -EINVAL; 284 | 285 | +#ifdef CONFIG_DEV_NS 286 | + client->grab = false; 287 | +#endif 288 | rcu_assign_pointer(evdev->grab, NULL); 289 | synchronize_rcu(); 290 | input_release_device(&evdev->handle); 291 | @@ -271,6 +466,10 @@ static int evdev_release(struct inode *inode, struct file *file) 292 | evdev_ungrab(evdev, client); 293 | mutex_unlock(&evdev->mutex); 294 | 295 | +#ifdef CONFIG_DEV_NS 296 | + evdev_ns_untrack_client(client); 297 | +#endif 298 | + 299 | evdev_detach_client(evdev, client); 300 | if (client->use_wake_lock) 301 | wake_lock_destroy(&client->wake_lock); 302 | @@ -325,22 +524,38 @@ static int evdev_open(struct inode *inode, struct file *file) 303 | 304 | client->bufsize = bufsize; 305 | spin_lock_init(&client->buffer_lock); 306 | - snprintf(client->name, sizeof(client->name), "%s-%d", 307 | + i = snprintf(client->name, sizeof(client->name), "%s-%d", 308 | dev_name(&evdev->dev), task_tgid_vnr(current)); 309 | +#ifdef CONFIG_DEV_NS 310 | + /* uniquely identify evdev across device namespace */ 311 | + snprintf(client->name + i, sizeof(client->name) - i, "[ns:%d]", 312 | + dev_ns_init_pid(current_dev_ns())); 313 | +#endif 314 | client->evdev = evdev; 315 | + 316 | +#ifdef CONFIG_DEV_NS 317 | + error = evdev_ns_track_client(client); 318 | + if (error) 319 | + goto err_free_client; 320 | +#endif 321 | + 322 | evdev_attach_client(evdev, client); 323 | 324 | error = evdev_open_device(evdev); 325 | if (error) 326 | - goto err_free_client; 327 | + goto err_detach_client; 328 | 329 | file->private_data = client; 330 | nonseekable_open(inode, file); 331 | 332 | return 0; 333 | 334 | - err_free_client: 335 | + err_detach_client: 336 | evdev_detach_client(evdev, client); 337 | +#ifdef CONFIG_DEV_NS 338 | + evdev_ns_untrack_client(client); 339 | + err_free_client: 340 | +#endif 341 | kfree(client); 342 | err_put_evdev: 343 | put_device(&evdev->dev); 344 | @@ -374,6 +589,10 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, 345 | } 346 | retval += input_event_size(); 347 | 348 | +#ifdef CONFIG_DEV_NS 349 | + if (!evdev_client_is_active(client)) 350 | + continue; 351 | +#endif 352 | input_inject_event(&evdev->handle, 353 | event.type, event.code, event.value); 354 | } while (retval + input_event_size() <= count); 355 | @@ -739,12 +958,20 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, 356 | if (get_user(v, ip + 1)) 357 | return -EFAULT; 358 | 359 | +#ifdef CONFIG_DEV_NS 360 | + if (!evdev_client_is_active(client)) 361 | + return 0; 362 | +#endif 363 | input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); 364 | input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); 365 | 366 | return 0; 367 | 368 | case EVIOCRMFF: 369 | +#ifdef CONFIG_DEV_NS 370 | + if (!evdev_client_is_active(client)) 371 | + return 0; 372 | +#endif 373 | return input_ff_erase(dev, (int)(unsigned long) p, file); 374 | 375 | case EVIOCGEFFECTS: 376 | @@ -755,6 +982,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, 377 | return 0; 378 | 379 | case EVIOCGRAB: 380 | +#ifdef CONFIG_DEV_NS 381 | + if (!evdev_client_is_active(client)) { 382 | + if (p) 383 | + client->grab = true; 384 | + else 385 | + client->grab = false; 386 | + return 0; 387 | + } /* else */ 388 | +#endif 389 | if (p) 390 | return evdev_grab(evdev, client); 391 | else 392 | @@ -772,6 +1008,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, 393 | return evdev_handle_get_keycode(dev, p); 394 | 395 | case EVIOCSKEYCODE: 396 | +#ifdef CONFIG_DEV_NS 397 | + if (!evdev_client_is_active(client)) 398 | + return 0; 399 | +#endif 400 | return evdev_handle_set_keycode(dev, p); 401 | 402 | case EVIOCGKEYCODE_V2: 403 | @@ -825,6 +1065,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, 404 | return str_to_user(dev->uniq, size, p); 405 | 406 | case EVIOC_MASK_SIZE(EVIOCSFF): 407 | +#ifdef CONFIG_DEV_NS 408 | + if (!evdev_client_is_active(client)) 409 | + return 0; 410 | +#endif 411 | if (input_ff_effect_from_user(p, size, &effect)) 412 | return -EFAULT; 413 | 414 | @@ -865,6 +1109,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, 415 | 416 | if (_IOC_DIR(cmd) == _IOC_WRITE) { 417 | 418 | +#ifdef CONFIG_DEV_NS 419 | + if (!evdev_client_is_active(client)) 420 | + return 0; 421 | +#endif 422 | if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { 423 | 424 | if (!dev->absinfo) 425 | @@ -1094,11 +1342,26 @@ static struct input_handler evdev_handler = { 426 | 427 | static int __init evdev_init(void) 428 | { 429 | - return input_register_handler(&evdev_handler); 430 | + int ret; 431 | + 432 | + ret = input_register_handler(&evdev_handler); 433 | + if (ret < 0) 434 | + return ret; 435 | +#ifdef CONFIG_DEV_NS 436 | + ret = DEV_NS_REGISTER(evdev, "event dev"); 437 | + if (ret < 0) { 438 | + input_unregister_handler(&evdev_handler); 439 | + return ret; 440 | + } 441 | +#endif 442 | + return 0; 443 | } 444 | 445 | static void __exit evdev_exit(void) 446 | { 447 | +#ifdef CONFIG_DEV_NS 448 | + DEV_NS_UNREGISTER(evdev); 449 | +#endif 450 | input_unregister_handler(&evdev_handler); 451 | } 452 | 453 | diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c 454 | index 0110b5a..64f8cd6 100644 455 | --- a/drivers/input/mousedev.c 456 | +++ b/drivers/input/mousedev.c 457 | @@ -25,6 +25,9 @@ 458 | #include 459 | #include 460 | #include 461 | +#ifdef CONFIG_DEV_NS 462 | +#include 463 | +#endif 464 | #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX 465 | #include 466 | #endif 467 | @@ -107,6 +110,10 @@ struct mousedev_client { 468 | unsigned char imexseq, impsseq; 469 | enum mousedev_emul mode; 470 | unsigned long last_buttons; 471 | + 472 | +#ifdef CONFIG_DEV_NS 473 | + struct dev_namespace *dev_ns; 474 | +#endif 475 | }; 476 | 477 | #define MOUSEDEV_SEQ_LEN 6 478 | @@ -277,6 +284,10 @@ static void mousedev_notify_readers(struct mousedev *mousedev, 479 | rcu_read_lock(); 480 | list_for_each_entry_rcu(client, &mousedev->client_list, node) { 481 | 482 | +#ifdef CONFIG_DEV_NS 483 | + if (!is_active_dev_ns(client->dev_ns)) 484 | + continue; 485 | +#endif 486 | /* Just acquire the lock, interrupts already disabled */ 487 | spin_lock(&client->packet_lock); 488 | 489 | @@ -524,6 +535,9 @@ static int mousedev_release(struct inode *inode, struct file *file) 490 | struct mousedev_client *client = file->private_data; 491 | struct mousedev *mousedev = client->mousedev; 492 | 493 | +#ifdef CONFIG_DEV_NS 494 | + put_dev_ns(client->dev_ns); 495 | +#endif 496 | mousedev_detach_client(mousedev, client); 497 | kfree(client); 498 | 499 | @@ -573,6 +587,9 @@ static int mousedev_open(struct inode *inode, struct file *file) 500 | client->pos_x = xres / 2; 501 | client->pos_y = yres / 2; 502 | client->mousedev = mousedev; 503 | +#ifdef CONFIG_DEV_NS 504 | + client->dev_ns = get_dev_ns(current_dev_ns()); 505 | +#endif 506 | mousedev_attach_client(mousedev, client); 507 | 508 | error = mousedev_open_device(mousedev); 509 | @@ -583,6 +600,9 @@ static int mousedev_open(struct inode *inode, struct file *file) 510 | return 0; 511 | 512 | err_free_client: 513 | +#ifdef CONFIG_DEV_NS 514 | + put_dev_ns(client->dev_ns); 515 | +#endif 516 | mousedev_detach_client(mousedev, client); 517 | kfree(client); 518 | err_put_mousedev: 519 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | NOTE! This copyright does *not* cover user programs that use kernel 3 | services by normal system calls - this is merely considered normal use 4 | of the kernel, and does *not* fall under the heading of "derived work". 5 | Also note that the GPL below is copyrighted by the Free Software 6 | Foundation, but the instance of code that it refers to (the Linux 7 | kernel) is copyrighted by me and others who actually wrote it. 8 | 9 | Also note that the only valid version of the GPL as far as the kernel 10 | is concerned is _this_ particular version of the license (ie v2, not 11 | v2.2 or v3.x or whatever), unless explicitly otherwise stated. 12 | 13 | Linus Torvalds 14 | 15 | ---------------------------------------- 16 | 17 | GNU GENERAL PUBLIC LICENSE 18 | Version 2, June 1991 19 | 20 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 21 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | Everyone is permitted to copy and distribute verbatim copies 23 | of this license document, but changing it is not allowed. 24 | 25 | Preamble 26 | 27 | The licenses for most software are designed to take away your 28 | freedom to share and change it. By contrast, the GNU General Public 29 | License is intended to guarantee your freedom to share and change free 30 | software--to make sure the software is free for all its users. This 31 | General Public License applies to most of the Free Software 32 | Foundation's software and to any other program whose authors commit to 33 | using it. (Some other Free Software Foundation software is covered by 34 | the GNU Library General Public License instead.) You can apply it to 35 | your programs, too. 36 | 37 | When we speak of free software, we are referring to freedom, not 38 | price. Our General Public Licenses are designed to make sure that you 39 | have the freedom to distribute copies of free software (and charge for 40 | this service if you wish), that you receive source code or can get it 41 | if you want it, that you can change the software or use pieces of it 42 | in new free programs; and that you know you can do these things. 43 | 44 | To protect your rights, we need to make restrictions that forbid 45 | anyone to deny you these rights or to ask you to surrender the rights. 46 | These restrictions translate to certain responsibilities for you if you 47 | distribute copies of the software, or if you modify it. 48 | 49 | For example, if you distribute copies of such a program, whether 50 | gratis or for a fee, you must give the recipients all the rights that 51 | you have. You must make sure that they, too, receive or can get the 52 | source code. And you must show them these terms so they know their 53 | rights. 54 | 55 | We protect your rights with two steps: (1) copyright the software, and 56 | (2) offer you this license which gives you legal permission to copy, 57 | distribute and/or modify the software. 58 | 59 | Also, for each author's protection and ours, we want to make certain 60 | that everyone understands that there is no warranty for this free 61 | software. If the software is modified by someone else and passed on, we 62 | want its recipients to know that what they have is not the original, so 63 | that any problems introduced by others will not reflect on the original 64 | authors' reputations. 65 | 66 | Finally, any free program is threatened constantly by software 67 | patents. We wish to avoid the danger that redistributors of a free 68 | program will individually obtain patent licenses, in effect making the 69 | program proprietary. To prevent this, we have made it clear that any 70 | patent must be licensed for everyone's free use or not licensed at all. 71 | 72 | The precise terms and conditions for copying, distribution and 73 | modification follow. 74 | 75 | GNU GENERAL PUBLIC LICENSE 76 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 77 | 78 | 0. This License applies to any program or other work which contains 79 | a notice placed by the copyright holder saying it may be distributed 80 | under the terms of this General Public License. The "Program", below, 81 | refers to any such program or work, and a "work based on the Program" 82 | means either the Program or any derivative work under copyright law: 83 | that is to say, a work containing the Program or a portion of it, 84 | either verbatim or with modifications and/or translated into another 85 | language. (Hereinafter, translation is included without limitation in 86 | the term "modification".) Each licensee is addressed as "you". 87 | 88 | Activities other than copying, distribution and modification are not 89 | covered by this License; they are outside its scope. The act of 90 | running the Program is not restricted, and the output from the Program 91 | is covered only if its contents constitute a work based on the 92 | Program (independent of having been made by running the Program). 93 | Whether that is true depends on what the Program does. 94 | 95 | 1. You may copy and distribute verbatim copies of the Program's 96 | source code as you receive it, in any medium, provided that you 97 | conspicuously and appropriately publish on each copy an appropriate 98 | copyright notice and disclaimer of warranty; keep intact all the 99 | notices that refer to this License and to the absence of any warranty; 100 | and give any other recipients of the Program a copy of this License 101 | along with the Program. 102 | 103 | You may charge a fee for the physical act of transferring a copy, and 104 | you may at your option offer warranty protection in exchange for a fee. 105 | 106 | 2. You may modify your copy or copies of the Program or any portion 107 | of it, thus forming a work based on the Program, and copy and 108 | distribute such modifications or work under the terms of Section 1 109 | above, provided that you also meet all of these conditions: 110 | 111 | a) You must cause the modified files to carry prominent notices 112 | stating that you changed the files and the date of any change. 113 | 114 | b) You must cause any work that you distribute or publish, that in 115 | whole or in part contains or is derived from the Program or any 116 | part thereof, to be licensed as a whole at no charge to all third 117 | parties under the terms of this License. 118 | 119 | c) If the modified program normally reads commands interactively 120 | when run, you must cause it, when started running for such 121 | interactive use in the most ordinary way, to print or display an 122 | announcement including an appropriate copyright notice and a 123 | notice that there is no warranty (or else, saying that you provide 124 | a warranty) and that users may redistribute the program under 125 | these conditions, and telling the user how to view a copy of this 126 | License. (Exception: if the Program itself is interactive but 127 | does not normally print such an announcement, your work based on 128 | the Program is not required to print an announcement.) 129 | 130 | These requirements apply to the modified work as a whole. If 131 | identifiable sections of that work are not derived from the Program, 132 | and can be reasonably considered independent and separate works in 133 | themselves, then this License, and its terms, do not apply to those 134 | sections when you distribute them as separate works. But when you 135 | distribute the same sections as part of a whole which is a work based 136 | on the Program, the distribution of the whole must be on the terms of 137 | this License, whose permissions for other licensees extend to the 138 | entire whole, and thus to each and every part regardless of who wrote it. 139 | 140 | Thus, it is not the intent of this section to claim rights or contest 141 | your rights to work written entirely by you; rather, the intent is to 142 | exercise the right to control the distribution of derivative or 143 | collective works based on the Program. 144 | 145 | In addition, mere aggregation of another work not based on the Program 146 | with the Program (or with a work based on the Program) on a volume of 147 | a storage or distribution medium does not bring the other work under 148 | the scope of this License. 149 | 150 | 3. You may copy and distribute the Program (or a work based on it, 151 | under Section 2) in object code or executable form under the terms of 152 | Sections 1 and 2 above provided that you also do one of the following: 153 | 154 | a) Accompany it with the complete corresponding machine-readable 155 | source code, which must be distributed under the terms of Sections 156 | 1 and 2 above on a medium customarily used for software interchange; or, 157 | 158 | b) Accompany it with a written offer, valid for at least three 159 | years, to give any third party, for a charge no more than your 160 | cost of physically performing source distribution, a complete 161 | machine-readable copy of the corresponding source code, to be 162 | distributed under the terms of Sections 1 and 2 above on a medium 163 | customarily used for software interchange; or, 164 | 165 | c) Accompany it with the information you received as to the offer 166 | to distribute corresponding source code. (This alternative is 167 | allowed only for noncommercial distribution and only if you 168 | received the program in object code or executable form with such 169 | an offer, in accord with Subsection b above.) 170 | 171 | The source code for a work means the preferred form of the work for 172 | making modifications to it. For an executable work, complete source 173 | code means all the source code for all modules it contains, plus any 174 | associated interface definition files, plus the scripts used to 175 | control compilation and installation of the executable. However, as a 176 | special exception, the source code distributed need not include 177 | anything that is normally distributed (in either source or binary 178 | form) with the major components (compiler, kernel, and so on) of the 179 | operating system on which the executable runs, unless that component 180 | itself accompanies the executable. 181 | 182 | If distribution of executable or object code is made by offering 183 | access to copy from a designated place, then offering equivalent 184 | access to copy the source code from the same place counts as 185 | distribution of the source code, even though third parties are not 186 | compelled to copy the source along with the object code. 187 | 188 | 4. You may not copy, modify, sublicense, or distribute the Program 189 | except as expressly provided under this License. Any attempt 190 | otherwise to copy, modify, sublicense or distribute the Program is 191 | void, and will automatically terminate your rights under this License. 192 | However, parties who have received copies, or rights, from you under 193 | this License will not have their licenses terminated so long as such 194 | parties remain in full compliance. 195 | 196 | 5. You are not required to accept this License, since you have not 197 | signed it. However, nothing else grants you permission to modify or 198 | distribute the Program or its derivative works. These actions are 199 | prohibited by law if you do not accept this License. Therefore, by 200 | modifying or distributing the Program (or any work based on the 201 | Program), you indicate your acceptance of this License to do so, and 202 | all its terms and conditions for copying, distributing or modifying 203 | the Program or works based on it. 204 | 205 | 6. Each time you redistribute the Program (or any work based on the 206 | Program), the recipient automatically receives a license from the 207 | original licensor to copy, distribute or modify the Program subject to 208 | these terms and conditions. You may not impose any further 209 | restrictions on the recipients' exercise of the rights granted herein. 210 | You are not responsible for enforcing compliance by third parties to 211 | this License. 212 | 213 | 7. If, as a consequence of a court judgment or allegation of patent 214 | infringement or for any other reason (not limited to patent issues), 215 | conditions are imposed on you (whether by court order, agreement or 216 | otherwise) that contradict the conditions of this License, they do not 217 | excuse you from the conditions of this License. If you cannot 218 | distribute so as to satisfy simultaneously your obligations under this 219 | License and any other pertinent obligations, then as a consequence you 220 | may not distribute the Program at all. For example, if a patent 221 | license would not permit royalty-free redistribution of the Program by 222 | all those who receive copies directly or indirectly through you, then 223 | the only way you could satisfy both it and this License would be to 224 | refrain entirely from distribution of the Program. 225 | 226 | If any portion of this section is held invalid or unenforceable under 227 | any particular circumstance, the balance of the section is intended to 228 | apply and the section as a whole is intended to apply in other 229 | circumstances. 230 | 231 | It is not the purpose of this section to induce you to infringe any 232 | patents or other property right claims or to contest validity of any 233 | such claims; this section has the sole purpose of protecting the 234 | integrity of the free software distribution system, which is 235 | implemented by public license practices. Many people have made 236 | generous contributions to the wide range of software distributed 237 | through that system in reliance on consistent application of that 238 | system; it is up to the author/donor to decide if he or she is willing 239 | to distribute software through any other system and a licensee cannot 240 | impose that choice. 241 | 242 | This section is intended to make thoroughly clear what is believed to 243 | be a consequence of the rest of this License. 244 | 245 | 8. If the distribution and/or use of the Program is restricted in 246 | certain countries either by patents or by copyrighted interfaces, the 247 | original copyright holder who places the Program under this License 248 | may add an explicit geographical distribution limitation excluding 249 | those countries, so that distribution is permitted only in or among 250 | countries not thus excluded. In such case, this License incorporates 251 | the limitation as if written in the body of this License. 252 | 253 | 9. The Free Software Foundation may publish revised and/or new versions 254 | of the General Public License from time to time. Such new versions will 255 | be similar in spirit to the present version, but may differ in detail to 256 | address new problems or concerns. 257 | 258 | Each version is given a distinguishing version number. If the Program 259 | specifies a version number of this License which applies to it and "any 260 | later version", you have the option of following the terms and conditions 261 | either of that version or of any later version published by the Free 262 | Software Foundation. If the Program does not specify a version number of 263 | this License, you may choose any version ever published by the Free Software 264 | Foundation. 265 | 266 | 10. If you wish to incorporate parts of the Program into other free 267 | programs whose distribution conditions are different, write to the author 268 | to ask for permission. For software which is copyrighted by the Free 269 | Software Foundation, write to the Free Software Foundation; we sometimes 270 | make exceptions for this. Our decision will be guided by the two goals 271 | of preserving the free status of all derivatives of our free software and 272 | of promoting the sharing and reuse of software generally. 273 | 274 | NO WARRANTY 275 | 276 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 277 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 278 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 279 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 280 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 281 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 282 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 283 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 284 | REPAIR OR CORRECTION. 285 | 286 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 287 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 288 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 289 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 290 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 291 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 292 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 293 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 294 | POSSIBILITY OF SUCH DAMAGES. 295 | 296 | END OF TERMS AND CONDITIONS 297 | 298 | How to Apply These Terms to Your New Programs 299 | 300 | If you develop a new program, and you want it to be of the greatest 301 | possible use to the public, the best way to achieve this is to make it 302 | free software which everyone can redistribute and change under these terms. 303 | 304 | To do so, attach the following notices to the program. It is safest 305 | to attach them to the start of each source file to most effectively 306 | convey the exclusion of warranty; and each file should have at least 307 | the "copyright" line and a pointer to where the full notice is found. 308 | 309 | 310 | Copyright (C) 311 | 312 | This program is free software; you can redistribute it and/or modify 313 | it under the terms of the GNU General Public License as published by 314 | the Free Software Foundation; either version 2 of the License, or 315 | (at your option) any later version. 316 | 317 | This program is distributed in the hope that it will be useful, 318 | but WITHOUT ANY WARRANTY; without even the implied warranty of 319 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 320 | GNU General Public License for more details. 321 | 322 | You should have received a copy of the GNU General Public License 323 | along with this program; if not, write to the Free Software 324 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 325 | 326 | 327 | Also add information on how to contact you by electronic and paper mail. 328 | 329 | If the program is interactive, make it output a short notice like this 330 | when it starts in an interactive mode: 331 | 332 | Gnomovision version 69, Copyright (C) year name of author 333 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 334 | This is free software, and you are welcome to redistribute it 335 | under certain conditions; type `show c' for details. 336 | 337 | The hypothetical commands `show w' and `show c' should show the appropriate 338 | parts of the General Public License. Of course, the commands you use may 339 | be called something other than `show w' and `show c'; they could even be 340 | mouse-clicks or menu items--whatever suits your program. 341 | 342 | You should also get your employer (if you work as a programmer) or your 343 | school, if any, to sign a "copyright disclaimer" for the program, if 344 | necessary. Here is a sample; alter the names: 345 | 346 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 347 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 348 | 349 | , 1 April 1989 350 | Ty Coon, President of Vice 351 | 352 | This General Public License does not permit incorporating your program into 353 | proprietary programs. If your program is a subroutine library, you may 354 | consider it more useful to permit linking proprietary applications with the 355 | library. If this is what you want to do, use the GNU Library General 356 | Public License instead of this License. 357 | -------------------------------------------------------------------------------- /dev_ns__introduce_device_namespaces: -------------------------------------------------------------------------------- 1 | dev_ns: introduce device namespaces 2 | 3 | Introduce device namespaces: 4 | 5 | The idea with a device namespace comes from the Android-Cells project where 6 | namespaces are utilized to create a container-like environment on Linux, only 7 | where there's a notion of an 'active' namespace and all other namespaces are 8 | inactive. In such a case only processes residing within the active device 9 | namespace should communicate with actual devices, where processes inside 10 | inactive containers should be able to communicate gracefully with the device 11 | driver, but not the device. 12 | 13 | Changelog: 14 | [11-Sep-2014] v2 - fix compile and crashes with !CONFIG_DEV_NS 15 | - replace reference of pid_ns with pid_t in dev_ns [report: Jeremy Andrus] 16 | - fix compile errors when !COFNIG_DEV_NS [report: Jeremy Andrus] 17 | - fix kernel crash when !CONFIG_DEV_NS [report: Jeremy Andrus] 18 | [16-Aug-2013] v1 - initial version 19 | 20 | Change-Id: I38ef9fdf76086794d42679f4ec05612ba0cfb179 21 | Signed-off-by: Oren Laadan 22 | Signed-off-by: Amir Goldstein 23 | 24 | --- 25 | include/linux/dev_namespace.h | 269 +++++++++++++++++ 26 | include/linux/nsproxy.h | 1 + 27 | init/Kconfig | 10 + 28 | kernel/Makefile | 1 + 29 | kernel/dev_namespace.c | 659 ++++++++++++++++++++++++++++++++++++++++++ 30 | kernel/nsproxy.c | 13 + 31 | 6 files changed, 953 insertions(+) 32 | 33 | diff --git a/include/linux/dev_namespace.h b/include/linux/dev_namespace.h 34 | new file mode 100644 35 | index 0000000..1809e6a 36 | --- /dev/null 37 | +++ b/include/linux/dev_namespace.h 38 | @@ -0,0 +1,269 @@ 39 | +/* 40 | + * include/linux/dev_namespace.h 41 | + * 42 | + * Copyright (c) 2011-2013 Cellrox Ltd. Certain portions are copyrighted by 43 | + * Columbia University. This program is free software licensed under the GNU 44 | + * General Public License Version 2 (GPL 2). You can distribute it and/or 45 | + * modify it under the terms of the GPL 2. 46 | + * 47 | + * This program is distributed in the hope that it will be useful, but WITHOUT 48 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 49 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL 2 license for more details. 50 | + * The full GPL 2 License is included in this distribution in the file called 51 | + * COPYING 52 | + * 53 | + * Cellrox can be contacted at oss@cellrox.com 54 | + */ 55 | + 56 | +#ifndef _LINUX_DEV_NS_H 57 | +#define _LINUX_DEV_NS_H 58 | + 59 | +#include 60 | +#include 61 | +#include 62 | +#include 63 | +#include 64 | + 65 | +#ifdef __KERNEL__ 66 | + 67 | +#define DEV_NS_TAG_LEN 4 68 | +#define DEV_NS_DESC_MAX 16 69 | + 70 | +struct dev_namespace; 71 | +struct dev_ns_info; 72 | + 73 | +struct dev_namespace { 74 | + 75 | + bool active; 76 | + atomic_t count; 77 | + pid_t init_pid; 78 | + char tag[DEV_NS_TAG_LEN + 1]; 79 | + struct blocking_notifier_head notifiers; 80 | + unsigned long timestamp; /* jiffies */ 81 | + 82 | + struct mutex mutex; 83 | + struct dev_ns_info *info[DEV_NS_DESC_MAX]; 84 | +}; 85 | + 86 | +struct dev_ns_info { 87 | + struct dev_namespace *dev_ns; 88 | + struct list_head list; 89 | + struct notifier_block nb; 90 | + atomic_t count; 91 | +}; 92 | + 93 | +#ifdef CONFIG_DEV_NS 94 | + 95 | +struct dev_ns_ops { 96 | + struct dev_ns_info *(*create) (struct dev_namespace *dev_ns); 97 | + void (*release) (struct dev_ns_info *dev_ns_info); 98 | +}; 99 | + 100 | +/* device namespace notifications */ 101 | +#define DEV_NS_EVENT_ACTIVATE 0x1 102 | +#define DEV_NS_EVENT_DEACTIVATE 0x2 103 | + 104 | +extern struct dev_namespace init_dev_ns; 105 | +extern struct dev_namespace *active_dev_ns; 106 | + 107 | +extern void __put_dev_ns(struct dev_namespace *dev_ns); 108 | + 109 | +static inline void put_dev_ns(struct dev_namespace *dev_ns) 110 | +{ 111 | + if (atomic_dec_and_test(&dev_ns->count)) 112 | + __put_dev_ns(dev_ns); 113 | +} 114 | + 115 | +static inline struct dev_namespace *get_dev_ns(struct dev_namespace *dev_ns) 116 | +{ 117 | + atomic_inc(&dev_ns->count); 118 | + return dev_ns; 119 | +} 120 | + 121 | +/* return the device namespaces of the current process */ 122 | +static inline struct dev_namespace *current_dev_ns(void) 123 | +{ 124 | + BUG_ON(in_interrupt()); 125 | + return current->nsproxy->dev_ns; 126 | +} 127 | + 128 | +/* return whether given device namespace is active */ 129 | +static inline bool is_active_dev_ns(struct dev_namespace *dev_ns) 130 | +{ 131 | + return dev_ns->active; 132 | +} 133 | + 134 | +/* return whether given device namespace is init dev_ns */ 135 | +static inline bool is_init_dev_ns(struct dev_namespace *dev_ns) 136 | +{ 137 | + return dev_ns == &init_dev_ns; 138 | +} 139 | + 140 | +/* return and get the device namespace of a given task */ 141 | +extern struct dev_namespace *get_dev_ns_by_task(struct task_struct *task); 142 | +extern struct dev_namespace *get_dev_ns_by_vpid(pid_t vpid); 143 | + 144 | +/* 145 | + * set_active_dev_ns() will lock and unlock dev_namespace_lock 146 | + * and call all registered activate and inactivate notifiers. 147 | + */ 148 | +extern void set_active_dev_ns(struct dev_namespace *dev_ns); 149 | + 150 | +/* return the tag of the current namespace */ 151 | +extern void get_dev_ns_tag(char *to, struct dev_namespace *dev); 152 | + 153 | +/* return root pid of the init process in a device namespace */ 154 | +static inline pid_t dev_ns_init_pid(struct dev_namespace *dev_ns) 155 | +{ 156 | + return dev_ns->init_pid; 157 | +} 158 | + 159 | +/* device namespaces: notifiers (de)registration */ 160 | +extern void dev_ns_register_notify(struct dev_namespace *dev_ns, 161 | + struct notifier_block *nb); 162 | +extern void dev_ns_unregister_notify(struct dev_namespace *dev_ns, 163 | + struct notifier_block *nb); 164 | + 165 | +extern struct dev_namespace *copy_dev_ns(unsigned long flags, 166 | + struct task_struct *task); 167 | + 168 | +/* helpers for per-driver logic of device namespace */ 169 | + 170 | +extern int register_dev_ns_ops(char *name, struct dev_ns_ops *ops); 171 | +extern void unregister_dev_ns_ops(int ns_id); 172 | +extern struct dev_ns_info *get_dev_ns_info(int ns_id, 173 | + struct dev_namespace *dev_ns, 174 | + bool lock, bool create); 175 | +extern struct dev_ns_info *get_dev_ns_info_task(int ns_id, 176 | + struct task_struct *task); 177 | +extern void put_dev_ns_info(int ns_id, 178 | + struct dev_ns_info *dev_ns_info, 179 | + int lock); 180 | +extern void loop_dev_ns_info(int ns_id, void *ptr, 181 | + void (*func)(struct dev_ns_info *dev_ns_info, 182 | + void *ptr)); 183 | + 184 | +/* macro-mania to reduce repetitive code - not for the faint of heart */ 185 | + 186 | +#define i_to_x_dev_ns(i, x) container_of(i, struct x ## _dev_ns, dev_ns_info) 187 | + 188 | +#define _dev_ns_id(X) \ 189 | + static int X ## _ns_id; 190 | + 191 | +#define _dev_ns_get(X) \ 192 | + static inline \ 193 | + struct X ## _dev_ns *get_ ## X ## _ns(struct dev_namespace *dev_ns) \ 194 | + { \ 195 | + struct dev_ns_info *info; \ 196 | + info = get_dev_ns_info(X ## _ns_id, dev_ns, 1, 1); \ 197 | + return info ? i_to_x_dev_ns(info, X) : NULL; \ 198 | + } 199 | + 200 | +#define _dev_ns_find(X) \ 201 | + static inline \ 202 | + struct X ## _dev_ns *find_ ## X ## _ns(struct dev_namespace *dev_ns) \ 203 | + { \ 204 | + struct dev_ns_info *info; \ 205 | + info = get_dev_ns_info(X ## _ns_id, dev_ns, 0, 0); \ 206 | + return info ? i_to_x_dev_ns(info, X) : NULL; \ 207 | + } 208 | + 209 | + 210 | +#define _dev_ns_get_cur(X) \ 211 | + static inline struct X ## _dev_ns *get_ ## X ## _ns_cur(void) \ 212 | + { \ 213 | + struct dev_ns_info *info; \ 214 | + info = get_dev_ns_info_task(X ## _ns_id, current); \ 215 | + return info ? i_to_x_dev_ns(info, X) : NULL; \ 216 | + } 217 | + 218 | +#define _dev_ns_put(X) \ 219 | + static inline void put_ ## X ## _ns(struct X ## _dev_ns *X ## _ns) \ 220 | + { \ 221 | + put_dev_ns_info(X ## _ns_id, &X ## _ns->dev_ns_info, 1); \ 222 | + } 223 | + 224 | +/* 225 | + * Finally, this is what a driver author really needs to use: 226 | + * DEFINE_DEV_NS_INFO(X): X_ns_id, put_X_ns(), get_X_ns(), get_X_ns_cur() 227 | + * DEV_NS_REGISTER(X): will register X with device namespace 228 | + * DEV_NS_UNREGISTER(X): will unregister X from device namespace 229 | + */ 230 | + 231 | +#define DEFINE_DEV_NS_INFO(X) \ 232 | + _dev_ns_id(X) \ 233 | + _dev_ns_find(X) \ 234 | + _dev_ns_get(X) \ 235 | + _dev_ns_get_cur(X) \ 236 | + _dev_ns_put(X) 237 | + 238 | +#define DEV_NS_REGISTER(X, s) \ 239 | + (X ## _ns_id = register_dev_ns_ops(s, &X ## _ns_ops)) 240 | + 241 | +#define DEV_NS_UNREGISTER(X) \ 242 | + unregister_dev_ns_ops(X ## _ns_id) 243 | + 244 | + 245 | +#else /* !CONFIG_DEV_NS */ 246 | + 247 | +/* appease static assignment in kernel/nsproxy.c */ 248 | +#define init_dev_ns (*(struct dev_namespace *) NULL) 249 | + 250 | +static inline void put_dev_ns(struct dev_namespace *dev_ns) 251 | +{ /* nothing */ } 252 | + 253 | +/* 254 | + * Driver authors should use this macro instead if !CONFIG_DEV_NS: 255 | + * DEFINE_DEV_NS_INIT(X): put_X_ns(), get_X_ns(), get_X_ns_cur() 256 | + */ 257 | +#define DEFINE_DEV_NS_INIT(x) \ 258 | + static struct x ## _dev_ns init_ ## x ## _ns; \ 259 | + static inline \ 260 | + struct x ## _dev_ns *find_ ## x ## _ns(struct dev_namespace *dev_ns) \ 261 | + { return &init_ ## x ## _ns; } \ 262 | + static inline \ 263 | + struct x ## _dev_ns *get_ ## x ## _ns(struct dev_namespace *dev_ns) \ 264 | + { return &init_ ## x ## _ns; } \ 265 | + static inline struct x ## _dev_ns *get_ ## x ## _ns_cur(void) \ 266 | + { return &init_ ## x ## _ns; } \ 267 | + static inline void put_ ## x ## _ns(struct x ## _dev_ns *x ## _ns) \ 268 | + { /* */ } 269 | + 270 | +static inline struct dev_namespace *current_dev_ns(void) 271 | +{ 272 | + return &init_dev_ns; 273 | +} 274 | + 275 | +static inline struct dev_namespace *copy_dev_ns(unsigned long flags, 276 | + struct task_struct *task) 277 | +{ 278 | + if (flags & CLONE_NEWPID) 279 | + return ERR_PTR(-EINVAL); 280 | + return task->nsproxy->dev_ns; 281 | +} 282 | + 283 | +static inline bool is_active_dev_ns(struct dev_namespace *dev_ns) 284 | +{ 285 | + return true; 286 | +} 287 | + 288 | +static inline bool is_init_dev_ns(struct dev_namespace *dev_ns) 289 | +{ 290 | + return true; 291 | +} 292 | + 293 | +static inline pid_t dev_ns_init_pid(struct dev_namespace *dev_ns) 294 | +{ 295 | + return init_task.pid; 296 | +} 297 | + 298 | +static inline void get_dev_ns_tag(char *to, struct dev_namespace *dev_ns) 299 | +{ 300 | + strcpy(to, ""); 301 | +} 302 | + 303 | +#endif /* CONFIG_DEV_NS */ 304 | + 305 | + 306 | +#endif /* __KERNEL__ */ 307 | +#endif /* _LINUX_DEV_NS_H */ 308 | diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h 309 | index cc37a55..bad297b 100644 310 | --- a/include/linux/nsproxy.h 311 | +++ b/include/linux/nsproxy.h 312 | @@ -29,6 +29,7 @@ struct nsproxy { 313 | struct mnt_namespace *mnt_ns; 314 | struct pid_namespace *pid_ns; 315 | struct net *net_ns; 316 | + struct dev_namespace *dev_ns; 317 | }; 318 | extern struct nsproxy init_nsproxy; 319 | 320 | diff --git a/init/Kconfig b/init/Kconfig 321 | index a7cffc8..9f1e2a3 100644 322 | --- a/init/Kconfig 323 | +++ b/init/Kconfig 324 | @@ -850,6 +850,16 @@ config NET_NS 325 | Allow user space to create what appear to be multiple instances 326 | of the network stack. 327 | 328 | +config DEV_NS 329 | + bool "Device Namespaces (EXPERIMENTAL)" 330 | + default n 331 | + depends on PID_NS 332 | + help 333 | + Support device driver namespaces. This allows drivers to multiplex 334 | + access to its physical or logical resources. A device namespace has 335 | + a one-to-one relationship with a PID namespace currentlly, but this 336 | + will likely change in the future. 337 | + 338 | endif # NAMESPACES 339 | 340 | config SCHED_AUTOGROUP 341 | diff --git a/kernel/Makefile b/kernel/Makefile 342 | index cb41b95..e3f4aab 100644 343 | --- a/kernel/Makefile 344 | +++ b/kernel/Makefile 345 | @@ -62,6 +62,7 @@ obj-$(CONFIG_CPUSETS) += cpuset.o 346 | obj-$(CONFIG_UTS_NS) += utsname.o 347 | obj-$(CONFIG_USER_NS) += user_namespace.o 348 | obj-$(CONFIG_PID_NS) += pid_namespace.o 349 | +obj-$(CONFIG_DEV_NS) += dev_namespace.o 350 | obj-$(CONFIG_IKCONFIG) += configs.o 351 | obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o 352 | obj-$(CONFIG_SMP) += stop_machine.o 353 | diff --git a/kernel/dev_namespace.c b/kernel/dev_namespace.c 354 | new file mode 100644 355 | index 0000000..bd57a7f 356 | --- /dev/null 357 | +++ b/kernel/dev_namespace.c 358 | @@ -0,0 +1,659 @@ 359 | +/* 360 | + * kernel/dev_namespace.c 361 | + * 362 | + * Copyright (c) 2011-2013 Cellrox Ltd. Certain portions are copyrighted by 363 | + * Columbia University. This program is free software licensed under the GNU 364 | + * General Public License Version 2 (GPL 2). You can distribute it and/or 365 | + * modify it under the terms of the GPL 2. 366 | + * 367 | + * Based on device_namespace.c by Jeremy C. Andrus 368 | + * and Christoffer Dall . 369 | + * 370 | + * Device namespaces: 371 | + * 372 | + * The idea with a device namespace comes from the Android-Cells project: 373 | + * namespaces are utilized to create a container-like environment on Linux, 374 | + * and there is a notion of an 'active' namespace while other namespaces are 375 | + * non-active. In such a case only processes residing within the active device 376 | + * namespace should communicate with actual devices, where processes inside 377 | + * non-active containers should be able to communicate gracefully with the 378 | + * device driver, but not the device. 379 | + * 380 | + * The device namespace allows a device driver to register itself and pass a 381 | + * pointer to its device specific namespace structure and register notifiers 382 | + * which are called when the active namepace becomes non-active and when an 383 | + * non-active namespace becomes active. 384 | + * 385 | + * 386 | + * This program is distributed in the hope that it will be useful, but WITHOUT 387 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 388 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL 2 license for more details. 389 | + * The full GPL 2 License is included in this distribution in the file called 390 | + * COPYING 391 | + * 392 | + * Cellrox can be contacted at oss@cellrox.com 393 | + */ 394 | + 395 | +#include 396 | +#include 397 | +#include 398 | +#include 399 | +#include 400 | +#include 401 | +#include 402 | +#include 403 | +#include 404 | +#include 405 | +#include 406 | +#include 407 | +#include 408 | +#include 409 | + 410 | + 411 | +/* protects active namespace and switches */ 412 | +static DECLARE_RWSEM(global_dev_ns_lock); 413 | + 414 | + 415 | +struct dev_namespace init_dev_ns = { 416 | + .active = true, 417 | + .count = ATOMIC_INIT(2), /* extra reference for active_dev_ns */ 418 | + .init_pid = 1, 419 | + .notifiers = BLOCKING_NOTIFIER_INIT(init_dev_ns.notifiers), 420 | + .timestamp = 0, 421 | + .mutex = __MUTEX_INITIALIZER(init_dev_ns.mutex), 422 | +}; 423 | +EXPORT_SYMBOL_GPL(init_dev_ns); 424 | + 425 | + 426 | +struct dev_namespace *active_dev_ns = &init_dev_ns; 427 | + 428 | + 429 | +static void dev_ns_lock(struct dev_namespace *dev_ns) 430 | +{ 431 | + mutex_lock(&dev_ns->mutex); 432 | +} 433 | + 434 | +static void dev_ns_unlock(struct dev_namespace *dev_ns) 435 | +{ 436 | + mutex_unlock(&dev_ns->mutex); 437 | +} 438 | + 439 | +static struct dev_namespace *create_dev_ns(void) 440 | +{ 441 | + struct dev_namespace *dev_ns; 442 | + 443 | + dev_ns = kzalloc(sizeof(struct dev_namespace), GFP_KERNEL); 444 | + if (!dev_ns) 445 | + return ERR_PTR(-ENOMEM); 446 | + 447 | + atomic_set(&dev_ns->count, 1); 448 | + BLOCKING_INIT_NOTIFIER_HEAD(&dev_ns->notifiers); 449 | + mutex_init(&dev_ns->mutex); 450 | + 451 | + /* 452 | + * The pid is yet unknown, so init_pid will remain zero until 453 | + * a dev_ns is accessed for the first time by get_dev_ns_by_xxx() 454 | + * or new_dev_ns_info() functions. 455 | + */ 456 | + 457 | + return dev_ns; 458 | +} 459 | + 460 | +struct dev_namespace *copy_dev_ns(unsigned long flags, 461 | + struct task_struct *task) 462 | +{ 463 | + struct dev_namespace *dev_ns = task->nsproxy->dev_ns; 464 | + 465 | + /* 466 | + * Couple device namespace semantics with pid-namespace. 467 | + * It's convenient, and we ran out of clone flags anyway. 468 | + */ 469 | + if (!(flags & CLONE_NEWPID)) 470 | + return get_dev_ns(dev_ns); 471 | + else 472 | + return create_dev_ns(); 473 | +} 474 | + 475 | +static void lazy_set_dev_ns_init_pid(struct nsproxy *nsproxy) 476 | +{ 477 | + /* 478 | + * dev_ns->init_pid is set here, lazily, because it was 479 | + * not known at creation time in copy_dev_ns() - see above. 480 | + */ 481 | + if (unlikely(nsproxy->dev_ns->init_pid == 0)) 482 | + nsproxy->dev_ns->init_pid = nsproxy->pid_ns->child_reaper->pid; 483 | +} 484 | + 485 | +void __put_dev_ns(struct dev_namespace *dev_ns) 486 | +{ 487 | + kfree(dev_ns); 488 | +} 489 | + 490 | +static struct dev_namespace *get_dev_ns_from_nsproxy(struct nsproxy *nsproxy) 491 | +{ 492 | + if (nsproxy) { 493 | + lazy_set_dev_ns_init_pid(nsproxy); 494 | + return get_dev_ns(nsproxy->dev_ns); 495 | + } else 496 | + return NULL; 497 | +} 498 | + 499 | +struct dev_namespace *get_dev_ns_by_task(struct task_struct *task) 500 | +{ 501 | + struct dev_namespace *dev_ns = NULL; 502 | + struct nsproxy *nsproxy; 503 | + 504 | + rcu_read_lock(); 505 | + nsproxy = task_nsproxy(task); 506 | + dev_ns = get_dev_ns_from_nsproxy(nsproxy); 507 | + rcu_read_unlock(); 508 | + 509 | + return dev_ns; 510 | +} 511 | + 512 | +struct dev_namespace *get_dev_ns_by_vpid(pid_t vpid) 513 | +{ 514 | + struct dev_namespace *dev_ns = NULL; 515 | + struct task_struct *task; 516 | + struct nsproxy *nsproxy; 517 | + 518 | + rcu_read_lock(); 519 | + task = find_task_by_pid_ns(vpid, &init_pid_ns); 520 | + if (task) { 521 | + nsproxy = task_nsproxy(task); 522 | + dev_ns = get_dev_ns_from_nsproxy(nsproxy); 523 | + } 524 | + rcu_read_unlock(); 525 | + 526 | + return dev_ns; 527 | +} 528 | + 529 | +/** 530 | + * notifications: activate/deactive device namespace 531 | + */ 532 | +static BLOCKING_NOTIFIER_HEAD(dev_ns_notifiers); 533 | +void dev_ns_register_notify(struct dev_namespace *dev_ns, 534 | + struct notifier_block *nb) 535 | +{ 536 | + if (dev_ns != NULL) 537 | + blocking_notifier_chain_register(&dev_ns->notifiers, nb); 538 | + else 539 | + blocking_notifier_chain_register(&dev_ns_notifiers, nb); 540 | +} 541 | + 542 | +void dev_ns_unregister_notify(struct dev_namespace *dev_ns, 543 | + struct notifier_block *nb) 544 | +{ 545 | + if (dev_ns != NULL) 546 | + blocking_notifier_chain_unregister(&dev_ns->notifiers, nb); 547 | + else 548 | + blocking_notifier_chain_unregister(&dev_ns_notifiers, nb); 549 | +} 550 | + 551 | +/* 552 | + * Helpers for per-driver logic of device-namepace 553 | + * 554 | + * Drivers should embed 'struct dev_ns_info' in driver-specific, 555 | + * per-device-namespace data, e.g.: 556 | + * 557 | + * struct xxxx_namespace { 558 | + * ... (data specific to xxxx) 559 | + * struct dev_ns_info devns_info; 560 | + * }; 561 | + * 562 | + * Drivers should register a 'struct dev_ns_ops' with ->create() 563 | + * and ->release() methods, and keep an identifier (dev_ns_xxx_id), 564 | + * for use by device namespace generic code 565 | + * 566 | + * Drivers can iterate over per-driver data in all namespaces: 567 | + * void loop_dev_ns_info(int dev_ns_id, void *ptr, 568 | + * void (*func)(struct dev_ns_info *dev_ns_info, void *ptr)) 569 | + * 570 | + * See include/linux/dev_namespace.h for helper macros to hide these details. 571 | + */ 572 | + 573 | +struct dev_ns_desc { 574 | + char *name; 575 | + struct dev_ns_ops *ops; 576 | + struct list_head head; 577 | +}; 578 | + 579 | +static struct dev_ns_desc dev_ns_desc[DEV_NS_DESC_MAX]; 580 | +static DEFINE_SPINLOCK(dev_ns_desc_lock); 581 | + 582 | +int register_dev_ns_ops(char *name, struct dev_ns_ops *ops) 583 | +{ 584 | + struct dev_ns_desc *desc; 585 | + int n, ret = -ENOMEM; 586 | + 587 | + if (!name) 588 | + return -EINVAL; 589 | + 590 | + spin_lock(&dev_ns_desc_lock); 591 | + for (n = 0; n < DEV_NS_DESC_MAX; n++) { 592 | + desc = &dev_ns_desc[n]; 593 | + if (!desc->name && ret < 0) 594 | + ret = n; 595 | + else if (desc->name && !strcmp(desc->name, name)) { 596 | + ret = -EBUSY; 597 | + break; 598 | + } 599 | + } 600 | + if (ret >= 0) { 601 | + pr_info("dev_ns: register info %s\n", name); 602 | + desc = &dev_ns_desc[ret]; 603 | + desc->name = name; 604 | + desc->ops = ops; 605 | + INIT_LIST_HEAD(&desc->head); 606 | + } 607 | + spin_unlock(&dev_ns_desc_lock); 608 | + 609 | + return ret; 610 | +} 611 | + 612 | +void unregister_dev_ns_ops(int dev_ns_id) 613 | +{ 614 | + struct dev_ns_desc *desc = &dev_ns_desc[dev_ns_id]; 615 | + 616 | + spin_lock(&dev_ns_desc_lock); 617 | + pr_info("dev_ns: unregister desc %s\n", desc->name); 618 | + memset(&dev_ns_desc[dev_ns_id], 0, sizeof(*desc)); 619 | + spin_unlock(&dev_ns_desc_lock); 620 | +} 621 | + 622 | +/* this function is called with dev_ns_lock(dev_ns) held */ 623 | +static struct dev_ns_info *new_dev_ns_info(int dev_ns_id, 624 | + struct dev_namespace *dev_ns) 625 | +{ 626 | + struct dev_ns_desc *desc = &dev_ns_desc[dev_ns_id]; 627 | + struct dev_ns_info *dev_ns_info; 628 | + 629 | + pr_debug("dev_ns: [0x%p] new info %s\n", dev_ns, desc->name); 630 | + 631 | + lazy_set_dev_ns_init_pid(current->nsproxy); 632 | + 633 | + dev_ns_info = desc->ops->create(dev_ns); 634 | + if (!dev_ns_info) 635 | + return NULL; 636 | + 637 | + pr_debug("dev_ns: [0x%p] got info 0x%p\n", dev_ns, dev_ns_info); 638 | + 639 | + dev_ns->info[dev_ns_id] = dev_ns_info; 640 | + dev_ns_info->dev_ns = get_dev_ns(dev_ns); 641 | + atomic_set(&dev_ns_info->count, 0); 642 | + 643 | + spin_lock(&dev_ns_desc_lock); 644 | + list_add(&dev_ns_info->list, &desc->head); 645 | + spin_unlock(&dev_ns_desc_lock); 646 | + 647 | + return dev_ns_info; 648 | +} 649 | + 650 | +/* this function is called with dev_ns_lock(dev_ns) held */ 651 | +static void del_dev_ns_info(int dev_ns_id, struct dev_ns_info *dev_ns_info) 652 | +{ 653 | + struct dev_namespace *dev_ns = dev_ns_info->dev_ns; 654 | + 655 | + pr_debug("dev_ns: [0x%p] destory info 0x%p\n", dev_ns, dev_ns_info); 656 | + 657 | + spin_lock(&dev_ns_desc_lock); 658 | + list_del(&dev_ns_info->list); 659 | + dev_ns->info[dev_ns_id] = NULL; 660 | + spin_unlock(&dev_ns_desc_lock); 661 | + 662 | + dev_ns_desc[dev_ns_id].ops->release(dev_ns_info); 663 | + put_dev_ns(dev_ns); 664 | +} 665 | + 666 | +/* 667 | + * get_dev_ns_info() is intended for internal use only. It is exported only 668 | + * to enable the helper macros in dev_namepsace.h to work properly. 669 | + * 670 | + * @create tells whether to create a new instance if none is found already, 671 | + * or just return NULL. 672 | + * 673 | + * @lock tells whether the @dev_ns should be locked against concurrent 674 | + * changes, or the caller is the one responsible (in which case there is 675 | + * not even a need for an extra refefence count). 676 | + */ 677 | +struct dev_ns_info *get_dev_ns_info(int dev_ns_id, 678 | + struct dev_namespace *dev_ns, 679 | + bool lock, bool create) 680 | +{ 681 | + struct dev_ns_info *dev_ns_info; 682 | + 683 | + if (lock) { 684 | + down_read(&global_dev_ns_lock); 685 | + dev_ns_lock(dev_ns); 686 | + } 687 | + 688 | + dev_ns_info = dev_ns->info[dev_ns_id]; 689 | + 690 | + if (!dev_ns_info && create) 691 | + dev_ns_info = new_dev_ns_info(dev_ns_id, dev_ns); 692 | + 693 | + if (dev_ns_info) { 694 | + pr_debug("dev_ns: [0x%p] get info 0x%p count %d+\n", dev_ns, 695 | + dev_ns_info, atomic_read(&dev_ns_info->count)); 696 | + } 697 | + 698 | + if (dev_ns_info && lock) 699 | + atomic_inc(&dev_ns_info->count); 700 | + 701 | + if (lock) { 702 | + dev_ns_unlock(dev_ns); 703 | + up_read(&global_dev_ns_lock); 704 | + } 705 | + 706 | + return dev_ns_info; 707 | +} 708 | + 709 | +struct dev_ns_info *get_dev_ns_info_task(int dev_ns_id, struct task_struct *tsk) 710 | +{ 711 | + struct dev_ns_info *dev_ns_info; 712 | + struct dev_namespace *dev_ns; 713 | + 714 | + dev_ns = get_dev_ns_by_task(tsk); 715 | + dev_ns_info = dev_ns ? get_dev_ns_info(dev_ns_id, dev_ns, 1, 1) : NULL; 716 | + put_dev_ns(dev_ns); 717 | + 718 | + return dev_ns_info; 719 | +} 720 | + 721 | +void put_dev_ns_info(int dev_ns_id, struct dev_ns_info *dev_ns_info, int lock) 722 | +{ 723 | + struct dev_namespace *dev_ns; 724 | + 725 | + /* 726 | + * keep extra reference, or else the concluding dev_ns_unlock() 727 | + * could theoretically execute after the last dev_ns_put().. 728 | + */ 729 | + dev_ns = get_dev_ns(dev_ns_info->dev_ns); 730 | + 731 | + if (lock) { 732 | + down_read(&global_dev_ns_lock); 733 | + dev_ns_lock(dev_ns); 734 | + } 735 | + 736 | + pr_debug("dev_ns: [0x%p] put info 0x%p count %d-\n", dev_ns, 737 | + dev_ns_info, atomic_read(&dev_ns_info->count)); 738 | + if (atomic_dec_and_test(&dev_ns_info->count)) 739 | + del_dev_ns_info(dev_ns_id, dev_ns_info); 740 | + 741 | + if (lock) { 742 | + dev_ns_unlock(dev_ns); 743 | + up_read(&global_dev_ns_lock); 744 | + } 745 | + 746 | + put_dev_ns(dev_ns); 747 | +} 748 | + 749 | +/* 750 | + * @dev_ns_id: id of device namespace subsystem 751 | + * @ptr: data pointer to be passed to callback 752 | + * @func: callback for each device namespace (atomic, must not sleep) 753 | + */ 754 | +void loop_dev_ns_info(int dev_ns_id, void *ptr, 755 | + void (*func)(struct dev_ns_info *dev_ns_info, void *ptr)) 756 | +{ 757 | + struct dev_ns_desc *desc; 758 | + struct dev_ns_info *dev_ns_info; 759 | + 760 | + spin_lock(&dev_ns_desc_lock); 761 | + desc = &dev_ns_desc[dev_ns_id]; 762 | + list_for_each_entry(dev_ns_info, &desc->head, list) { 763 | + pr_debug("dev_ns: loop info 0x%p (dev_ns 0x%p) of %s\n", 764 | + dev_ns_info, dev_ns_info->dev_ns, desc->name); 765 | + (*func)(dev_ns_info, ptr); 766 | + } 767 | + spin_unlock(&dev_ns_desc_lock); 768 | +} 769 | + 770 | +/** 771 | + * Set the active device namespace (will call registered notifiers to 772 | + * allow device drivers to make device specific context store/restore) 773 | + * 774 | + * @dev_ns: The new active device namespace 775 | + */ 776 | +void set_active_dev_ns(struct dev_namespace *next_ns) 777 | +{ 778 | + struct dev_namespace *prev_ns; 779 | + 780 | + BUG_ON(next_ns == NULL); 781 | + 782 | + down_write(&global_dev_ns_lock); 783 | + 784 | + if (next_ns == active_dev_ns) 785 | + goto done; 786 | + 787 | + pr_info("dev_ns: activate 0x%p\n", next_ns); 788 | + 789 | + prev_ns = active_dev_ns; 790 | + 791 | + /* 792 | + * deactivate previous dev_ns: 793 | + * - set active-state of previous dev_ns to false 794 | + * - call previous dev_ns's notifiers with deactivate event 795 | + * - call global notifiers with deactivate event 796 | + */ 797 | + 798 | + dev_ns_lock(prev_ns); 799 | + 800 | + prev_ns->active = false; 801 | + prev_ns->timestamp = jiffies; 802 | + 803 | + (void) blocking_notifier_call_chain(&prev_ns->notifiers, 804 | + DEV_NS_EVENT_DEACTIVATE, prev_ns); 805 | + (void) blocking_notifier_call_chain(&dev_ns_notifiers, 806 | + DEV_NS_EVENT_DEACTIVATE, prev_ns); 807 | + 808 | + dev_ns_unlock(prev_ns); 809 | + 810 | + /* 811 | + * activate next dev_ns: 812 | + * - set active-state of next dev_ns to true 813 | + * - call next dev_ns's notifiers with activate event 814 | + * - call global notifiers with activate event 815 | + */ 816 | + 817 | + dev_ns_lock(next_ns); 818 | + 819 | + next_ns->active = true; 820 | + next_ns->timestamp = jiffies; 821 | + 822 | + /* make the switch */ 823 | + active_dev_ns = next_ns; 824 | + 825 | + (void) blocking_notifier_call_chain(&next_ns->notifiers, 826 | + DEV_NS_EVENT_ACTIVATE, next_ns); 827 | + (void) blocking_notifier_call_chain(&dev_ns_notifiers, 828 | + DEV_NS_EVENT_ACTIVATE, next_ns); 829 | + 830 | + dev_ns_unlock(next_ns); 831 | + 832 | + get_dev_ns(next_ns); 833 | + put_dev_ns(prev_ns); 834 | + 835 | + pr_info("dev_ns: activate 0x%p done\n", next_ns); 836 | + done: 837 | + up_write(&global_dev_ns_lock); 838 | +} 839 | + 840 | +void get_dev_ns_tag(char *to, struct dev_namespace *dev_ns) 841 | +{ 842 | + /* buf must be at least sizeof(dev_ns->tag) in size */ 843 | + to[DEV_NS_TAG_LEN] = '\0'; 844 | + strncpy(to, dev_ns->tag, DEV_NS_TAG_LEN); 845 | +} 846 | + 847 | +void set_dev_ns_tag(struct dev_namespace *dev_ns, char *from) 848 | +{ 849 | + dev_ns_lock(dev_ns); 850 | + strncpy(dev_ns->tag, from, DEV_NS_TAG_LEN); 851 | + dev_ns_unlock(dev_ns); 852 | +} 853 | + 854 | + 855 | +/** 856 | + * Setup for /proc/dev_ns 857 | + */ 858 | + 859 | +static struct proc_dir_entry *proc_dev_ns_dir; 860 | + 861 | +struct proc_dir_entry * 862 | +create_dev_ns_proc(const char *name, const struct file_operations *fops) 863 | +{ 864 | + struct proc_dir_entry *entry; 865 | + entry = proc_create(name, 0, proc_dev_ns_dir, fops); 866 | + return entry; 867 | +} 868 | + 869 | +static int proc_active_ns_show(struct seq_file *seq, void *offset) 870 | +{ 871 | + down_read(&global_dev_ns_lock); 872 | + seq_printf(seq, "%d\n", dev_ns_init_pid(active_dev_ns)); 873 | + up_read(&global_dev_ns_lock); 874 | + return 0; 875 | +} 876 | + 877 | +static int proc_ns_tag_show(struct seq_file *seq, void *offset) 878 | +{ 879 | + down_read(&global_dev_ns_lock); 880 | + seq_printf(seq, "active: %d timestamp: %ld tag: %s\n", 881 | + dev_ns_init_pid(active_dev_ns), 882 | + active_dev_ns->timestamp, 883 | + active_dev_ns->tag); 884 | + up_read(&global_dev_ns_lock); 885 | + return 0; 886 | +} 887 | + 888 | +static bool dev_ns_proc_permission(void) 889 | +{ 890 | + return current_dev_ns() == &init_dev_ns; 891 | +} 892 | + 893 | +static int proc_active_ns_open(struct inode *inode, struct file *file) 894 | +{ 895 | + if (!dev_ns_proc_permission()) 896 | + return -EPERM; 897 | + return single_open(file, proc_active_ns_show, PDE(inode)->data); 898 | +} 899 | + 900 | +static int proc_ns_tag_open(struct inode *inode, struct file *file) 901 | +{ 902 | + if (!dev_ns_proc_permission()) 903 | + return -EPERM; 904 | + return single_open(file, proc_ns_tag_show, PDE(inode)->data); 905 | +} 906 | + 907 | +static ssize_t dev_ns_proc_write(struct file *file, 908 | + const char __user *buffer, 909 | + size_t count, loff_t *ppos, int setactive) 910 | +{ 911 | + struct dev_namespace *dev_ns; 912 | + char strbuf[16]; /* 10 chars for 32-bit pid + ':' + 4 chars for tag */ 913 | + char *pid_str, *tag_str; 914 | + pid_t new_pid = 0; 915 | + 916 | + /* only init ns may change active ns */ 917 | + if (!dev_ns_proc_permission()) 918 | + return -EPERM; 919 | + 920 | + if (count >= sizeof(strbuf) || count == 0) 921 | + return -EFAULT; 922 | + 923 | + if (copy_from_user(strbuf, buffer, count)) 924 | + return -EFAULT; 925 | + 926 | + strbuf[count] = '\0'; 927 | + pid_str = strim(strbuf); 928 | + tag_str = strchr(pid_str, ':'); 929 | + if (tag_str) 930 | + *(tag_str++) = '\0'; 931 | + 932 | + if (kstrtoint(pid_str, 10, &new_pid) || !new_pid) { 933 | + pr_warning("dev_ns: bad PID format '%s'\n", pid_str); 934 | + return -EINVAL; 935 | + } 936 | + 937 | + dev_ns = get_dev_ns_by_vpid(new_pid); 938 | + if (!dev_ns) { 939 | + pr_warning("dev_ns: non-existing PID %d\n", new_pid); 940 | + return -EINVAL; 941 | + } 942 | + 943 | + if (setactive) { 944 | + set_active_dev_ns(dev_ns); 945 | + } else if (tag_str) { 946 | + /* set dev_ns tag if format was : */ 947 | + /* (safe: last byte of tag always remains NULL) */ 948 | + set_dev_ns_tag(dev_ns, tag_str); 949 | + } else { 950 | + pr_warning("dev_ns: bad PID:tag format '%s'\n", pid_str); 951 | + count = -EINVAL; 952 | + } 953 | + 954 | + put_dev_ns(dev_ns); 955 | + return count; 956 | +} 957 | + 958 | +static ssize_t proc_active_ns_write(struct file *file, 959 | + const char __user *buffer, 960 | + size_t count, loff_t *ppos) 961 | +{ 962 | + return dev_ns_proc_write(file, buffer, count, ppos, 1); 963 | +} 964 | + 965 | +static ssize_t proc_ns_tag_write(struct file *file, 966 | + const char __user *buffer, 967 | + size_t count, loff_t *ppos) 968 | +{ 969 | + return dev_ns_proc_write(file, buffer, count, ppos, 0); 970 | +} 971 | + 972 | +static const struct file_operations active_ns_fileops = { 973 | + .owner = THIS_MODULE, 974 | + .open = proc_active_ns_open, 975 | + .read = seq_read, 976 | + .write = proc_active_ns_write, 977 | + .llseek = seq_lseek, 978 | + .release = single_release, 979 | +}; 980 | + 981 | +static const struct file_operations ns_tag_fileops = { 982 | + .owner = THIS_MODULE, 983 | + .open = proc_ns_tag_open, 984 | + .read = seq_read, 985 | + .write = proc_ns_tag_write, 986 | + .llseek = seq_lseek, 987 | + .release = single_release, 988 | +}; 989 | + 990 | +static __init int dev_namespace_init(void) 991 | +{ 992 | + struct proc_dir_entry *entry; 993 | + 994 | + proc_dev_ns_dir = proc_mkdir("dev_ns", NULL); 995 | + if (!proc_dev_ns_dir) 996 | + return -ENOMEM; 997 | + 998 | + entry = proc_create("active_ns_pid", 0644, proc_dev_ns_dir, 999 | + &active_ns_fileops); 1000 | + if (!entry) 1001 | + goto out_fail_active_ns; 1002 | + 1003 | + entry = proc_create("ns_tag", 0644, proc_dev_ns_dir, 1004 | + &ns_tag_fileops); 1005 | + if (!entry) 1006 | + goto out_fail_ns_tag; 1007 | + 1008 | + return 0; 1009 | + 1010 | +out_fail_ns_tag: 1011 | + remove_proc_entry("active_ns_pid", proc_dev_ns_dir); 1012 | +out_fail_active_ns: 1013 | + remove_proc_entry("dev_ns", NULL); 1014 | + return -ENOMEM; 1015 | +} 1016 | + 1017 | +device_initcall(dev_namespace_init); 1018 | diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c 1019 | index b576f7f..fee38a0 100644 1020 | --- a/kernel/nsproxy.c 1021 | +++ b/kernel/nsproxy.c 1022 | @@ -22,6 +22,7 @@ 1023 | #include 1024 | #include 1025 | #include 1026 | +#include 1027 | #include 1028 | #include 1029 | #include 1030 | @@ -39,6 +40,7 @@ struct nsproxy init_nsproxy = { 1031 | #ifdef CONFIG_NET 1032 | .net_ns = &init_net, 1033 | #endif 1034 | + .dev_ns = &init_dev_ns, 1035 | }; 1036 | 1037 | static inline struct nsproxy *create_nsproxy(void) 1038 | @@ -96,8 +98,17 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, 1039 | goto out_net; 1040 | } 1041 | 1042 | + new_nsp->dev_ns = copy_dev_ns(flags, tsk); 1043 | + if (IS_ERR(new_nsp->dev_ns)) { 1044 | + err = PTR_ERR(new_nsp->dev_ns); 1045 | + goto out_dev; 1046 | + } 1047 | + 1048 | return new_nsp; 1049 | 1050 | +out_dev: 1051 | + if (new_nsp->net_ns) 1052 | + put_net(new_nsp->net_ns); 1053 | out_net: 1054 | if (new_nsp->pid_ns) 1055 | put_pid_ns(new_nsp->pid_ns); 1056 | @@ -175,6 +186,8 @@ void free_nsproxy(struct nsproxy *ns) 1057 | if (ns->pid_ns) 1058 | put_pid_ns(ns->pid_ns); 1059 | put_net(ns->net_ns); 1060 | + if (ns->dev_ns) 1061 | + put_dev_ns(ns->dev_ns); 1062 | kmem_cache_free(nsproxy_cachep, ns); 1063 | } 1064 | 1065 | -------------------------------------------------------------------------------- /dev_ns__adopt_the_framebuffer_subsystem: -------------------------------------------------------------------------------- 1 | dev_ns: adopt the framebuffer subsystem 2 | 3 | Adds device namespace logic to the framebuffer subsystem. 4 | 5 | Isolate framebuffer state between device namespaces, and multiplex access 6 | to hardware state between the virtual framebuffers. 7 | 8 | Associate with each device namespace a shadow, virtual 'struct fb_info' 9 | (vinfo) and a backing buffer, for each registered hardware framebuffer. 10 | Access from an active (foreground) device namespace is passed-through 11 | directly to the underlying device. Access from a passive (background) 12 | device namespace is directed to the virtal fb_info. 13 | 14 | The backing buffer is used for mmap-ed memory in passive namespace, so 15 | any drawing occurs to the backing buffer instead of to the real driver. 16 | When the active namespace changes, perform a context-switch and swap 17 | such mappings: remap memory previously in foreground to the backing 18 | buffer, and from background to the underlying device. The contents of 19 | the framebuffer is saved and restored in (from) the bacgkround (active) 20 | backing buffer, respectively. 21 | 22 | Changelog: 23 | [16-Aug-13] v1 - initial version 24 | 25 | Change-Id: I4d9065a27c12be5da872643009002c5f9c905428 26 | Signed-off-by: Oren Laadan 27 | 28 | --- 29 | drivers/video/Kconfig | 48 ++ 30 | drivers/video/Makefile | 1 + 31 | drivers/video/fb_devns.c | 1251 ++++++++++++++++++++++++++++++++++++++++++++++ 32 | drivers/video/fbmem.c | 82 ++- 33 | include/linux/fb.h | 3 + 34 | include/linux/fb_devns.h | 43 ++ 35 | 6 files changed, 1419 insertions(+), 9 deletions(-) 36 | 37 | diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig 38 | index 527422b..a5fd2cd 100644 39 | --- a/drivers/video/Kconfig 40 | +++ b/drivers/video/Kconfig 41 | @@ -69,6 +69,54 @@ menuconfig FB 42 | (e.g. an accelerated X server) and that are not frame buffer 43 | device-aware may cause unexpected results. If unsure, say N. 44 | 45 | +config FB_DEV_NS 46 | + bool "Enable FB device namespace" 47 | + depends on FB 48 | + depends on DEV_NS 49 | + select FB_SYS_FOPS 50 | + select FB_CFB_FILLRECT 51 | + select FB_CFB_COPYAREA 52 | + select FB_CFB_IMAGEBLIT 53 | + default n 54 | + ---help--- 55 | + Allow per-namespace virtual framebuffers by mulplexing access to 56 | + the hardware framebuffer. This is intended for containers with 57 | + device namespace (see COFNIG_DEV_NS) interactive use case, where 58 | + there is a single "foreground" container and possibly multiple 59 | + "background" containers. 60 | + 61 | + It provdies virtualization for the framebuffer subsystem. When 62 | + activated, each container has a private view of the framebuffer, 63 | + but only the foreground container has pass-through access to the 64 | + hardware framebuffer, while the others see a virtual framebuffer. 65 | + 66 | + If unsure, say N. 67 | + 68 | +config FB_DEV_NS_PAN 69 | + bool "Force update of FB on namespace switch" 70 | + depends on FB_DEV_NS 71 | + default n 72 | + ---help--- 73 | + Force-update the display by calling fb_pan_display() directly. 74 | + 75 | + Usually, the plain memory copy in fb_ns_swap_vmem() suffices. 76 | + But if not (e.g., on x86-KVM emulator), this re-enforces the 77 | + restored display contents. 78 | + 79 | + (Caution: in the prehistory, this could cause occasional 1/2 80 | + second hiccups, if fb_pan_display() occured when another was 81 | + already in progress - because fb drivers usually don't handle 82 | + two irq callbacks and will timeout waiting for display vsync). 83 | + 84 | + If unsure, say N. 85 | + 86 | +config FB_DEV_NS_DEBUG 87 | + bool "Enable FB device namespace debugging" 88 | + depends on FB_DEV_NS 89 | + default n 90 | + ---help--- 91 | + Turn on additional verbosity and debugging output. 92 | + 93 | config FIRMWARE_EDID 94 | bool "Enable firmware EDID" 95 | depends on FB 96 | diff --git a/drivers/video/Makefile b/drivers/video/Makefile 97 | index 53166e9..2f36714 100644 98 | --- a/drivers/video/Makefile 99 | +++ b/drivers/video/Makefile 100 | @@ -9,6 +9,7 @@ obj-y += fb_notify.o 101 | obj-$(CONFIG_FB) += fb.o 102 | fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ 103 | modedb.o fbcvt.o 104 | +obj-$(CONFIG_FB_DEV_NS) += fb_devns.o 105 | fb-objs := $(fb-y) 106 | 107 | obj-$(CONFIG_VT) += console/ 108 | diff --git a/drivers/video/fb_devns.c b/drivers/video/fb_devns.c 109 | new file mode 100644 110 | index 0000000..bdb57e2 111 | --- /dev/null 112 | +++ b/drivers/video/fb_devns.c 113 | @@ -0,0 +1,1251 @@ 114 | +/* 115 | + * drivers/video/fb_devns.c 116 | + * 117 | + * Copyright (c) 2011-2013 Cellrox Ltd. Certain portions are copyrighted by 118 | + * Columbia University. This program is free software licensed under the GNU 119 | + * General Public License Version 2 (GPL 2). You can distribute it and/or 120 | + * modify it under the terms of the GPL 2. 121 | + * 122 | + * Based on mux_fb.c by Jeremy C. Andrus 123 | + * 124 | + * 125 | + * Framebuffer support for device namespace: 126 | + * 127 | + * Isolate framebuffer state between device namespaces, and multiplex access 128 | + * to hardware state between the virtual framebuffers. 129 | + * 130 | + * Associate with each device namespace a shadow, virtual 'struct fb_info' 131 | + * (vinfo) and a backing buffer, for each registered hardware framebuffer. 132 | + * Access from an active (foreground) device namespace is passed-through 133 | + * directly to the underlying device. Access from a passive (background) 134 | + * device namespace is directed to the virtal fb_info. 135 | + * 136 | + * The backing buffer is used for mmap-ed memory in passive namespace, so 137 | + * any drawing occurs to the backing buffer instead of to the real driver. 138 | + * When the active namespace changes, perform a context-switch and swap 139 | + * such mappings: remap memory previously in foreground to the backing 140 | + * buffer, and from background to the underlying device. The contents of 141 | + * the framebuffer is saved and restored in (from) the bacgkround (active) 142 | + * backing buffer, respectively. 143 | + * 144 | + * 145 | + * This program is distributed in the hope that it will be useful, but WITHOUT 146 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 147 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL 2 license for more details. 148 | + * The full GPL 2 License is included in this distribution in the file called 149 | + * COPYING 150 | + * 151 | + * Cellrox can be contacted at oss@cellrox.com 152 | + */ 153 | + 154 | +#ifdef CONFIG_DEV_NS 155 | +#define DEBUG 156 | +#define FB_DEV_NS_DEBUG_NOISE 157 | + 158 | +#ifdef FB_DEV_NS_DEBUG 159 | +#define pr_fmt(fmt) \ 160 | + "[%d] devns:framebuffer [%s:%d]: " fmt, \ 161 | + current->pid, __func__, __LINE__ 162 | +#else 163 | +#define pr_fmt(fmt) \ 164 | + "[%d] devns:framebuffer: " fmt, current->pid 165 | +#endif 166 | + 167 | +#endif /* CONFIG_DEV_NS */ 168 | + 169 | +#include 170 | +#include 171 | +#include 172 | +#include 173 | +#include 174 | +#include 175 | +#include 176 | +#include 177 | +#include 178 | + 179 | + 180 | +static DEFINE_MUTEX(fb_ns_mutex); 181 | + 182 | +struct fb_inode { 183 | + struct inode *inode; 184 | + int count; 185 | +}; 186 | + 187 | +struct fb_colreg { 188 | + __u32 regno; 189 | + __u16 red; 190 | + __u16 green; 191 | + __u16 blue; 192 | + __u16 transp; 193 | +}; 194 | + 195 | +struct fb_ns_info { 196 | + struct fb_dev_ns *fb_ns; 197 | + struct fb_info *info; 198 | + 199 | + struct fb_var_screeninfo var; 200 | + 201 | + struct fb_colreg *colreg; 202 | + int colreg_pos; 203 | + int colreg_len; 204 | + 205 | + void *vmem_buf; 206 | + size_t vmem_len; 207 | +#ifdef CONFIG_FB_DEV_NS_DEBUG 208 | + void *screen_base; 209 | + unsigned long screen_size; 210 | +#endif 211 | + 212 | + struct fb_inode *inodes; 213 | + int inodes_len; 214 | +}; 215 | + 216 | +struct fb_dev_ns { 217 | + struct fb_info *fb[FB_MAX]; 218 | + struct dev_ns_info dev_ns_info; 219 | +}; 220 | + 221 | +/* fb_ns_id, get_fb_ns(), get_fb_ns_cur(), put_fb_ns() */ 222 | +DEFINE_DEV_NS_INFO(fb) 223 | + 224 | +/**********************************************************************/ 225 | + 226 | +static inline bool fb_info_is_virt(struct fb_info *fb_info) 227 | +{ 228 | + return fb_info->flags & FBINFO_DEV_NS; 229 | +} 230 | + 231 | +/**********************************************************************/ 232 | + 233 | +#ifdef FB_DEV_NS_DEBUG_NOISE 234 | +#define FB_NOISE(...) pr_debug(__VA_ARGS__) 235 | +#else 236 | +#define FB_NOISE(...) do { /* */ } while (0) 237 | +#endif 238 | + 239 | +#ifdef FB_DEV_NS_DEBUG 240 | +#define fb_debug_info(info) \ 241 | + _fb_debug_info(info, __func__, __stringify(__LINE__)) 242 | +#define fb_debug_diff(info) \ 243 | + _fb_debug_diff(info, __func__, __stringify(__LINE__)) 244 | +#else 245 | +#define fb_debug_info(info) \ 246 | + _fb_debug_info(info, "", "") 247 | +#define fb_debug_diff(info) \ 248 | + _fb_debug_diff(info, "", "") 249 | +#endif 250 | + 251 | +static void _fb_debug_info(struct fb_info *info, 252 | + const char *func, const char *line) 253 | +{ 254 | + printk(KERN_DEBUG "[%d] devns:framebuffer [%s:%s]:\n" 255 | + " |-> info 0x%p node %d (%s)\n" 256 | + " |-> info smem_start 0x%lx smem_len 0x%x\n" 257 | + " |-> info screen base 0x%p screen_size 0x%lx\n", 258 | + current->pid, func ? : "", line ? : "", 259 | + info, info->node, fb_info_is_virt(info) ? "virt" : "info", 260 | + info->fix.smem_start, info->fix.smem_len, 261 | + info->screen_base, info->screen_size); 262 | +} 263 | + 264 | +static void _fb_debug_diff(struct fb_info *virt, 265 | + const char *func, const char *line) 266 | +{ 267 | + struct fb_ns_info *fb_ns_info; 268 | + struct fb_info *info; 269 | + 270 | + BUG_ON(!fb_info_is_virt(virt)); 271 | + 272 | + fb_ns_info = virt->par; 273 | + info = fb_virt_to_info(virt); 274 | + 275 | +#define FB_DEBUG_DIFF(field, format, func, line) \ 276 | + do { \ 277 | + if (virt->field != info->field) \ 278 | + printk(KERN_DEBUG "[%d] devns:framebuffer [%s:%s]: " \ 279 | + "*** fb_info diff *** " # field \ 280 | + " virt " format " info " format " (0x%p)\n", \ 281 | + current->pid, func ? : "", line ? : "", \ 282 | + virt->field, info->field, info); \ 283 | + } while (0) 284 | + 285 | + FB_DEBUG_DIFF(fix.smem_len, "0x%d", func, line); 286 | + FB_DEBUG_DIFF(screen_size, "0x%lx", func, line); 287 | + 288 | +#define FB_DEBUG_DIFF_2(field1, format1, field2, format2, func, line) \ 289 | + do { \ 290 | + if ((unsigned long) field1 != (unsigned long) field2) \ 291 | + printk(KERN_DEBUG "[%d] devns:framebuffer [%s:%s]: " \ 292 | + "*** fb_info diff *** " \ 293 | + # field1 " " format1 " != " \ 294 | + # field2 " " format2 " (0x%p)\n", \ 295 | + current->pid, func ? : "", line ? : "", \ 296 | + field1, field2, info); \ 297 | + } while (0) 298 | + 299 | + 300 | +#ifdef CONFIG_FB_DEV_NS_DEBUG 301 | + FB_DEBUG_DIFF_2(fb_ns_info->screen_base, "0x%p", 302 | + info->screen_base, "0x%p", func, line); 303 | + FB_DEBUG_DIFF_2(fb_ns_info->screen_size, "0x%lx", 304 | + info->screen_size, "0x%lx", func, line); 305 | + BUG_ON(fb_ns_info->screen_base != info->screen_base || 306 | + fb_ns_info->screen_size != info->screen_size); 307 | +#endif 308 | + FB_DEBUG_DIFF_2(fb_ns_info->vmem_len, "0x%zx", 309 | + info->fix.smem_len, "0x%x", func, line); 310 | + 311 | + WARN_ON(fb_ns_info->vmem_len != info->fix.smem_len); 312 | +} 313 | + 314 | + 315 | +/**********************************************************************/ 316 | + 317 | +static struct notifier_block fb_ns_switch_notifier; 318 | + 319 | +static struct dev_ns_info *fb_devns_create(struct dev_namespace *dev_ns) 320 | +{ 321 | + struct fb_dev_ns *fb_ns; 322 | + struct dev_ns_info *dev_ns_info; 323 | + 324 | + fb_ns = kzalloc(sizeof(*fb_ns), GFP_KERNEL); 325 | + if (!fb_ns) 326 | + return ERR_PTR(-ENOMEM); 327 | + 328 | + pr_info("new fb_dev_ns 0x%p (d 0x%p)\n", fb_ns, dev_ns); 329 | + 330 | + dev_ns_info = &fb_ns->dev_ns_info; 331 | + 332 | + dev_ns_info->nb = fb_ns_switch_notifier; 333 | + dev_ns_register_notify(dev_ns, &dev_ns_info->nb); 334 | + 335 | + return dev_ns_info; 336 | +} 337 | + 338 | +static void fb_devns_release(struct dev_ns_info *dev_ns_info) 339 | +{ 340 | + struct fb_dev_ns *fb_ns; 341 | + 342 | + fb_ns = container_of(dev_ns_info, struct fb_dev_ns, dev_ns_info); 343 | + 344 | + pr_info("del fb_dev_ns 0x%p (d 0x%p)\n", fb_ns, dev_ns_info->dev_ns); 345 | + dev_ns_unregister_notify(dev_ns_info->dev_ns, &dev_ns_info->nb); 346 | + 347 | + kfree(fb_ns); 348 | +} 349 | + 350 | +static struct dev_ns_ops fb_ns_ops = { 351 | + .create = fb_devns_create, 352 | + .release = fb_devns_release, 353 | +}; 354 | + 355 | +/**********************************************************************/ 356 | + 357 | +/* Given a virtual fb, indicate if it's active (foreground) */ 358 | +static bool fb_virt_is_active(struct fb_info *virt) 359 | +{ 360 | + struct fb_ns_info *fb_ns_info; 361 | + 362 | + BUG_ON(!fb_info_is_virt(virt)); 363 | + 364 | + fb_ns_info = virt->par; 365 | + return is_active_dev_ns(fb_ns_info->fb_ns->dev_ns_info.dev_ns); 366 | +} 367 | + 368 | +/* Given a fb, convert from virt info to HW info (if necessary) */ 369 | +struct fb_info *fb_virt_to_info(struct fb_info *virt) 370 | +{ 371 | + struct fb_info *info; 372 | + struct fb_ns_info *fb_ns_info; 373 | + 374 | + if (fb_info_is_virt(virt)) { 375 | + fb_ns_info = virt->par; 376 | + info = fb_ns_info->info; 377 | + 378 | + /* 379 | + * TODO: add debug divergence checks between virt & info: 380 | + * matching screen_size, fix contents, etc. 381 | + */ 382 | + } else 383 | + info = virt; 384 | + 385 | + return info; 386 | +} 387 | + 388 | +/* Given a fb, convert from virt info to HW info if active (foreground) */ 389 | +struct fb_info *fb_virt_to_info_ns(struct fb_info *virt) 390 | +{ 391 | + if (fb_info_is_virt(virt) && fb_virt_is_active(virt)) 392 | + return fb_virt_to_info(virt); 393 | + else 394 | + return virt; 395 | +} 396 | + 397 | +/* 398 | + * Track inodes pointing to the device, to easily find whoever mmaps it. 399 | + * (unlikely to have more than one or a few inodes) 400 | + */ 401 | + 402 | +struct fb_inode *find_fb_inode(struct fb_info *virt, struct inode *inode) 403 | +{ 404 | + struct fb_ns_info *fb_ns_info = virt->par; 405 | + struct fb_inode *inodes; 406 | + int i; 407 | + 408 | + inodes = fb_ns_info->inodes; 409 | + 410 | + for (i = 0; i < fb_ns_info->inodes_len; i++) { 411 | + if (inodes[i].inode == inode) 412 | + return &inodes[i]; 413 | + } 414 | + 415 | + return NULL; 416 | +} 417 | + 418 | +int track_fb_inode(struct fb_info *virt, struct inode *inode) 419 | +{ 420 | + struct fb_ns_info *fb_ns_info = virt->par; 421 | + struct fb_inode *fb_inode; 422 | + 423 | + mutex_lock(&fb_ns_mutex); 424 | + fb_inode = find_fb_inode(virt, inode); 425 | + if (!fb_inode) { 426 | + int len = fb_ns_info->inodes_len; 427 | + 428 | + pr_debug("fb_inode alloc new len %d\n", len + 1); 429 | + fb_inode = krealloc(fb_ns_info->inodes, 430 | + (len + 1) * sizeof(*fb_inode), 431 | + GFP_KERNEL); 432 | + if (!fb_inode) { 433 | + mutex_unlock(&fb_ns_mutex); 434 | + return -ENOMEM; 435 | + } 436 | + 437 | + fb_ns_info->inodes = fb_inode; 438 | + fb_ns_info->inodes_len = len + 1; 439 | + 440 | + fb_inode = &fb_inode[len]; 441 | + fb_inode->inode = inode; 442 | + fb_inode->count = 0; 443 | + } 444 | + fb_inode->count++; 445 | + pr_debug("fb_inode track 0x%p (idx %d)\n", inode, virt->node); 446 | + mutex_unlock(&fb_ns_mutex); 447 | + 448 | + return 0; 449 | +} 450 | + 451 | +void untrack_fb_inode(struct fb_info *virt, struct inode *inode) 452 | +{ 453 | + struct fb_ns_info *fb_ns_info = virt->par; 454 | + struct fb_inode *fb_inode; 455 | + 456 | + mutex_lock(&fb_ns_mutex); 457 | + fb_inode = find_fb_inode(virt, inode); 458 | + BUG_ON(!fb_inode); 459 | + 460 | + pr_debug("fb_inode untrack 0x%p (idx %d)\n", inode, virt->node); 461 | + if (fb_inode && --fb_inode->count == 0) { 462 | + int len = fb_ns_info->inodes_len - 1; 463 | + 464 | + *fb_inode = fb_ns_info->inodes[len]; 465 | + fb_ns_info->inodes_len = len; 466 | + fb_ns_info->inodes = krealloc(fb_ns_info->inodes, 467 | + len, GFP_KERNEL); 468 | + pr_debug("fb_inode drop len %d\n", len); 469 | + } 470 | + mutex_unlock(&fb_ns_mutex); 471 | +} 472 | + 473 | + 474 | +static struct fb_ops fb_devns_ops; 475 | + 476 | +/* 477 | + * Duplicate the fb_ops for the virtual fb_info, substituting certain 478 | + * methods with those that should be used when running in background. 479 | + * This is needed to maintain compatibility with original call-paths, 480 | + * regardless of whether the underlying device has or has not defined 481 | + * said methods. 482 | + */ 483 | +static struct fb_ops *fb_ns_make_fb_ops(struct fb_info *info) 484 | +{ 485 | + struct fb_ops *fbops; 486 | + 487 | + fbops = kmalloc(sizeof(*fbops), GFP_KERNEL); 488 | + if (fbops == NULL) 489 | + return NULL; 490 | + 491 | + memcpy(fbops, &fb_devns_ops, sizeof(*fbops)); 492 | + 493 | + if (!info->fbops->fb_check_var) 494 | + fbops->fb_check_var = NULL; 495 | + if (!info->fbops->fb_setcmap) 496 | + fbops->fb_setcmap = NULL; 497 | + 498 | + /* TODO: almost always used from console... add sanity 499 | + * that complains if called? fb_rotate never called */ 500 | + if (!info->fbops->fb_fillrect) 501 | + fbops->fb_fillrect = NULL; 502 | + if (!info->fbops->fb_copyarea) 503 | + fbops->fb_copyarea = NULL; 504 | + if (!info->fbops->fb_imageblit) 505 | + fbops->fb_imageblit = NULL; 506 | + if (!info->fbops->fb_cursor) 507 | + fbops->fb_cursor = NULL; 508 | + if (!info->fbops->fb_rotate) 509 | + fbops->fb_rotate = NULL; 510 | + 511 | + if (!info->fbops->fb_ioctl) 512 | + fbops->fb_ioctl = NULL; 513 | + if (!info->fbops->fb_get_caps) 514 | + fbops->fb_get_caps = NULL; 515 | + if (!info->fbops->fb_compat_ioctl) 516 | + fbops->fb_compat_ioctl = NULL; 517 | + 518 | + /* TODO: almost always used from console... add sanity 519 | + * that complains if called? */ 520 | + if (!info->fbops->fb_debug_enter) 521 | + fbops->fb_debug_enter = NULL; 522 | + if (!info->fbops->fb_debug_leave) 523 | + fbops->fb_debug_enter = NULL; 524 | + 525 | + fbops->owner = info->fbops->owner; 526 | + 527 | + return fbops; 528 | +} 529 | + 530 | +static void fb_ns_free_fb_ops(struct fb_ops *fbops) 531 | +{ 532 | + kfree(fbops); 533 | +} 534 | + 535 | +static struct fb_info *fb_ns_info_alloc(struct fb_dev_ns *fb_ns, 536 | + struct fb_info *fb_info) 537 | +{ 538 | + struct fb_info *fb_virt; 539 | + struct fb_ns_info *fb_ns_info; 540 | + void *vmem = NULL; 541 | + size_t vlen; 542 | + 543 | + fb_virt = framebuffer_alloc(sizeof(struct fb_ns_info), fb_info->device); 544 | + if (fb_virt == NULL) 545 | + return NULL; 546 | + 547 | + pr_info("new fb_virt 0x%p for fb_info 0x%p (%s) idx %d (d 0x%p)\n", 548 | + fb_virt, fb_info, fb_info->fix.id, 549 | + fb_info->node, fb_ns->dev_ns_info.dev_ns); 550 | + 551 | + /* copy main pieces from underlying fb_info */ 552 | + fb_virt->node = fb_info->node; 553 | + fb_virt->flags = fb_info->flags; 554 | + atomic_set(&fb_virt->count, 0); 555 | + mutex_init(&fb_virt->lock); 556 | + mutex_init(&fb_virt->mm_lock); 557 | + 558 | + strncpy(fb_virt->fix.id, fb_info->fix.id, sizeof(fb_virt->fix.id)); 559 | + 560 | + fb_virt->pseudo_palette = fb_info->pseudo_palette; 561 | + fb_virt->cmap = fb_info->cmap; 562 | + fb_virt->pixmap = fb_info->pixmap; 563 | + fb_virt->fix = fb_info->fix; 564 | + fb_virt->var = fb_info->var; 565 | + 566 | + /* allocate per-namespace virtual buffer */ 567 | + vlen = fb_info->fix.smem_len; 568 | + if (vlen) { 569 | + vmem = vmalloc_user(vlen); /* already zeroed out */ 570 | + if (vmem == NULL) { 571 | + framebuffer_release(fb_virt); 572 | + return NULL; 573 | + } 574 | + } 575 | + 576 | + fb_debug_info(fb_info); 577 | + fb_debug_info(fb_virt); 578 | + 579 | + /* setup namespace aware data */ 580 | + fb_ns_info = fb_virt->par; 581 | + 582 | + fb_ns_info->vmem_len = vlen; 583 | + fb_ns_info->vmem_buf = vmem; 584 | + 585 | +#ifdef CONFIG_FB_DEV_NS_DEBUG 586 | + fb_ns_info->screen_base = fb_info->screen_base; 587 | + fb_ns_info->screen_size = fb_info->screen_size; 588 | +#endif 589 | + 590 | + fb_ns_info->colreg_pos = 0; 591 | + fb_ns_info->colreg_len = 0; 592 | + 593 | + fb_ns_info->fb_ns = fb_ns; 594 | + fb_ns_info->info = fb_info; 595 | + 596 | + /* make it namespace aware */ 597 | + fb_virt->flags |= FBINFO_DEV_NS; 598 | + fb_virt->fbops = fb_ns_make_fb_ops(fb_info); 599 | + if (fb_virt->fbops == NULL) { 600 | + vfree(fb_ns_info->vmem_buf); 601 | + framebuffer_release(fb_virt); 602 | + return NULL; 603 | + } 604 | + 605 | + fb_virt->screen_base = vmem; 606 | + fb_virt->screen_size = fb_info->screen_size; 607 | + fb_virt->fix.smem_start = (unsigned long) vmem; 608 | + fb_virt->fix.smem_len = vlen; 609 | + 610 | + fb_debug_info(fb_info); 611 | + fb_debug_info(fb_virt); 612 | + 613 | + return fb_virt; 614 | +} 615 | + 616 | +static void fb_ns_info_free(struct fb_info *fb_virt) 617 | +{ 618 | + struct fb_ns_info *fb_ns_info; 619 | + 620 | + fb_ns_info = fb_virt->par; 621 | + 622 | + pr_info("del fb_virt 0x%p for fb_info 0x%p idx %d\n", 623 | + fb_virt, fb_ns_info->info, fb_virt->node); 624 | + 625 | + vfree(fb_ns_info->vmem_buf); 626 | + kfree(fb_ns_info->colreg); 627 | + fb_ns_free_fb_ops(fb_virt->fbops); 628 | + framebuffer_release(fb_virt); 629 | +} 630 | + 631 | +/* 632 | + * Return the namespace-aware fb_info for a given fb_info. 633 | + * From now onward, all access is via the namespace-aware fb_info, 634 | + * including the cleanup via the callback fb_ns_destroy(). 635 | + */ 636 | +struct fb_info *get_fb_info_ns(struct fb_info *fb_info) 637 | +{ 638 | + struct fb_dev_ns *fb_ns; 639 | + struct fb_info *fb_virt; 640 | + int idx; 641 | + 642 | + fb_ns = get_fb_ns_cur(); 643 | + if (!fb_ns) 644 | + return NULL; 645 | + 646 | + idx = fb_info->node; 647 | + 648 | + pr_debug("get fb_info 0x%p idx %d (d 0x%p)\n", 649 | + fb_info, fb_info->node, fb_ns->dev_ns_info.dev_ns); 650 | + 651 | + mutex_lock(&fb_ns_mutex); 652 | + if (fb_ns->fb[idx] == NULL) 653 | + fb_ns->fb[idx] = fb_ns_info_alloc(fb_ns, fb_info); 654 | + 655 | + fb_virt = fb_ns->fb[idx]; 656 | + 657 | + if (fb_virt) 658 | + atomic_inc(&fb_virt->count); 659 | + else 660 | + fb_virt = NULL; 661 | + mutex_unlock(&fb_ns_mutex); 662 | + 663 | + pr_debug("got fb_virt 0x%p idx %d (d 0x%p)\n", 664 | + fb_virt, fb_info->node, fb_ns->dev_ns_info.dev_ns); 665 | + 666 | + if (fb_virt == NULL) 667 | + put_fb_ns(fb_ns); 668 | + 669 | + return fb_virt; 670 | +} 671 | + 672 | +void put_fb_info_ns(struct fb_info *fb_virt) 673 | +{ 674 | + struct fb_ns_info *fb_ns_info; 675 | + 676 | + if (fb_virt == NULL) 677 | + return; 678 | + 679 | + pr_debug("put fb_info 0x%p idx %d\n", fb_virt, fb_virt->node); 680 | + 681 | + fb_ns_info = fb_virt->par; 682 | + put_fb_ns(fb_ns_info->fb_ns); 683 | + 684 | + if (atomic_dec_and_test(&fb_virt->count)) { 685 | + /* 686 | + * Note: it is safe to do this without fb_ns_mutex 687 | + * because we reach here only once per fb_ns_info. 688 | + */ 689 | + fb_ns_info->fb_ns->fb[fb_virt->node] = NULL; 690 | + fb_ns_info_free(fb_virt); 691 | + } 692 | +} 693 | + 694 | +/**********************************************************************/ 695 | + 696 | +/* for fb_xxxx operations that should never occur */ 697 | +static int fb_ns_open(struct fb_info *virt, int user) { BUG(); } 698 | +static int fb_ns_release(struct fb_info *virt, int user) { BUG(); } 699 | +static void fb_ns_destroy(struct fb_info *virt) { BUG(); } 700 | + 701 | +static int fb_ns_check_var(struct fb_var_screeninfo *var, struct fb_info *virt) 702 | +{ 703 | + struct fb_info *info; 704 | + int ret = 0; 705 | + 706 | + BUG_ON(!fb_info_is_virt(virt)); 707 | + 708 | + /* TODO: maybe do check against current HW par? */ 709 | + 710 | + fb_debug_info(virt); 711 | + fb_debug_diff(virt); 712 | + 713 | + info = fb_virt_to_info(virt); 714 | + if (info->fbops->fb_check_var) 715 | + ret = info->fbops->fb_check_var(var, info); 716 | + 717 | + return ret; 718 | +} 719 | + 720 | +static int fb_ns_set_par(struct fb_info *virt) 721 | +{ 722 | + struct fb_ns_info *fb_ns_info = virt->par; 723 | + 724 | + BUG_ON(!fb_info_is_virt(virt)); 725 | + 726 | + /* TODO: check var in FB_ACTIVATE_TEST against real info? */ 727 | + /* TODO: save certain operations on var for later? */ 728 | + 729 | + fb_debug_info(virt); 730 | + fb_debug_diff(virt); 731 | + 732 | + /* stash parameters for later, when fb becomes active */ 733 | + fb_ns_info->var = virt->var; 734 | + 735 | + return 0; 736 | +} 737 | + 738 | +static int fb_ns_setcolreg(unsigned regno, unsigned red, unsigned green, 739 | + unsigned blue, unsigned transp, struct fb_info *virt) 740 | +{ 741 | + struct fb_ns_info *fb_ns_info; 742 | + struct fb_colreg *colreg; 743 | + 744 | + BUG_ON(!fb_info_is_virt(virt)); 745 | + 746 | + /* TODO: can search and replace existing regno, if exists */ 747 | + 748 | + fb_debug_info(virt); 749 | + fb_debug_diff(virt); 750 | + 751 | + /* 752 | + * The following is protected from races because the framebuffer 753 | + * system calls fb_setcmap()->fb_setcolreg() with lock_fb_info() 754 | + */ 755 | + fb_ns_info = virt->par; 756 | + colreg = fb_ns_info->colreg; 757 | + 758 | + if (fb_ns_info->colreg_pos == fb_ns_info->colreg_len) { 759 | + int size = fb_ns_info->colreg_pos + 256; 760 | + colreg = krealloc(colreg, size * sizeof(*colreg), GFP_KERNEL); 761 | + if (colreg == NULL) 762 | + return -ENOMEM; 763 | + fb_ns_info->colreg = colreg; 764 | + fb_ns_info->colreg_len = size; 765 | + } 766 | + 767 | + colreg = &colreg[fb_ns_info->colreg_pos++]; 768 | + 769 | + colreg->regno = regno; 770 | + colreg->red = red; 771 | + colreg->green = green; 772 | + colreg->blue = blue; 773 | + colreg->transp = transp; 774 | + 775 | + FB_NOISE("fb_info 0x%p idx %d COLREG regno %d, pos %d\n", 776 | + virt, virt->node, regno, fb_ns_info->colreg_pos); 777 | + 778 | + return 0; 779 | +} 780 | + 781 | +static int fb_ns_setcmap(struct fb_cmap *cmap, struct fb_info *virt) 782 | +{ 783 | + BUG_ON(!fb_info_is_virt(virt)); 784 | + 785 | + /* TODO: this can be device specific, may need more work? */ 786 | + /* 787 | + * in particular, HW can keep its own map, and we here override 788 | + * an entry. if the other namespace does/expects the same value 789 | + * all is well, but if each namespace to its own - we need to 790 | + * save these values and context-switch (HW) on namespace swtich. 791 | + */ 792 | + 793 | + fb_debug_info(virt); 794 | + fb_debug_diff(virt); 795 | + 796 | + FB_NOISE("fb_info 0x%p idx %d CMAP start %d, len %d\n", 797 | + virt, virt->node, cmap->start, cmap->len); 798 | + 799 | + /* TODO: see also fbmem.c:fb_set_cmap() */ 800 | + 801 | + return 0; 802 | +} 803 | + 804 | +static int fb_ns_cursor(struct fb_info *virt, struct fb_cursor *cursor) 805 | +{ 806 | + BUG_ON(!fb_info_is_virt(virt)); 807 | + 808 | + pr_info("unexpected fb_cursor() on virt fb_info 0x%p idx %d\n", 809 | + virt, virt->node); 810 | + 811 | + return 0; 812 | +} 813 | + 814 | +static void fb_ns_rotate(struct fb_info *virt, int angle) 815 | +{ 816 | + pr_info("unexpected fb_rotate() on virt fb_info 0x%p idx %d\n", 817 | + virt, virt->node); 818 | +} 819 | + 820 | +static int fb_ns_ioctl(struct fb_info *virt, unsigned int cmd, 821 | + unsigned long arg) 822 | +{ 823 | + BUG_ON(!fb_info_is_virt(virt)); 824 | + 825 | + fb_debug_diff(virt); 826 | + 827 | + pr_info("specialized fb_ioctl() on virt fb_info 0x%p idx %d\n", 828 | + virt, virt->node); 829 | + pr_info(" |-> ioctl cmd %d, arg %ld/0x%lx\n", cmd, arg, arg); 830 | + 831 | + return 0; 832 | +} 833 | + 834 | +static int fb_ns_compat_ioctl(struct fb_info *virt, unsigned cmd, 835 | + unsigned long arg) 836 | +{ 837 | + BUG_ON(!fb_info_is_virt(virt)); 838 | + 839 | + fb_debug_diff(virt); 840 | + 841 | + pr_info("specialized fb_compat_ioctl() on virt fb_info 0x%p idx %d\n", 842 | + virt, virt->node); 843 | + pr_info(" |-> ioctl cmd %d, arg %ld/0x%lx\n", cmd, arg, arg); 844 | + 845 | + return 0; 846 | +} 847 | + 848 | +static int fb_ns_mmap(struct fb_info *virt, struct vm_area_struct *vma) 849 | +{ 850 | + void *addr; 851 | + 852 | + BUG_ON(!fb_info_is_virt(virt)); 853 | + 854 | + fb_debug_info(virt); 855 | + fb_debug_diff(virt); 856 | + 857 | + addr = (void *) virt->fix.smem_start; 858 | + 859 | + pr_info("fb_mmap fb_info 0x%p idx %d\n", virt, virt->node); 860 | + pr_info(" |-> addr 0x%p vm_start 0x%lx vm_size 0x%lx vm_pgoff 0x%lx\n", 861 | + addr, vma->vm_start, vma->vm_end-vma->vm_start, vma->vm_pgoff); 862 | + 863 | + return remap_vmalloc_range(vma, addr, vma->vm_pgoff); 864 | +} 865 | + 866 | +static void fb_ns_get_caps(struct fb_info *virt, struct fb_blit_caps *caps, 867 | + struct fb_var_screeninfo *var) 868 | +{ 869 | + struct fb_info *info; 870 | + 871 | + /* TODO: may need to copy HW's var temporarily for this: e.g. 872 | + * for s3fb.c which answers based on its var->bits_per_pixel 873 | + */ 874 | + 875 | + BUG_ON(!fb_info_is_virt(virt)); 876 | + 877 | + fb_debug_diff(virt); 878 | + 879 | + info = fb_virt_to_info(virt); 880 | + info->fbops->fb_get_caps(info, caps, var); 881 | +} 882 | + 883 | +static int fb_ns_debug_enter(struct fb_info *virt) 884 | +{ 885 | + struct fb_info *info; 886 | + 887 | + /* resort to whatever underlying HW logic */ 888 | + 889 | + BUG_ON(!fb_info_is_virt(virt)); 890 | + 891 | + pr_info("unexpected fb_debug_enter() on virt fb_info 0x%p idx %d\n", 892 | + virt, virt->node); 893 | + 894 | + info = fb_virt_to_info(virt); 895 | + return info->fbops->fb_debug_enter(info); 896 | +} 897 | + 898 | +static int fb_ns_debug_leave(struct fb_info *virt) 899 | +{ 900 | + struct fb_info *info; 901 | + 902 | + /* resort to whatever underlying HW logic */ 903 | + 904 | + BUG_ON(!fb_info_is_virt(virt)); 905 | + 906 | + pr_info("unexpected fb_debug_leave() on virt fb_info 0x%p idx %d\n", 907 | + virt, virt->node); 908 | + 909 | + info = fb_virt_to_info(virt); 910 | + return info->fbops->fb_debug_leave(info); 911 | +} 912 | + 913 | +static struct fb_ops fb_devns_ops = { 914 | + .fb_open = fb_ns_open, 915 | + .fb_release = fb_ns_release, 916 | + .fb_read = fb_sys_read, 917 | + .fb_write = fb_sys_write, 918 | + .fb_check_var = fb_ns_check_var, 919 | + .fb_set_par = fb_ns_set_par, 920 | + .fb_setcolreg = fb_ns_setcolreg, 921 | + .fb_setcmap = fb_ns_setcmap, 922 | + .fb_blank = NULL, /* keep default action */ 923 | + .fb_pan_display = NULL, /* keep default action */ 924 | + .fb_fillrect = cfb_fillrect, /* like vfb */ 925 | + .fb_copyarea = cfb_copyarea, /* like vfb */ 926 | + .fb_imageblit = cfb_imageblit, /* like vfb */ 927 | + .fb_cursor = fb_ns_cursor, 928 | + .fb_rotate = fb_ns_rotate, 929 | + .fb_sync = NULL, /* no virtual sync */ 930 | + .fb_ioctl = fb_ns_ioctl, 931 | + .fb_compat_ioctl = fb_ns_compat_ioctl, 932 | + .fb_mmap = fb_ns_mmap, 933 | + .fb_get_caps = fb_ns_get_caps, 934 | + .fb_destroy = fb_ns_destroy, 935 | + .fb_debug_enter = fb_ns_debug_enter, 936 | + .fb_debug_leave = fb_ns_debug_leave, 937 | +}; 938 | + 939 | +/**********************************************************************/ 940 | + 941 | +static int fb_ns_apply_colreg(struct fb_info *virt) 942 | +{ 943 | + struct fb_ns_info *fb_ns_info; 944 | + struct fb_colreg *colreg; 945 | + struct fb_info *info; 946 | + int i, ret, err = 0; 947 | + 948 | + fb_ns_info = virt->par; 949 | + info = fb_virt_to_info(virt); 950 | + 951 | + /* 952 | + * fb_ns_setcolreg() is protected by framebuffer system using 953 | + * lock_fb_info() - use same lock here to protect colreg data. 954 | + */ 955 | + if (!lock_fb_info(virt)) 956 | + BUG(); 957 | + 958 | + colreg = fb_ns_info->colreg; 959 | + i = fb_ns_info->colreg_pos; 960 | + 961 | + pr_debug("virt 0x%p pending colregs %d\n", virt, i); 962 | + 963 | + while (i--) { 964 | + ret = info->fbops->fb_setcolreg(colreg->regno, colreg->red, 965 | + colreg->green, colreg->blue, 966 | + colreg->transp, info); 967 | + if (ret < 0) 968 | + pr_info("fb_info 0x%p colreg err %d/%d, regno %d\n", 969 | + virt, ret, err, colreg->regno); 970 | + if (ret < 0 && !err) 971 | + err = ret; 972 | + colreg++; 973 | + } 974 | + 975 | + fb_ns_info->colreg_pos = 0; 976 | + 977 | + unlock_fb_info(virt); 978 | + return err; 979 | +} 980 | + 981 | +static int fb_ns_apply_set_par(struct fb_info *virt) 982 | +{ 983 | + struct fb_ns_info *fb_ns_info; 984 | + struct fb_var_screeninfo *var; 985 | + struct fb_info *info; 986 | + int ret = 0; 987 | + 988 | + fb_ns_info = virt->par; 989 | + info = fb_virt_to_info(virt); 990 | + 991 | + /* 992 | + * fb_ns_set_par() is protected by framebuffer system using 993 | + * lock_fb_info() - use same lock here to protect var/par data. 994 | + */ 995 | + if (!lock_fb_info(virt)) 996 | + BUG(); 997 | + 998 | + var = &fb_ns_info->var; 999 | + 1000 | + /* if xres == 0, then there was no pending fb_set_par */ 1001 | + if (var->xres == 0) 1002 | + goto out; 1003 | + 1004 | + info->var = *var; 1005 | + ret = info->fbops->fb_set_par(info); 1006 | + 1007 | + if (ret < 0) 1008 | + pr_info("fb_info 0x%p setpar err %d\n", virt, ret); 1009 | + 1010 | + var->xres = 0; 1011 | + 1012 | + out: 1013 | + unlock_fb_info(virt); 1014 | + return ret; 1015 | +} 1016 | + 1017 | +/* 1018 | + * Save current hardware buffer in the virtual buffer of @prev, 1019 | + * and restore the contents saved in the virtual buffer of @next. 1020 | + */ 1021 | +static void fb_ns_swap_vmem(struct fb_info *virt, int activate) 1022 | +{ 1023 | + struct fb_info *info; 1024 | + size_t size; 1025 | + 1026 | + info = fb_virt_to_info(virt); 1027 | + size = virt->fix.smem_len; 1028 | + 1029 | + fb_debug_info(virt); 1030 | + fb_debug_diff(virt); 1031 | + 1032 | + pr_info("fb_info 0x%p idx %d copy %s virtual buffer (size 0x%zx)\n", 1033 | + info, info->node, activate ? "from" : "to", size); 1034 | + 1035 | + /* do nothing for zero size framebuffers */ 1036 | + if (size == 0 || !info->screen_base || !virt->screen_base) 1037 | + return; 1038 | + 1039 | + /* follow logic in fb_read()/fb_write() */ 1040 | + if (info->fbops->fb_sync) 1041 | + info->fbops->fb_sync(info); 1042 | + 1043 | + pr_info("fb_info 0x%p screen 0x%p, virt 0x%p virt->screen 0x%p\n", 1044 | + info, info->screen_base, virt, virt->screen_base); 1045 | + 1046 | + if (activate) 1047 | + fb_memcpy_tofb(info->screen_base, virt->screen_base, size); 1048 | + else 1049 | + fb_memcpy_fromfb(virt->screen_base, info->screen_base, size); 1050 | +} 1051 | + 1052 | +struct fb_mmlist { 1053 | + struct mm_struct *mm; 1054 | + unsigned long start; 1055 | + unsigned long size; 1056 | + struct fb_mmlist *next; 1057 | +}; 1058 | + 1059 | +static int do_fb_ns_remap(struct fb_info *virt, struct address_space *mapping) 1060 | +{ 1061 | + struct vm_area_struct *vma; 1062 | + struct prio_tree_iter iter; 1063 | + struct fb_mmlist *mmhead = NULL; 1064 | + struct fb_mmlist *mmlist; 1065 | + int ret, err = 0; 1066 | + 1067 | + /* find mm_structs that have hardware/virtual buffer to remap */ 1068 | + mutex_lock(&mapping->i_mmap_mutex); 1069 | + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, 0, ULONG_MAX) { 1070 | + mmlist = kmalloc(sizeof(*mmlist), GFP_KERNEL); 1071 | + if (!mmlist) { 1072 | + err = -ENOMEM; 1073 | + break; 1074 | + } 1075 | + 1076 | + mmlist->mm = vma->vm_mm; 1077 | + mmlist->start = vma->vm_start; 1078 | + mmlist->size = vma->vm_end - vma->vm_start; 1079 | + atomic_inc(&vma->vm_mm->mm_count); 1080 | + 1081 | + mmlist->next = mmhead; 1082 | + mmhead = mmlist; 1083 | + 1084 | + pr_debug(" |-> collect mm 0x%p (0x%lx, 0x%lx)\n", 1085 | + vma->vm_mm, mmlist->start, mmlist->size); 1086 | + } 1087 | + mutex_unlock(&mapping->i_mmap_mutex); 1088 | + 1089 | + if (err < 0) 1090 | + goto out; 1091 | + 1092 | + /* loop over found mm_structs: unmap old buffer, map new one */ 1093 | + for (mmlist = mmhead; mmlist; mmlist = mmlist->next) { 1094 | + struct mm_struct *mm = mmlist->mm; 1095 | + unsigned long start = mmlist->start; 1096 | + unsigned long size = mmlist->size; 1097 | + 1098 | + down_write(&mm->mmap_sem); 1099 | + 1100 | + vma = find_vma(mm, mmlist->start); 1101 | + BUG_ON(vma->vm_file->f_mapping != mapping); 1102 | + 1103 | + if (vma) { 1104 | + pr_debug(" |-> vma (0x%lx 0x%lx 0x%lx) inode 0x%p\n", 1105 | + vma->vm_start, vma->vm_end - vma->vm_start, 1106 | + vma->vm_pgoff, 1107 | + vma->vm_file->f_dentry->d_inode); 1108 | + pr_debug(" |-> remap mm 0x%p (0x%lx 0x%lx)\n", 1109 | + vma->vm_mm, mmlist->start, mmlist->size); 1110 | + 1111 | + /* remove previous mapping */ 1112 | + zap_page_range(vma, start, size, NULL); 1113 | + 1114 | + ret = vma_adjust(vma, vma->vm_start, vma->vm_end, 1115 | + 0, NULL); 1116 | + if (ret < 0) 1117 | + pr_info(" |-> vma adjust %d\n", ret); 1118 | + 1119 | + /* 1120 | + * This may not be IO memory now, but may have been. 1121 | + * If active, it will be re-set by underlying mmap. 1122 | + */ 1123 | + vma->vm_flags &= ~VM_IO; 1124 | + 1125 | + /* 1126 | + * Create new mapping. (Note: the native mmap will 1127 | + * eventually call the fb_mmap of info (if active) 1128 | + * or virt (if inactive). 1129 | + */ 1130 | + ret = vma->vm_file->f_op->mmap(vma->vm_file, vma); 1131 | + if (ret < 0) 1132 | + pr_info(" |-> remap mm error %d\n", ret); 1133 | + 1134 | + /* proceed in case of errors */ 1135 | + if (ret < 0 && !err) 1136 | + err = ret; 1137 | + } 1138 | + 1139 | + up_write(&mm->mmap_sem); 1140 | + } 1141 | + 1142 | +out: 1143 | + while (mmhead) { 1144 | + mmlist = mmhead; 1145 | + mmhead = mmhead->next; 1146 | + mmdrop(mmlist->mm); 1147 | + kfree(mmlist); 1148 | + } 1149 | + 1150 | + return err; 1151 | +} 1152 | + 1153 | +static int fb_ns_swap_mmap(struct fb_info *virt, bool activate) 1154 | +{ 1155 | + struct fb_ns_info *fb_ns_info = virt->par; 1156 | + struct inode *inode; 1157 | + int i, ret, err = 0; 1158 | + 1159 | + /* iterate through tracked inodes to locate users */ 1160 | + 1161 | + for (i = 0; i < fb_ns_info->inodes_len; i++) { 1162 | + inode = fb_ns_info->inodes[i].inode; 1163 | + pr_debug("tracked inode 0x%p fb_info 0x%p idx %d\n", 1164 | + inode, virt, virt->node); 1165 | + ret = do_fb_ns_remap(virt, inode->i_mapping); 1166 | + if (ret < 0 || !err) 1167 | + err = ret; 1168 | + } 1169 | + 1170 | + return err; 1171 | +} 1172 | + 1173 | +/* 1174 | + * Switch active (foreground) framebuffer: 1175 | + * - Swap mapping of framebuffer memory in switched namespaces. 1176 | + * - Save framebuffer memory in backing buffer of previous active namespace, 1177 | + * and restore from backing buffer of the newly active namespace. 1178 | + * - Copy (pending) state from the virtual fb_info to the underlying device 1179 | + * 1180 | + * Save/restore of the framebuffer memory may not be strictly necessary if 1181 | + * the new foreground completely redraws the screen; However, it may provide 1182 | + * a (visually) smoother transition if the redraw doesn't occur promptly. 1183 | + * 1184 | + * By default it is expected that after being switched to foreground mode, 1185 | + * userspace will redraw the screen. (see below why). 1186 | + */ 1187 | + 1188 | +static int do_fb_activate_ns(struct fb_info *virt, bool activate) 1189 | +{ 1190 | + int ret; 1191 | + 1192 | + pr_info("%sactivate fb_info 0x%p idx %d\n", 1193 | + activate ? "" : "de", virt, virt->node); 1194 | + 1195 | + ret = fb_ns_swap_mmap(virt, activate); 1196 | + if (ret < 0) 1197 | + return ret; 1198 | + 1199 | + fb_ns_swap_vmem(virt, activate); 1200 | + 1201 | + if (activate) { 1202 | + (void) fb_ns_apply_colreg(virt); 1203 | + (void) fb_ns_apply_set_par(virt); 1204 | + } 1205 | + 1206 | +#ifdef CONFIG_FB_DEV_NS_PAN 1207 | + /* 1208 | + * Force-update the display by calling fb_pan_display() directly. 1209 | + * 1210 | + * Usually, the plain memory copy in fb_ns_swap_vmem() suffices. 1211 | + * But if not (e.g., on x86-KVM emulator), this re-enforces the 1212 | + * restored display contents. 1213 | + * 1214 | + * (Caution: in the prehistory, this could cause occasional 1/2 1215 | + * second hiccups, if fb_pan_display() occured when another was 1216 | + * already in progress - because fb drivers usually don't handle 1217 | + * two irq callbacks and will timeout waiting for display vsync). 1218 | + */ 1219 | + if (activate) { 1220 | + struct fb_info *info = fb_virt_to_info(virt); 1221 | + /* 1222 | + * lock both virt an info: protect against both a change 1223 | + * to virt->var and a concurrent info->fb_pan_dipslay() 1224 | + */ 1225 | + (void) lock_fb_info(virt); /* can't fail */ 1226 | + (void) lock_fb_info(info); /* can't fail */ 1227 | + 1228 | + fb_pan_display(info, &virt->var); 1229 | + 1230 | + unlock_fb_info(info); 1231 | + unlock_fb_info(virt); 1232 | + } 1233 | +#endif 1234 | + 1235 | + return 0; 1236 | +} 1237 | + 1238 | +/* dev_ns and respective fb_dev_ns protected (by caller) */ 1239 | +static int fb_activate_ns(struct dev_namespace *dev_ns, bool activate) 1240 | +{ 1241 | + struct fb_dev_ns *fb_ns; 1242 | + struct fb_info *virt; 1243 | + int ret, err = 0; 1244 | + int i; 1245 | + 1246 | + mutex_lock(&fb_ns_mutex); 1247 | + 1248 | + pr_info(" |-> %sactivate devns 0x%p (%s)\n", 1249 | + activate ? "" : "de", dev_ns, dev_ns->tag); 1250 | + 1251 | + /* while in switch callback, @dev_ns and @fb_dev_ns are protectd */ 1252 | + fb_ns = find_fb_ns(dev_ns); 1253 | + WARN(fb_ns == NULL, "devns 0x%p: no matching fb_ns\n", dev_ns); 1254 | + 1255 | + for (i = 0; i < FB_MAX; i++) { 1256 | + virt = fb_ns->fb[i]; 1257 | + if (!virt) 1258 | + continue; 1259 | + ret = do_fb_activate_ns(virt, activate); 1260 | + if (ret < 0) 1261 | + err = ret; 1262 | + } 1263 | + 1264 | + mutex_unlock(&fb_ns_mutex); 1265 | + return err; 1266 | +} 1267 | + 1268 | +/* dev_ns and respective fb_dev_ns protected by caller */ 1269 | +static int fb_ns_switch_callback(struct notifier_block *self, 1270 | + unsigned long action, void *data) 1271 | +{ 1272 | + struct dev_namespace *dev_ns = data; 1273 | + int ret = 0; 1274 | + 1275 | + switch (action) { 1276 | + case DEV_NS_EVENT_ACTIVATE: 1277 | + pr_info("switch to devns 0x%p (%s)\n", dev_ns, dev_ns->tag); 1278 | + ret = fb_activate_ns(dev_ns, true); 1279 | + break; 1280 | + case DEV_NS_EVENT_DEACTIVATE: 1281 | + pr_info("switch from devns 0x%p (%s)\n", dev_ns, dev_ns->tag); 1282 | + ret = fb_activate_ns(dev_ns, false); 1283 | + break; 1284 | + } 1285 | + 1286 | + return ret; 1287 | +} 1288 | + 1289 | +static struct notifier_block fb_ns_switch_notifier = { 1290 | + .notifier_call = fb_ns_switch_callback, 1291 | +}; 1292 | + 1293 | +/* 1294 | + * Get notifications about framebuffer register/unregister, so that 1295 | + * we can properly deactivate a namespace-aware framebuffer. 1296 | + */ 1297 | +static int fb_ns_event_callback(struct notifier_block *self, 1298 | + unsigned long action, void *data) 1299 | +{ 1300 | + struct fb_event *event = data; 1301 | + struct fb_info *info; 1302 | + struct fb_info *virt; 1303 | + int ret = 0; 1304 | + 1305 | + if (!event || !event->info) 1306 | + return 0; 1307 | + 1308 | + info = event->info; 1309 | + 1310 | + pr_debug("FB_EVENT %lu fb_info 0x%p idx %d\n", 1311 | + action, info, info->node); 1312 | + fb_debug_info(info); 1313 | + 1314 | + switch (action) { 1315 | + case FB_EVENT_FB_REGISTERED: 1316 | + /* TODO: debug info */ 1317 | + break; 1318 | + case FB_EVENT_FB_UNREGISTERED: 1319 | + pr_debug("FB_UNREGISTERED 0x%p idx %d\n", info, info->node); 1320 | + fb_debug_info(info); 1321 | + 1322 | + /* TODO: can unregister come from "dead" FB? */ 1323 | + 1324 | + virt = get_fb_info_ns(info); 1325 | + if (fb_virt_is_active(virt)) 1326 | + (void) do_fb_activate_ns(virt, false); 1327 | + put_fb_info_ns(virt); 1328 | + break; 1329 | + } 1330 | + 1331 | + return ret; 1332 | +} 1333 | + 1334 | +static struct notifier_block fb_ns_event_notifier = { 1335 | + .notifier_call = fb_ns_event_callback, 1336 | +}; 1337 | + 1338 | +static int __init fb_init(void) 1339 | +{ 1340 | + int ret; 1341 | + 1342 | + ret = fb_register_client(&fb_ns_event_notifier); 1343 | + if (ret < 0) 1344 | + goto err; 1345 | + ret = DEV_NS_REGISTER(fb, "framebuffer"); 1346 | + if (ret < 0) 1347 | + goto err_fb; 1348 | + return 0; 1349 | + 1350 | + err_fb: 1351 | + fb_unregister_client(&fb_ns_event_notifier); 1352 | + err: 1353 | + return ret; 1354 | +} 1355 | + 1356 | +static void __exit fb_exit(void) 1357 | +{ 1358 | + DEV_NS_UNREGISTER(fb); 1359 | + fb_unregister_client(&fb_ns_event_notifier); 1360 | +} 1361 | + 1362 | +module_init(fb_init); 1363 | +module_exit(fb_exit); 1364 | + 1365 | diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c 1366 | index c6ce416..4e5a3a0 100644 1367 | --- a/drivers/video/fbmem.c 1368 | +++ b/drivers/video/fbmem.c 1369 | @@ -32,6 +32,7 @@ 1370 | #include 1371 | #include 1372 | #include 1373 | +#include 1374 | 1375 | #include 1376 | 1377 | @@ -730,9 +731,22 @@ static struct fb_info *file_fb_info(struct file *file) 1378 | struct inode *inode = file->f_path.dentry->d_inode; 1379 | int fbidx = iminor(inode); 1380 | struct fb_info *info = registered_fb[fbidx]; 1381 | + struct fb_info *virt = file->private_data; 1382 | 1383 | - if (info != file->private_data) 1384 | - info = NULL; 1385 | + if (info != fb_virt_to_info(virt)) 1386 | + virt = NULL; 1387 | + return virt; 1388 | +} 1389 | + 1390 | +static struct fb_info *file_fb_info_ns(struct file *file) 1391 | +{ 1392 | + struct fb_info *info; 1393 | + 1394 | + info = file_fb_info(file); 1395 | +#ifdef CONFIG_FB_DEV_NS 1396 | + if (info != NULL) 1397 | + info = fb_virt_to_info_ns(info); 1398 | +#endif 1399 | return info; 1400 | } 1401 | 1402 | @@ -740,7 +754,7 @@ static ssize_t 1403 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 1404 | { 1405 | unsigned long p = *ppos; 1406 | - struct fb_info *info = file_fb_info(file); 1407 | + struct fb_info *info = file_fb_info_ns(file); 1408 | u8 *buffer, *dst; 1409 | u8 __iomem *src; 1410 | int c, cnt = 0, err = 0; 1411 | @@ -805,7 +819,7 @@ static ssize_t 1412 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 1413 | { 1414 | unsigned long p = *ppos; 1415 | - struct fb_info *info = file_fb_info(file); 1416 | + struct fb_info *info = file_fb_info_ns(file); 1417 | u8 *buffer, *src; 1418 | u8 __iomem *dst; 1419 | int c, cnt = 0, err = 0; 1420 | @@ -1078,6 +1092,12 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, 1421 | void __user *argp = (void __user *)arg; 1422 | long ret = 0; 1423 | 1424 | +#ifdef CONFIG_FB_DEV_NS 1425 | + /* operate on virtual fbinfo, if not in active namespace */ 1426 | + struct fb_info *virt = info; 1427 | + info = fb_virt_to_info_ns(virt); 1428 | +#endif 1429 | + 1430 | switch (cmd) { 1431 | case FBIOGET_VSCREENINFO: 1432 | if (!lock_fb_info(info)) 1433 | @@ -1192,12 +1212,19 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, 1434 | ret = -ENOTTY; 1435 | unlock_fb_info(info); 1436 | } 1437 | + 1438 | +#ifdef CONFIG_FB_DEV_NS 1439 | + if (ret < 0) 1440 | + printk(KERN_ERR "fb_devns: IOCTL info 0x%p index %d ret %ld\n", 1441 | + info, info->node, ret); 1442 | +#endif 1443 | + 1444 | return ret; 1445 | } 1446 | 1447 | static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1448 | { 1449 | - struct fb_info *info = file_fb_info(file); 1450 | + struct fb_info *info = file_fb_info_ns(file); 1451 | 1452 | if (!info) 1453 | return -ENODEV; 1454 | @@ -1321,7 +1348,7 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, 1455 | static long fb_compat_ioctl(struct file *file, unsigned int cmd, 1456 | unsigned long arg) 1457 | { 1458 | - struct fb_info *info = file_fb_info(file); 1459 | + struct fb_info *info = file_fb_info_ns(file); 1460 | struct fb_ops *fb; 1461 | long ret = -ENOIOCTLCMD; 1462 | 1463 | @@ -1360,7 +1387,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, 1464 | static int 1465 | fb_mmap(struct file *file, struct vm_area_struct * vma) 1466 | { 1467 | - struct fb_info *info = file_fb_info(file); 1468 | + struct fb_info *info = file_fb_info_ns(file); 1469 | struct fb_ops *fb; 1470 | unsigned long off; 1471 | unsigned long start; 1472 | @@ -1418,8 +1445,13 @@ __releases(&info->lock) 1473 | { 1474 | int fbidx = iminor(inode); 1475 | struct fb_info *info; 1476 | + struct fb_info *virt; 1477 | int res = 0; 1478 | +#ifdef CONFIG_FB_DEV_NS 1479 | + bool tracked = false; 1480 | +#endif 1481 | 1482 | + virt = NULL; 1483 | info = get_fb_info(fbidx); 1484 | if (!info) { 1485 | request_module("fb%d", fbidx); 1486 | @@ -1436,6 +1468,27 @@ __releases(&info->lock) 1487 | goto out; 1488 | } 1489 | file->private_data = info; 1490 | + 1491 | +#ifdef CONFIG_FB_DEV_NS 1492 | + /* locate or dynamically allocate the per-namespace data */ 1493 | + virt = get_fb_info_ns(info); 1494 | + if (virt == NULL) { 1495 | + res = -ENOMEM; 1496 | + module_put(info->fbops->owner); 1497 | + goto out; 1498 | + } 1499 | + 1500 | + file->private_data = virt; /* if dev_ns, use virtual */ 1501 | + 1502 | + /* track who uses this inode, if remapping is needed later */ 1503 | + res = track_fb_inode(virt, inode); 1504 | + if (res) { 1505 | + module_put(info->fbops->owner); 1506 | + goto out; 1507 | + } 1508 | + tracked = true; 1509 | +#endif 1510 | + 1511 | if (info->fbops->fb_open) { 1512 | res = info->fbops->fb_open(info,1); 1513 | if (res) 1514 | @@ -1447,8 +1500,14 @@ __releases(&info->lock) 1515 | #endif 1516 | out: 1517 | mutex_unlock(&info->lock); 1518 | - if (res) 1519 | + if (res) { 1520 | +#ifdef CONFIG_FB_DEV_NS 1521 | + if (tracked) 1522 | + untrack_fb_inode(virt, inode); 1523 | + put_fb_info_ns(virt); 1524 | +#endif 1525 | put_fb_info(info); 1526 | + } 1527 | return res; 1528 | } 1529 | 1530 | @@ -1457,13 +1516,18 @@ fb_release(struct inode *inode, struct file *file) 1531 | __acquires(&info->lock) 1532 | __releases(&info->lock) 1533 | { 1534 | - struct fb_info * const info = file->private_data; 1535 | + struct fb_info * const virt = file->private_data; 1536 | + struct fb_info * const info = fb_virt_to_info(virt); 1537 | 1538 | mutex_lock(&info->lock); 1539 | if (info->fbops->fb_release) 1540 | info->fbops->fb_release(info,1); 1541 | module_put(info->fbops->owner); 1542 | mutex_unlock(&info->lock); 1543 | +#ifdef CONFIG_FB_DEV_NS 1544 | + untrack_fb_inode(virt, inode); 1545 | + put_fb_info_ns(virt); 1546 | +#endif 1547 | put_fb_info(info); 1548 | return 0; 1549 | } 1550 | diff --git a/include/linux/fb.h b/include/linux/fb.h 1551 | index d31cb68..e75c1ad 100644 1552 | --- a/include/linux/fb.h 1553 | +++ b/include/linux/fb.h 1554 | @@ -836,6 +836,9 @@ struct fb_tile_ops { 1555 | output like oopses */ 1556 | #define FBINFO_CAN_FORCE_OUTPUT 0x200000 1557 | 1558 | +/* corresponds to a namespace-aware (multiplexed) virtual fbinfo */ 1559 | +#define FBINFO_DEV_NS 0x400000 1560 | + 1561 | struct fb_info { 1562 | atomic_t count; 1563 | int node; 1564 | diff --git a/include/linux/fb_devns.h b/include/linux/fb_devns.h 1565 | new file mode 100644 1566 | index 0000000..56ca929 1567 | --- /dev/null 1568 | +++ b/include/linux/fb_devns.h 1569 | @@ -0,0 +1,43 @@ 1570 | +#ifndef _LINUX_FB_DEVNS_H 1571 | +#define _LINUX_FB_DEVNS_H 1572 | + 1573 | +/* 1574 | + * include/linux/fb_devns.h 1575 | + * 1576 | + * Copyright (c) 2011-2013 Cellrox Ltd. Certain portions are copyrighted by 1577 | + * Columbia University. This program is free software licensed under the GNU 1578 | + * General Public License Version 2 (GPL 2). You can distribute it and/or 1579 | + * modify it under the terms of the GPL 2. 1580 | + * 1581 | + * This program is distributed in the hope that it will be useful, but WITHOUT 1582 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1583 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL 2 license for more details. 1584 | + * The full GPL 2 License is included in this distribution in the file called 1585 | + * COPYING 1586 | + * 1587 | + * Cellrox can be contacted at oss@cellrox.com 1588 | + */ 1589 | + 1590 | +#ifdef __KERNEL__ 1591 | + 1592 | +#ifdef CONFIG_FB_DEV_NS 1593 | + 1594 | +extern struct fb_info *get_fb_info_ns(struct fb_info *fb_info); 1595 | +extern void put_fb_info_ns(struct fb_info *fb_info); 1596 | + 1597 | +extern struct fb_info *fb_virt_to_info(struct fb_info *fb_virt); 1598 | +extern struct fb_info *fb_virt_to_info_ns(struct fb_info *fb_virt); 1599 | + 1600 | +extern int track_fb_inode(struct fb_info *fb_info, struct inode *inode); 1601 | +extern void untrack_fb_inode(struct fb_info *fb_info, struct inode *inode); 1602 | + 1603 | +#else 1604 | + 1605 | +#define fb_virt_to_info(fb_info) fb_info 1606 | +#define fb_virt_to_info_ns(fb_info) fb_info 1607 | + 1608 | +#endif 1609 | + 1610 | +#endif /* __KERNEL__ */ 1611 | + 1612 | +#endif /* _LINUX_FB_DEVNS_H */ 1613 | --------------------------------------------------------------------------------