Skip to content

Commit 4804e38

Browse files
committed
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 <[email protected]> Acked-by: Andy Lutomirski <[email protected]>
1 parent ea5f1cd commit 4804e38

File tree

3 files changed

+50
-42
lines changed

3 files changed

+50
-42
lines changed

arch/x86/include/asm/io_bitmap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@
22
#ifndef _ASM_X86_IOBITMAP_H
33
#define _ASM_X86_IOBITMAP_H
44

5+
#include <linux/refcount.h>
56
#include <asm/processor.h>
67

78
struct io_bitmap {
89
u64 sequence;
10+
refcount_t refcnt;
911
/* The maximum number of bytes to copy so all zero bits are covered */
1012
unsigned int max;
1113
unsigned long bitmap[IO_BITMAP_LONGS];
1214
};
1315

16+
struct task_struct;
17+
18+
void io_bitmap_share(struct task_struct *tsk);
1419
void io_bitmap_exit(void);
1520

1621
void tss_update_io_bitmap(void);

arch/x86/kernel/ioport.c

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616

1717
static atomic64_t io_bitmap_sequence;
1818

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+
1930
void io_bitmap_exit(void)
2031
{
2132
struct io_bitmap *iobm = current->thread.io_bitmap;
@@ -25,7 +36,8 @@ void io_bitmap_exit(void)
2536
preempt_disable();
2637
tss_update_io_bitmap();
2738
preempt_enable();
28-
kfree(iobm);
39+
if (iobm && refcount_dec_and_test(&iobm->refcnt))
40+
kfree(iobm);
2941
}
3042

3143
/*
@@ -58,8 +70,31 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
5870
return -ENOMEM;
5971

6072
memset(iobm->bitmap, 0xff, sizeof(iobm->bitmap));
73+
refcount_set(&iobm->refcnt, 1);
74+
}
75+
76+
/*
77+
* If the bitmap is not shared, then nothing can take a refcount as
78+
* current can obviously not fork at the same time. If it's shared
79+
* duplicate it and drop the refcount on the original one.
80+
*/
81+
if (refcount_read(&iobm->refcnt) > 1) {
82+
iobm = kmemdup(iobm, sizeof(*iobm), GFP_KERNEL);
83+
if (!iobm)
84+
return -ENOMEM;
85+
refcount_set(&iobm->refcnt, 1);
86+
io_bitmap_exit();
6187
}
6288

89+
/*
90+
* Store the bitmap pointer (might be the same if the task already
91+
* head one). Must be done here so freeing the bitmap when all
92+
* permissions are dropped has the pointer set up.
93+
*/
94+
t->io_bitmap = iobm;
95+
/* Mark it active for context switching and exit to user mode */
96+
set_thread_flag(TIF_IO_BITMAP);
97+
6398
/*
6499
* Update the tasks bitmap. The update of the TSS bitmap happens on
65100
* exit to user mode. So this needs no protection.
@@ -86,16 +121,11 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
86121

87122
iobm->max = (max_long + 1) * sizeof(unsigned long);
88123

89-
/* Update the sequence number to force an update in switch_to() */
90-
iobm->sequence = atomic64_add_return(1, &io_bitmap_sequence);
91-
92124
/*
93-
* Store the bitmap pointer (might be the same if the task already
94-
* head one). Set the TIF flag, just in case this is the first
95-
* invocation.
125+
* Update the sequence number to force a TSS update on return to
126+
* user mode.
96127
*/
97-
t->io_bitmap = iobm;
98-
set_thread_flag(TIF_IO_BITMAP);
128+
iobm->sequence = atomic64_add_return(1, &io_bitmap_sequence);
99129

100130
return 0;
101131
}

arch/x86/kernel/process.c

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -122,37 +122,13 @@ static int set_new_tls(struct task_struct *p, unsigned long tls)
122122
return do_set_thread_area_64(p, ARCH_SET_FS, tls);
123123
}
124124

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-
149125
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
150126
unsigned long arg, struct task_struct *p, unsigned long tls)
151127
{
152128
struct inactive_task_frame *frame;
153129
struct fork_frame *fork_frame;
154130
struct pt_regs *childregs;
155-
int ret;
131+
int ret = 0;
156132

157133
childregs = task_pt_regs(p);
158134
fork_frame = container_of(childregs, struct fork_frame, regs);
@@ -199,16 +175,13 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
199175
task_user_gs(p) = get_user_gs(current_pt_regs());
200176
#endif
201177

202-
ret = copy_io_bitmap(p);
203-
if (ret)
204-
return ret;
205-
206178
/* Set a new TLS for the child thread? */
207-
if (clone_flags & CLONE_SETTLS) {
179+
if (clone_flags & CLONE_SETTLS)
208180
ret = set_new_tls(p, tls);
209-
if (ret)
210-
free_io_bitmap(p);
211-
}
181+
182+
if (!ret && unlikely(test_tsk_thread_flag(current, TIF_IO_BITMAP)))
183+
io_bitmap_share(p);
184+
212185
return ret;
213186
}
214187

0 commit comments

Comments
 (0)