Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/ioperm: Share I/O bitmap if identical

The I/O bitmap is duplicated on fork. That's wasting memory and slows down
fork. There is no point to do so. As long as the bitmap is not modified it
can be shared between threads and processes.

Add a refcount and just share it on fork. If a task modifies the bitmap
then it has to do the duplication if and only if it is shared.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Andy Lutomirski <luto@kernel.org>

+50 -42
+5
arch/x86/include/asm/io_bitmap.h
··· 2 2 #ifndef _ASM_X86_IOBITMAP_H 3 3 #define _ASM_X86_IOBITMAP_H 4 4 5 + #include <linux/refcount.h> 5 6 #include <asm/processor.h> 6 7 7 8 struct io_bitmap { 8 9 u64 sequence; 10 + refcount_t refcnt; 9 11 /* The maximum number of bytes to copy so all zero bits are covered */ 10 12 unsigned int max; 11 13 unsigned long bitmap[IO_BITMAP_LONGS]; 12 14 }; 13 15 16 + struct task_struct; 17 + 18 + void io_bitmap_share(struct task_struct *tsk); 14 19 void io_bitmap_exit(void); 15 20 16 21 void tss_update_io_bitmap(void);
+39 -9
arch/x86/kernel/ioport.c
··· 16 16 17 17 static atomic64_t io_bitmap_sequence; 18 18 19 + void io_bitmap_share(struct task_struct *tsk) 20 + { 21 + /* 22 + * Take a refcount on current's bitmap. It can be used by 23 + * both tasks as long as none of them changes the bitmap. 24 + */ 25 + refcount_inc(&current->thread.io_bitmap->refcnt); 26 + tsk->thread.io_bitmap = current->thread.io_bitmap; 27 + set_tsk_thread_flag(tsk, TIF_IO_BITMAP); 28 + } 29 + 19 30 void io_bitmap_exit(void) 20 31 { 21 32 struct io_bitmap *iobm = current->thread.io_bitmap; ··· 36 25 preempt_disable(); 37 26 tss_update_io_bitmap(); 38 27 preempt_enable(); 39 - kfree(iobm); 28 + if (iobm && refcount_dec_and_test(&iobm->refcnt)) 29 + kfree(iobm); 40 30 } 41 31 42 32 /* ··· 70 58 return -ENOMEM; 71 59 72 60 memset(iobm->bitmap, 0xff, sizeof(iobm->bitmap)); 61 + refcount_set(&iobm->refcnt, 1); 73 62 } 63 + 64 + /* 65 + * If the bitmap is not shared, then nothing can take a refcount as 66 + * current can obviously not fork at the same time. If it's shared 67 + * duplicate it and drop the refcount on the original one. 68 + */ 69 + if (refcount_read(&iobm->refcnt) > 1) { 70 + iobm = kmemdup(iobm, sizeof(*iobm), GFP_KERNEL); 71 + if (!iobm) 72 + return -ENOMEM; 73 + refcount_set(&iobm->refcnt, 1); 74 + io_bitmap_exit(); 75 + } 76 + 77 + /* 78 + * Store the bitmap pointer (might be the same if the task already 79 + * head one). Must be done here so freeing the bitmap when all 80 + * permissions are dropped has the pointer set up. 81 + */ 82 + t->io_bitmap = iobm; 83 + /* Mark it active for context switching and exit to user mode */ 84 + set_thread_flag(TIF_IO_BITMAP); 74 85 75 86 /* 76 87 * Update the tasks bitmap. The update of the TSS bitmap happens on ··· 121 86 122 87 iobm->max = (max_long + 1) * sizeof(unsigned long); 123 88 124 - /* Update the sequence number to force an update in switch_to() */ 125 - iobm->sequence = atomic64_add_return(1, &io_bitmap_sequence); 126 - 127 89 /* 128 - * Store the bitmap pointer (might be the same if the task already 129 - * head one). Set the TIF flag, just in case this is the first 130 - * invocation. 90 + * Update the sequence number to force a TSS update on return to 91 + * user mode. 131 92 */ 132 - t->io_bitmap = iobm; 133 - set_thread_flag(TIF_IO_BITMAP); 93 + iobm->sequence = atomic64_add_return(1, &io_bitmap_sequence); 134 94 135 95 return 0; 136 96 }
+6 -33
arch/x86/kernel/process.c
··· 122 122 return do_set_thread_area_64(p, ARCH_SET_FS, tls); 123 123 } 124 124 125 - static inline int copy_io_bitmap(struct task_struct *tsk) 126 - { 127 - struct io_bitmap *iobm = current->thread.io_bitmap; 128 - 129 - if (likely(!test_tsk_thread_flag(current, TIF_IO_BITMAP))) 130 - return 0; 131 - 132 - tsk->thread.io_bitmap = kmemdup(iobm, sizeof(*iobm), GFP_KERNEL); 133 - 134 - if (!tsk->thread.io_bitmap) 135 - return -ENOMEM; 136 - 137 - set_tsk_thread_flag(tsk, TIF_IO_BITMAP); 138 - return 0; 139 - } 140 - 141 - static inline void free_io_bitmap(struct task_struct *tsk) 142 - { 143 - if (tsk->thread.io_bitmap) { 144 - kfree(tsk->thread.io_bitmap); 145 - tsk->thread.io_bitmap = NULL; 146 - } 147 - } 148 - 149 125 int copy_thread_tls(unsigned long clone_flags, unsigned long sp, 150 126 unsigned long arg, struct task_struct *p, unsigned long tls) 151 127 { 152 128 struct inactive_task_frame *frame; 153 129 struct fork_frame *fork_frame; 154 130 struct pt_regs *childregs; 155 - int ret; 131 + int ret = 0; 156 132 157 133 childregs = task_pt_regs(p); 158 134 fork_frame = container_of(childregs, struct fork_frame, regs); ··· 175 199 task_user_gs(p) = get_user_gs(current_pt_regs()); 176 200 #endif 177 201 178 - ret = copy_io_bitmap(p); 179 - if (ret) 180 - return ret; 181 - 182 202 /* Set a new TLS for the child thread? */ 183 - if (clone_flags & CLONE_SETTLS) { 203 + if (clone_flags & CLONE_SETTLS) 184 204 ret = set_new_tls(p, tls); 185 - if (ret) 186 - free_io_bitmap(p); 187 - } 205 + 206 + if (!ret && unlikely(test_tsk_thread_flag(current, TIF_IO_BITMAP))) 207 + io_bitmap_share(p); 208 + 188 209 return ret; 189 210 } 190 211