Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / fs / aufs / wkq.c
1 /*
2  * Copyright (C) 2005-2013 Junjiro R. Okajima
3  *
4  * This program, aufs is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 /*
20  * workqueue for asynchronous/super-io operations
21  * todo: try new dredential scheme
22  */
23
24 #include <linux/module.h>
25 #include "aufs.h"
26
27 /* internal workqueue named AUFS_WKQ_NAME */
28
29 static struct workqueue_struct *au_wkq;
30
31 struct au_wkinfo {
32         struct work_struct wk;
33         struct kobject *kobj;
34
35         unsigned int flags; /* see wkq.h */
36
37         au_wkq_func_t func;
38         void *args;
39
40         struct completion *comp;
41 };
42
43 /* ---------------------------------------------------------------------- */
44
45 static void wkq_func(struct work_struct *wk)
46 {
47         struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
48
49         AuDebugOn(current_fsuid());
50         AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY);
51
52         wkinfo->func(wkinfo->args);
53         if (au_ftest_wkq(wkinfo->flags, WAIT))
54                 complete(wkinfo->comp);
55         else {
56                 kobject_put(wkinfo->kobj);
57                 module_put(THIS_MODULE); /* todo: ?? */
58                 kfree(wkinfo);
59         }
60 }
61
62 /*
63  * Since struct completion is large, try allocating it dynamically.
64  */
65 #if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */
66 #define AuWkqCompDeclare(name)  struct completion *comp = NULL
67
68 static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)
69 {
70         *comp = kmalloc(sizeof(**comp), GFP_NOFS);
71         if (*comp) {
72                 init_completion(*comp);
73                 wkinfo->comp = *comp;
74                 return 0;
75         }
76         return -ENOMEM;
77 }
78
79 static void au_wkq_comp_free(struct completion *comp)
80 {
81         kfree(comp);
82 }
83
84 #else
85
86 /* no braces */
87 #define AuWkqCompDeclare(name) \
88         DECLARE_COMPLETION_ONSTACK(_ ## name); \
89         struct completion *comp = &_ ## name
90
91 static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)
92 {
93         wkinfo->comp = *comp;
94         return 0;
95 }
96
97 static void au_wkq_comp_free(struct completion *comp __maybe_unused)
98 {
99         /* empty */
100 }
101 #endif /* 4KSTACKS */
102
103 static void au_wkq_run(struct au_wkinfo *wkinfo)
104 {
105         if (au_ftest_wkq(wkinfo->flags, NEST)) {
106                 if (au_wkq_test()) {
107                         AuWarn1("wkq from wkq, due to a dead dir by UDBA?\n");
108                         AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT));
109                 }
110         } else
111                 au_dbg_verify_kthread();
112
113         if (au_ftest_wkq(wkinfo->flags, WAIT)) {
114                 INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func);
115                 queue_work(au_wkq, &wkinfo->wk);
116         } else {
117                 INIT_WORK(&wkinfo->wk, wkq_func);
118                 schedule_work(&wkinfo->wk);
119         }
120 }
121
122 /*
123  * Be careful. It is easy to make deadlock happen.
124  * processA: lock, wkq and wait
125  * processB: wkq and wait, lock in wkq
126  * --> deadlock
127  */
128 int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args)
129 {
130         int err;
131         AuWkqCompDeclare(comp);
132         struct au_wkinfo wkinfo = {
133                 .flags  = flags,
134                 .func   = func,
135                 .args   = args
136         };
137
138         err = au_wkq_comp_alloc(&wkinfo, &comp);
139         if (!err) {
140                 au_wkq_run(&wkinfo);
141                 /* no timeout, no interrupt */
142                 wait_for_completion(wkinfo.comp);
143                 au_wkq_comp_free(comp);
144                 destroy_work_on_stack(&wkinfo.wk);
145         }
146
147         return err;
148
149 }
150
151 /*
152  * Note: dget/dput() in func for aufs dentries are not supported. It will be a
153  * problem in a concurrent umounting.
154  */
155 int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb,
156                   unsigned int flags)
157 {
158         int err;
159         struct au_wkinfo *wkinfo;
160
161         atomic_inc(&au_sbi(sb)->si_nowait.nw_len);
162
163         /*
164          * wkq_func() must free this wkinfo.
165          * it highly depends upon the implementation of workqueue.
166          */
167         err = 0;
168         wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS);
169         if (wkinfo) {
170                 wkinfo->kobj = &au_sbi(sb)->si_kobj;
171                 wkinfo->flags = flags & ~AuWkq_WAIT;
172                 wkinfo->func = func;
173                 wkinfo->args = args;
174                 wkinfo->comp = NULL;
175                 kobject_get(wkinfo->kobj);
176                 __module_get(THIS_MODULE); /* todo: ?? */
177
178                 au_wkq_run(wkinfo);
179         } else {
180                 err = -ENOMEM;
181                 au_nwt_done(&au_sbi(sb)->si_nowait);
182         }
183
184         return err;
185 }
186
187 /* ---------------------------------------------------------------------- */
188
189 void au_nwt_init(struct au_nowait_tasks *nwt)
190 {
191         atomic_set(&nwt->nw_len, 0);
192         /* smp_mb(); */ /* atomic_set */
193         init_waitqueue_head(&nwt->nw_wq);
194 }
195
196 void au_wkq_fin(void)
197 {
198         destroy_workqueue(au_wkq);
199 }
200
201 int __init au_wkq_init(void)
202 {
203         int err;
204
205         err = 0;
206         BUILD_BUG_ON(!WQ_RESCUER);
207         au_wkq = alloc_workqueue(AUFS_WKQ_NAME, !WQ_RESCUER, WQ_DFL_ACTIVE);
208         if (IS_ERR(au_wkq))
209                 err = PTR_ERR(au_wkq);
210         else if (!au_wkq)
211                 err = -ENOMEM;
212
213         return err;
214 }