| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * JFFS2 -- Journalling Flash File System, Version 2. | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2001-2003 Red Hat, Inc. | 
|  | 5 | * | 
|  | 6 | * Created by David Woodhouse <dwmw2@infradead.org> | 
|  | 7 | * | 
|  | 8 | * For licensing information, see the file 'LICENCE' in this directory. | 
|  | 9 | * | 
|  | 10 | * $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $ | 
|  | 11 | * | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/kernel.h> | 
|  | 15 | #include <linux/jffs2.h> | 
|  | 16 | #include <linux/mtd/mtd.h> | 
|  | 17 | #include <linux/completion.h> | 
|  | 18 | #include "nodelist.h" | 
|  | 19 |  | 
|  | 20 |  | 
|  | 21 | static int jffs2_garbage_collect_thread(void *); | 
|  | 22 |  | 
|  | 23 | void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) | 
|  | 24 | { | 
|  | 25 | spin_lock(&c->erase_completion_lock); | 
|  | 26 | if (c->gc_task && jffs2_thread_should_wake(c)) | 
|  | 27 | send_sig(SIGHUP, c->gc_task, 1); | 
|  | 28 | spin_unlock(&c->erase_completion_lock); | 
|  | 29 | } | 
|  | 30 |  | 
|  | 31 | /* This must only ever be called when no GC thread is currently running */ | 
|  | 32 | int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) | 
|  | 33 | { | 
|  | 34 | pid_t pid; | 
|  | 35 | int ret = 0; | 
|  | 36 |  | 
|  | 37 | if (c->gc_task) | 
|  | 38 | BUG(); | 
|  | 39 |  | 
|  | 40 | init_MUTEX_LOCKED(&c->gc_thread_start); | 
|  | 41 | init_completion(&c->gc_thread_exit); | 
|  | 42 |  | 
|  | 43 | pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); | 
|  | 44 | if (pid < 0) { | 
|  | 45 | printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); | 
|  | 46 | complete(&c->gc_thread_exit); | 
|  | 47 | ret = pid; | 
|  | 48 | } else { | 
|  | 49 | /* Wait for it... */ | 
|  | 50 | D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); | 
|  | 51 | down(&c->gc_thread_start); | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | return ret; | 
|  | 55 | } | 
|  | 56 |  | 
|  | 57 | void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) | 
|  | 58 | { | 
|  | 59 | spin_lock(&c->erase_completion_lock); | 
|  | 60 | if (c->gc_task) { | 
|  | 61 | D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); | 
|  | 62 | send_sig(SIGKILL, c->gc_task, 1); | 
|  | 63 | } | 
|  | 64 | spin_unlock(&c->erase_completion_lock); | 
|  | 65 | wait_for_completion(&c->gc_thread_exit); | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | static int jffs2_garbage_collect_thread(void *_c) | 
|  | 69 | { | 
|  | 70 | struct jffs2_sb_info *c = _c; | 
|  | 71 |  | 
|  | 72 | daemonize("jffs2_gcd_mtd%d", c->mtd->index); | 
|  | 73 | allow_signal(SIGKILL); | 
|  | 74 | allow_signal(SIGSTOP); | 
|  | 75 | allow_signal(SIGCONT); | 
|  | 76 |  | 
|  | 77 | c->gc_task = current; | 
|  | 78 | up(&c->gc_thread_start); | 
|  | 79 |  | 
|  | 80 | set_user_nice(current, 10); | 
|  | 81 |  | 
|  | 82 | for (;;) { | 
|  | 83 | allow_signal(SIGHUP); | 
|  | 84 |  | 
|  | 85 | if (!jffs2_thread_should_wake(c)) { | 
|  | 86 | set_current_state (TASK_INTERRUPTIBLE); | 
|  | 87 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); | 
|  | 88 | /* Yes, there's a race here; we checked jffs2_thread_should_wake() | 
|  | 89 | before setting current->state to TASK_INTERRUPTIBLE. But it doesn't | 
|  | 90 | matter - We don't care if we miss a wakeup, because the GC thread | 
|  | 91 | is only an optimisation anyway. */ | 
|  | 92 | schedule(); | 
|  | 93 | } | 
|  | 94 |  | 
| Andrew Morton | 34f18a9 | 2005-06-26 03:27:20 -0700 | [diff] [blame] | 95 | if (try_to_freeze()) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | continue; | 
|  | 97 |  | 
|  | 98 | cond_resched(); | 
|  | 99 |  | 
|  | 100 | /* Put_super will send a SIGKILL and then wait on the sem. | 
|  | 101 | */ | 
|  | 102 | while (signal_pending(current)) { | 
|  | 103 | siginfo_t info; | 
|  | 104 | unsigned long signr; | 
|  | 105 |  | 
|  | 106 | signr = dequeue_signal_lock(current, ¤t->blocked, &info); | 
|  | 107 |  | 
|  | 108 | switch(signr) { | 
|  | 109 | case SIGSTOP: | 
|  | 110 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); | 
|  | 111 | set_current_state(TASK_STOPPED); | 
|  | 112 | schedule(); | 
|  | 113 | break; | 
|  | 114 |  | 
|  | 115 | case SIGKILL: | 
|  | 116 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); | 
|  | 117 | goto die; | 
|  | 118 |  | 
|  | 119 | case SIGHUP: | 
|  | 120 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); | 
|  | 121 | break; | 
|  | 122 | default: | 
|  | 123 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); | 
|  | 124 | } | 
|  | 125 | } | 
|  | 126 | /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ | 
|  | 127 | disallow_signal(SIGHUP); | 
|  | 128 |  | 
|  | 129 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); | 
|  | 130 | if (jffs2_garbage_collect_pass(c) == -ENOSPC) { | 
|  | 131 | printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); | 
|  | 132 | goto die; | 
|  | 133 | } | 
|  | 134 | } | 
|  | 135 | die: | 
|  | 136 | spin_lock(&c->erase_completion_lock); | 
|  | 137 | c->gc_task = NULL; | 
|  | 138 | spin_unlock(&c->erase_completion_lock); | 
|  | 139 | complete_and_exit(&c->gc_thread_exit, 0); | 
|  | 140 | } |