mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
ramdump: add ramdump support for kernel [4/6]
PD#165764: add ramdump support 1. Add Makefile and driver of ramdump; 2. Flush all cache before panic reboot; 3. Change panic reboot reason when ramdump is disabled; 4. Add dts support/open watch dog for chips. Change-Id: Ieeb418f038bfda119c3156a7f8f8b05c8bc58ad2 Signed-off-by: tao zeng <tao.zeng@amlogic.com>
This commit is contained in:
@@ -13505,6 +13505,7 @@ F: drivers/amlogic/cpufreq/*
|
||||
AMLOGIC driver for memory extend
|
||||
M: Tao Zeng <tao.zeng@amlogic.com>
|
||||
F: drivers/amlogic/memory_ext/*
|
||||
F: include/linux/amlogic/ramdump.h
|
||||
|
||||
AMLOGIC driver for memory extend
|
||||
M: Tao Zeng <tao.zeng@amlogic.com>
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
|
||||
wdt_ee: watchdog@0xffd0f0d0 {
|
||||
compatible = "amlogic, meson-wdt";
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
default_timeout=<10>;
|
||||
reset_watchdog_method=<1>; /* 0:sysfs,1:kernel */
|
||||
reset_watchdog_time=<2>;
|
||||
@@ -271,6 +271,11 @@
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
|
||||
ram-dump {
|
||||
compatible = "amlogic, ram_dump";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pinctrl_aobus: pinctrl@ff800014{
|
||||
compatible = "amlogic,meson-axg-aobus-pinctrl";
|
||||
#address-cells = <2>;
|
||||
|
||||
@@ -425,6 +425,11 @@
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
|
||||
ram-dump {
|
||||
compatible = "amlogic, ram_dump";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
jtag {
|
||||
compatible = "amlogic, jtag";
|
||||
status = "okay";
|
||||
@@ -439,7 +444,6 @@
|
||||
&gpio GPIOC_5 0>;
|
||||
};
|
||||
|
||||
|
||||
saradc:saradc {
|
||||
compatible = "amlogic,meson-g12a-saradc";
|
||||
status = "okay";
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
|
||||
watchdog {
|
||||
compatible = "amlogic, meson-wdt";
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
default_timeout=<10>;
|
||||
reset_watchdog_method=<1>; /* 0:sysfs,1:kernel */
|
||||
reset_watchdog_time=<2>;
|
||||
@@ -232,6 +232,11 @@
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
|
||||
ram-dump {
|
||||
compatible = "amlogic, ram_dump";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
jtag {
|
||||
compatible = "amlogic, jtag";
|
||||
status = "okay";
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
|
||||
watchdog {
|
||||
compatible = "amlogic, meson-wdt";
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
default_timeout=<10>;
|
||||
reset_watchdog_method=<1>; /* 0:sysfs,1:kernel */
|
||||
reset_watchdog_time=<2>;
|
||||
@@ -255,6 +255,11 @@
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
|
||||
ram-dump {
|
||||
compatible = "amlogic, ram_dump";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
jtag {
|
||||
compatible = "amlogic, jtag";
|
||||
status = "disabled";
|
||||
|
||||
@@ -258,7 +258,7 @@
|
||||
|
||||
wdt_ee: watchdog@0xffd0f0d0 {
|
||||
compatible = "amlogic, meson-wdt";
|
||||
status = "disabled";
|
||||
status = "watch";
|
||||
default_timeout=<10>;
|
||||
reset_watchdog_method=<1>; /* 0:sysfs,1:kernel */
|
||||
reset_watchdog_time=<2>;
|
||||
@@ -270,6 +270,11 @@
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
|
||||
ram-dump {
|
||||
compatible = "amlogic, ram_dump";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
amlogic-jtag {
|
||||
compatible = "amlogic, jtag";
|
||||
status = "okay";
|
||||
|
||||
@@ -344,6 +344,7 @@ CONFIG_AMLOGIC_SARADC=y
|
||||
CONFIG_AMLOGIC_DDR_TOOL=y
|
||||
CONFIG_AMLOGIC_DDR_BANDWIDTH=y
|
||||
CONFIG_AMLOGIC_TEE=y
|
||||
CONFIG_AMLOGIC_RAMDUMP=y
|
||||
CONFIG_AMLOGIC_GPIO_IRQ=y
|
||||
CONFIG_AMLOGIC_ATV_DEMOD=y
|
||||
CONFIG_AMLOGIC_DEBUG=y
|
||||
|
||||
@@ -19,6 +19,17 @@ config AMLOGIC_PAGE_TRACE
|
||||
with allocate page count information of each caller functions from
|
||||
/proc/pagetrace
|
||||
|
||||
config AMLOGIC_RAMDUMP
|
||||
bool "Amlogic RAM DUMP support"
|
||||
depends on AMLOGIC_MEMORY_EXTEND
|
||||
depends on PANIC_TIMEOUT != 0
|
||||
default n
|
||||
help
|
||||
RAM dump support for amlogic platform, when system is rebooted
|
||||
after panic/watch dog reboot, you may need this option to save
|
||||
whole ram to /data/ partition. Which can be used for analyze
|
||||
crash reason.
|
||||
|
||||
config AMLOGIC_CMA
|
||||
bool "Amlogic CMA change"
|
||||
depends on AMLOGIC_MEMORY_EXTEND
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
obj-$(CONFIG_AMLOGIC_PAGE_TRACE) += page_trace.o
|
||||
obj-$(CONFIG_AMLOGIC_CMA) += aml_cma.o
|
||||
obj-$(CONFIG_AMLOGIC_SLUB_DEBUG) += aml_slub_debug.o
|
||||
obj-$(CONFIG_AMLOGIC_RAMDUMP) += ram_dump.o
|
||||
|
||||
357
drivers/amlogic/memory_ext/ram_dump.c
Normal file
357
drivers/amlogic/memory_ext/ram_dump.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* drivers/amlogic/memory_ext/ram_dump.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/amlogic/ramdump.h>
|
||||
#include <linux/amlogic/reboot.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
static unsigned long ramdump_base __initdata;
|
||||
static unsigned long ramdump_size __initdata;
|
||||
static bool ramdump_disable __initdata;
|
||||
|
||||
struct ramdump {
|
||||
void __iomem *mem_base;
|
||||
unsigned long mem_size;
|
||||
struct mutex lock;
|
||||
struct kobject *kobj;
|
||||
struct work_struct clear_work;
|
||||
int disable;
|
||||
};
|
||||
|
||||
static struct ramdump *ram;
|
||||
|
||||
static void meson_set_reboot_reason(int reboot_reason)
|
||||
{
|
||||
struct arm_smccc_res smccc;
|
||||
|
||||
arm_smccc_smc(SET_REBOOT_REASON,
|
||||
reboot_reason, 0, 0, 0, 0, 0, 0, &smccc);
|
||||
return;
|
||||
}
|
||||
|
||||
static int __init early_ramdump_para(char *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
pr_info("%s:%s\n", __func__, buf);
|
||||
if (strcmp(buf, "disabled") == 0) {
|
||||
ramdump_disable = 1;
|
||||
} else {
|
||||
ret = sscanf(buf, "%lx,%lx", &ramdump_base, &ramdump_size);
|
||||
if (ret != 2) {
|
||||
pr_err("invalid boot args\n");
|
||||
ramdump_disable = 1;
|
||||
}
|
||||
pr_info("%s, base:%lx, size:%lx\n",
|
||||
__func__, ramdump_base, ramdump_size);
|
||||
ret = memblock_reserve(ramdump_base, PAGE_ALIGN(ramdump_size));
|
||||
if (ret < 0) {
|
||||
pr_info("reserve memblock %lx - %lx failed\n",
|
||||
ramdump_base,
|
||||
ramdump_base + PAGE_ALIGN(ramdump_size));
|
||||
ramdump_disable = 1;
|
||||
}
|
||||
}
|
||||
if (ramdump_disable)
|
||||
meson_set_reboot_reason(MESON_NORMAL_BOOT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("ramdump", early_ramdump_para);
|
||||
|
||||
static ssize_t ramdump_bin_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
void *p = NULL;
|
||||
|
||||
if (!ram->mem_base || off >= ram->mem_size)
|
||||
return 0;
|
||||
|
||||
if (off + count > ram->mem_size)
|
||||
count = ram->mem_size - off;
|
||||
|
||||
p = ram->mem_base + off;
|
||||
mutex_lock(&ram->lock);
|
||||
memcpy(buf, p, count);
|
||||
mutex_unlock(&ram->lock);
|
||||
|
||||
/* debug when read end */
|
||||
if (off + count >= ram->mem_size)
|
||||
pr_info("%s, p=%p %p, off:%lli, c:%zi\n",
|
||||
__func__, buf, p, off, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int ramdump_disabled(void)
|
||||
{
|
||||
if (ram)
|
||||
return ram->disable;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ramdump_disabled);
|
||||
|
||||
static ssize_t ramdump_bin_write(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
if (ram->mem_base && !strncmp("reboot", buf, 6))
|
||||
kernel_restart("RAM-DUMP finished\n");
|
||||
|
||||
if (!strncmp("disable", buf, 7)) {
|
||||
ram->disable = 1;
|
||||
meson_set_reboot_reason(MESON_NORMAL_BOOT);
|
||||
}
|
||||
if (!strncmp("enable", buf, 6)) {
|
||||
ram->disable = 0;
|
||||
meson_set_reboot_reason(MESON_KERNEL_PANIC);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute ramdump_attr = {
|
||||
.attr = {
|
||||
.name = "compmsg",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
.read = ramdump_bin_read,
|
||||
.write = ramdump_bin_write,
|
||||
};
|
||||
|
||||
/*
|
||||
* clear memory to avoid large amount of memory not used.
|
||||
* for ramdom data, it's hard to compress
|
||||
*/
|
||||
static void lazy_clear_work(struct work_struct *work)
|
||||
{
|
||||
struct page *page;
|
||||
struct list_head head, *pos, *next;
|
||||
void *virt;
|
||||
int order;
|
||||
gfp_t flags = __GFP_NORETRY |
|
||||
__GFP_NOWARN |
|
||||
__GFP_MOVABLE;
|
||||
unsigned long clear = 0, size = 0, free = 0, tick;
|
||||
|
||||
INIT_LIST_HEAD(&head);
|
||||
order = MAX_ORDER - 1;
|
||||
tick = sched_clock();
|
||||
do {
|
||||
page = alloc_pages(flags, order);
|
||||
if (page) {
|
||||
list_add(&page->lru, &head);
|
||||
virt = page_address(page);
|
||||
size = (1 << order) * PAGE_SIZE;
|
||||
memset(virt, 0, size);
|
||||
clear += size;
|
||||
}
|
||||
} while (page);
|
||||
tick = sched_clock() - tick;
|
||||
|
||||
list_for_each_safe(pos, next, &head) {
|
||||
page = list_entry(pos, struct page, lru);
|
||||
list_del(&page->lru);
|
||||
__free_pages(page, order);
|
||||
free += size;
|
||||
}
|
||||
pr_info("clear:%lx, free:%lx, tick:%ld us\n",
|
||||
clear, free, tick / 1000);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
void ramdump_sync_data(void)
|
||||
{
|
||||
/*
|
||||
* back port from old kernel verion for function
|
||||
* flush_cache_all(), we need it for ram dump
|
||||
*/
|
||||
asm volatile (
|
||||
"mov x12, x30 \n"
|
||||
"dsb sy \n"
|
||||
"mrs x0, clidr_el1 \n"
|
||||
"and x3, x0, #0x7000000 \n"
|
||||
"lsr x3, x3, #23 \n"
|
||||
"cbz x3, finished \n"
|
||||
"mov x10, #0 \n"
|
||||
"loop1: \n"
|
||||
"add x2, x10, x10, lsr #1 \n"
|
||||
"lsr x1, x0, x2 \n"
|
||||
"and x1, x1, #7 \n"
|
||||
"cmp x1, #2 \n"
|
||||
"b.lt skip \n"
|
||||
"mrs x9, daif \n"
|
||||
"msr daifset, #2 \n"
|
||||
"msr csselr_el1, x10 \n"
|
||||
"isb \n"
|
||||
"mrs x1, ccsidr_el1 \n"
|
||||
"msr daif, x9 \n"
|
||||
"and x2, x1, #7 \n"
|
||||
"add x2, x2, #4 \n"
|
||||
"mov x4, #0x3ff \n"
|
||||
"and x4, x4, x1, lsr #3 \n"
|
||||
"clz w5, w4 \n"
|
||||
"mov x7, #0x7fff \n"
|
||||
"and x7, x7, x1, lsr #13 \n"
|
||||
"loop2: \n"
|
||||
"mov x9, x4 \n"
|
||||
"loop3: \n"
|
||||
"lsl x6, x9, x5 \n"
|
||||
"orr x11, x10, x6 \n"
|
||||
"lsl x6, x7, x2 \n"
|
||||
"orr x11, x11, x6 \n"
|
||||
"dc cisw, x11 \n"
|
||||
"subs x9, x9, #1 \n"
|
||||
"b.ge loop3 \n"
|
||||
"subs x7, x7, #1 \n"
|
||||
"b.ge loop2 \n"
|
||||
"skip: \n"
|
||||
"add x10, x10, #2 \n"
|
||||
"cmp x3, x10 \n"
|
||||
"b.gt loop1 \n"
|
||||
"finished: \n"
|
||||
"mov x10, #0 \n"
|
||||
"msr csselr_el1, x10 \n"
|
||||
"dsb sy \n"
|
||||
"isb \n"
|
||||
"mov x0, #0 \n"
|
||||
"ic ialluis \n"
|
||||
"ret x12 \n"
|
||||
);
|
||||
}
|
||||
#else
|
||||
void ramdump_sync_data(void)
|
||||
{
|
||||
flush_cache_all();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ramdump_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *p;
|
||||
|
||||
ram = kzalloc(sizeof(struct ramdump), GFP_KERNEL);
|
||||
if (!ram)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ramdump_disable)
|
||||
ram->disable = 1;
|
||||
|
||||
if (!ramdump_base || !ramdump_size) {
|
||||
pr_info("NO valid ramdump args:%lx %lx\n",
|
||||
ramdump_base, ramdump_size);
|
||||
} else {
|
||||
p = ioremap_cache(ramdump_base, ramdump_size);
|
||||
ram->mem_base = p;
|
||||
ram->mem_size = ramdump_size;
|
||||
pr_info("%s, mem_base:%p, %lx, size:%lx\n",
|
||||
__func__, p, ramdump_base, ramdump_size);
|
||||
}
|
||||
ram->kobj = kobject_create_and_add("mdump", kernel_kobj);
|
||||
if (!ram->kobj) {
|
||||
pr_err("%s, create sysfs failed\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
ramdump_attr.size = ram->mem_size;
|
||||
if (sysfs_create_bin_file(ram->kobj, &ramdump_attr)) {
|
||||
pr_err("%s, create sysfs1 failed\n", __func__);
|
||||
goto err1;
|
||||
}
|
||||
mutex_init(&ram->lock);
|
||||
if (!ram->disable && !ram->mem_size) {
|
||||
INIT_WORK(&ram->clear_work, lazy_clear_work);
|
||||
schedule_work(&ram->clear_work);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
kobject_put(ram->kobj);
|
||||
err:
|
||||
if (ram->mem_base)
|
||||
iounmap(ram->mem_base);
|
||||
kfree(ram);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ramdump_remove(struct platform_device *pdev)
|
||||
{
|
||||
sysfs_remove_bin_file(ram->kobj, &ramdump_attr);
|
||||
iounmap(ram->mem_base);
|
||||
kobject_put(ram->kobj);
|
||||
kfree(ram);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ramdump_dt_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, ram_dump",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver ramdump_driver = {
|
||||
.driver = {
|
||||
.name = "mdump",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = ramdump_dt_match,
|
||||
#endif
|
||||
},
|
||||
.probe = ramdump_probe,
|
||||
.remove = ramdump_remove,
|
||||
};
|
||||
|
||||
static int __init ramdump_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&ramdump_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ramdump_uninit(void)
|
||||
{
|
||||
platform_driver_unregister(&ramdump_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(ramdump_init);
|
||||
module_exit(ramdump_uninit);
|
||||
MODULE_DESCRIPTION("AMLOGIC ramdump driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -33,6 +33,10 @@
|
||||
#include <asm/compiler.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#ifdef CONFIG_AMLOGIC_RAMDUMP
|
||||
#include <linux/amlogic/ramdump.h>
|
||||
#define RAMDUMP_REPLACE_MSG "ramdump disabled, replase panic to normal\n"
|
||||
#endif /* CONFIG_AMLOGIC_RAMDUMP */
|
||||
|
||||
static u32 psci_function_id_restart;
|
||||
static u32 psci_function_id_poweroff;
|
||||
@@ -59,8 +63,17 @@ static u32 parse_reason(const char *cmd)
|
||||
reboot_reason = MESON_UBOOT_SUSPEND;
|
||||
} else {
|
||||
if (kernel_panic) {
|
||||
if (strcmp(kernel_panic, "kernel_panic") == 0)
|
||||
if (strcmp(kernel_panic, "kernel_panic") == 0) {
|
||||
#ifdef CONFIG_AMLOGIC_RAMDUMP
|
||||
if (ramdump_disabled()) {
|
||||
reboot_reason = MESON_NORMAL_BOOT;
|
||||
pr_info(RAMDUMP_REPLACE_MSG);
|
||||
} else
|
||||
reboot_reason = MESON_KERNEL_PANIC;
|
||||
#else
|
||||
reboot_reason = MESON_KERNEL_PANIC;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
26
include/linux/amlogic/ramdump.h
Normal file
26
include/linux/amlogic/ramdump.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* include/linux/amlogic/ramdump.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RAMDUMP_H__
|
||||
#define __RAMDUMP_H__
|
||||
|
||||
#define SET_REBOOT_REASON 0x82000049
|
||||
|
||||
extern int ramdump_disabled(void);
|
||||
extern void ramdump_sync_data(void);
|
||||
|
||||
#endif /* __RAMDUMP_H__ */
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/bug.h>
|
||||
#ifdef CONFIG_AMLOGIC_RAMDUMP
|
||||
#include <linux/amlogic/ramdump.h>
|
||||
#endif
|
||||
|
||||
#define PANIC_TIMER_STEP 100
|
||||
#define PANIC_BLINK_SPD 18
|
||||
@@ -179,6 +182,9 @@ void panic(const char *fmt, ...)
|
||||
dump_stack();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_RAMDUMP
|
||||
ramdump_sync_data();
|
||||
#endif
|
||||
/*
|
||||
* If we have crashed and we have a crash kernel loaded let it handle
|
||||
* everything else.
|
||||
|
||||
Reference in New Issue
Block a user