uprobes: _register() should always do register_for_each_vma(true)
To support the filtering uprobe_register() should do register_for_each_vma(true) every time the new consumer comes, we need to install the previously nacked breakpoints. Note: - uprobes_mutex[] should die, what it actually protects is alloc_uprobe(). - UPROBE_RUN_HANDLER should die too, obviously it can't work unless uprobe has a single consumer. The consumer should serialize with _register/_unregister itself. Or this flag should live in uprobe_consumer->state. - Perhaps we can do some optimizations later. For example, if filter_chain() never returns false uprobe can record this fact and avoid the unnecessary register_for_each_vma(). Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
This commit is contained in:
parent
04aab9b200
commit
9a98e03cc1
1 changed files with 13 additions and 18 deletions
|
@ -482,16 +482,12 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
|
||||||
up_read(&uprobe->consumer_rwsem);
|
up_read(&uprobe->consumer_rwsem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the previous consumer */
|
static void consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
||||||
static struct uprobe_consumer *
|
|
||||||
consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
|
||||||
{
|
{
|
||||||
down_write(&uprobe->consumer_rwsem);
|
down_write(&uprobe->consumer_rwsem);
|
||||||
uc->next = uprobe->consumers;
|
uc->next = uprobe->consumers;
|
||||||
uprobe->consumers = uc;
|
uprobe->consumers = uc;
|
||||||
up_write(&uprobe->consumer_rwsem);
|
up_write(&uprobe->consumer_rwsem);
|
||||||
|
|
||||||
return uc->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -820,9 +816,15 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __uprobe_register(struct uprobe *uprobe)
|
static int __uprobe_register(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
||||||
{
|
{
|
||||||
return register_for_each_vma(uprobe, true);
|
int err;
|
||||||
|
|
||||||
|
consumer_add(uprobe, uc);
|
||||||
|
err = register_for_each_vma(uprobe, true);
|
||||||
|
if (!err) /* TODO: pointless unless the first consumer */
|
||||||
|
set_bit(UPROBE_RUN_HANDLER, &uprobe->flags);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
||||||
|
@ -867,21 +869,14 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *
|
||||||
if (offset > i_size_read(inode))
|
if (offset > i_size_read(inode))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = 0;
|
ret = -ENOMEM;
|
||||||
mutex_lock(uprobes_hash(inode));
|
mutex_lock(uprobes_hash(inode));
|
||||||
uprobe = alloc_uprobe(inode, offset);
|
uprobe = alloc_uprobe(inode, offset);
|
||||||
|
if (uprobe) {
|
||||||
if (!uprobe) {
|
ret = __uprobe_register(uprobe, uc);
|
||||||
ret = -ENOMEM;
|
if (ret)
|
||||||
} else if (!consumer_add(uprobe, uc)) {
|
|
||||||
ret = __uprobe_register(uprobe);
|
|
||||||
if (ret) {
|
|
||||||
__uprobe_unregister(uprobe, uc);
|
__uprobe_unregister(uprobe, uc);
|
||||||
} else {
|
|
||||||
set_bit(UPROBE_RUN_HANDLER, &uprobe->flags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(uprobes_hash(inode));
|
mutex_unlock(uprobes_hash(inode));
|
||||||
if (uprobe)
|
if (uprobe)
|
||||||
put_uprobe(uprobe);
|
put_uprobe(uprobe);
|
||||||
|
|
Loading…
Add table
Reference in a new issue