forked from Minki/linux
Merge branch 'for-rmk/perf' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into devel-stable
This commit is contained in:
commit
901e7e34f8
@ -51,6 +51,8 @@ config ARM
|
|||||||
select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND
|
select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND
|
||||||
select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
|
select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
|
||||||
select HAVE_PERF_EVENTS
|
select HAVE_PERF_EVENTS
|
||||||
|
select HAVE_PERF_REGS
|
||||||
|
select HAVE_PERF_USER_STACK_DUMP
|
||||||
select HAVE_REGS_AND_STACK_ACCESS_API
|
select HAVE_REGS_AND_STACK_ACCESS_API
|
||||||
select HAVE_SYSCALL_TRACEPOINTS
|
select HAVE_SYSCALL_TRACEPOINTS
|
||||||
select HAVE_UID16
|
select HAVE_UID16
|
||||||
|
@ -7,6 +7,7 @@ header-y += hwcap.h
|
|||||||
header-y += ioctls.h
|
header-y += ioctls.h
|
||||||
header-y += kvm_para.h
|
header-y += kvm_para.h
|
||||||
header-y += mman.h
|
header-y += mman.h
|
||||||
|
header-y += perf_regs.h
|
||||||
header-y += posix_types.h
|
header-y += posix_types.h
|
||||||
header-y += ptrace.h
|
header-y += ptrace.h
|
||||||
header-y += setup.h
|
header-y += setup.h
|
||||||
|
23
arch/arm/include/uapi/asm/perf_regs.h
Normal file
23
arch/arm/include/uapi/asm/perf_regs.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef _ASM_ARM_PERF_REGS_H
|
||||||
|
#define _ASM_ARM_PERF_REGS_H
|
||||||
|
|
||||||
|
enum perf_event_arm_regs {
|
||||||
|
PERF_REG_ARM_R0,
|
||||||
|
PERF_REG_ARM_R1,
|
||||||
|
PERF_REG_ARM_R2,
|
||||||
|
PERF_REG_ARM_R3,
|
||||||
|
PERF_REG_ARM_R4,
|
||||||
|
PERF_REG_ARM_R5,
|
||||||
|
PERF_REG_ARM_R6,
|
||||||
|
PERF_REG_ARM_R7,
|
||||||
|
PERF_REG_ARM_R8,
|
||||||
|
PERF_REG_ARM_R9,
|
||||||
|
PERF_REG_ARM_R10,
|
||||||
|
PERF_REG_ARM_FP,
|
||||||
|
PERF_REG_ARM_IP,
|
||||||
|
PERF_REG_ARM_SP,
|
||||||
|
PERF_REG_ARM_LR,
|
||||||
|
PERF_REG_ARM_PC,
|
||||||
|
PERF_REG_ARM_MAX,
|
||||||
|
};
|
||||||
|
#endif /* _ASM_ARM_PERF_REGS_H */
|
@ -78,6 +78,7 @@ obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
|
|||||||
obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
|
obj-$(CONFIG_CPU_MOHAWK) += xscale-cp0.o
|
||||||
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
|
obj-$(CONFIG_CPU_PJ4) += pj4-cp0.o
|
||||||
obj-$(CONFIG_IWMMXT) += iwmmxt.o
|
obj-$(CONFIG_IWMMXT) += iwmmxt.o
|
||||||
|
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
|
||||||
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
|
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_event_cpu.o
|
||||||
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
||||||
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
||||||
|
@ -256,12 +256,11 @@ validate_event(struct pmu_hw_events *hw_events,
|
|||||||
struct perf_event *event)
|
struct perf_event *event)
|
||||||
{
|
{
|
||||||
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
|
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
|
||||||
struct pmu *leader_pmu = event->group_leader->pmu;
|
|
||||||
|
|
||||||
if (is_software_event(event))
|
if (is_software_event(event))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF)
|
if (event->state < PERF_EVENT_STATE_OFF)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
|
if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec)
|
||||||
|
30
arch/arm/kernel/perf_regs.c
Normal file
30
arch/arm/kernel/perf_regs.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
|
#include <asm/perf_regs.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
|
|
||||||
|
u64 perf_reg_value(struct pt_regs *regs, int idx)
|
||||||
|
{
|
||||||
|
if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM_MAX))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return regs->uregs[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REG_RESERVED (~((1ULL << PERF_REG_ARM_MAX) - 1))
|
||||||
|
|
||||||
|
int perf_reg_validate(u64 mask)
|
||||||
|
{
|
||||||
|
if (!mask || mask & REG_RESERVED)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 perf_reg_abi(struct task_struct *task)
|
||||||
|
{
|
||||||
|
return PERF_SAMPLE_REGS_ABI_32;
|
||||||
|
}
|
@ -2,3 +2,6 @@ ifndef NO_DWARF
|
|||||||
PERF_HAVE_DWARF_REGS := 1
|
PERF_HAVE_DWARF_REGS := 1
|
||||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||||
endif
|
endif
|
||||||
|
ifndef NO_LIBUNWIND
|
||||||
|
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
|
||||||
|
endif
|
||||||
|
54
tools/perf/arch/arm/include/perf_regs.h
Normal file
54
tools/perf/arch/arm/include/perf_regs.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef ARCH_PERF_REGS_H
|
||||||
|
#define ARCH_PERF_REGS_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "../../util/types.h"
|
||||||
|
#include <asm/perf_regs.h>
|
||||||
|
|
||||||
|
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1)
|
||||||
|
#define PERF_REG_IP PERF_REG_ARM_PC
|
||||||
|
#define PERF_REG_SP PERF_REG_ARM_SP
|
||||||
|
|
||||||
|
static inline const char *perf_reg_name(int id)
|
||||||
|
{
|
||||||
|
switch (id) {
|
||||||
|
case PERF_REG_ARM_R0:
|
||||||
|
return "r0";
|
||||||
|
case PERF_REG_ARM_R1:
|
||||||
|
return "r1";
|
||||||
|
case PERF_REG_ARM_R2:
|
||||||
|
return "r2";
|
||||||
|
case PERF_REG_ARM_R3:
|
||||||
|
return "r3";
|
||||||
|
case PERF_REG_ARM_R4:
|
||||||
|
return "r4";
|
||||||
|
case PERF_REG_ARM_R5:
|
||||||
|
return "r5";
|
||||||
|
case PERF_REG_ARM_R6:
|
||||||
|
return "r6";
|
||||||
|
case PERF_REG_ARM_R7:
|
||||||
|
return "r7";
|
||||||
|
case PERF_REG_ARM_R8:
|
||||||
|
return "r8";
|
||||||
|
case PERF_REG_ARM_R9:
|
||||||
|
return "r9";
|
||||||
|
case PERF_REG_ARM_R10:
|
||||||
|
return "r10";
|
||||||
|
case PERF_REG_ARM_FP:
|
||||||
|
return "fp";
|
||||||
|
case PERF_REG_ARM_IP:
|
||||||
|
return "ip";
|
||||||
|
case PERF_REG_ARM_SP:
|
||||||
|
return "sp";
|
||||||
|
case PERF_REG_ARM_LR:
|
||||||
|
return "lr";
|
||||||
|
case PERF_REG_ARM_PC:
|
||||||
|
return "pc";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ARCH_PERF_REGS_H */
|
48
tools/perf/arch/arm/util/unwind.c
Normal file
48
tools/perf/arch/arm/util/unwind.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include "perf_regs.h"
|
||||||
|
#include "../../util/unwind.h"
|
||||||
|
|
||||||
|
int unwind__arch_reg_id(int regnum)
|
||||||
|
{
|
||||||
|
switch (regnum) {
|
||||||
|
case UNW_ARM_R0:
|
||||||
|
return PERF_REG_ARM_R0;
|
||||||
|
case UNW_ARM_R1:
|
||||||
|
return PERF_REG_ARM_R1;
|
||||||
|
case UNW_ARM_R2:
|
||||||
|
return PERF_REG_ARM_R2;
|
||||||
|
case UNW_ARM_R3:
|
||||||
|
return PERF_REG_ARM_R3;
|
||||||
|
case UNW_ARM_R4:
|
||||||
|
return PERF_REG_ARM_R4;
|
||||||
|
case UNW_ARM_R5:
|
||||||
|
return PERF_REG_ARM_R5;
|
||||||
|
case UNW_ARM_R6:
|
||||||
|
return PERF_REG_ARM_R6;
|
||||||
|
case UNW_ARM_R7:
|
||||||
|
return PERF_REG_ARM_R7;
|
||||||
|
case UNW_ARM_R8:
|
||||||
|
return PERF_REG_ARM_R8;
|
||||||
|
case UNW_ARM_R9:
|
||||||
|
return PERF_REG_ARM_R9;
|
||||||
|
case UNW_ARM_R10:
|
||||||
|
return PERF_REG_ARM_R10;
|
||||||
|
case UNW_ARM_R11:
|
||||||
|
return PERF_REG_ARM_FP;
|
||||||
|
case UNW_ARM_R12:
|
||||||
|
return PERF_REG_ARM_IP;
|
||||||
|
case UNW_ARM_R13:
|
||||||
|
return PERF_REG_ARM_SP;
|
||||||
|
case UNW_ARM_R14:
|
||||||
|
return PERF_REG_ARM_LR;
|
||||||
|
case UNW_ARM_R15:
|
||||||
|
return PERF_REG_ARM_PC;
|
||||||
|
default:
|
||||||
|
pr_err("unwind: invalid reg id %d\n", regnum);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
@ -29,6 +29,10 @@ ifeq ($(ARCH),x86_64)
|
|||||||
NO_PERF_REGS := 0
|
NO_PERF_REGS := 0
|
||||||
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
|
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),arm)
|
||||||
|
NO_PERF_REGS := 0
|
||||||
|
LIBUNWIND_LIBS = -lunwind -lunwind-arm
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(NO_PERF_REGS),0)
|
ifeq ($(NO_PERF_REGS),0)
|
||||||
CFLAGS += -DHAVE_PERF_REGS
|
CFLAGS += -DHAVE_PERF_REGS
|
||||||
@ -208,8 +212,7 @@ ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y)
|
|||||||
endif # try-cc
|
endif # try-cc
|
||||||
endif # NO_LIBELF
|
endif # NO_LIBELF
|
||||||
|
|
||||||
# There's only x86 (both 32 and 64) support for CFI unwind so far
|
ifeq ($(LIBUNWIND_LIBS),)
|
||||||
ifneq ($(ARCH),x86)
|
|
||||||
NO_LIBUNWIND := 1
|
NO_LIBUNWIND := 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -223,9 +226,13 @@ endif
|
|||||||
|
|
||||||
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
|
FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
|
||||||
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y)
|
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y)
|
||||||
msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99);
|
msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 1.1);
|
||||||
NO_LIBUNWIND := 1
|
NO_LIBUNWIND := 1
|
||||||
endif # Libunwind support
|
endif # Libunwind support
|
||||||
|
ifneq ($(call try-cc,$(SOURCE_LIBUNWIND_DEBUG_FRAME),$(FLAGS_UNWIND),libunwind debug_frame),y)
|
||||||
|
msg := $(warning No debug_frame support found in libunwind);
|
||||||
|
CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
|
||||||
|
endif # debug_frame support in libunwind
|
||||||
endif # NO_LIBUNWIND
|
endif # NO_LIBUNWIND
|
||||||
|
|
||||||
ifndef NO_LIBUNWIND
|
ifndef NO_LIBUNWIND
|
||||||
|
@ -185,7 +185,6 @@ extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
|
|||||||
unw_proc_info_t *pi,
|
unw_proc_info_t *pi,
|
||||||
int need_unwind_info, void *arg);
|
int need_unwind_info, void *arg);
|
||||||
|
|
||||||
|
|
||||||
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
@ -197,6 +196,26 @@ int main(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
define SOURCE_LIBUNWIND_DEBUG_FRAME
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern int
|
||||||
|
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
|
||||||
|
unw_word_t ip, unw_word_t segbase,
|
||||||
|
const char *obj_name, unw_word_t start,
|
||||||
|
unw_word_t end);
|
||||||
|
|
||||||
|
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
endef
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef NO_BACKTRACE
|
ifndef NO_BACKTRACE
|
||||||
|
@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
|
|||||||
|
|
||||||
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
||||||
|
|
||||||
|
extern int
|
||||||
|
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
|
||||||
|
unw_word_t ip,
|
||||||
|
unw_word_t segbase,
|
||||||
|
const char *obj_name, unw_word_t start,
|
||||||
|
unw_word_t end);
|
||||||
|
|
||||||
|
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
|
||||||
|
|
||||||
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
|
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
|
||||||
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
|
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
|
||||||
|
|
||||||
@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
|
||||||
u64 *table_data, u64 *segbase, u64 *fde_count)
|
u64 *table_data, u64 *segbase,
|
||||||
|
u64 *fde_count)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL, fd;
|
int ret = -EINVAL, fd;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check the .eh_frame section for unwinding info */
|
||||||
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
|||||||
table_data, segbase,
|
table_data, segbase,
|
||||||
fde_count);
|
fde_count);
|
||||||
|
|
||||||
/* TODO .debug_frame check if eh_frame_hdr fails */
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
||||||
|
static int read_unwind_spec_debug_frame(struct dso *dso,
|
||||||
|
struct machine *machine, u64 *offset)
|
||||||
|
{
|
||||||
|
int fd = dso__data_fd(dso, machine);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check the .debug_frame section for unwinding info */
|
||||||
|
*offset = elf_section_offset(fd, ".debug_frame");
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (*offset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
||||||
{
|
{
|
||||||
struct addr_location al;
|
struct addr_location al;
|
||||||
@ -291,10 +321,9 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|||||||
|
|
||||||
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
|
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
|
||||||
|
|
||||||
if (read_unwind_spec(map->dso, ui->machine,
|
/* Check the .eh_frame section for unwinding info */
|
||||||
&table_data, &segbase, &fde_count))
|
if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
|
||||||
return -EINVAL;
|
&table_data, &segbase, &fde_count)) {
|
||||||
|
|
||||||
memset(&di, 0, sizeof(di));
|
memset(&di, 0, sizeof(di));
|
||||||
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
||||||
di.start_ip = map->start;
|
di.start_ip = map->start;
|
||||||
@ -305,6 +334,20 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|||||||
/ sizeof(unw_word_t);
|
/ sizeof(unw_word_t);
|
||||||
return dwarf_search_unwind_table(as, ip, &di, pi,
|
return dwarf_search_unwind_table(as, ip, &di, pi,
|
||||||
need_unwind_info, arg);
|
need_unwind_info, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
||||||
|
/* Check the .debug_frame section for unwinding info */
|
||||||
|
if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
|
||||||
|
memset(&di, 0, sizeof(di));
|
||||||
|
dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name,
|
||||||
|
map->start, map->end);
|
||||||
|
return dwarf_search_unwind_table(as, ip, &di, pi,
|
||||||
|
need_unwind_info, arg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int access_fpreg(unw_addr_space_t __maybe_unused as,
|
static int access_fpreg(unw_addr_space_t __maybe_unused as,
|
||||||
|
Loading…
Reference in New Issue
Block a user