tasking: prevent many page faults
This commit is contained in:
@@ -201,6 +201,9 @@ int vfs_read_file_by_path(const char* path, uint8_t** buffer)
|
|||||||
err = follow_path(path, file, dir);
|
err = follow_path(path, file, dir);
|
||||||
if(err != L9660_OK)
|
if(err != L9660_OK)
|
||||||
{
|
{
|
||||||
|
free(file);
|
||||||
|
free(dir);
|
||||||
|
*buffer = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -20,11 +20,21 @@ page_fault_handler:
|
|||||||
; Get the faulting address from CR2
|
; Get the faulting address from CR2
|
||||||
mov eax, cr2
|
mov eax, cr2
|
||||||
|
|
||||||
|
; Stack layout AFTER pushad + 4 pushes (user-mode fault, ring change):
|
||||||
|
; [esp+0..15] gs, fs, es, ds (our pushes, gs is lowest)
|
||||||
|
; [esp+16..47] pushad registers (edi..eax, edi is lowest)
|
||||||
|
; [esp+48] Error Code (pushed by CPU last = lowest CPU-pushed addr)
|
||||||
|
; [esp+52] EIP
|
||||||
|
; [esp+56] CS
|
||||||
|
; [esp+60] EFLAGS
|
||||||
|
; [esp+64] ESP (user, only on privilege change)
|
||||||
|
; [esp+68] SS (user, only on privilege change)
|
||||||
|
mov ebx, [esp + 48] ; Error code is at this offset
|
||||||
|
|
||||||
; Push parameters for the C function:
|
; Push parameters for the C function:
|
||||||
; - Faulting address (from CR2)
|
; - Faulting address (from CR2)
|
||||||
; - Error code (at [esp + 48], after pushed registers and segment selectors)
|
; - Error code (from correct offset)
|
||||||
push eax ; Push CR2 (faulting address)
|
push eax ; Push CR2 (faulting address)
|
||||||
mov ebx, [esp + 48] ; Get error code (adjust offset based on stack layout)
|
|
||||||
push ebx ; Push error code
|
push ebx ; Push error code
|
||||||
|
|
||||||
; Call the C handler
|
; Call the C handler
|
||||||
|
|||||||
+2
-2
@@ -123,9 +123,9 @@ __attribute__((interrupt)) void isr_timer(struct interrupt_frame *frame)
|
|||||||
pic_end_int(0x0);
|
pic_end_int(0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//scheduler_lock();
|
scheduler_lock();
|
||||||
schedule();
|
schedule();
|
||||||
//scheduler_unlock();
|
scheduler_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void isr_custom(struct interrupt_frame *frame)
|
__attribute__((interrupt)) void isr_custom(struct interrupt_frame *frame)
|
||||||
|
|||||||
+6
-2
@@ -136,10 +136,14 @@ void* phys_to_virt2(void* phys)
|
|||||||
uint32_t* create_page_dir()
|
uint32_t* create_page_dir()
|
||||||
{
|
{
|
||||||
void* ppp = alloc_page();
|
void* ppp = alloc_page();
|
||||||
memset(ppp, 0, 0x1000);
|
if (!ppp) {
|
||||||
|
debug_log("create_page_dir: alloc_page returned NULL\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
uint32_t* new_pd = (uint32_t*)phys_to_virt((uint32_t)ppp);
|
||||||
|
memset(new_pd, 0, 0x1000);
|
||||||
|
|
||||||
uint32_t* current_pd = (uint32_t*)0xFFFFF000; // Current PD (self-mapped)
|
uint32_t* current_pd = (uint32_t*)0xFFFFF000; // Current PD (self-mapped)
|
||||||
uint32_t* new_pd = (uint32_t*)phys_to_virt((uint32_t)ppp);
|
|
||||||
|
|
||||||
for(int i = 768; i < 1024; i++)
|
for(int i = 768; i < 1024; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ void exec_from_file(l9660_file* fs_file, l9660_dir* dir)
|
|||||||
//debug_log("PHENTSIZE: %X\n\n", program_header_table_entry_size);
|
//debug_log("PHENTSIZE: %X\n\n", program_header_table_entry_size);
|
||||||
|
|
||||||
uint32_t* proc_pd = create_page_dir();
|
uint32_t* proc_pd = create_page_dir();
|
||||||
|
if (!proc_pd) {
|
||||||
|
printf("exec_from_file: create_page_dir failed\n");
|
||||||
|
free(file);
|
||||||
|
asm volatile("sti");
|
||||||
|
return;
|
||||||
|
}
|
||||||
uint32_t proc_pd_phys = (uint32_t)get_physaddr((void*)proc_pd);
|
uint32_t proc_pd_phys = (uint32_t)get_physaddr((void*)proc_pd);
|
||||||
debug_log("proc_pd_phys: 0x%X\n", proc_pd_phys);
|
debug_log("proc_pd_phys: 0x%X\n", proc_pd_phys);
|
||||||
|
|
||||||
@@ -83,6 +89,14 @@ void exec_from_file(l9660_file* fs_file, l9660_dir* dir)
|
|||||||
{
|
{
|
||||||
uint32_t vaddr = start_page + j * 0x1000;
|
uint32_t vaddr = start_page + j * 0x1000;
|
||||||
void* phys_addr = alloc_page();
|
void* phys_addr = alloc_page();
|
||||||
|
if (!phys_addr) {
|
||||||
|
printf("exec_from_file: alloc_page failed for segment\n");
|
||||||
|
set_page_dir(curr_pd_phys);
|
||||||
|
//TODO: free previously allocated pages
|
||||||
|
free(file);
|
||||||
|
asm volatile("sti");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
debug_log("mapping 0x%X -> 0x%X\n", phys_addr, vaddr);
|
debug_log("mapping 0x%X -> 0x%X\n", phys_addr, vaddr);
|
||||||
map_page(phys_addr, (void*)vaddr, PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
map_page(phys_addr, (void*)vaddr, PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
||||||
@@ -136,6 +150,13 @@ void exec_from_file(l9660_file* fs_file, l9660_dir* dir)
|
|||||||
|
|
||||||
set_page_dir(curr_pd_phys);
|
set_page_dir(curr_pd_phys);
|
||||||
Process* p_task1 = task_create(elf_ehdr->e_entry, USER_STACK_TOP, 3, proc_pd);
|
Process* p_task1 = task_create(elf_ehdr->e_entry, USER_STACK_TOP, 3, proc_pd);
|
||||||
|
if (!p_task1) {
|
||||||
|
printf("exec_from_file: task_create failed\n");
|
||||||
|
set_page_dir(curr_pd_phys);
|
||||||
|
free(file);
|
||||||
|
asm volatile("sti");
|
||||||
|
return;
|
||||||
|
}
|
||||||
p_task1->cwd = dir;
|
p_task1->cwd = dir;
|
||||||
|
|
||||||
uint32_t brk_address = DivRoundUp(bss_end+1, 0x1000)*0x1000;
|
uint32_t brk_address = DivRoundUp(bss_end+1, 0x1000)*0x1000;
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ sys_exit_handler:
|
|||||||
global execve_return
|
global execve_return
|
||||||
|
|
||||||
execve_return:
|
execve_return:
|
||||||
mov esp, [esp+4]
|
; tf is passed as first argument in cdecl at [esp+4]
|
||||||
|
; We need to move ESP to point to the TrapFrame that tf points to.
|
||||||
|
; Save tf pointer in eax first (before changing esp)
|
||||||
|
mov eax, [esp+4] ; eax = tf pointer
|
||||||
|
; Now load ESP with the trapframe
|
||||||
|
mov esp, eax ; esp = tf, now stack is: gs, fs, es, ds, edi, esi...
|
||||||
|
|
||||||
pop gs
|
pop gs
|
||||||
pop fs
|
pop fs
|
||||||
@@ -42,8 +47,6 @@ execve_return:
|
|||||||
pop ds
|
pop ds
|
||||||
popa
|
popa
|
||||||
|
|
||||||
;add esp, 8
|
|
||||||
|
|
||||||
iret
|
iret
|
||||||
|
|
||||||
section .data
|
section .data
|
||||||
|
|||||||
+54
-13
@@ -77,6 +77,7 @@ int sys_fork(TrapFrame *tf)
|
|||||||
uint32_t* new_pd = copy_page_dir(current->pagedir);
|
uint32_t* new_pd = copy_page_dir(current->pagedir);
|
||||||
if (!new_pd) {
|
if (!new_pd) {
|
||||||
debug_log("Failed to copy page directory\n");
|
debug_log("Failed to copy page directory\n");
|
||||||
|
scheduler_unlock();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
debug_log("new_pd: %X\n", new_pd);
|
debug_log("new_pd: %X\n", new_pd);
|
||||||
@@ -84,6 +85,7 @@ int sys_fork(TrapFrame *tf)
|
|||||||
void *p_physical = alloc_page();
|
void *p_physical = alloc_page();
|
||||||
if (!p_physical) {
|
if (!p_physical) {
|
||||||
destroy_page_dir(new_pd);
|
destroy_page_dir(new_pd);
|
||||||
|
scheduler_unlock();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +105,13 @@ int sys_fork(TrapFrame *tf)
|
|||||||
child->ring = current->ring;
|
child->ring = current->ring;
|
||||||
child->pagedir = new_pd;
|
child->pagedir = new_pd;
|
||||||
child->cwd = malloc(sizeof(l9660_dir));
|
child->cwd = malloc(sizeof(l9660_dir));
|
||||||
|
if (!child->cwd) {
|
||||||
|
debug_log("sys_fork: malloc for cwd failed\n");
|
||||||
|
destroy_page_dir(new_pd);
|
||||||
|
free_page(p_physical);
|
||||||
|
scheduler_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
memcpy(child->cwd, current->cwd, sizeof(l9660_dir));
|
memcpy(child->cwd, current->cwd, sizeof(l9660_dir));
|
||||||
|
|
||||||
void *kstack_physical = alloc_page();
|
void *kstack_physical = alloc_page();
|
||||||
@@ -110,6 +119,7 @@ int sys_fork(TrapFrame *tf)
|
|||||||
destroy_page_dir(new_pd);
|
destroy_page_dir(new_pd);
|
||||||
free_page(p_physical);
|
free_page(p_physical);
|
||||||
debug_log("Failed to allocate kernel stack\n");
|
debug_log("Failed to allocate kernel stack\n");
|
||||||
|
scheduler_unlock();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,6 +351,8 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
char **argv = (char**)tf->ecx;
|
char **argv = (char**)tf->ecx;
|
||||||
char **envp = (char**)tf->edx;
|
char **envp = (char**)tf->edx;
|
||||||
|
|
||||||
|
scheduler_lock();
|
||||||
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
while(argv[argc] != NULL) argc++;
|
while(argv[argc] != NULL) argc++;
|
||||||
|
|
||||||
@@ -348,7 +360,10 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
while(envp[envc] != NULL) envc++;
|
while(envp[envc] != NULL) envc++;
|
||||||
|
|
||||||
char** kernel_argv = malloc((argc + 1) * sizeof(char*));
|
char** kernel_argv = malloc((argc + 1) * sizeof(char*));
|
||||||
if (!kernel_argv) return -1;
|
if (!kernel_argv) {
|
||||||
|
scheduler_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for(int i = 0; i < argc; i++) {
|
for(int i = 0; i < argc; i++) {
|
||||||
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
||||||
@@ -358,7 +373,10 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
kernel_argv[argc] = NULL;
|
kernel_argv[argc] = NULL;
|
||||||
|
|
||||||
char** kernel_envp = malloc((envc + 1) * sizeof(char*));
|
char** kernel_envp = malloc((envc + 1) * sizeof(char*));
|
||||||
if (!kernel_envp) return -1; // TODO: cleanup kernel_argv
|
if (!kernel_envp) {
|
||||||
|
scheduler_unlock(); // TODO: cleanup kernel_argv
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for(int i = 0; i < envc; i++) {
|
for(int i = 0; i < envc; i++) {
|
||||||
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
||||||
@@ -367,7 +385,7 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
}
|
}
|
||||||
kernel_envp[envc] = NULL;
|
kernel_envp[envc] = NULL;
|
||||||
|
|
||||||
uint8_t* file_buffer;
|
uint8_t* file_buffer = NULL;
|
||||||
int err = vfs_read_file_by_path(filename, &file_buffer);
|
int err = vfs_read_file_by_path(filename, &file_buffer);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
// TODO: Free kernel_argv/envp
|
// TODO: Free kernel_argv/envp
|
||||||
@@ -377,6 +395,7 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
||||||
free(kernel_envp);
|
free(kernel_envp);
|
||||||
free(file_buffer);
|
free(file_buffer);
|
||||||
|
scheduler_unlock();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,22 +407,23 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
||||||
free(kernel_envp);
|
free(kernel_envp);
|
||||||
free(file_buffer);
|
free(file_buffer);
|
||||||
|
scheduler_unlock();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t* new_page_dir = create_page_dir();
|
uint32_t* new_page_dir = create_page_dir();
|
||||||
if (!new_page_dir) return -1;
|
if (!new_page_dir) {
|
||||||
|
scheduler_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t new_page_dir_phys = (uint32_t)get_physaddr((void*)new_page_dir);
|
uint32_t new_page_dir_phys = (uint32_t)get_physaddr((void*)new_page_dir);
|
||||||
|
if (!new_page_dir_phys) {
|
||||||
void* process_phys = get_physaddr(current);
|
debug_log("execve: get_physaddr(new_page_dir) failed\n");
|
||||||
map_page_in_directory(new_page_dir, process_phys, current, PAGE_PRESENT | PAGE_RW);
|
destroy_page_dir(new_page_dir);
|
||||||
|
scheduler_unlock();
|
||||||
void* kstack_phys = get_physaddr(current->kstack);
|
return -1;
|
||||||
map_page_in_directory(new_page_dir, kstack_phys, current->kstack, PAGE_PRESENT | PAGE_RW);
|
}
|
||||||
|
|
||||||
void* argv_phys = get_physaddr(kernel_argv);
|
|
||||||
map_page_in_directory(new_page_dir, argv_phys, kernel_argv, PAGE_PRESENT | PAGE_RW);
|
|
||||||
|
|
||||||
uint32_t* old_page_dir_to_free = current->pagedir;
|
uint32_t* old_page_dir_to_free = current->pagedir;
|
||||||
set_page_dir(new_page_dir_phys);
|
set_page_dir(new_page_dir_phys);
|
||||||
@@ -420,6 +440,15 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
|
|
||||||
for (uint32_t vaddr = start_page; vaddr <= end_page; vaddr += PAGE_SIZE) {
|
for (uint32_t vaddr = start_page; vaddr <= end_page; vaddr += PAGE_SIZE) {
|
||||||
void* phys_addr = alloc_page();
|
void* phys_addr = alloc_page();
|
||||||
|
if (!phys_addr) {
|
||||||
|
debug_log("execve: alloc_page failed for segment\n");
|
||||||
|
//TODO: cleanup allocated pages and restore old page dir
|
||||||
|
set_page_dir(virt_to_phys(old_page_dir_to_free));
|
||||||
|
current->pagedir = old_page_dir_to_free;
|
||||||
|
destroy_page_dir(new_page_dir);
|
||||||
|
scheduler_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
map_page(phys_addr, (void*)vaddr, PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
map_page(phys_addr, (void*)vaddr, PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +462,16 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
|
|
||||||
for (uint32_t vaddr = USER_STACK_BOTTOM; vaddr <= USER_STACK_TOP; vaddr += PAGE_SIZE) {
|
for (uint32_t vaddr = USER_STACK_BOTTOM; vaddr <= USER_STACK_TOP; vaddr += PAGE_SIZE) {
|
||||||
void* phys_addr = alloc_page();
|
void* phys_addr = alloc_page();
|
||||||
|
if (!phys_addr) {
|
||||||
|
debug_log("execve: alloc_page failed for user stack\n");
|
||||||
|
set_page_dir((uint32_t)old_page_dir_to_free);
|
||||||
|
current->pagedir = old_page_dir_to_free;
|
||||||
|
destroy_page_dir(new_page_dir);
|
||||||
|
scheduler_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
map_page(phys_addr, (void*)(vaddr & ~0xFFF), PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
map_page(phys_addr, (void*)(vaddr & ~0xFFF), PAGE_PRESENT | PAGE_RW | PAGE_USER);
|
||||||
|
memset((void*)(vaddr & ~0xFFF), 0, PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t user_esp = USER_STACK_TOP;
|
uint32_t user_esp = USER_STACK_TOP;
|
||||||
@@ -491,9 +529,12 @@ int sys_execve(TrapFrame *tf) {
|
|||||||
tf->eflags = FL_IF;
|
tf->eflags = FL_IF;
|
||||||
tf->usermode_esp = user_esp;
|
tf->usermode_esp = user_esp;
|
||||||
tf->usermode_ss = SEG_UDATA | DPL_USER;
|
tf->usermode_ss = SEG_UDATA | DPL_USER;
|
||||||
|
tf->esp = (uint32_t)tf + offsetof(TrapFrame, eip);
|
||||||
|
|
||||||
destroy_page_dir(old_page_dir_to_free);
|
destroy_page_dir(old_page_dir_to_free);
|
||||||
|
|
||||||
|
scheduler_unlock();
|
||||||
|
|
||||||
execve_return(tf);
|
execve_return(tf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -21,6 +21,10 @@ extern void switchProcess(Process* next);
|
|||||||
Process* task_create(uint32_t func, uint32_t user_esp, uint32_t ring, uint32_t* pagedir)
|
Process* task_create(uint32_t func, uint32_t user_esp, uint32_t ring, uint32_t* pagedir)
|
||||||
{
|
{
|
||||||
void *p_physical = alloc_page();
|
void *p_physical = alloc_page();
|
||||||
|
if (!p_physical) {
|
||||||
|
debug_log("task_create: alloc_page failed for process\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
Process* p = (Process*)((uint32_t)p_physical+0xC0000000);
|
Process* p = (Process*)((uint32_t)p_physical+0xC0000000);
|
||||||
map_page(p_physical, (void*)p, PAGE_PRESENT | PAGE_RW);
|
map_page(p_physical, (void*)p, PAGE_PRESENT | PAGE_RW);
|
||||||
memset(p, 0, sizeof(Process));
|
memset(p, 0, sizeof(Process));
|
||||||
@@ -30,6 +34,11 @@ Process* task_create(uint32_t func, uint32_t user_esp, uint32_t ring, uint32_t*
|
|||||||
|
|
||||||
// Выделяем стек ядра
|
// Выделяем стек ядра
|
||||||
void *kstack_physical = alloc_page();
|
void *kstack_physical = alloc_page();
|
||||||
|
if (!kstack_physical) {
|
||||||
|
debug_log("task_create: alloc_page failed for kernel stack\n");
|
||||||
|
//TODO: free p_physical and its page table entry
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
p->kstack = (char*)((uint32_t)kstack_physical+0xC0000000);
|
p->kstack = (char*)((uint32_t)kstack_physical+0xC0000000);
|
||||||
map_page(kstack_physical, p->kstack, PAGE_PRESENT | PAGE_RW);
|
map_page(kstack_physical, p->kstack, PAGE_PRESENT | PAGE_RW);
|
||||||
debug_log("kstack: %X!!!!!!!!!!!!\n", p->kstack);
|
debug_log("kstack: %X!!!!!!!!!!!!\n", p->kstack);
|
||||||
@@ -52,6 +61,7 @@ Process* task_create(uint32_t func, uint32_t user_esp, uint32_t ring, uint32_t*
|
|||||||
|
|
||||||
p->tf->eflags = FL_IF;
|
p->tf->eflags = FL_IF;
|
||||||
p->tf->eip = (uint32_t)func;
|
p->tf->eip = (uint32_t)func;
|
||||||
|
p->tf->esp = (uint32_t)p->tf + offsetof(TrapFrame, eip);
|
||||||
|
|
||||||
p->pagedir = pagedir;
|
p->pagedir = pagedir;
|
||||||
|
|
||||||
@@ -196,8 +206,16 @@ void task2()
|
|||||||
void scheduler_init()
|
void scheduler_init()
|
||||||
{
|
{
|
||||||
uint32_t* kernel_tasks_pagedir = (uint32_t*)create_page_dir();
|
uint32_t* kernel_tasks_pagedir = (uint32_t*)create_page_dir();
|
||||||
|
if (!kernel_tasks_pagedir) {
|
||||||
|
printf("scheduler_init: create_page_dir failed\n");
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
|
||||||
task_create((uint32_t)idle, 0, 0, kernel_tasks_pagedir);
|
Process* idle_proc = task_create((uint32_t)idle, 0, 0, kernel_tasks_pagedir);
|
||||||
|
if (!idle_proc) {
|
||||||
|
printf("scheduler_init: task_create failed\n");
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void task_kill(Process* proc) {
|
void task_kill(Process* proc) {
|
||||||
|
|||||||
Reference in New Issue
Block a user